@@ -86,6 +86,7 @@ export namespace Layout {
default: 'bg-blueA3 text-blue10',
primary: 'bg-blueA3 text-blue10',
warning: 'bg-amberA2 text-amber8',
+ destructive: 'bg-red3 text-red10',
},
},
defaultVariants: {
@@ -140,9 +141,7 @@ export namespace Layout {
// Wallet Footer
export function Wallet(props: Wallet.Props) {
- const address =
- props.address ??
- Hooks.usePortoStore(porto, (state) => state.accounts[0]?.address)
+ const address = props.address ?? Hooks.useAccount(porto)?.address
if (!address) return null
return (
diff --git a/app/src/lib/Query.ts b/app/src/lib/Query.ts
index af1c2b29..a9c09498 100644
--- a/app/src/lib/Query.ts
+++ b/app/src/lib/Query.ts
@@ -5,6 +5,7 @@ export const client = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1000 * 60 * 60 * 24, // 24 hours
+ retry: 0,
},
},
})
diff --git a/app/src/routeTree.gen.ts b/app/src/routeTree.gen.ts
index e1c139c7..358b1f2e 100644
--- a/app/src/routeTree.gen.ts
+++ b/app/src/routeTree.gen.ts
@@ -13,10 +13,12 @@
import { Route as rootRoute } from './routes/__root'
import { Route as DialogImport } from './routes/_dialog'
import { Route as IndexImport } from './routes/index'
+import { Route as DialogDialogWalletsendCallsImport } from './routes/_dialog.dialog/wallet_sendCalls'
import { Route as DialogDialogWalletconnectImport } from './routes/_dialog.dialog/wallet_connect'
import { Route as DialogDialogPersonalsignImport } from './routes/_dialog.dialog/personal_sign'
import { Route as DialogDialogExperimentalcreateAccountImport } from './routes/_dialog.dialog/experimental_createAccount'
import { Route as DialogDialogExperimentalauthorizeKeyImport } from './routes/_dialog.dialog/experimental_authorizeKey'
+import { Route as DialogDialogEthsendTransactionImport } from './routes/_dialog.dialog/eth_sendTransaction'
import { Route as DialogDialogEthrequestAccountsImport } from './routes/_dialog.dialog/eth_requestAccounts'
import { Route as DialogDialogSplatImport } from './routes/_dialog.dialog/$'
@@ -33,6 +35,13 @@ const IndexRoute = IndexImport.update({
getParentRoute: () => rootRoute,
} as any)
+const DialogDialogWalletsendCallsRoute =
+ DialogDialogWalletsendCallsImport.update({
+ id: '/dialog/wallet_sendCalls',
+ path: '/dialog/wallet_sendCalls',
+ getParentRoute: () => DialogRoute,
+ } as any)
+
const DialogDialogWalletconnectRoute = DialogDialogWalletconnectImport.update({
id: '/dialog/wallet_connect',
path: '/dialog/wallet_connect',
@@ -59,6 +68,13 @@ const DialogDialogExperimentalauthorizeKeyRoute =
getParentRoute: () => DialogRoute,
} as any)
+const DialogDialogEthsendTransactionRoute =
+ DialogDialogEthsendTransactionImport.update({
+ id: '/dialog/eth_sendTransaction',
+ path: '/dialog/eth_sendTransaction',
+ getParentRoute: () => DialogRoute,
+ } as any)
+
const DialogDialogEthrequestAccountsRoute =
DialogDialogEthrequestAccountsImport.update({
id: '/dialog/eth_requestAccounts',
@@ -104,6 +120,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DialogDialogEthrequestAccountsImport
parentRoute: typeof DialogImport
}
+ '/_dialog/dialog/eth_sendTransaction': {
+ id: '/_dialog/dialog/eth_sendTransaction'
+ path: '/dialog/eth_sendTransaction'
+ fullPath: '/dialog/eth_sendTransaction'
+ preLoaderRoute: typeof DialogDialogEthsendTransactionImport
+ parentRoute: typeof DialogImport
+ }
'/_dialog/dialog/experimental_authorizeKey': {
id: '/_dialog/dialog/experimental_authorizeKey'
path: '/dialog/experimental_authorizeKey'
@@ -132,6 +155,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DialogDialogWalletconnectImport
parentRoute: typeof DialogImport
}
+ '/_dialog/dialog/wallet_sendCalls': {
+ id: '/_dialog/dialog/wallet_sendCalls'
+ path: '/dialog/wallet_sendCalls'
+ fullPath: '/dialog/wallet_sendCalls'
+ preLoaderRoute: typeof DialogDialogWalletsendCallsImport
+ parentRoute: typeof DialogImport
+ }
}
}
@@ -140,21 +170,25 @@ declare module '@tanstack/react-router' {
interface DialogRouteChildren {
DialogDialogSplatRoute: typeof DialogDialogSplatRoute
DialogDialogEthrequestAccountsRoute: typeof DialogDialogEthrequestAccountsRoute
+ DialogDialogEthsendTransactionRoute: typeof DialogDialogEthsendTransactionRoute
DialogDialogExperimentalauthorizeKeyRoute: typeof DialogDialogExperimentalauthorizeKeyRoute
DialogDialogExperimentalcreateAccountRoute: typeof DialogDialogExperimentalcreateAccountRoute
DialogDialogPersonalsignRoute: typeof DialogDialogPersonalsignRoute
DialogDialogWalletconnectRoute: typeof DialogDialogWalletconnectRoute
+ DialogDialogWalletsendCallsRoute: typeof DialogDialogWalletsendCallsRoute
}
const DialogRouteChildren: DialogRouteChildren = {
DialogDialogSplatRoute: DialogDialogSplatRoute,
DialogDialogEthrequestAccountsRoute: DialogDialogEthrequestAccountsRoute,
+ DialogDialogEthsendTransactionRoute: DialogDialogEthsendTransactionRoute,
DialogDialogExperimentalauthorizeKeyRoute:
DialogDialogExperimentalauthorizeKeyRoute,
DialogDialogExperimentalcreateAccountRoute:
DialogDialogExperimentalcreateAccountRoute,
DialogDialogPersonalsignRoute: DialogDialogPersonalsignRoute,
DialogDialogWalletconnectRoute: DialogDialogWalletconnectRoute,
+ DialogDialogWalletsendCallsRoute: DialogDialogWalletsendCallsRoute,
}
const DialogRouteWithChildren =
@@ -165,10 +199,12 @@ export interface FileRoutesByFullPath {
'': typeof DialogRouteWithChildren
'/dialog/$': typeof DialogDialogSplatRoute
'/dialog/eth_requestAccounts': typeof DialogDialogEthrequestAccountsRoute
+ '/dialog/eth_sendTransaction': typeof DialogDialogEthsendTransactionRoute
'/dialog/experimental_authorizeKey': typeof DialogDialogExperimentalauthorizeKeyRoute
'/dialog/experimental_createAccount': typeof DialogDialogExperimentalcreateAccountRoute
'/dialog/personal_sign': typeof DialogDialogPersonalsignRoute
'/dialog/wallet_connect': typeof DialogDialogWalletconnectRoute
+ '/dialog/wallet_sendCalls': typeof DialogDialogWalletsendCallsRoute
}
export interface FileRoutesByTo {
@@ -176,10 +212,12 @@ export interface FileRoutesByTo {
'': typeof DialogRouteWithChildren
'/dialog/$': typeof DialogDialogSplatRoute
'/dialog/eth_requestAccounts': typeof DialogDialogEthrequestAccountsRoute
+ '/dialog/eth_sendTransaction': typeof DialogDialogEthsendTransactionRoute
'/dialog/experimental_authorizeKey': typeof DialogDialogExperimentalauthorizeKeyRoute
'/dialog/experimental_createAccount': typeof DialogDialogExperimentalcreateAccountRoute
'/dialog/personal_sign': typeof DialogDialogPersonalsignRoute
'/dialog/wallet_connect': typeof DialogDialogWalletconnectRoute
+ '/dialog/wallet_sendCalls': typeof DialogDialogWalletsendCallsRoute
}
export interface FileRoutesById {
@@ -188,10 +226,12 @@ export interface FileRoutesById {
'/_dialog': typeof DialogRouteWithChildren
'/_dialog/dialog/$': typeof DialogDialogSplatRoute
'/_dialog/dialog/eth_requestAccounts': typeof DialogDialogEthrequestAccountsRoute
+ '/_dialog/dialog/eth_sendTransaction': typeof DialogDialogEthsendTransactionRoute
'/_dialog/dialog/experimental_authorizeKey': typeof DialogDialogExperimentalauthorizeKeyRoute
'/_dialog/dialog/experimental_createAccount': typeof DialogDialogExperimentalcreateAccountRoute
'/_dialog/dialog/personal_sign': typeof DialogDialogPersonalsignRoute
'/_dialog/dialog/wallet_connect': typeof DialogDialogWalletconnectRoute
+ '/_dialog/dialog/wallet_sendCalls': typeof DialogDialogWalletsendCallsRoute
}
export interface FileRouteTypes {
@@ -201,30 +241,36 @@ export interface FileRouteTypes {
| ''
| '/dialog/$'
| '/dialog/eth_requestAccounts'
+ | '/dialog/eth_sendTransaction'
| '/dialog/experimental_authorizeKey'
| '/dialog/experimental_createAccount'
| '/dialog/personal_sign'
| '/dialog/wallet_connect'
+ | '/dialog/wallet_sendCalls'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
| ''
| '/dialog/$'
| '/dialog/eth_requestAccounts'
+ | '/dialog/eth_sendTransaction'
| '/dialog/experimental_authorizeKey'
| '/dialog/experimental_createAccount'
| '/dialog/personal_sign'
| '/dialog/wallet_connect'
+ | '/dialog/wallet_sendCalls'
id:
| '__root__'
| '/'
| '/_dialog'
| '/_dialog/dialog/$'
| '/_dialog/dialog/eth_requestAccounts'
+ | '/_dialog/dialog/eth_sendTransaction'
| '/_dialog/dialog/experimental_authorizeKey'
| '/_dialog/dialog/experimental_createAccount'
| '/_dialog/dialog/personal_sign'
| '/_dialog/dialog/wallet_connect'
+ | '/_dialog/dialog/wallet_sendCalls'
fileRoutesById: FileRoutesById
}
@@ -260,10 +306,12 @@ export const routeTree = rootRoute
"children": [
"/_dialog/dialog/$",
"/_dialog/dialog/eth_requestAccounts",
+ "/_dialog/dialog/eth_sendTransaction",
"/_dialog/dialog/experimental_authorizeKey",
"/_dialog/dialog/experimental_createAccount",
"/_dialog/dialog/personal_sign",
- "/_dialog/dialog/wallet_connect"
+ "/_dialog/dialog/wallet_connect",
+ "/_dialog/dialog/wallet_sendCalls"
]
},
"/_dialog/dialog/$": {
@@ -274,6 +322,10 @@ export const routeTree = rootRoute
"filePath": "_dialog.dialog/eth_requestAccounts.tsx",
"parent": "/_dialog"
},
+ "/_dialog/dialog/eth_sendTransaction": {
+ "filePath": "_dialog.dialog/eth_sendTransaction.tsx",
+ "parent": "/_dialog"
+ },
"/_dialog/dialog/experimental_authorizeKey": {
"filePath": "_dialog.dialog/experimental_authorizeKey.tsx",
"parent": "/_dialog"
@@ -289,6 +341,10 @@ export const routeTree = rootRoute
"/_dialog/dialog/wallet_connect": {
"filePath": "_dialog.dialog/wallet_connect.tsx",
"parent": "/_dialog"
+ },
+ "/_dialog/dialog/wallet_sendCalls": {
+ "filePath": "_dialog.dialog/wallet_sendCalls.tsx",
+ "parent": "/_dialog"
}
}
}
diff --git a/app/src/routes/_dialog.dialog/-components/TransactionRequest.tsx b/app/src/routes/_dialog.dialog/-components/TransactionRequest.tsx
new file mode 100644
index 00000000..addfed91
--- /dev/null
+++ b/app/src/routes/_dialog.dialog/-components/TransactionRequest.tsx
@@ -0,0 +1,191 @@
+import { useMutation, useQuery } from '@tanstack/react-query'
+import { cx } from 'cva'
+import type { RpcSchema } from 'ox'
+import { Delegation } from 'porto'
+import { Actions, Hooks } from 'porto/remote'
+
+import { Button } from '~/components/Button'
+import { Layout } from '~/components/Layout'
+import { Spinner } from '~/components/Spinner'
+import * as Dialog from '~/lib/Dialog'
+import { porto } from '~/lib/Porto'
+import { ValueFormatter } from '~/utils'
+import ArrowDownLeft from '~icons/lucide/arrow-down-left'
+import ArrowUpRight from '~icons/lucide/arrow-up-right'
+import TriangleAlert from '~icons/lucide/triangle-alert'
+import Star from '~icons/ph/star-four-bold'
+
+export function TransactionRequest(props: TransactionRequest.Props) {
+ const { calls } = props
+
+ const account = Hooks.useAccount(porto)
+ const client = Hooks.useClient(porto)
+ const origin = Dialog.useStore((state) => state.referrer?.origin)
+ const chainId =
+ typeof props.chainId === 'number'
+ ? props.chainId
+ : Hooks.useChain(porto)?.id
+
+ // TODO
+ chainId
+
+ const request = Hooks.useRequest(porto)
+ const respond = useMutation({
+ mutationFn() {
+ return Actions.respond(porto, request!)
+ },
+ })
+
+ const simulate = useQuery({
+ queryKey: ['simulate', client.uid, calls],
+ queryFn: async () => {
+ const { balances, results } = await Delegation.simulate(client, {
+ account: account!.address!,
+ calls: calls as any,
+ })
+ const failure = results.find((x) => x.status === 'failure')
+ if (failure) throw failure.error
+ return { balances, results }
+ },
+ })
+ const balances =
+ simulate.data?.balances.filter((x) => x.value.diff !== 0n) ?? []
+
+ return (
+
+
+ Review the action to perform below.>}
+ variant={simulate.status === 'error' ? 'destructive' : 'default'}
+ />
+
+
+
+ {simulate.status === 'pending' && (
+
+ )}
+
+ {simulate.status === 'success' && (
+
+
+ {balances.map((balance) => {
+ const { token, value } = balance
+ if (value.diff === BigInt(0)) return null
+
+ const { decimals, symbol } = token
+
+ const receiving = value.diff > BigInt(0)
+ const formatted = ValueFormatter.format(value.diff, decimals)
+
+ const Icon = receiving ? ArrowDownLeft : ArrowUpRight
+
+ return (
+
+
+
+
+
+ {receiving ? 'Receive' : 'Send'}{' '}
+
+ {formatted}
+ {' '}
+ {symbol}
+
+
+ )
+ })}
+
+
+ {/*
+
+
*/}
+
+ )}
+
+ {simulate.status === 'error' && (
+
+
Error
+
+ An error occurred while simulating the action. Contact{' '}
+ {origin?.hostname} for more
+ information.
+
+
+ )}
+
+
+
+ {simulate.status === 'success' && (
+
+
+
+
+
+ )}
+
+ {simulate.status === 'error' && (
+
+
+
+ )}
+
+
+
+ )
+}
+
+export namespace TransactionRequest {
+ export type Props = {
+ calls: RpcSchema.ExtractParams<
+ RpcSchema.Wallet,
+ 'wallet_sendCalls'
+ >[0]['calls']
+ chainId?: number | undefined
+ }
+}
diff --git a/app/src/routes/_dialog.dialog/eth_sendTransaction.tsx b/app/src/routes/_dialog.dialog/eth_sendTransaction.tsx
new file mode 100644
index 00000000..88ca8718
--- /dev/null
+++ b/app/src/routes/_dialog.dialog/eth_sendTransaction.tsx
@@ -0,0 +1,26 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Hex, type RpcSchema } from 'ox'
+import type { RpcSchema as porto_RpcSchema } from 'porto'
+
+import { TransactionRequest } from './-components/TransactionRequest'
+
+export const Route = createFileRoute('/_dialog/dialog/eth_sendTransaction')({
+ component: RouteComponent,
+ validateSearch(
+ search,
+ ): RpcSchema.ExtractParams
{
+ return search as never
+ },
+})
+
+function RouteComponent() {
+ const { 0: parameters } = Route.useSearch() ?? {}
+ const { chainId, data, to, value } = parameters
+
+ return (
+
+ )
+}
diff --git a/app/src/routes/_dialog.dialog/wallet_sendCalls.tsx b/app/src/routes/_dialog.dialog/wallet_sendCalls.tsx
new file mode 100644
index 00000000..36582039
--- /dev/null
+++ b/app/src/routes/_dialog.dialog/wallet_sendCalls.tsx
@@ -0,0 +1,26 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Hex, type RpcSchema } from 'ox'
+import type { RpcSchema as porto_RpcSchema } from 'porto'
+
+import { TransactionRequest } from './-components/TransactionRequest'
+
+export const Route = createFileRoute('/_dialog/dialog/wallet_sendCalls')({
+ component: RouteComponent,
+ validateSearch(
+ search,
+ ): RpcSchema.ExtractParams {
+ return search as never
+ },
+})
+
+function RouteComponent() {
+ const { 0: parameters } = Route.useSearch() ?? {}
+ const { calls, chainId } = parameters
+
+ return (
+
+ )
+}
diff --git a/playground/src/App.tsx b/playground/src/App.tsx
index 34119aa4..9e85fbe5 100644
--- a/playground/src/App.tsx
+++ b/playground/src/App.tsx
@@ -473,7 +473,7 @@ function SendCalls() {
},
]
- if (action === 'approve-transfer')
+ if (action === 'transfer')
return [
{
to: ExperimentERC20.address,
@@ -495,6 +495,50 @@ function SendCalls() {
},
] as const
+ if (action === 'mint-transfer')
+ return [
+ {
+ to: '0x390dd40042a844f92b499069cfe983236d9fe204',
+ data: AbiFunction.encodeData(
+ AbiFunction.fromAbi(ExperimentERC20.abi, 'mint'),
+ [account, Value.fromEther('100')],
+ ),
+ },
+ {
+ to: ExperimentERC20.address,
+ data: AbiFunction.encodeData(
+ AbiFunction.fromAbi(ExperimentERC20.abi, 'approve'),
+ [account, Value.fromEther('50')],
+ ),
+ },
+ {
+ to: ExperimentERC20.address,
+ data: AbiFunction.encodeData(
+ AbiFunction.fromAbi(ExperimentERC20.abi, 'transferFrom'),
+ [
+ account,
+ '0x0000000000000000000000000000000000000000',
+ Value.fromEther('50'),
+ ],
+ ),
+ },
+ ] as const
+
+ if (action === 'revert')
+ return [
+ {
+ to: '0x390dd40042a844f92b499069cfe983236d9fe204',
+ data: AbiFunction.encodeData(
+ AbiFunction.fromAbi(ExperimentERC20.abi, 'transferFrom'),
+ [
+ '0x0000000000000000000000000000000000000000',
+ account,
+ Value.fromEther('100'),
+ ],
+ ),
+ },
+ ] as const
+
return [
{
to: '0x0000000000000000000000000000000000000000',
@@ -524,7 +568,9 @@ function SendCalls() {
diff --git a/playground/src/contracts.ts b/playground/src/contracts.ts
index a194c640..3fc6930f 100644
--- a/playground/src/contracts.ts
+++ b/playground/src/contracts.ts
@@ -185,5 +185,5 @@ export const ExperimentERC20 = {
{ type: 'error', name: 'PermitExpired', inputs: [] },
{ type: 'error', name: 'TotalSupplyOverflow', inputs: [] },
],
- address: '0x238c8CD93ee9F8c7Edf395548eF60c0d2e46665E',
+ address: '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c',
} as const
diff --git a/src/core/Delegation.test.ts b/src/core/Delegation.test.ts
index dc586f15..e7e60287 100644
--- a/src/core/Delegation.test.ts
+++ b/src/core/Delegation.test.ts
@@ -855,7 +855,7 @@ describe('simulate', () => {
{
"token": {
"address": "0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c",
- "decimals": 18n,
+ "decimals": 18,
"symbol": "EXP",
},
"value": {
diff --git a/src/core/Delegation.ts b/src/core/Delegation.ts
index 0c4ef3ed..aafddf7b 100644
--- a/src/core/Delegation.ts
+++ b/src/core/Delegation.ts
@@ -672,7 +672,7 @@ export async function simulate<
throw new BaseError('invalid decimals or symbol')
return {
address: erc20Addresses[i - 1]! as Address.Address,
- decimals: decimals_,
+ decimals: Number(decimals_),
symbol: symbol_,
}
})()
diff --git a/src/core/Porto.ts b/src/core/Porto.ts
index 2925554f..6cf68996 100644
--- a/src/core/Porto.ts
+++ b/src/core/Porto.ts
@@ -194,6 +194,8 @@ export type Store<
[['zustand/subscribeWithSelector', never], ['zustand/persist', any]]
>
+const clientCache = new Map()
+
/**
* Extracts a Viem Client from a Porto instance, and an optional chain ID.
* By default, the Client for the current chain ID will be extracted.
@@ -238,7 +240,8 @@ export function getClient<
default_ = transport
}
- return createClient({
+ if (clientCache.has(chain.id)) return clientCache.get(chain.id)!
+ const client = createClient({
chain,
transport: relay
? fallback([
@@ -250,6 +253,8 @@ export function getClient<
: default_,
pollingInterval: 1_000,
})
+ clientCache.set(chain.id, client)
+ return client
}
export type Client = viem_Client<
diff --git a/src/core/RpcSchema.ts b/src/core/RpcSchema.ts
index cd9f3380..4dae1cb2 100644
--- a/src/core/RpcSchema.ts
+++ b/src/core/RpcSchema.ts
@@ -1,9 +1,10 @@
import type * as RpcSchema from 'ox/RpcSchema'
+import type * as Key from './internal/key.js'
import type * as internal from './internal/rpcSchema.js'
export type Schema = RpcSchema.From<
- | RpcSchema.Default
+ | Exclude
| {
Request: {
method: 'porto_ping'
@@ -58,4 +59,25 @@ export type Schema = RpcSchema.From<
}
ReturnType: undefined
}
+ | {
+ Request: {
+ method: 'wallet_sendCalls'
+ params: [
+ Omit<
+ RpcSchema.ExtractParams[0],
+ 'capabilities'
+ > & {
+ capabilities?:
+ | {
+ key?: { publicKey: Key.Rpc['publicKey'] } | undefined
+ }
+ | undefined
+ },
+ ]
+ }
+ ReturnType: RpcSchema.ExtractReturnType<
+ RpcSchema.Wallet,
+ 'wallet_sendCalls'
+ >
+ }
>
diff --git a/src/core/internal/provider.ts b/src/core/internal/provider.ts
index e9d67c72..9821263e 100644
--- a/src/core/internal/provider.ts
+++ b/src/core/internal/provider.ts
@@ -10,7 +10,7 @@ import * as Porto from '../Porto.js'
import type * as Schema from '../RpcSchema.js'
import type * as Call from './call.js'
import * as Key from './key.js'
-import type * as RpcSchema_internal from './rpcSchema.js'
+import type * as Schema_internal from './rpcSchema.js'
export type Provider = ox_Provider.Provider<{
includeEvents: true
@@ -660,7 +660,7 @@ function requireParameter(
}
function assertKeys(
- keys: readonly RpcSchema_internal.AuthorizeKeyParameters[] | undefined,
+ keys: readonly Schema_internal.AuthorizeKeyParameters[] | undefined,
) {
if (!keys) return
for (const key of keys) {
diff --git a/src/remote/Hooks.ts b/src/remote/Hooks.ts
index 68cc78a5..01bfb1bc 100644
--- a/src/remote/Hooks.ts
+++ b/src/remote/Hooks.ts
@@ -2,9 +2,50 @@ import { useStore } from 'zustand'
import { useShallow } from 'zustand/shallow'
import type * as Chains from '../core/Chains.js'
-import type * as Porto from '../core/Porto.js'
+import * as Porto from '../core/Porto.js'
import type * as Remote from './Porto.js'
+/**
+ * Hook to access and subscribe to the current account.
+ *
+ * @param porto - Porto instance.
+ * @returns Account.
+ */
+export function useAccount(porto: Pick, '_internal'>) {
+ return usePortoStore(porto, (x) => x.accounts[0])
+}
+
+/**
+ * Hook to access and subscribe to the current accounts.
+ *
+ * @param porto - Porto instance.
+ * @returns Accounts.
+ */
+export function useAccounts(porto: Pick, '_internal'>) {
+ return usePortoStore(porto, (x) => x.accounts)
+}
+
+/**
+ * Hook to access and subscribe to the current chain.
+ *
+ * @param porto - Porto instance.
+ * @returns Chain.
+ */
+export function useChain(porto: Pick, '_internal'>) {
+ return usePortoStore(porto, (x) => x.chain)
+}
+
+/**
+ * Hook to access and subscribe to the client of the Porto instance.
+ *
+ * @param porto - Porto instance.
+ * @returns Client.
+ */
+export function useClient(porto: Pick, '_internal'>) {
+ const chain = useChain(porto)
+ return Porto.getClient(porto, { chainId: chain.id })
+}
+
/**
* Hook to access and subscribe to the store of the Porto instance.
*
diff --git a/src/remote/Porto.ts b/src/remote/Porto.ts
index b18a0a39..8461b02c 100644
--- a/src/remote/Porto.ts
+++ b/src/remote/Porto.ts
@@ -5,7 +5,7 @@ import type * as Chains from '../core/Chains.js'
import * as Implementation from '../core/Implementation.js'
import * as Messenger from '../core/Messenger.js'
import * as Porto_ from '../core/Porto.js'
-import type { Schema } from '../core/RpcSchema.js'
+import type * as RpcSchema from '../core/RpcSchema.js'
import * as Storage from '../core/Storage.js'
import type { ExactPartial } from '../core/internal/types.js'
@@ -41,7 +41,7 @@ export type State<
export type RemoteState = {
requests: readonly (Porto_.QueuedRequest & {
- request: RpcRequest.RpcRequest
+ request: RpcRequest.RpcRequest
})[]
}