Skip to content

Commit 9faaeb3

Browse files
committed
wip: send tx sync
1 parent 562d411 commit 9faaeb3

10 files changed

Lines changed: 247 additions & 0 deletions

File tree

src/actions/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ test('exports actions', () => {
7676
"revert": [Function],
7777
"sendCalls": [Function],
7878
"sendRawTransaction": [Function],
79+
"sendRawTransactionSync": [Function],
7980
"sendTransaction": [Function],
8081
"sendUnsignedTransaction": [Function],
8182
"setAutomine": [Function],

src/actions/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,12 @@ export {
525525
type SendRawTransactionReturnType,
526526
sendRawTransaction,
527527
} from './wallet/sendRawTransaction.js'
528+
export {
529+
type SendRawTransactionSyncErrorType,
530+
type SendRawTransactionSyncParameters,
531+
type SendRawTransactionSyncReturnType,
532+
sendRawTransactionSync,
533+
} from './wallet/sendRawTransactionSync.js'
528534
export {
529535
type SendTransactionErrorType,
530536
type SendTransactionParameters,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { expect, test } from 'vitest'
2+
import { accounts } from '~test/src/constants.js'
3+
import { anvilMainnet } from '../../../test/src/anvil.js'
4+
import { privateKeyToAccount } from '../../accounts/privateKeyToAccount.js'
5+
import { wait } from '../../utils/wait.js'
6+
import { mine } from '../index.js'
7+
import { prepareTransactionRequest } from './prepareTransactionRequest.js'
8+
import { sendRawTransactionSync } from './sendRawTransactionSync.js'
9+
import { signTransaction } from './signTransaction.js'
10+
11+
const client = anvilMainnet.getClient()
12+
13+
test('default', async () => {
14+
const request = await prepareTransactionRequest(client, {
15+
account: privateKeyToAccount(accounts[0].privateKey),
16+
to: accounts[1].address,
17+
value: 1n,
18+
})
19+
const serializedTransaction = await signTransaction(client, request)
20+
const [receipt] = await Promise.all([
21+
sendRawTransactionSync(client, {
22+
serializedTransaction,
23+
}),
24+
(async () => {
25+
await wait(100)
26+
await mine(client, { blocks: 1 })
27+
})(),
28+
])
29+
expect(receipt).toBeDefined()
30+
})
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { Client } from '../../clients/createClient.js'
2+
import type { Transport } from '../../clients/transports/createTransport.js'
3+
import type { ErrorType } from '../../errors/utils.js'
4+
import type { Chain } from '../../types/chain.js'
5+
import type { TransactionSerializedGeneric } from '../../types/transaction.js'
6+
import type { RequestErrorType } from '../../utils/buildRequest.js'
7+
import { formatTransactionReceipt } from '../../utils/formatters/transactionReceipt.js'
8+
import {
9+
type FormattedTransactionReceipt,
10+
numberToHex,
11+
} from '../../utils/index.js'
12+
13+
export type SendRawTransactionSyncParameters = {
14+
/** The signed serialized transaction. */
15+
serializedTransaction: TransactionSerializedGeneric
16+
/** The timeout for the transaction. */
17+
timeout?: number | undefined
18+
}
19+
20+
export type SendRawTransactionSyncReturnType<
21+
chain extends Chain | undefined = undefined,
22+
> = FormattedTransactionReceipt<chain>
23+
24+
export type SendRawTransactionSyncErrorType = RequestErrorType | ErrorType
25+
26+
/**
27+
* Sends a **signed** transaction to the network synchronously,
28+
* and waits for the transaction to be included in a block.
29+
*
30+
* - Docs: https://viem.sh/docs/actions/wallet/sendRawTransactionSync
31+
* - JSON-RPC Method: [`eth_sendRawTransactionSync`](https://eips.ethereum.org/EIPS/eip-7966)
32+
*
33+
* @param client - Client to use
34+
* @param parameters - {@link SendRawTransactionParameters}
35+
* @returns The transaction receipt. {@link SendRawTransactionSyncReturnType}
36+
*
37+
* @example
38+
* import { createWalletClient, custom } from 'viem'
39+
* import { mainnet } from 'viem/chains'
40+
* import { sendRawTransactionSync } from 'viem/wallet'
41+
*
42+
* const client = createWalletClient({
43+
* chain: mainnet,
44+
* transport: custom(window.ethereum),
45+
* })
46+
*
47+
* const receipt = await sendRawTransactionSync(client, {
48+
* serializedTransaction: '0x02f850018203118080825208808080c080a04012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1a04e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33'
49+
* })
50+
*/
51+
export async function sendRawTransactionSync<chain extends Chain | undefined>(
52+
client: Client<Transport, chain>,
53+
{ serializedTransaction, timeout }: SendRawTransactionSyncParameters,
54+
): Promise<SendRawTransactionSyncReturnType<chain>> {
55+
const receipt = await client.request(
56+
{
57+
method: 'eth_sendRawTransactionSync',
58+
params: timeout
59+
? [serializedTransaction, numberToHex(timeout)]
60+
: [serializedTransaction],
61+
},
62+
{ retryCount: 0 },
63+
)
64+
const format =
65+
client.chain?.formatters?.transactionReceipt?.format ||
66+
formatTransactionReceipt
67+
return format(receipt) as SendRawTransactionSyncReturnType<chain>
68+
}

src/clients/decorators/public.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ test('default', async () => {
7373
"prepareTransactionRequest": [Function],
7474
"readContract": [Function],
7575
"sendRawTransaction": [Function],
76+
"sendRawTransactionSync": [Function],
7677
"simulate": [Function],
7778
"simulateBlocks": [Function],
7879
"simulateCalls": [Function],
@@ -463,6 +464,25 @@ describe('smoke test', () => {
463464
).toBeDefined()
464465
})
465466

467+
test('sendRawTransactionSync', async () => {
468+
const request = await client.prepareTransactionRequest({
469+
account: privateKeyToAccount(accounts[0].privateKey),
470+
to: accounts[1].address,
471+
value: parseEther('1'),
472+
})
473+
const serializedTransaction = await signTransaction(client, request)
474+
const [receipt] = await Promise.all([
475+
client.sendRawTransactionSync({
476+
serializedTransaction,
477+
}),
478+
(async () => {
479+
await wait(100)
480+
await mine(client, { blocks: 1 })
481+
})(),
482+
])
483+
expect(receipt).toBeDefined()
484+
})
485+
466486
test('readContract', async () => {
467487
expect(
468488
await client.readContract({

src/clients/decorators/public.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ import {
261261
type SendRawTransactionReturnType,
262262
sendRawTransaction,
263263
} from '../../actions/wallet/sendRawTransaction.js'
264+
import {
265+
type SendRawTransactionSyncParameters,
266+
type SendRawTransactionSyncReturnType,
267+
sendRawTransactionSync,
268+
} from '../../actions/wallet/sendRawTransactionSync.js'
264269
import type { Account } from '../../types/account.js'
265270
import type { BlockNumber, BlockTag } from '../../types/block.js'
266271
import type { Chain } from '../../types/chain.js'
@@ -1535,6 +1540,33 @@ export type PublicActions<
15351540
sendRawTransaction: (
15361541
args: SendRawTransactionParameters,
15371542
) => Promise<SendRawTransactionReturnType>
1543+
/**
1544+
* Sends a **signed** transaction to the network
1545+
*
1546+
* - Docs: https://viem.sh/docs/actions/wallet/sendRawTransactionSync
1547+
* - JSON-RPC Method: [`eth_sendRawTransactionSync`](https://eips.ethereum.org/EIPS/eip-7966)
1548+
*
1549+
* @param client - Client to use
1550+
* @param parameters - {@link SendRawTransactionSyncParameters}
1551+
* @returns The transaction receipt. {@link SendRawTransactionSyncReturnType}
1552+
*
1553+
* @example
1554+
* import { createWalletClient, custom } from 'viem'
1555+
* import { mainnet } from 'viem/chains'
1556+
* import { sendRawTransactionSync } from 'viem/wallet'
1557+
*
1558+
* const client = createWalletClient({
1559+
* chain: mainnet,
1560+
* transport: custom(window.ethereum),
1561+
* })
1562+
*
1563+
* const receipt = await client.sendRawTransactionSync({
1564+
* serializedTransaction: '0x02f850018203118080825208808080c080a04012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1a04e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33'
1565+
* })
1566+
*/
1567+
sendRawTransactionSync: (
1568+
args: SendRawTransactionSyncParameters,
1569+
) => Promise<SendRawTransactionSyncReturnType<chain>>
15381570
/**
15391571
* @deprecated Use `simulateBlocks` instead.
15401572
*/
@@ -2018,6 +2050,7 @@ export function publicActions<
20182050
prepareTransactionRequest(client as any, args as any) as any,
20192051
readContract: (args) => readContract(client, args),
20202052
sendRawTransaction: (args) => sendRawTransaction(client, args),
2053+
sendRawTransactionSync: (args) => sendRawTransactionSync(client, args),
20212054
simulate: (args) => simulateBlocks(client, args),
20222055
simulateBlocks: (args) => simulateBlocks(client, args),
20232056
simulateCalls: (args) => simulateCalls(client, args),

src/clients/decorators/wallet.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { avalanche } from '../../chains/index.js'
88
import { parseEther } from '../../utils/unit/parseEther.js'
99

1010
import { walletActions } from './wallet.js'
11+
import { wait } from '../../utils/wait.js'
12+
import { mine } from '../../actions/test/mine.js'
1113

1214
const walletClient = anvilMainnet.getClient().extend(walletActions)
1315
const walletClientWithAccount = anvilMainnet
@@ -30,6 +32,7 @@ test('default', async () => {
3032
"requestPermissions": [Function],
3133
"sendCalls": [Function],
3234
"sendRawTransaction": [Function],
35+
"sendRawTransactionSync": [Function],
3336
"sendTransaction": [Function],
3437
"showCallsStatus": [Function],
3538
"signAuthorization": [Function],
@@ -119,6 +122,25 @@ describe('smoke test', () => {
119122
).toBeDefined()
120123
})
121124

125+
test('sendRawTransactionSync', async () => {
126+
const request = await walletClient.prepareTransactionRequest({
127+
account: privateKeyToAccount(accounts[0].privateKey),
128+
to: accounts[1].address,
129+
value: parseEther('1'),
130+
})
131+
const serializedTransaction = await walletClient.signTransaction(request)
132+
const [receipt] = await Promise.all([
133+
walletClient.sendRawTransactionSync({
134+
serializedTransaction,
135+
}),
136+
(async () => {
137+
await wait(100)
138+
await mine(walletClient, { blocks: 1 })
139+
})(),
140+
])
141+
expect(receipt).toBeDefined()
142+
})
143+
122144
test('sendTransaction', async () => {
123145
expect(
124146
await walletClient.sendTransaction({

src/clients/decorators/wallet.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ import {
6262
type SendRawTransactionReturnType,
6363
sendRawTransaction,
6464
} from '../../actions/wallet/sendRawTransaction.js'
65+
import {
66+
type SendRawTransactionSyncParameters,
67+
type SendRawTransactionSyncReturnType,
68+
sendRawTransactionSync,
69+
} from '../../actions/wallet/sendRawTransactionSync.js'
6570
import {
6671
type SendTransactionParameters,
6772
type SendTransactionRequest,
@@ -498,6 +503,34 @@ export type WalletActions<
498503
sendRawTransaction: (
499504
args: SendRawTransactionParameters,
500505
) => Promise<SendRawTransactionReturnType>
506+
/**
507+
* Sends a **signed** transaction to the network synchronously,
508+
* and waits for the transaction to be included in a block.
509+
*
510+
* - Docs: https://viem.sh/docs/actions/wallet/sendRawTransactionSync
511+
* - JSON-RPC Method: [`eth_sendRawTransactionSync`](https://eips.ethereum.org/EIPS/eip-7966)
512+
*
513+
* @param client - Client to use
514+
* @param parameters - {@link SendRawTransactionSyncParameters}
515+
* @returns The transaction receipt. {@link SendRawTransactionSyncReturnType}
516+
*
517+
* @example
518+
* import { createWalletClient, custom } from 'viem'
519+
* import { mainnet } from 'viem/chains'
520+
* import { sendRawTransactionSync } from 'viem/wallet'
521+
*
522+
* const client = createWalletClient({
523+
* chain: mainnet,
524+
* transport: custom(window.ethereum),
525+
* })
526+
*
527+
* const receipt = await client.sendRawTransactionSync({
528+
* serializedTransaction: '0x02f850018203118080825208808080c080a04012522854168b27e5dc3d5839bab5e6b39e1a0ffd343901ce1622e3d64b48f1a04e00902ae0502c4728cbf12156290df99c3ed7de85b1dbfe20b5c36931733a33'
529+
* })
530+
*/
531+
sendRawTransactionSync: (
532+
args: SendRawTransactionSyncParameters,
533+
) => Promise<SendRawTransactionSyncReturnType<chain>>
501534
/**
502535
* Creates, signs, and sends a new transaction to the network.
503536
*
@@ -973,6 +1006,7 @@ export function walletActions<
9731006
requestPermissions: (args) => requestPermissions(client, args),
9741007
sendCalls: (args) => sendCalls(client, args),
9751008
sendRawTransaction: (args) => sendRawTransaction(client, args),
1009+
sendRawTransactionSync: (args) => sendRawTransactionSync(client, args),
9761010
sendTransaction: (args) => sendTransaction(client, args),
9771011
showCallsStatus: (args) => showCallsStatus(client, args),
9781012
signAuthorization: (args) => signAuthorization(client, args),

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,11 @@ export type {
485485
SendRawTransactionParameters,
486486
SendRawTransactionReturnType,
487487
} from './actions/wallet/sendRawTransaction.js'
488+
export type {
489+
SendRawTransactionSyncErrorType,
490+
SendRawTransactionSyncParameters,
491+
SendRawTransactionSyncReturnType,
492+
} from './actions/wallet/sendRawTransactionSync.js'
488493
export type {
489494
SendTransactionErrorType,
490495
SendTransactionParameters,

src/types/eip1193.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,20 @@ export type PublicRpcSchema = [
11681168
Parameters: [signedTransaction: Hex]
11691169
ReturnType: Hash
11701170
},
1171+
/**
1172+
* @description Sends a **signed** transaction to the network synchronously
1173+
* @link https://eips.ethereum.org/EIPS/eip-7966
1174+
* @example
1175+
* provider.request({ method: 'eth_sendRawTransactionSync', params: ['0x...'] })
1176+
* // => '0x...'
1177+
*/
1178+
{
1179+
Method: 'eth_sendRawTransactionSync'
1180+
Parameters:
1181+
| [signedTransaction: Hex]
1182+
| [signedTransaction: Hex, timeout: Hex]
1183+
ReturnType: TransactionReceipt
1184+
},
11711185
/**
11721186
* @description Simulates execution of a set of calls with optional block and state overrides.
11731187
* @example
@@ -1711,6 +1725,20 @@ export type WalletRpcSchema = [
17111725
Parameters: [signedTransaction: Hex]
17121726
ReturnType: Hash
17131727
},
1728+
/**
1729+
* @description Sends and already-signed transaction to the network synchronously
1730+
* @link https://eips.ethereum.org/EIPS/eip-7966
1731+
* @example
1732+
* provider.request({ method: 'eth_sendRawTransactionSync', params: ['0x...'] })
1733+
* // => '0x...'
1734+
*/
1735+
{
1736+
Method: 'eth_sendRawTransactionSync'
1737+
Parameters:
1738+
| [signedTransaction: Hex]
1739+
| [signedTransaction: Hex, timeout: Hex]
1740+
ReturnType: TransactionReceipt
1741+
},
17141742
/**
17151743
* @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`
17161744
* @link https://eips.ethereum.org/EIPS/eip-1474

0 commit comments

Comments
 (0)