Skip to content
Open
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
9 changes: 6 additions & 3 deletions advanced/wallets/react-wallet-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"@rhinestone/module-sdk": "0.1.25",
"@solana/spl-token": "^0.4.13",
"@solana/web3.js": "1.98.2",
"@stacks/wallet-sdk": "^7.0.6",
"@stacks/transactions": "7.1.0",
"@stacks/wallet-sdk": "7.1.0",
"@taquito/signer": "^15.1.0",
"@taquito/taquito": "^15.1.0",
"@types/semver": "^7.5.8",
Expand Down Expand Up @@ -68,7 +69,8 @@
"tronweb": "^4.4.0",
"valtio": "1.13.2",
"viem": "2.17.8",
"webauthn-p256": "0.0.2"
"webauthn-p256": "0.0.2",
"@walletconnect/utils": "2.21.3"
},
"devDependencies": {
"@types/node": "17.0.35",
Expand All @@ -80,7 +82,8 @@
"eslint-plugin-package-json": "^0.13.1",
"jsonc-eslint-parser": "^2.4.0",
"prettier": "2.6.2",
"typescript": "5.2.2"
"typescript": "5.2.2",
"@walletconnect/types": "2.21.3"
},
"resolutions": {
"@walletconnect/core": "2.21.3",
Expand Down
3 changes: 3 additions & 0 deletions advanced/wallets/react-wallet-v2/src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import SessionSignSuiTransactionModal from '@/views/SessionSignSuiTransactionMod
import SessionSignSuiAndExecuteTransactionModal from '@/views/SessionSignAndExecuteSuiTransactionModal'
import SessionSendStacksTransferModal from '@/views/SessionSendStacksTransferModal'
import SessionSignStacksMessageModal from '@/views/SessionSignStacksMessageModal'
import SessionGetSuiGetAcccountsModal from '@/views/SessionGetSuiGetAcccountsModal'

export default function Modal() {
const { open, view } = useSnapshot(ModalStore.state)
Expand Down Expand Up @@ -88,6 +89,8 @@ export default function Modal() {
return <SessionSignSuiTransactionModal />
case 'SessionSignSuiAndExecuteTransactionModal':
return <SessionSignSuiAndExecuteTransactionModal />
case 'SessionGetSuiAccountsModal':
return <SessionGetSuiGetAcccountsModal />
case 'SessionSendStacksTransferModal':
return <SessionSendStacksTransferModal />
case 'SessionSignStacksMessageModal':
Expand Down
3 changes: 2 additions & 1 deletion advanced/wallets/react-wallet-v2/src/data/SuiData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ export const SUI_MAINNET_CHAINS = { ...SUI_MAINNET } as Record<
export const SUI_SIGNING_METHODS = {
SUI_SIGN_TRANSACTION: 'sui_signTransaction',
SUI_SIGN_AND_EXECUTE_TRANSACTION: 'sui_signAndExecuteTransaction',
SUI_SIGN_PERSONAL_MESSAGE: 'sui_signPersonalMessage'
SUI_SIGN_PERSONAL_MESSAGE: 'sui_signPersonalMessage',
SUI_GET_ACCOUNTS: 'sui_getAccounts'
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ export default function useWalletConnectEventsManager(initialized: boolean) {
requestEvent,
requestSession
})
case SUI_SIGNING_METHODS.SUI_GET_ACCOUNTS:
return ModalStore.open('SessionGetSuiAccountsModal', { requestEvent, requestSession })
case STACKS_SIGNING_METHODS.STACKS_SEND_TRANSFER:
return ModalStore.open('SessionSendStacksTransferModal', { requestEvent, requestSession })
case STACKS_SIGNING_METHODS.STACKS_SIGN_MESSAGE:
Expand Down
60 changes: 38 additions & 22 deletions advanced/wallets/react-wallet-v2/src/lib/StacksLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import {
makeSTXTokenTransfer,
privateKeyToPublic,
signMessageHashRsv,
verifySignature,
signWithKey,
hash160,
publicKeyFromSignatureVrs,
publicKeyFromSignatureRsv
publicKeyFromSignatureRsv,
publicKeyToHex,
} from '@stacks/transactions'
import { networkFromName, StacksNetworks } from '@stacks/network'
import { STACKS_MAINNET, STACKS_TESTNET, STACKS_TESTNET_CAIP2 } from '@/data/StacksData'
Expand Down Expand Up @@ -38,23 +35,35 @@ export interface StacksWallet {
*/
export default class StacksLib implements StacksWallet {
wallet: Wallet
addresses: {
mainnet: string
testnet: string
accounts: {
mainnet: {
address: string
publicKey: string
}
testnet: {
address: string
publicKey: string
}
}
mnemonic: string

constructor(wallet: Wallet, mnemonic: string) {
this.wallet = wallet
this.addresses = {
mainnet: getAddressFromPrivateKey(
wallet.accounts[0].stxPrivateKey,
networkFromName('mainnet')
),
testnet: getAddressFromPrivateKey(
wallet.accounts[0].stxPrivateKey,
networkFromName('testnet')
)
this.accounts = {
mainnet: {
address: getAddressFromPrivateKey(
wallet.accounts[0].stxPrivateKey,
networkFromName('mainnet')
),
publicKey: publicKeyToHex(privateKeyToPublic(wallet.accounts[0].stxPrivateKey))
},
testnet: {
address: getAddressFromPrivateKey(
wallet.accounts[0].stxPrivateKey,
networkFromName('testnet')
),
publicKey: publicKeyToHex(privateKeyToPublic(wallet.accounts[0].stxPrivateKey))
}
}
this.mnemonic = mnemonic
}
Expand All @@ -81,9 +90,9 @@ export default class StacksLib implements StacksWallet {

getAddress(chainId: string) {
if (chainId === STACKS_MAINNET_CAIP2) {
return this.addresses.mainnet
return this.accounts.mainnet.address
} else if (chainId === STACKS_TESTNET_CAIP2) {
return this.addresses.testnet
return this.accounts.testnet.address
}
console.error(
`No stacks address found for chainId: ${chainId}, supported chains: ${STACKS_MAINNET_CAIP2}, ${STACKS_TESTNET_CAIP2}`
Expand All @@ -92,7 +101,14 @@ export default class StacksLib implements StacksWallet {
}

getAddresses() {
return this.addresses
return {
mainnet: this.accounts.mainnet.address,
testnet: this.accounts.testnet.address
}
}

getAccounts() {
return this.accounts
}

async sendTransfer(request: {
Expand Down Expand Up @@ -175,9 +191,9 @@ export default class StacksLib implements StacksWallet {

private getNetworkFromAddress(address: string) {
switch (address) {
case this.addresses.mainnet:
case this.accounts.mainnet.address:
return 'mainnet'
case this.addresses.testnet:
case this.accounts.testnet.address:
return 'testnet'
default:
throw new Error(`Invalid address: ${address}`)
Expand Down
18 changes: 13 additions & 5 deletions advanced/wallets/react-wallet-v2/src/lib/SuiLib.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as bip39 from 'bip39'
import { mnemonicToSeedSync } from 'bip39'
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'
import { Ed25519Keypair, Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519'
import { verifyPersonalMessageSignature } from '@mysten/sui/verify'
import { derivePath } from 'ed25519-hd-key'
import { SerialTransactionExecutor, Transaction } from '@mysten/sui/transactions'
Expand Down Expand Up @@ -32,31 +32,39 @@ const SUI_PATH = "m/44'/784'/0'/0'/0'"
export default class SuiLib {
private keypair: Ed25519Keypair
private mnemonic: string
private address: string
private suiClients: Record<string, SuiClient> = {}
private publicKey: Ed25519PublicKey

constructor(mnemonic?: string) {
this.mnemonic = mnemonic ? mnemonic : bip39.generateMnemonic()
const seed = mnemonicToSeedSync(this.mnemonic)
const { key } = derivePath(SUI_PATH, seed.toString('hex'))

this.keypair = Ed25519Keypair.fromSecretKey(new Uint8Array(key))
this.address = this.keypair.getPublicKey().toSuiAddress()
console.log('Sui Address:', this.address)
this.publicKey = this.keypair.getPublicKey()
console.log('Sui Address:', this.publicKey.toSuiAddress())
}

static async init({ mnemonic }: IInitArguments) {
return new SuiLib(mnemonic)
}

public getPublicKey() {
return this.publicKey.toBase64()
}

public getAddress() {
return this.address
return this.publicKey.toSuiAddress()
}

public getMnemonic() {
return this.mnemonic
}

public getAccounts(): { address: string; pubkey: string }[] {
return [{ address: this.getAddress(), pubkey: this.getPublicKey() }]
}

public async signMessage({ message }: ISignMessageArguments) {
const messageToSign = new TextEncoder().encode(message)

Expand Down
1 change: 1 addition & 0 deletions advanced/wallets/react-wallet-v2/src/store/ModalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ interface State {
| 'SessionSignSuiAndExecuteTransactionModal'
| 'SessionSendStacksTransferModal'
| 'SessionSignStacksMessageModal'
| 'SessionGetSuiAccountsModal'
data?: ModalData
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function createOrRestoreStacksWallet() {
localStorage.setItem('STACKS_MNEMONIC_1', wallet1.getMnemonic())
console.log('STACKS_MNEMONIC_1', wallet1.getMnemonic())
}
console.log('stacks addresses', wallet1.addresses)
console.log('stacks addresses', wallet1.getAddresses())

const addresses = wallet1.getAddresses()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getWallet } from '@/utils/SuiWalletUtil'
import { getSignParamsMessage } from '@/utils/HelperUtil'
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'
import { SignClientTypes } from '@walletconnect/types'
import { getSdkError } from '@walletconnect/utils'
Expand All @@ -17,6 +16,15 @@ export async function approveSuiRequest(requestEvent: RequestEventArgs) {
const wallet = await getWallet()

switch (request.method) {
case SUI_SIGNING_METHODS.SUI_GET_ACCOUNTS:
try {
const result = await wallet.getAccounts()
return formatJsonRpcResult(id, result)
} catch (error: any) {
console.error(error)
alert(error.message)
return formatJsonRpcError(id, error.message)
}
case SUI_SIGNING_METHODS.SUI_SIGN_PERSONAL_MESSAGE:
try {
const message = request.params.message
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { Col, Divider, Row, Text } from '@nextui-org/react'

import RequestDataCard from '@/components/RequestDataCard'
import ModalStore from '@/store/ModalStore'
import { styledToast } from '@/utils/HelperUtil'
import { walletkit } from '@/utils/WalletConnectUtil'
import RequestModal from '../components/RequestModal'
import { useCallback, useState } from 'react'
import { approveSuiRequest, rejectSuiRequest } from '@/utils/SuiRequestHandlerUtil'
import { wallet1 as suiWallet } from '@/utils/SuiWalletUtil'

export default function SessionGetSuiGetAcccountsModal() {
// Get request and wallet data from store
const requestEvent = ModalStore.state.data?.requestEvent
const requestSession = ModalStore.state.data?.requestSession
const [isLoadingApprove, setIsLoadingApprove] = useState(false)
const [isLoadingReject, setIsLoadingReject] = useState(false)

// Ensure request and wallet are defined
if (!requestEvent || !requestSession) {
return <Text>Missing request data</Text>
}

const { topic } = requestEvent
const accounts = suiWallet.getAccounts()

// Handle approve action (logic varies based on request method)
const onApprove = useCallback(async () => {
if (requestEvent) {
const response = await approveSuiRequest(requestEvent)
try {
await walletkit.respondSessionRequest({
topic,
response
})
} catch (e) {
setIsLoadingApprove(false)
styledToast((e as Error).message, 'error')
return
}
setIsLoadingApprove(false)
ModalStore.close()
}
}, [requestEvent, topic])

// Handle reject action
const onReject = useCallback(async () => {
if (requestEvent) {
setIsLoadingReject(true)
const response = rejectSuiRequest(requestEvent)
try {
await walletkit.respondSessionRequest({
topic,
response
})
} catch (e) {
setIsLoadingReject(false)
styledToast((e as Error).message, 'error')
return
}
setIsLoadingReject(false)
ModalStore.close()
}
}, [requestEvent, topic])

if (!accounts || accounts.length === 0) {
onReject()
return <Text>No addresses found</Text>
}

return (
<RequestModal
intention="access your SUI addresses"
metadata={requestSession.peer.metadata}
onApprove={onApprove}
onReject={onReject}
approveLoader={{ active: isLoadingApprove }}
rejectLoader={{ active: isLoadingReject }}
>
<Row>
<Col>
<Text h5>Accounts</Text>
</Col>
</Row>
<Divider y={1} />
<RequestDataCard data={Object.fromEntries(accounts.entries())} />
</RequestModal>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { SUI_CHAINS, SUI_EVENTS, SUI_SIGNING_METHODS } from '@/data/SuiData'
import { suiAddresses } from '@/utils/SuiWalletUtil'
import { STACKS_CHAINS, STACKS_EVENTS, STACKS_SIGNING_METHODS } from '@/data/StacksData'
import { stacksAddresses, stacksWallet } from '@/utils/StacksWalletUtil'
import { getWallet as getSuiWallet } from '@/utils/SuiWalletUtil'
import StacksLib from '@/lib/StacksLib'

const StyledText = styled(Text, {
Expand Down Expand Up @@ -357,7 +358,23 @@ export default function SessionProposalModal() {
)
})
}

if (namespaces.sui) {
const suiWallet = await getSuiWallet()
const accounts = suiWallet.getAccounts()
sessionProperties.sui_getAccounts = JSON.stringify(accounts)
}

if (namespaces.stacks) {
const accounts = stacksWallet.getAccounts()
sessionProperties.stacks_getAddresses = JSON.stringify([
accounts.mainnet,
accounts.testnet
])
}

console.log('sessionProperties', sessionProperties)

await walletkit.approveSession({
id: proposal.id,
namespaces,
Expand Down
Loading