accounts 0.5.5 → 0.5.7
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 +12 -0
- package/dist/core/Client.d.ts +1 -1
- package/dist/core/Client.d.ts.map +1 -1
- package/dist/core/Client.js +43 -4
- package/dist/core/Client.js.map +1 -1
- package/dist/core/Dialog.d.ts.map +1 -1
- package/dist/core/Dialog.js +64 -23
- package/dist/core/Dialog.js.map +1 -1
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +27 -10
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/adapters/dangerous_secp256k1.d.ts +3 -0
- package/dist/core/adapters/dangerous_secp256k1.d.ts.map +1 -1
- package/dist/core/adapters/dangerous_secp256k1.js +15 -4
- package/dist/core/adapters/dangerous_secp256k1.js.map +1 -1
- package/dist/server/Handler.d.ts +7 -3
- package/dist/server/Handler.d.ts.map +1 -1
- package/dist/server/Handler.js +142 -13
- package/dist/server/Handler.js.map +1 -1
- package/dist/wagmi/Connector.js +2 -2
- package/dist/wagmi/Connector.js.map +1 -1
- package/package.json +2 -2
- package/src/core/Client.ts +53 -5
- package/src/core/Dialog.ts +69 -26
- package/src/core/Provider.test.ts +14 -0
- package/src/core/Provider.ts +28 -8
- package/src/core/adapters/dangerous_secp256k1.test.ts +24 -0
- package/src/core/adapters/dangerous_secp256k1.ts +17 -4
- package/src/server/Handler.test.ts +96 -4
- package/src/server/Handler.ts +180 -24
- package/src/wagmi/Connector.ts +2 -2
package/src/core/Dialog.ts
CHANGED
|
@@ -155,7 +155,12 @@ export function iframe(): Dialog {
|
|
|
155
155
|
root.appendChild(style)
|
|
156
156
|
root.appendChild(frame)
|
|
157
157
|
|
|
158
|
+
let readyResult: Messenger.ReadyOptions | undefined
|
|
159
|
+
let switchedToPopup = false
|
|
160
|
+
|
|
158
161
|
function createMessenger() {
|
|
162
|
+
readyResult = undefined
|
|
163
|
+
|
|
159
164
|
const m = Messenger.bridge({
|
|
160
165
|
from: Messenger.fromWindow(window, { targetOrigin: hostUrl.origin }),
|
|
161
166
|
to: Messenger.fromWindow(frame.contentWindow!, {
|
|
@@ -164,6 +169,23 @@ export function iframe(): Dialog {
|
|
|
164
169
|
waitForReady: true,
|
|
165
170
|
})
|
|
166
171
|
m.on('rpc-response', (response) => handleResponse(store!, response))
|
|
172
|
+
m.waitForReady().then((result) => {
|
|
173
|
+
readyResult = result
|
|
174
|
+
if (result.colorScheme) frame.style.colorScheme = result.colorScheme
|
|
175
|
+
})
|
|
176
|
+
m.on('switch-mode', () => {
|
|
177
|
+
hideDialog()
|
|
178
|
+
activatePage()
|
|
179
|
+
open = false
|
|
180
|
+
switchedToPopup = true
|
|
181
|
+
|
|
182
|
+
const pending = store
|
|
183
|
+
?.getState()
|
|
184
|
+
.requestQueue.filter(
|
|
185
|
+
(x): x is Store.QueuedRequest & { status: 'pending' } => x.status === 'pending',
|
|
186
|
+
)
|
|
187
|
+
if (pending && pending.length > 0) fallback?.syncRequests(pending)
|
|
188
|
+
})
|
|
167
189
|
return m
|
|
168
190
|
}
|
|
169
191
|
|
|
@@ -254,23 +276,6 @@ export function iframe(): Dialog {
|
|
|
254
276
|
}
|
|
255
277
|
}
|
|
256
278
|
|
|
257
|
-
messenger.waitForReady().then(({ colorScheme }) => {
|
|
258
|
-
if (colorScheme) frame.style.colorScheme = colorScheme
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
messenger.on('switch-mode', () => {
|
|
262
|
-
hideDialog()
|
|
263
|
-
activatePage()
|
|
264
|
-
open = false
|
|
265
|
-
|
|
266
|
-
const pending = store!
|
|
267
|
-
.getState()
|
|
268
|
-
.requestQueue.filter(
|
|
269
|
-
(x): x is Store.QueuedRequest & { status: 'pending' } => x.status === 'pending',
|
|
270
|
-
)
|
|
271
|
-
if (pending.length > 0) fallback!.syncRequests(pending)
|
|
272
|
-
})
|
|
273
|
-
|
|
274
279
|
const instance: Instance = {
|
|
275
280
|
close() {
|
|
276
281
|
fallback!.close()
|
|
@@ -304,7 +309,12 @@ export function iframe(): Dialog {
|
|
|
304
309
|
activateDialog()
|
|
305
310
|
},
|
|
306
311
|
async syncRequests(requests) {
|
|
307
|
-
|
|
312
|
+
if (switchedToPopup) {
|
|
313
|
+
fallback!.syncRequests(requests)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const { trustedHosts } = readyResult ?? (await messenger.waitForReady())
|
|
308
318
|
|
|
309
319
|
// Safari does not support WebAuthn credential creation in iframes.
|
|
310
320
|
if (
|
|
@@ -358,10 +368,6 @@ export function popup(options: popup.Options = {}): Dialog {
|
|
|
358
368
|
|
|
359
369
|
let win: Window | null = null
|
|
360
370
|
|
|
361
|
-
function onBlur() {
|
|
362
|
-
if (win) handleBlur(store)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
371
|
const offDetectClosed = (() => {
|
|
366
372
|
const timer = setInterval(() => {
|
|
367
373
|
if (win?.closed) handleBlur(store)
|
|
@@ -371,19 +377,57 @@ export function popup(options: popup.Options = {}): Dialog {
|
|
|
371
377
|
|
|
372
378
|
let messenger: Messenger.Bridge | undefined
|
|
373
379
|
|
|
380
|
+
const overlay = document.createElement('div')
|
|
381
|
+
Object.assign(overlay.style, {
|
|
382
|
+
alignItems: 'center',
|
|
383
|
+
background: 'rgba(0, 0, 0, 0.5)',
|
|
384
|
+
color: 'white',
|
|
385
|
+
display: 'none',
|
|
386
|
+
flexDirection: 'column',
|
|
387
|
+
fontFamily: 'system-ui, sans-serif',
|
|
388
|
+
fontSize: '16px',
|
|
389
|
+
gap: '12px',
|
|
390
|
+
inset: '0',
|
|
391
|
+
justifyContent: 'center',
|
|
392
|
+
position: 'fixed',
|
|
393
|
+
zIndex: '2147483647',
|
|
394
|
+
})
|
|
395
|
+
const overlayMessage = document.createElement('p')
|
|
396
|
+
Object.assign(overlayMessage.style, { margin: '0' })
|
|
397
|
+
overlayMessage.textContent = 'Continue in the popup window'
|
|
398
|
+
const overlayClose = document.createElement('button')
|
|
399
|
+
Object.assign(overlayClose.style, {
|
|
400
|
+
background: 'none',
|
|
401
|
+
border: 'none',
|
|
402
|
+
color: 'white',
|
|
403
|
+
cursor: 'pointer',
|
|
404
|
+
font: 'inherit',
|
|
405
|
+
padding: '0',
|
|
406
|
+
textDecoration: 'underline',
|
|
407
|
+
})
|
|
408
|
+
overlayClose.textContent = 'Close'
|
|
409
|
+
overlayClose.addEventListener('click', () => handleBlur(store))
|
|
410
|
+
overlay.appendChild(overlayMessage)
|
|
411
|
+
overlay.appendChild(overlayClose)
|
|
412
|
+
document.body.appendChild(overlay)
|
|
413
|
+
|
|
374
414
|
return {
|
|
375
415
|
close() {
|
|
416
|
+
overlay.style.display = 'none'
|
|
376
417
|
if (!win) return
|
|
377
418
|
win.close()
|
|
378
419
|
win = null
|
|
379
420
|
},
|
|
380
421
|
destroy() {
|
|
381
422
|
this.close()
|
|
382
|
-
window.removeEventListener('focus', onBlur)
|
|
383
423
|
messenger?.destroy()
|
|
384
424
|
offDetectClosed()
|
|
425
|
+
overlay.remove()
|
|
385
426
|
},
|
|
386
427
|
open() {
|
|
428
|
+
messenger?.destroy()
|
|
429
|
+
win?.close()
|
|
430
|
+
|
|
387
431
|
const referrer = getReferrer()
|
|
388
432
|
|
|
389
433
|
const hostUrl = new URL(host)
|
|
@@ -415,14 +459,13 @@ export function popup(options: popup.Options = {}): Dialog {
|
|
|
415
459
|
|
|
416
460
|
messenger.on('rpc-response', (response) => handleResponse(store, response))
|
|
417
461
|
|
|
418
|
-
|
|
419
|
-
window.addEventListener('focus', onBlur)
|
|
462
|
+
overlay.style.display = 'flex'
|
|
420
463
|
},
|
|
421
464
|
async syncRequests(requests) {
|
|
422
465
|
const requiresConfirm = requests.some((x) => x.status === 'pending')
|
|
423
466
|
if (requiresConfirm) {
|
|
424
467
|
if (!win || win.closed) this.open()
|
|
425
|
-
win
|
|
468
|
+
else win.focus()
|
|
426
469
|
}
|
|
427
470
|
messenger?.send('rpc-requests', {
|
|
428
471
|
account: getAccount(store),
|
|
@@ -1642,6 +1642,20 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
1642
1642
|
expect(signed).toMatch(/^0x/)
|
|
1643
1643
|
})
|
|
1644
1644
|
|
|
1645
|
+
test('behavior: feePayer URL on eth_fillTransaction', async () => {
|
|
1646
|
+
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
1647
|
+
|
|
1648
|
+
const connected = await connect(provider)
|
|
1649
|
+
await fund(connected)
|
|
1650
|
+
|
|
1651
|
+
const result = await provider.request({
|
|
1652
|
+
method: 'eth_fillTransaction',
|
|
1653
|
+
params: [{ calls: [transferCall], feePayer: server.url, from: connected }],
|
|
1654
|
+
})
|
|
1655
|
+
|
|
1656
|
+
expect((result.tx as { feePayerSignature?: unknown }).feePayerSignature).toBeDefined()
|
|
1657
|
+
})
|
|
1658
|
+
|
|
1645
1659
|
test('behavior: feePayer: true uses default from Provider.create', async () => {
|
|
1646
1660
|
const provider = Provider.create({
|
|
1647
1661
|
adapter: adapter(),
|
package/src/core/Provider.ts
CHANGED
|
@@ -208,6 +208,7 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
208
208
|
return (await actions.sendTransaction(
|
|
209
209
|
{
|
|
210
210
|
...rest,
|
|
211
|
+
chainId: decoded.chainId ?? store.getState().chainId,
|
|
211
212
|
...(calls ? { calls } : {}),
|
|
212
213
|
feePayer: resolveFeePayer(decoded.feePayer),
|
|
213
214
|
},
|
|
@@ -216,16 +217,31 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
case 'eth_fillTransaction': {
|
|
219
|
-
const [
|
|
220
|
+
const [decoded] = request._decoded.params
|
|
221
|
+
const parameters = { ...decoded }
|
|
220
222
|
const chainId = parameters.chainId
|
|
221
|
-
|
|
222
|
-
: undefined
|
|
223
|
+
const feePayer = resolveFeePayer(parameters.feePayer)
|
|
223
224
|
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
type FillParams = z.output<typeof Rpc.transactionRequest> & {
|
|
226
|
+
keyAuthorization?: unknown
|
|
227
|
+
}
|
|
228
|
+
const fill = (params: FillParams) => {
|
|
229
|
+
const client = getClient({ chainId, feePayer })
|
|
230
|
+
const fillRequest = {
|
|
231
|
+
...params,
|
|
232
|
+
chainId: params.chainId ?? client.chain?.id,
|
|
233
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
234
|
+
}
|
|
235
|
+
const formatter = client.chain?.formatters?.transactionRequest
|
|
236
|
+
const formatted =
|
|
237
|
+
formatter && !fillRequest.keyAuthorization
|
|
238
|
+
? formatter.format({ ...fillRequest } as never, 'fillTransaction')
|
|
239
|
+
: fillRequest
|
|
240
|
+
return client.request({
|
|
226
241
|
method: 'eth_fillTransaction',
|
|
227
|
-
params: [
|
|
242
|
+
params: [formatted as never],
|
|
228
243
|
})
|
|
244
|
+
}
|
|
229
245
|
|
|
230
246
|
// Inject pending keyAuthorization so the node accounts for
|
|
231
247
|
// key authorization gas during estimation.
|
|
@@ -243,8 +259,10 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
243
259
|
try {
|
|
244
260
|
const result = await fill({
|
|
245
261
|
...parameters,
|
|
246
|
-
|
|
247
|
-
|
|
262
|
+
keyAuthorization: {
|
|
263
|
+
address: keyAuth.address,
|
|
264
|
+
...KeyAuthorization.toRpc(keyAuth),
|
|
265
|
+
} as never,
|
|
248
266
|
})
|
|
249
267
|
AccessKey.removePending(account, { store })
|
|
250
268
|
return result
|
|
@@ -268,6 +286,7 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
268
286
|
return (await actions.signTransaction(
|
|
269
287
|
{
|
|
270
288
|
...rest,
|
|
289
|
+
chainId: decoded.chainId ?? store.getState().chainId,
|
|
271
290
|
...(calls ? { calls } : {}),
|
|
272
291
|
feePayer: resolveFeePayer(decoded.feePayer),
|
|
273
292
|
},
|
|
@@ -284,6 +303,7 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
284
303
|
return (await actions.sendTransactionSync(
|
|
285
304
|
{
|
|
286
305
|
...rest,
|
|
306
|
+
chainId: decoded.chainId ?? store.getState().chainId,
|
|
287
307
|
...(calls ? { calls } : {}),
|
|
288
308
|
feePayer: resolveFeePayer(decoded.feePayer),
|
|
289
309
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vp/test'
|
|
2
|
+
|
|
3
|
+
import { accounts, privateKeys } from '../../../test/config.js'
|
|
4
|
+
import * as Provider from '../Provider.js'
|
|
5
|
+
import * as Storage from '../Storage.js'
|
|
6
|
+
import { dangerous_secp256k1 } from './dangerous_secp256k1.js'
|
|
7
|
+
|
|
8
|
+
describe('dangerous_secp256k1', () => {
|
|
9
|
+
test('behavior: privateKey option pins the connected account', async () => {
|
|
10
|
+
const account = accounts[1]!
|
|
11
|
+
const provider = Provider.create({
|
|
12
|
+
adapter: dangerous_secp256k1({ privateKey: privateKeys[1]! }),
|
|
13
|
+
storage: Storage.memory({ key: 'dangerous-secp256k1-private-key' }),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const result = await provider.request({ method: 'wallet_connect' })
|
|
17
|
+
expect(result.accounts).toHaveLength(1)
|
|
18
|
+
expect(result.accounts[0]!.address).toBe(account.address)
|
|
19
|
+
|
|
20
|
+
const connected = await provider.request({ method: 'eth_accounts' })
|
|
21
|
+
expect(connected).toHaveLength(1)
|
|
22
|
+
expect(connected[0]).toBe(account.address)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Hex } from 'viem'
|
|
2
|
+
import { Account as TempoAccount, Secp256k1 } from 'viem/tempo'
|
|
2
3
|
|
|
3
4
|
import * as Adapter from '../Adapter.js'
|
|
4
5
|
import { local } from './local.js'
|
|
@@ -21,20 +22,30 @@ import { local } from './local.js'
|
|
|
21
22
|
* ```
|
|
22
23
|
*/
|
|
23
24
|
export function dangerous_secp256k1(options: dangerous_secp256k1.Options = {}): Adapter.Adapter {
|
|
24
|
-
const { icon, name, rdns } = options
|
|
25
|
+
const { icon, name, privateKey, rdns } = options
|
|
26
|
+
const fixed = privateKey
|
|
27
|
+
? {
|
|
28
|
+
address: TempoAccount.fromSecp256k1(privateKey).address,
|
|
29
|
+
keyType: 'secp256k1' as const,
|
|
30
|
+
privateKey,
|
|
31
|
+
}
|
|
32
|
+
: undefined
|
|
25
33
|
|
|
26
34
|
return Adapter.define({ icon, name, rdns }, (config) => {
|
|
27
35
|
const { store } = config
|
|
28
36
|
|
|
29
37
|
return local({
|
|
30
38
|
async createAccount() {
|
|
39
|
+
if (fixed) return { accounts: [fixed] }
|
|
40
|
+
|
|
31
41
|
const privateKey = Secp256k1.randomPrivateKey()
|
|
32
|
-
const
|
|
42
|
+
const generated = TempoAccount.fromSecp256k1(privateKey)
|
|
33
43
|
return {
|
|
34
|
-
accounts: [{ address:
|
|
44
|
+
accounts: [{ address: generated.address, keyType: 'secp256k1' as const, privateKey }],
|
|
35
45
|
}
|
|
36
46
|
},
|
|
37
47
|
async loadAccounts() {
|
|
48
|
+
if (fixed) return { accounts: [fixed] }
|
|
38
49
|
return { accounts: [...store.getState().accounts] }
|
|
39
50
|
},
|
|
40
51
|
})(config)
|
|
@@ -47,6 +58,8 @@ export declare namespace dangerous_secp256k1 {
|
|
|
47
58
|
icon?: `data:image/${string}` | undefined
|
|
48
59
|
/** Display name of the provider (e.g. `"My Wallet"`). @default "Injected Wallet" */
|
|
49
60
|
name?: string | undefined
|
|
61
|
+
/** Fixed private key to expose instead of generating/loading one from storage. */
|
|
62
|
+
privateKey?: Hex | undefined
|
|
50
63
|
/** Reverse DNS identifier. @default `com.{lowercase name}` */
|
|
51
64
|
rdns?: string | undefined
|
|
52
65
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Elysia } from 'elysia'
|
|
2
2
|
import express from 'express'
|
|
3
3
|
import { Hono } from 'hono'
|
|
4
|
-
import type
|
|
4
|
+
import { Json, type RpcRequest } from 'ox'
|
|
5
|
+
import { SignatureEnvelope, Transaction as core_Transaction, TxEnvelopeTempo } from 'ox/tempo'
|
|
5
6
|
import { sendTransactionSync } from 'viem/actions'
|
|
6
|
-
import { withFeePayer } from 'viem/tempo'
|
|
7
|
+
import { Transaction, withFeePayer } from 'viem/tempo'
|
|
7
8
|
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vp/test'
|
|
8
9
|
|
|
9
10
|
import { accounts, chain, getClient, http } from '../../test/config.js'
|
|
@@ -732,7 +733,98 @@ describe('feePayer', () => {
|
|
|
732
733
|
requests = []
|
|
733
734
|
})
|
|
734
735
|
|
|
736
|
+
async function rpc(request: Record<string, unknown>) {
|
|
737
|
+
return await fetch(server.url, {
|
|
738
|
+
body: Json.stringify(request),
|
|
739
|
+
headers: { 'content-type': 'application/json' },
|
|
740
|
+
method: 'POST',
|
|
741
|
+
}).then((response) => response.json())
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/** Signs a sponsor-bound Tempo transaction, preserving the feePayerSignature. */
|
|
745
|
+
async function signSponsoredTx(account: (typeof accounts)[number], transaction: object) {
|
|
746
|
+
const serialized = (await Transaction.serialize(transaction as never)) as `0x76${string}`
|
|
747
|
+
const envelope = TxEnvelopeTempo.deserialize(serialized)
|
|
748
|
+
const signature = await account.sign({
|
|
749
|
+
hash: TxEnvelopeTempo.getSignPayload(envelope),
|
|
750
|
+
})
|
|
751
|
+
return TxEnvelopeTempo.serialize(envelope, {
|
|
752
|
+
signature: SignatureEnvelope.from(signature),
|
|
753
|
+
})
|
|
754
|
+
}
|
|
755
|
+
|
|
735
756
|
describe('POST /', () => {
|
|
757
|
+
test('default: eth_fillTransaction returns a sponsor-bound transaction the sender can broadcast', async () => {
|
|
758
|
+
const response = (await rpc({
|
|
759
|
+
id: 1,
|
|
760
|
+
jsonrpc: '2.0',
|
|
761
|
+
method: 'eth_fillTransaction',
|
|
762
|
+
params: [
|
|
763
|
+
{
|
|
764
|
+
chainId: chain.id,
|
|
765
|
+
feePayer: true,
|
|
766
|
+
from: userAccount.address,
|
|
767
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
768
|
+
},
|
|
769
|
+
],
|
|
770
|
+
})) as {
|
|
771
|
+
result: {
|
|
772
|
+
sponsor: { address: string }
|
|
773
|
+
tx: Record<string, unknown>
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const prepared = core_Transaction.fromRpc(response.result.tx as never) as {
|
|
777
|
+
feePayerSignature?: unknown
|
|
778
|
+
}
|
|
779
|
+
const signed = await signSponsoredTx(userAccount, prepared)
|
|
780
|
+
const receipt = (await getClient().request({
|
|
781
|
+
method: 'eth_sendRawTransactionSync',
|
|
782
|
+
params: [signed],
|
|
783
|
+
})) as { feePayer?: string | undefined }
|
|
784
|
+
|
|
785
|
+
expect(response.result.sponsor.address).toBe(feePayerAccount.address)
|
|
786
|
+
expect(prepared?.feePayerSignature).toBeDefined()
|
|
787
|
+
expect(receipt.feePayer).toBe(feePayerAccount.address.toLowerCase())
|
|
788
|
+
expect(requests.map(({ method }) => method)).toMatchInlineSnapshot(`
|
|
789
|
+
[
|
|
790
|
+
"eth_fillTransaction",
|
|
791
|
+
]
|
|
792
|
+
`)
|
|
793
|
+
})
|
|
794
|
+
|
|
795
|
+
test('behavior: mutating a sponsor-bound transaction invalidates the fee payer binding', async () => {
|
|
796
|
+
const response = (await rpc({
|
|
797
|
+
id: 1,
|
|
798
|
+
jsonrpc: '2.0',
|
|
799
|
+
method: 'eth_fillTransaction',
|
|
800
|
+
params: [
|
|
801
|
+
{
|
|
802
|
+
chainId: chain.id,
|
|
803
|
+
feePayer: true,
|
|
804
|
+
from: userAccount.address,
|
|
805
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
806
|
+
},
|
|
807
|
+
],
|
|
808
|
+
})) as {
|
|
809
|
+
result: { tx: Record<string, unknown> }
|
|
810
|
+
}
|
|
811
|
+
const prepared = core_Transaction.fromRpc(response.result.tx as never) as {
|
|
812
|
+
gas?: bigint | undefined
|
|
813
|
+
feePayerSignature?: unknown
|
|
814
|
+
}
|
|
815
|
+
const signed = await signSponsoredTx(userAccount, {
|
|
816
|
+
...prepared,
|
|
817
|
+
gas: (prepared?.gas ?? 0n) + 1n,
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
await expect(
|
|
821
|
+
getClient().request({
|
|
822
|
+
method: 'eth_sendRawTransactionSync',
|
|
823
|
+
params: [signed],
|
|
824
|
+
}),
|
|
825
|
+
).rejects.toThrowError()
|
|
826
|
+
})
|
|
827
|
+
|
|
736
828
|
test('behavior: eth_signRawTransaction', async () => {
|
|
737
829
|
const client = getClient({
|
|
738
830
|
account: userAccount,
|
|
@@ -835,8 +927,8 @@ describe('feePayer', () => {
|
|
|
835
927
|
expect(data).toMatchInlineSnapshot(`
|
|
836
928
|
{
|
|
837
929
|
"error": {
|
|
838
|
-
"code": -
|
|
839
|
-
"name": "RpcResponse.
|
|
930
|
+
"code": -32602,
|
|
931
|
+
"name": "RpcResponse.InvalidParamsError",
|
|
840
932
|
"stack": "",
|
|
841
933
|
},
|
|
842
934
|
"id": 1,
|