Skip to content
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

fix: react-wallet-v2 fixed for Tezos #694

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
10 changes: 5 additions & 5 deletions advanced/wallets/react-wallet-v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ Your `.env.local` now contains the following environment variables:

## Navigating through example

1. Initial setup and initializations happen in [_app.ts](https://github.com/WalletConnect/web-examples/blob/main/wallets/react-wallet-v2/src/pages/_app.tsx) file
2. WalletConnect client, ethers and cosmos wallets are initialized in [useInitialization.ts ](https://github.com/WalletConnect/web-examples/blob/main/wallets/react-wallet-v2/src/hooks/useInitialization.ts) hook
3. Subscription and handling of WalletConnect events happens in [useWalletConnectEventsManager.ts](https://github.com/WalletConnect/web-examples/blob/main/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts) hook, that opens related [Modal views](https://github.com/WalletConnect/web-examples/tree/main/wallets/react-wallet-v2/src/views) and passes them all necessary data
4. [Modal views](https://github.com/WalletConnect/web-examples/tree/main/wallets/react-wallet-v2/src/views) are responsible for data display and handling approval or rejection actions
5. Upon approval or rejection, modals pass the request data to [RequestHandlerUtil.ts](https://github.com/WalletConnect/web-examples/blob/main/wallets/react-wallet-v2/src/utils/RequestHandlerUtil.ts) that performs all necessary work based on the request method and returns formated json rpc result data that can be then used for WalletConnect client responses
1. Initial setup and initializations happen in [_app.ts](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/pages/_app.tsx) file
2. WalletConnect client, ethers and cosmos wallets are initialized in [useInitialization.ts ](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/hooks/useInitialization.ts) hook
3. Subscription and handling of WalletConnect events happens in [useWalletConnectEventsManager.ts](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts) hook, that opens related [Modal views](https://github.com/WalletConnect/web-examples/tree/main/wallets/react-wallet-v2/src/views) and passes them all necessary data
4. [Modal views](https://github.com/WalletConnect/web-examples/tree/main/advanced/wallets/react-wallet-v2/src/views) are responsible for data display and handling approval or rejection actions
5. Upon approval or rejection, modals pass the request data to [RequestHandlerUtil.ts](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/utils/RequestHandlerUtil.ts) that performs all necessary work based on the request method and returns formated json rpc result data that can be then used for WalletConnect client responses

## Preview of wallet and dapp examples in action

Expand Down
6 changes: 4 additions & 2 deletions advanced/wallets/react-wallet-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"prettier:write": "prettier --write '**/*.{js,ts,jsx,tsx}'"
},
"dependencies": {
"@airgap/beacon-types": "^4.2.2",
"@cosmjs/amino": "0.32.3",
"@cosmjs/encoding": "0.32.3",
"@cosmjs/proto-signing": "0.32.3",
Expand All @@ -30,8 +31,9 @@
"@polkadot/types": "^9.3.3",
"@rhinestone/module-sdk": "0.1.18",
"@solana/web3.js": "1.89.2",
"@taquito/signer": "^15.1.0",
"@taquito/taquito": "^15.1.0",
"@taquito/rpc": "^20.0.1",
"@taquito/signer": "^20.0.1",
"@taquito/taquito": "^20.0.1",
"@types/semver": "^7.5.8",
"@reown/walletkit": "1.0.0",
"@zerodev/ecdsa-validator": "5.3.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function ModalFooter({
auto
flat
style={{ color: 'white', backgroundColor: 'grey' }}
onPress={onReject}
onClick={onReject}
data-testid="session-reject-button"
disabled={disableReject || rejectLoader?.active}
>
Expand All @@ -68,7 +68,7 @@ export default function ModalFooter({
flat
color={approveButtonColor}
disabled={disableApprove || approveLoader?.active}
onPress={onApprove}
onClick={onApprove}
data-testid="session-approve-button"
>
{approveLoader && approveLoader.active ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@ export default function PairingCard({ logo, name, url, topic, onDelete }: IProps
<Text h5 css={{ marginLeft: '$9' }} data-testid={'pairing-text-' + topic}>
{name}
</Text>
<Link href={url} css={{ marginLeft: '$9' }}>
<a data-testid={'pairing-text-' + topic}>
<Link href={url} css={{ marginLeft: '$9' }} data-testid={'pairing-text-' + topic}>
{truncate(url?.split('https://')[1] ?? 'Unknown', 23)}
</a>
</Link>
</div>
<Tooltip content="Delete" placement="left">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default function ProjectInfoCard({ metadata, intention }: IProps) {
<Col>
<Text h3 data-testid="session-info-card-text">
<span>{name}</span> <br />
<Text h4> wants to {intention ? intention : 'connect'}</Text>
<Text> wants to {intention ? intention : 'connect'}</Text>
</Text>
</Col>
</Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ export default function SessionCard({ logo, name, url, topic }: IProps) {
<Text h5 css={{ marginLeft: '$9' }} data-testid={`session-text`}>
{name}
</Text>
<Link href={url} css={{ marginLeft: '$9' }}>
<a data-testid={`session-link`}>
<Link href={url} css={{ marginLeft: '$9' }} data-testid={`session-link`}>
{truncate(url?.split('https://')[1] ?? 'Unknown', 23)}
</a>
</Link>
</div>

Expand Down
11 changes: 6 additions & 5 deletions advanced/wallets/react-wallet-v2/src/data/TezosData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type ChainMetadata = {
rgb: string
rpc: string
namespace: string
api?: string
}

/**
Expand All @@ -21,18 +22,18 @@ export const TEZOS_MAINNET_CHAINS: Record<string, ChainMetadata> = {
name: 'Tezos',
logo: '/chain-logos/tezos.svg',
rgb: '44, 125, 247',
rpc: 'https://mainnet.api.tez.ie',
rpc: 'https://rpc.tzbeta.net',
namespace: 'tezos'
}
}

export const TEZOS_TEST_CHAINS: Record<string, ChainMetadata> = {
'tezos:testnet': {
chainId: 'testnet',
name: 'Tezos Testnet',
'tezos:ghostnet': {
chainId: 'ghostnet',
name: 'Tezos Ghostnet',
logo: '/chain-logos/tezos.svg',
rgb: '44, 125, 247',
rpc: 'https://ghostnet.ecadinfra.com',
rpc: 'https://rpc.ghostnet.teztnets.com',
namespace: 'tezos'
}
}
Expand Down
211 changes: 186 additions & 25 deletions advanced/wallets/react-wallet-v2/src/lib/TezosLib.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { TezosToolkit } from '@taquito/taquito'
import { OpKind, ParamsWithKind, TezosToolkit } from '@taquito/taquito'
import { InMemorySigner } from '@taquito/signer'
import { localForger } from '@taquito/local-forging'

import { Wallet } from 'ethers/'
import { PvmKind, ScriptedContracts } from '@taquito/rpc'
import { PartialTezosOperation, TezosOperationType } from '@airgap/beacon-types'
import { TEZOS_CHAINS } from '@/data/TezosData'

/**
* Constants
Expand All @@ -22,7 +25,7 @@ interface IInitArguments {
* Library
*/
export default class TezosLib {
tezos: TezosToolkit
toolkits: Record<string, TezosToolkit>
signer: InMemorySigner
mnemonic: string
secretKey: string
Expand All @@ -31,15 +34,15 @@ export default class TezosLib {
curve: 'ed25519' | 'secp256k1'

constructor(
tezos: TezosToolkit,
toolkits: Record<string, TezosToolkit>,
mnemonic: string,
signer: InMemorySigner,
secretKey: string,
publicKey: string,
address: string,
curve: 'ed25519' | 'secp256k1'
) {
this.tezos = tezos
this.toolkits = toolkits
this.mnemonic = mnemonic
this.signer = signer
this.secretKey = secretKey
Expand All @@ -55,17 +58,35 @@ export default class TezosLib {
curve: curve ?? DEFAULT_CURVE
}

const Tezos = new TezosToolkit('https://mainnet.api.tez.ie')
dianasavvatina marked this conversation as resolved.
Show resolved Hide resolved

const signer = InMemorySigner.fromMnemonic(params)

Tezos.setSignerProvider(signer)
// we have wallets for multiple networks
// later we will determine the chain from the request
const toolkits: Record<string, TezosToolkit> = {}
for (const chainKey in TEZOS_CHAINS) {
const chain = TEZOS_CHAINS[chainKey]
const toolkit = new TezosToolkit(chain.rpc)

const secretKey = await signer.secretKey()
const publicKey = await signer.publicKey()
const address = await signer.publicKeyHash()
toolkit.setSignerProvider(signer)
toolkits[chainKey] = toolkit
}
const toolkit = toolkits['tezos:mainnet']
const secretKey = await toolkit.signer.secretKey()
const publicKey = await toolkit.signer.publicKey()
const address = await toolkit.signer.publicKeyHash()

return new TezosLib(Tezos, params.mnemonic, signer, secretKey, publicKey, address, params.curve)
if (!secretKey) {
throw new Error('Failed to generate secret key')
}
return new TezosLib(
toolkits,
params.mnemonic,
signer,
secretKey,
publicKey,
address,
params.curve
)
}

public getMnemonic() {
Expand All @@ -84,23 +105,163 @@ export default class TezosLib {
return this.address
}

public async signTransaction(transaction: any) {
const prepared = await this.tezos.prepare.batch(
transaction.map((tx: any) => ({
amount: tx.amount,
to: tx.destination,
kind: tx.kind,
mutez: true
}))
)
public convertToPartialParamsWithKind(op: PartialTezosOperation): ParamsWithKind {
switch (op.kind) {
case TezosOperationType.ACTIVATE_ACCOUNT:
return {
kind: OpKind.ACTIVATION,
pkh: op.pkh,
secret: op.secret
}
case TezosOperationType.DELEGATION:
return {
kind: OpKind.DELEGATION,
source: op.source ?? 'source not provided',
delegate: op.delegate,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined
}
case TezosOperationType.FAILING_NOOP:
return {
kind: OpKind.FAILING_NOOP,
arbitrary: op.arbitrary,
basedOnBlock: 'head'
}
case TezosOperationType.INCREASE_PAID_STORAGE:
return {
kind: OpKind.INCREASE_PAID_STORAGE,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
amount: Number(op.amount),
destination: op.destination
}
case TezosOperationType.ORIGINATION:
let script: ScriptedContracts = op.script as unknown as ScriptedContracts
return {
kind: OpKind.ORIGINATION,
balance: Number(op.balance),
code: script.code,
init: script.storage,
delegate: op.delegate,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined
}
case TezosOperationType.REGISTER_GLOBAL_CONSTANT:
return {
kind: OpKind.REGISTER_GLOBAL_CONSTANT,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
value: op.value
}
case TezosOperationType.SMART_ROLLUP_ADD_MESSAGES:
return {
kind: OpKind.SMART_ROLLUP_ADD_MESSAGES,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
message: op.message
}
case TezosOperationType.SMART_ROLLUP_ORIGINATE:
if (!Object.values(PvmKind).includes(op.pvm_kind)) {
throw new Error(`Invalid PvmKind: ${op.pvm_kind}`)
}
return {
kind: OpKind.SMART_ROLLUP_ORIGINATE,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
pvmKind: op.pvm_kind,
kernel: op.kernel,
parametersType: op.parameters_ty
}
case TezosOperationType.SMART_ROLLUP_EXECUTE_OUTBOX_MESSAGE:
return {
kind: OpKind.SMART_ROLLUP_EXECUTE_OUTBOX_MESSAGE,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
rollup: op.rollup,
cementedCommitment: op.cemented_commitment,
outputProof: op.output_proof
}
case TezosOperationType.TRANSACTION:
return {
kind: OpKind.TRANSACTION,
to: op.destination,
amount: Number(op.amount),
mutez: true,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
parameter: op.parameters
}
case TezosOperationType.TRANSFER_TICKET:
return {
kind: OpKind.TRANSFER_TICKET,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
ticketContents: op.ticket_contents,
ticketTy: op.ticket_ty,
ticketTicketer: op.ticket_ticketer,
ticketAmount: Number(op.ticket_amount),
destination: op.destination,
entrypoint: op.entrypoint
}
case TezosOperationType.UPDATE_CONSENSUS_KEY:
return {
kind: OpKind.UPDATE_CONSENSUS_KEY,
source: op.source,
fee: op.fee ? Number(op.fee) : undefined,
gasLimit: op.gas_limit ? Number(op.gas_limit) : undefined,
storageLimit: op.storage_limit ? Number(op.storage_limit) : undefined,
pk: op.pk
}
default:
throw new Error(`Operation kind cannot be converted to ParamsWithKind: ${op.kind}`)
}
}

public async signTransaction(transaction: any, chainId: string) {
// Map the transactions and prepare the batch
console.log(`Wallet: handling transaction: `, transaction)
const batchTransactions: ParamsWithKind[] = transaction.map((tx: PartialTezosOperation) => {
if (tx.kind === TezosOperationType.DELEGATION && !tx.source) {
tx.source = this.address
}
const op: ParamsWithKind = this.convertToPartialParamsWithKind(tx)
return op
})

const toolkit = this.toolkits[chainId]
if (!toolkit) {
throw new Error(`Toolkit not found for chainId: ${chainId}`)
}

const forged = await localForger.forge(prepared.opOb)
// Prepare the batch
console.log(`Wallet: prepared batchTransactions `, batchTransactions)
const batch = toolkit.contract.batch(batchTransactions)

const tx = await this.signer.sign(forged, new Uint8Array([3]))
// Send the batch and wait for the operation hash
console.log(`Wallet: sending batch `, batch)
const operation = await batch.send()

const hash = await this.tezos.rpc.injectOperation(tx.sbytes)
// Wait for confirmation
await operation.confirmation()

return hash
console.log('Wallet: operation confirmed:', operation)
return operation.hash
}

public async signPayload(payload: any) {
Expand Down
1 change: 0 additions & 1 deletion advanced/wallets/react-wallet-v2/src/utils/HelperUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { TRON_CHAINS, TTronChain } from '@/data/TronData'
import { KADENA_CHAINS, TKadenaChain } from '@/data/KadenaData'

import { utils } from 'ethers'
import { Verify } from '@walletconnect/types'
import bs58 from 'bs58'

/**
Expand Down
Loading