accounts 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/core/AccessKey.d.ts +55 -0
- package/dist/core/AccessKey.d.ts.map +1 -0
- package/dist/core/AccessKey.js +69 -0
- package/dist/core/AccessKey.js.map +1 -0
- package/dist/core/Account.d.ts +91 -0
- package/dist/core/Account.d.ts.map +1 -0
- package/dist/core/Account.js +64 -0
- package/dist/core/Account.js.map +1 -0
- package/dist/core/Adapter.d.ts +187 -0
- package/dist/core/Adapter.d.ts.map +1 -0
- package/dist/core/Adapter.js +7 -0
- package/dist/core/Adapter.js.map +1 -0
- package/dist/core/Ceremony.d.ts +109 -0
- package/dist/core/Ceremony.d.ts.map +1 -0
- package/dist/core/Ceremony.js +104 -0
- package/dist/core/Ceremony.js.map +1 -0
- package/dist/core/Client.d.ts +16 -0
- package/dist/core/Client.d.ts.map +1 -0
- package/dist/core/Client.js +18 -0
- package/dist/core/Client.js.map +1 -0
- package/dist/core/Dialog.d.ts +52 -0
- package/dist/core/Dialog.d.ts.map +1 -0
- package/dist/core/Dialog.js +342 -0
- package/dist/core/Dialog.js.map +1 -0
- package/dist/core/Expiry.d.ts +15 -0
- package/dist/core/Expiry.d.ts.map +1 -0
- package/dist/core/Expiry.js +29 -0
- package/dist/core/Expiry.js.map +1 -0
- package/dist/core/Messenger.d.ts +86 -0
- package/dist/core/Messenger.d.ts.map +1 -0
- package/dist/core/Messenger.js +127 -0
- package/dist/core/Messenger.js.map +1 -0
- package/dist/core/Provider.d.ts +69 -0
- package/dist/core/Provider.d.ts.map +1 -0
- package/dist/core/Provider.js +401 -0
- package/dist/core/Provider.js.map +1 -0
- package/dist/core/Remote.d.ts +114 -0
- package/dist/core/Remote.d.ts.map +1 -0
- package/dist/core/Remote.js +116 -0
- package/dist/core/Remote.js.map +1 -0
- package/dist/core/Schema.d.ts +805 -0
- package/dist/core/Schema.d.ts.map +1 -0
- package/dist/core/Schema.js +43 -0
- package/dist/core/Schema.js.map +1 -0
- package/dist/core/Storage.d.ts +42 -0
- package/dist/core/Storage.d.ts.map +1 -0
- package/dist/core/Storage.js +173 -0
- package/dist/core/Storage.js.map +1 -0
- package/dist/core/Store.d.ts +58 -0
- package/dist/core/Store.d.ts.map +1 -0
- package/dist/core/Store.js +58 -0
- package/dist/core/Store.js.map +1 -0
- package/dist/core/adapters/dangerous_secp256k1.d.ts +30 -0
- package/dist/core/adapters/dangerous_secp256k1.d.ts.map +1 -0
- package/dist/core/adapters/dangerous_secp256k1.js +39 -0
- package/dist/core/adapters/dangerous_secp256k1.js.map +1 -0
- package/dist/core/adapters/dialog.d.ts +31 -0
- package/dist/core/adapters/dialog.d.ts.map +1 -0
- package/dist/core/adapters/dialog.js +306 -0
- package/dist/core/adapters/dialog.js.map +1 -0
- package/dist/core/adapters/local.d.ts +33 -0
- package/dist/core/adapters/local.d.ts.map +1 -0
- package/dist/core/adapters/local.js +227 -0
- package/dist/core/adapters/local.js.map +1 -0
- package/dist/core/adapters/webAuthn.d.ts +36 -0
- package/dist/core/adapters/webAuthn.d.ts.map +1 -0
- package/dist/core/adapters/webAuthn.js +93 -0
- package/dist/core/adapters/webAuthn.js.map +1 -0
- package/dist/core/internal/withDedupe.d.ts +12 -0
- package/dist/core/internal/withDedupe.d.ts.map +1 -0
- package/dist/core/internal/withDedupe.js +12 -0
- package/dist/core/internal/withDedupe.js.map +1 -0
- package/dist/core/zod/request.d.ts +31 -0
- package/dist/core/zod/request.d.ts.map +1 -0
- package/dist/core/zod/request.js +41 -0
- package/dist/core/zod/request.js.map +1 -0
- package/dist/core/zod/rpc.d.ts +603 -0
- package/dist/core/zod/rpc.d.ts.map +1 -0
- package/dist/core/zod/rpc.js +293 -0
- package/dist/core/zod/rpc.js.map +1 -0
- package/dist/core/zod/utils.d.ts +18 -0
- package/dist/core/zod/utils.d.ts.map +1 -0
- package/dist/core/zod/utils.js +21 -0
- package/dist/core/zod/utils.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/types.d.ts +284 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +2 -0
- package/dist/internal/types.js.map +1 -0
- package/dist/server/Handler.d.ts +257 -0
- package/dist/server/Handler.d.ts.map +1 -0
- package/dist/server/Handler.js +433 -0
- package/dist/server/Handler.js.map +1 -0
- package/dist/server/Kv.d.ts +16 -0
- package/dist/server/Kv.d.ts.map +1 -0
- package/dist/server/Kv.js +30 -0
- package/dist/server/Kv.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/internal/requestListener.d.ts +124 -0
- package/dist/server/internal/requestListener.d.ts.map +1 -0
- package/dist/server/internal/requestListener.js +173 -0
- package/dist/server/internal/requestListener.js.map +1 -0
- package/dist/wagmi/Connector.d.ts +93 -0
- package/dist/wagmi/Connector.d.ts.map +1 -0
- package/dist/wagmi/Connector.js +238 -0
- package/dist/wagmi/Connector.js.map +1 -0
- package/dist/wagmi/index.d.ts +3 -0
- package/dist/wagmi/index.d.ts.map +1 -0
- package/dist/wagmi/index.js +3 -0
- package/dist/wagmi/index.js.map +1 -0
- package/package.json +56 -2
- package/src/core/AccessKey.test.ts +257 -0
- package/src/core/AccessKey.ts +123 -0
- package/src/core/Account.test.ts +309 -0
- package/src/core/Account.ts +152 -0
- package/src/core/Adapter.ts +238 -0
- package/src/core/Ceremony.browser.test.ts +239 -0
- package/src/core/Ceremony.test.ts +151 -0
- package/src/core/Ceremony.ts +203 -0
- package/src/core/Client.ts +36 -0
- package/src/core/Dialog.browser.test.ts +309 -0
- package/src/core/Dialog.test-d.ts +19 -0
- package/src/core/Dialog.ts +442 -0
- package/src/core/Expiry.ts +34 -0
- package/src/core/Messenger.ts +206 -0
- package/src/core/Provider.browser.test.ts +774 -0
- package/src/core/Provider.connect.browser.test.ts +415 -0
- package/src/core/Provider.test-d.ts +53 -0
- package/src/core/Provider.test.ts +1566 -0
- package/src/core/Provider.ts +559 -0
- package/src/core/Remote.ts +262 -0
- package/src/core/Schema.test-d.ts +211 -0
- package/src/core/Schema.ts +143 -0
- package/src/core/Storage.ts +213 -0
- package/src/core/Store.test.ts +287 -0
- package/src/core/Store.ts +129 -0
- package/src/core/adapters/dangerous_secp256k1.ts +53 -0
- package/src/core/adapters/dialog.ts +379 -0
- package/src/core/adapters/local.test.ts +97 -0
- package/src/core/adapters/local.ts +277 -0
- package/src/core/adapters/webAuthn.ts +129 -0
- package/src/core/internal/withDedupe.test.ts +116 -0
- package/src/core/internal/withDedupe.ts +20 -0
- package/src/core/mppx.test.ts +83 -0
- package/src/core/zod/request.test.ts +121 -0
- package/src/core/zod/request.ts +70 -0
- package/src/core/zod/rpc.ts +374 -0
- package/src/core/zod/utils.test.ts +69 -0
- package/src/core/zod/utils.ts +40 -0
- package/src/index.ts +14 -0
- package/src/internal/types.ts +378 -0
- package/src/server/Handler.test.ts +1014 -0
- package/src/server/Handler.ts +605 -0
- package/src/server/Kv.ts +46 -0
- package/src/server/index.ts +2 -0
- package/src/server/internal/requestListener.ts +273 -0
- package/src/tsconfig.json +9 -0
- package/src/wagmi/Connector.ts +287 -0
- package/src/wagmi/index.ts +2 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { announceProvider } from 'mipd'
|
|
2
|
+
import { Mppx, tempo as mppx_tempo } from 'mppx/client'
|
|
3
|
+
import { Hash, Hex, Json, Provider as ox_Provider, RpcResponse } from 'ox'
|
|
4
|
+
import type { Chain, Client as ViemClient, Transport } from 'viem'
|
|
5
|
+
import { tempo, tempoModerato } from 'viem/chains'
|
|
6
|
+
import { Actions } from 'viem/tempo'
|
|
7
|
+
import * as z from 'zod/mini'
|
|
8
|
+
|
|
9
|
+
import * as Account from './Account.js'
|
|
10
|
+
import type * as Adapter from './Adapter.js'
|
|
11
|
+
import { dialog } from './adapters/dialog.js'
|
|
12
|
+
import * as Client from './Client.js'
|
|
13
|
+
import { withDedupe } from './internal/withDedupe.js'
|
|
14
|
+
import * as Schema from './Schema.js'
|
|
15
|
+
import * as Storage from './Storage.js'
|
|
16
|
+
import * as Store from './Store.js'
|
|
17
|
+
import * as Request from './zod/request.js'
|
|
18
|
+
import * as Rpc from './zod/rpc.js'
|
|
19
|
+
|
|
20
|
+
export type Provider = ox_Provider.Provider<{ schema: Schema.Ox }> &
|
|
21
|
+
ox_Provider.Emitter & {
|
|
22
|
+
/** Configured chains. */
|
|
23
|
+
chains: readonly [Chain, ...Chain[]]
|
|
24
|
+
/** Returns a viem Account for the given address (or active account). */
|
|
25
|
+
getAccount: Account.Find
|
|
26
|
+
/** Returns a viem Client for the given (or current) chain ID. */
|
|
27
|
+
getClient(options?: {
|
|
28
|
+
chainId?: number | undefined
|
|
29
|
+
feePayer?: string | undefined
|
|
30
|
+
}): ViemClient<Transport, typeof tempo>
|
|
31
|
+
/** Reactive state store. */
|
|
32
|
+
store: Store.Store
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates an EIP-1193 provider with a pluggable adapter.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { Provider } from 'accounts'
|
|
41
|
+
*
|
|
42
|
+
* const provider = Provider.create()
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function create(options: create.Options = {}): create.ReturnType {
|
|
46
|
+
const {
|
|
47
|
+
adapter = dialog(),
|
|
48
|
+
chains = [tempo, tempoModerato],
|
|
49
|
+
feePayerUrl,
|
|
50
|
+
persistCredentials,
|
|
51
|
+
testnet,
|
|
52
|
+
storage = typeof window !== 'undefined' ? Storage.idb() : Storage.memory(),
|
|
53
|
+
} = options
|
|
54
|
+
|
|
55
|
+
const defaultChain = testnet
|
|
56
|
+
? (chains.find((c) => c.testnet) ?? chains[chains.length - 1]!)
|
|
57
|
+
: chains[0]!
|
|
58
|
+
|
|
59
|
+
const store = Store.create({
|
|
60
|
+
chainId: defaultChain.id,
|
|
61
|
+
persistCredentials,
|
|
62
|
+
storage,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const getAccount: Account.Find = (options = {}) => Account.find({ ...options, store }) as never
|
|
66
|
+
|
|
67
|
+
function getClient(
|
|
68
|
+
options: { chainId?: number | undefined; feePayer?: string | undefined } = {},
|
|
69
|
+
) {
|
|
70
|
+
const { chainId, feePayer } = options
|
|
71
|
+
return Client.fromChainId(chainId, { chains, feePayer, store })
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const instance = adapter({ getAccount, getClient, storage, store })
|
|
75
|
+
const { actions } = instance
|
|
76
|
+
|
|
77
|
+
const emitter = ox_Provider.createEmitter()
|
|
78
|
+
|
|
79
|
+
// Emit EIP-1193 events on state changes.
|
|
80
|
+
store.subscribe(
|
|
81
|
+
(state) => state.accounts.map((a) => a.address).join(),
|
|
82
|
+
() =>
|
|
83
|
+
emitter.emit(
|
|
84
|
+
'accountsChanged',
|
|
85
|
+
store.getState().accounts.map((a) => a.address),
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
store.subscribe(
|
|
89
|
+
(state) => state.chainId,
|
|
90
|
+
(chainId) => emitter.emit('chainChanged', Hex.fromNumber(chainId)),
|
|
91
|
+
)
|
|
92
|
+
store.subscribe(
|
|
93
|
+
(state) => state.accounts.length > 0,
|
|
94
|
+
(connected) => {
|
|
95
|
+
if (connected) emitter.emit('connect', { chainId: Hex.fromNumber(store.getState().chainId) })
|
|
96
|
+
else emitter.emit('disconnect', new ox_Provider.DisconnectedError())
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
/** Throws `DisconnectedError` if no accounts are connected. */
|
|
101
|
+
function assertConnected() {
|
|
102
|
+
if (store.getState().accounts.length === 0)
|
|
103
|
+
throw new ox_Provider.DisconnectedError({ message: 'No accounts connected.' })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Returns accounts to persist. When `persistAccounts` is set, merges new accounts with existing ones. */
|
|
107
|
+
function resolveAccounts(accounts: readonly Account.Store[]) {
|
|
108
|
+
if (!instance.persistAccounts) return accounts
|
|
109
|
+
const merged = [...accounts]
|
|
110
|
+
for (const a of store.getState().accounts)
|
|
111
|
+
if (!merged.some((m) => m.address.toLowerCase() === a.address.toLowerCase())) merged.push(a)
|
|
112
|
+
return merged
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Resolves the `feePayer` field from a transaction request into a URL string or `undefined`. */
|
|
116
|
+
function resolveFeePayer(feePayer: string | boolean | undefined): string | undefined {
|
|
117
|
+
if (typeof feePayer === 'string') return feePayer
|
|
118
|
+
if (feePayer === true) return feePayerUrl
|
|
119
|
+
return undefined
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const provider = Object.assign(
|
|
123
|
+
ox_Provider.from(
|
|
124
|
+
{
|
|
125
|
+
...(emitter as unknown as ox_Provider.Emitter),
|
|
126
|
+
async request({ method, params }: { method: string; params?: any }) {
|
|
127
|
+
await Store.waitForHydration(store)
|
|
128
|
+
|
|
129
|
+
const shouldDedupe = [
|
|
130
|
+
'eth_accounts',
|
|
131
|
+
'eth_chainId',
|
|
132
|
+
'eth_requestAccounts',
|
|
133
|
+
'wallet_connect',
|
|
134
|
+
'wallet_getBalances',
|
|
135
|
+
'wallet_getCapabilities',
|
|
136
|
+
].includes(method)
|
|
137
|
+
|
|
138
|
+
return withDedupe(
|
|
139
|
+
async () => {
|
|
140
|
+
// Validate known methods. Unknown methods fall through to the RPC proxy.
|
|
141
|
+
let request: Request.WithDecoded<typeof Schema.Request>
|
|
142
|
+
try {
|
|
143
|
+
request = Request.validate(Schema.Request, { method, params })
|
|
144
|
+
} catch (e) {
|
|
145
|
+
if (!(e instanceof ox_Provider.UnsupportedMethodError)) throw e
|
|
146
|
+
// Proxy unknown methods to the RPC node.
|
|
147
|
+
return await Client.fromChainId(undefined, { chains, store }).request({
|
|
148
|
+
method: method as any,
|
|
149
|
+
params: params as any,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = await (async () => {
|
|
154
|
+
switch (request.method) {
|
|
155
|
+
case 'eth_accounts': {
|
|
156
|
+
const { accounts, activeAccount } = store.getState()
|
|
157
|
+
if (accounts.length === 0) return []
|
|
158
|
+
const activeAddr = accounts[activeAccount]?.address
|
|
159
|
+
const activeIdx = accounts.findIndex((a) => a.address === activeAddr)
|
|
160
|
+
const sorted = [...accounts]
|
|
161
|
+
if (activeIdx >= 0) {
|
|
162
|
+
const [active] = sorted.splice(activeIdx, 1)
|
|
163
|
+
return [active!.address, ...sorted.map((a) => a.address)]
|
|
164
|
+
}
|
|
165
|
+
return sorted.map(
|
|
166
|
+
(a) => a.address,
|
|
167
|
+
) satisfies Rpc.eth_accounts.Encoded['returns']
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
case 'eth_chainId':
|
|
171
|
+
return Hex.fromNumber(
|
|
172
|
+
store.getState().chainId,
|
|
173
|
+
) satisfies Rpc.eth_chainId.Encoded['returns']
|
|
174
|
+
|
|
175
|
+
case 'eth_requestAccounts': {
|
|
176
|
+
const { accounts } = await actions.loadAccounts(undefined, {
|
|
177
|
+
method: 'wallet_connect',
|
|
178
|
+
params: undefined,
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
store.setState({ accounts: resolveAccounts(accounts), activeAccount: 0 })
|
|
182
|
+
|
|
183
|
+
return accounts.map(
|
|
184
|
+
(a) => a.address,
|
|
185
|
+
) satisfies Rpc.eth_requestAccounts.Encoded['returns']
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
case 'eth_sendTransaction': {
|
|
189
|
+
assertConnected()
|
|
190
|
+
const [decoded] = request._decoded.params
|
|
191
|
+
return (await actions.sendTransaction(
|
|
192
|
+
{
|
|
193
|
+
...decoded,
|
|
194
|
+
feePayer: resolveFeePayer(decoded.feePayer),
|
|
195
|
+
},
|
|
196
|
+
request,
|
|
197
|
+
)) satisfies Rpc.eth_sendTransaction.Encoded['returns']
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
case 'eth_signTransaction': {
|
|
201
|
+
assertConnected()
|
|
202
|
+
const [decoded] = request._decoded.params
|
|
203
|
+
return (await actions.signTransaction(
|
|
204
|
+
{
|
|
205
|
+
...decoded,
|
|
206
|
+
feePayer: resolveFeePayer(decoded.feePayer),
|
|
207
|
+
},
|
|
208
|
+
request,
|
|
209
|
+
)) satisfies Rpc.eth_signTransaction.Encoded['returns']
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
case 'eth_sendTransactionSync': {
|
|
213
|
+
assertConnected()
|
|
214
|
+
const [decoded] = request._decoded.params
|
|
215
|
+
return (await actions.sendTransactionSync(
|
|
216
|
+
{
|
|
217
|
+
...decoded,
|
|
218
|
+
feePayer: resolveFeePayer(decoded.feePayer),
|
|
219
|
+
},
|
|
220
|
+
request,
|
|
221
|
+
)) satisfies Rpc.eth_sendTransactionSync.Encoded['returns']
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case 'eth_signTypedData_v4': {
|
|
225
|
+
assertConnected()
|
|
226
|
+
const [address, data] = request._decoded.params
|
|
227
|
+
return (await actions.signTypedData(
|
|
228
|
+
{
|
|
229
|
+
address,
|
|
230
|
+
data,
|
|
231
|
+
},
|
|
232
|
+
request,
|
|
233
|
+
)) satisfies Rpc.eth_signTypedData_v4.Encoded['returns']
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case 'personal_sign': {
|
|
237
|
+
assertConnected()
|
|
238
|
+
const [data, address] = request._decoded.params
|
|
239
|
+
return (await actions.signPersonalMessage(
|
|
240
|
+
{
|
|
241
|
+
address,
|
|
242
|
+
data,
|
|
243
|
+
},
|
|
244
|
+
request,
|
|
245
|
+
)) satisfies Rpc.personal_sign.Encoded['returns']
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
case 'wallet_sendCalls': {
|
|
249
|
+
assertConnected()
|
|
250
|
+
const decoded = request._decoded.params?.[0]
|
|
251
|
+
const { calls = [], capabilities, chainId, from } = decoded ?? {}
|
|
252
|
+
const sync = capabilities?.sync
|
|
253
|
+
const feePayer = resolveFeePayer(feePayerUrl ? true : undefined)
|
|
254
|
+
const txRequest = {
|
|
255
|
+
calls,
|
|
256
|
+
chainId,
|
|
257
|
+
from,
|
|
258
|
+
...(feePayer ? { feePayer } : {}),
|
|
259
|
+
}
|
|
260
|
+
if (!sync) {
|
|
261
|
+
const hash = await actions.sendTransaction(txRequest, {
|
|
262
|
+
method: 'eth_sendTransaction',
|
|
263
|
+
params: [z.encode(Rpc.transactionRequest, txRequest)] as const,
|
|
264
|
+
})
|
|
265
|
+
const chainId = Hex.fromNumber(store.getState().chainId)
|
|
266
|
+
const id = Hex.concat(hash, Hex.padLeft(chainId, 32), sendCallsMagic)
|
|
267
|
+
return { capabilities: { sync }, id }
|
|
268
|
+
}
|
|
269
|
+
const receipt = await actions.sendTransactionSync(txRequest as never, {
|
|
270
|
+
method: 'eth_sendTransactionSync',
|
|
271
|
+
params: [z.encode(Rpc.transactionRequest, txRequest)] as const,
|
|
272
|
+
})
|
|
273
|
+
const hash = receipt.transactionHash
|
|
274
|
+
const chainIdHex = Hex.fromNumber(store.getState().chainId)
|
|
275
|
+
const id = Hex.concat(hash, Hex.padLeft(chainIdHex, 32), sendCallsMagic)
|
|
276
|
+
return {
|
|
277
|
+
atomic: true,
|
|
278
|
+
capabilities: { sync },
|
|
279
|
+
chainId: chainIdHex,
|
|
280
|
+
id,
|
|
281
|
+
receipts: [receipt],
|
|
282
|
+
status: (receipt as { status: string }).status === '0x1' ? 200 : 500,
|
|
283
|
+
version: '2.0.0',
|
|
284
|
+
} satisfies Rpc.wallet_sendCalls.Encoded['returns']
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
case 'wallet_getBalances': {
|
|
288
|
+
const decoded = request._decoded.params?.[0]
|
|
289
|
+
const { accounts, activeAccount } = store.getState()
|
|
290
|
+
const account = decoded?.account ?? accounts[activeAccount]?.address
|
|
291
|
+
if (!account)
|
|
292
|
+
throw new ox_Provider.DisconnectedError({
|
|
293
|
+
message: 'No accounts connected.',
|
|
294
|
+
})
|
|
295
|
+
const tokens = decoded?.tokens
|
|
296
|
+
// TODO: hook up to indexer
|
|
297
|
+
if (!tokens || tokens.length === 0)
|
|
298
|
+
throw new RpcResponse.InvalidParamsError({
|
|
299
|
+
message: '`tokens` is required.',
|
|
300
|
+
})
|
|
301
|
+
const client = Client.fromChainId(decoded?.chainId, { chains, store })
|
|
302
|
+
return (await Promise.all(
|
|
303
|
+
tokens.map(async (token) => {
|
|
304
|
+
const [balance, metadata] = await Promise.all([
|
|
305
|
+
Actions.token.getBalance(client, { account, token }),
|
|
306
|
+
Actions.token.getMetadata(client, { token }),
|
|
307
|
+
])
|
|
308
|
+
const value = Number(balance) / 10 ** metadata.decimals
|
|
309
|
+
const display = new Intl.NumberFormat('en-US', {
|
|
310
|
+
style: 'currency',
|
|
311
|
+
currency: metadata.currency,
|
|
312
|
+
}).format(value)
|
|
313
|
+
return {
|
|
314
|
+
address: token,
|
|
315
|
+
balance: Hex.fromNumber(balance),
|
|
316
|
+
decimals: metadata.decimals,
|
|
317
|
+
display,
|
|
318
|
+
name: metadata.name,
|
|
319
|
+
symbol: metadata.symbol,
|
|
320
|
+
}
|
|
321
|
+
}),
|
|
322
|
+
)) satisfies Rpc.wallet_getBalances.Encoded['returns']
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
case 'wallet_getCallsStatus': {
|
|
326
|
+
const [id] = request._decoded.params ?? []
|
|
327
|
+
if (!id) throw new Error('`id` not found')
|
|
328
|
+
if (!id.endsWith(sendCallsMagic.slice(2))) throw new Error('`id` not supported')
|
|
329
|
+
Hex.assert(id)
|
|
330
|
+
const hash = Hex.slice(id, 0, 32)
|
|
331
|
+
const chainId = Hex.fromNumber(Number(Hex.slice(id, 32, 64)))
|
|
332
|
+
const client = Client.fromChainId(Number(chainId), { chains, store })
|
|
333
|
+
const receipt = await client.request({
|
|
334
|
+
method: 'eth_getTransactionReceipt',
|
|
335
|
+
params: [hash],
|
|
336
|
+
})
|
|
337
|
+
return {
|
|
338
|
+
atomic: true,
|
|
339
|
+
chainId,
|
|
340
|
+
id,
|
|
341
|
+
receipts: receipt ? [receipt as never] : [],
|
|
342
|
+
status: receipt?.status === '0x1' ? 200 : 500,
|
|
343
|
+
version: '2.0.0',
|
|
344
|
+
} satisfies Rpc.wallet_getCallsStatus.Encoded['returns']
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
case 'wallet_getCapabilities': {
|
|
348
|
+
const decoded = request._decoded.params
|
|
349
|
+
const address = decoded?.[0]
|
|
350
|
+
const chainIds = decoded?.[1]
|
|
351
|
+
|
|
352
|
+
if (address) {
|
|
353
|
+
const { accounts } = store.getState()
|
|
354
|
+
if (!accounts.some((a) => a.address.toLowerCase() === address.toLowerCase()))
|
|
355
|
+
throw new ox_Provider.UnauthorizedError({
|
|
356
|
+
message: `Address ${address} is not connected.`,
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const filtered = chainIds
|
|
361
|
+
? chains.filter((c) => chainIds.includes(Hex.fromNumber(c.id)))
|
|
362
|
+
: chains
|
|
363
|
+
|
|
364
|
+
const result: Record<
|
|
365
|
+
string,
|
|
366
|
+
{
|
|
367
|
+
accessKeys: { status: 'supported' }
|
|
368
|
+
atomic: { status: 'supported' }
|
|
369
|
+
}
|
|
370
|
+
> = {}
|
|
371
|
+
for (const chain of filtered)
|
|
372
|
+
result[Hex.fromNumber(chain.id)] = {
|
|
373
|
+
accessKeys: { status: 'supported' },
|
|
374
|
+
atomic: { status: 'supported' },
|
|
375
|
+
}
|
|
376
|
+
return result as Rpc.wallet_getCapabilities.Encoded['returns']
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
case 'wallet_connect': {
|
|
380
|
+
const capabilities = request._decoded.params?.[0]?.capabilities
|
|
381
|
+
const authorizeAccessKey =
|
|
382
|
+
capabilities?.authorizeAccessKey ?? options.authorizeAccessKey?.()
|
|
383
|
+
|
|
384
|
+
const { keyAuthorization, accounts, signature } = await (async () => {
|
|
385
|
+
if (capabilities?.method === 'register')
|
|
386
|
+
return await actions.createAccount(
|
|
387
|
+
{
|
|
388
|
+
digest: capabilities.digest,
|
|
389
|
+
authorizeAccessKey,
|
|
390
|
+
name: capabilities.name ?? 'default',
|
|
391
|
+
userId: capabilities.userId,
|
|
392
|
+
},
|
|
393
|
+
request,
|
|
394
|
+
)
|
|
395
|
+
return await actions.loadAccounts(
|
|
396
|
+
{
|
|
397
|
+
credentialId: capabilities?.credentialId,
|
|
398
|
+
digest: capabilities?.digest,
|
|
399
|
+
authorizeAccessKey,
|
|
400
|
+
selectAccount: capabilities?.selectAccount,
|
|
401
|
+
},
|
|
402
|
+
request,
|
|
403
|
+
)
|
|
404
|
+
})()
|
|
405
|
+
|
|
406
|
+
store.setState({ accounts: resolveAccounts(accounts), activeAccount: 0 })
|
|
407
|
+
|
|
408
|
+
const accountAddress = accounts[0]?.address
|
|
409
|
+
return {
|
|
410
|
+
accounts: accounts.map((a) => ({
|
|
411
|
+
address: a.address,
|
|
412
|
+
capabilities:
|
|
413
|
+
a.address === accountAddress
|
|
414
|
+
? {
|
|
415
|
+
...(keyAuthorization
|
|
416
|
+
? {
|
|
417
|
+
keyAuthorization: {
|
|
418
|
+
...keyAuthorization,
|
|
419
|
+
address: keyAuthorization.keyId,
|
|
420
|
+
},
|
|
421
|
+
}
|
|
422
|
+
: {}),
|
|
423
|
+
...(signature && capabilities?.digest ? { signature } : {}),
|
|
424
|
+
}
|
|
425
|
+
: {},
|
|
426
|
+
})),
|
|
427
|
+
} satisfies Rpc.wallet_connect.Encoded['returns']
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
case 'wallet_disconnect':
|
|
431
|
+
await actions.disconnect?.()
|
|
432
|
+
store.setState({ accessKeys: [], accounts: [], activeAccount: 0 })
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
case 'wallet_authorizeAccessKey': {
|
|
436
|
+
assertConnected()
|
|
437
|
+
if (!actions.authorizeAccessKey)
|
|
438
|
+
throw new ox_Provider.UnsupportedMethodError({
|
|
439
|
+
message: '`authorizeAccessKey` not supported by adapter.',
|
|
440
|
+
})
|
|
441
|
+
const decoded = request._decoded.params[0]
|
|
442
|
+
const result = await actions.authorizeAccessKey(decoded, request)
|
|
443
|
+
return {
|
|
444
|
+
...result,
|
|
445
|
+
address: result.keyId,
|
|
446
|
+
} satisfies Rpc.wallet_authorizeAccessKey.Encoded['returns']
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
case 'wallet_revokeAccessKey': {
|
|
450
|
+
assertConnected()
|
|
451
|
+
if (!actions.revokeAccessKey)
|
|
452
|
+
throw new ox_Provider.UnsupportedMethodError({
|
|
453
|
+
message: '`revokeAccessKey` not supported by adapter.',
|
|
454
|
+
})
|
|
455
|
+
const [decoded] = request._decoded.params
|
|
456
|
+
await actions.revokeAccessKey(
|
|
457
|
+
{
|
|
458
|
+
...decoded,
|
|
459
|
+
},
|
|
460
|
+
request,
|
|
461
|
+
)
|
|
462
|
+
return
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
case 'wallet_switchEthereumChain': {
|
|
466
|
+
const { chainId } = request._decoded.params[0]
|
|
467
|
+
if (!chains.some((c) => c.id === chainId))
|
|
468
|
+
throw new ox_Provider.UnsupportedChainIdError({
|
|
469
|
+
message: `Chain ${chainId} not configured.`,
|
|
470
|
+
})
|
|
471
|
+
await actions.switchChain?.({ chainId })
|
|
472
|
+
store.setState({ chainId })
|
|
473
|
+
return
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
})()
|
|
477
|
+
|
|
478
|
+
return result
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
enabled: shouldDedupe,
|
|
482
|
+
id: Json.stringify({ method, params }),
|
|
483
|
+
},
|
|
484
|
+
)
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
{ schema: Schema.ox },
|
|
488
|
+
),
|
|
489
|
+
{ chains, getAccount, getClient, store },
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
if (typeof window !== 'undefined') {
|
|
493
|
+
announceProvider({
|
|
494
|
+
info: {
|
|
495
|
+
icon: adapter.icon ?? defaultIcon,
|
|
496
|
+
name: adapter.name ?? 'Injected Wallet',
|
|
497
|
+
rdns:
|
|
498
|
+
adapter.rdns ??
|
|
499
|
+
`com.${(adapter.name ?? 'Injected Wallet').toLowerCase().replace(/\s+/g, '')}`,
|
|
500
|
+
uuid: crypto.randomUUID(),
|
|
501
|
+
},
|
|
502
|
+
provider,
|
|
503
|
+
} as never)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (options.mpp)
|
|
507
|
+
Mppx.create({
|
|
508
|
+
methods: [
|
|
509
|
+
mppx_tempo({
|
|
510
|
+
getClient: ({ chainId }) => {
|
|
511
|
+
const client = Client.fromChainId(chainId, { chains, store })
|
|
512
|
+
const account = Account.find({ store, signable: true })
|
|
513
|
+
return Object.assign(client, { account })
|
|
514
|
+
},
|
|
515
|
+
}),
|
|
516
|
+
],
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
return provider
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const defaultIcon =
|
|
523
|
+
'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"><rect width="1" height="1"/></svg>' as const
|
|
524
|
+
const sendCallsMagic = Hash.keccak256(Hex.fromString('TEMPO_5792'))
|
|
525
|
+
|
|
526
|
+
export declare namespace create {
|
|
527
|
+
type Options = {
|
|
528
|
+
/** Adapter to use for account management. @default dialog() */
|
|
529
|
+
adapter?: Adapter.Adapter | undefined
|
|
530
|
+
/**
|
|
531
|
+
* Default access key parameters for `wallet_connect`.
|
|
532
|
+
*
|
|
533
|
+
* When set, `wallet_connect` will automatically authorize an access key.
|
|
534
|
+
*/
|
|
535
|
+
authorizeAccessKey?: (() => Adapter.authorizeAccessKey.Parameters) | undefined
|
|
536
|
+
/**
|
|
537
|
+
* Supported chains. First chain is the default.
|
|
538
|
+
* @default [tempo, tempoModerato]
|
|
539
|
+
*/
|
|
540
|
+
chains?: readonly [Chain, ...Chain[]] | undefined
|
|
541
|
+
/**
|
|
542
|
+
* Fee payer URL for interacting with a service running `Handler.feePayer`
|
|
543
|
+
* from `accounts/server`.
|
|
544
|
+
*/
|
|
545
|
+
feePayerUrl?: string | undefined
|
|
546
|
+
/** Enable Machine Payment Protocol (mppx) support. @default false */
|
|
547
|
+
mpp?: boolean | undefined
|
|
548
|
+
/** Whether to persist credentials and access keys to storage. When `false`, only account addresses are persisted. @default true */
|
|
549
|
+
persistCredentials?: boolean | undefined
|
|
550
|
+
/** Storage adapter for persistence. @default Storage.idb() in browser, Storage.memory() otherwise. */
|
|
551
|
+
storage?: Storage.Storage | undefined
|
|
552
|
+
/**
|
|
553
|
+
* Use testnet.
|
|
554
|
+
* @default false
|
|
555
|
+
*/
|
|
556
|
+
testnet?: boolean | undefined
|
|
557
|
+
}
|
|
558
|
+
type ReturnType = Provider
|
|
559
|
+
}
|