diff --git a/.changeset/.lucky-actors-smell.md b/.changeset/.lucky-actors-smell.md
index 2c16bdffb3..b84c5c7ca6 100644
--- a/.changeset/.lucky-actors-smell.md
+++ b/.changeset/.lucky-actors-smell.md
@@ -2,4 +2,4 @@
"viem": patch
---
-Added Crab and Koi chain
\ No newline at end of file
+Added Atleta Olympia chain
\ No newline at end of file
diff --git a/.changeset/chilly-bugs-tell.md b/.changeset/chilly-bugs-tell.md
deleted file mode 100644
index 9730cedf66..0000000000
--- a/.changeset/chilly-bugs-tell.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"viem": patch
----
-
-Exported `universalSignatureValidatorAbi`.
diff --git a/.changeset/rich-seas-jog.md b/.changeset/rich-seas-jog.md
new file mode 100644
index 0000000000..b46fcd3cee
--- /dev/null
+++ b/.changeset/rich-seas-jog.md
@@ -0,0 +1,5 @@
+---
+"viem": patch
+---
+
+Added Tron chain
diff --git a/.changeset/slimy-melons-bathe.md b/.changeset/slimy-melons-bathe.md
new file mode 100644
index 0000000000..f3feddf08f
--- /dev/null
+++ b/.changeset/slimy-melons-bathe.md
@@ -0,0 +1,5 @@
+---
+"viem": minor
+---
+
+Added built-in support for Linea gas & fee estimations.
diff --git a/.changeset/three-fireants-beg.md b/.changeset/three-fireants-beg.md
new file mode 100644
index 0000000000..454b984a28
--- /dev/null
+++ b/.changeset/three-fireants-beg.md
@@ -0,0 +1,5 @@
+---
+"viem": patch
+---
+
+Adjusted sophon native token symbol.
diff --git a/.changeset/wise-planets-press.md b/.changeset/wise-planets-press.md
new file mode 100644
index 0000000000..9b629546cc
--- /dev/null
+++ b/.changeset/wise-planets-press.md
@@ -0,0 +1,5 @@
+---
+"viem": minor
+---
+
+Deprecated `chain.fees.defaultPriorityFee`, use `chain.fees.maxPriorityFeePerGas` instead.
diff --git a/FUNDING.json b/FUNDING.json
index fa29d44658..feeec679d8 100644
--- a/FUNDING.json
+++ b/FUNDING.json
@@ -3,5 +3,8 @@
"ethereum": {
"ownedBy": "0xd2135CfB216b74109775236E36d4b433F1DF507B"
}
+ },
+ "opRetro": {
+ "projectId": "0x6bd057da522918a4675396313ae33a2f2788a1ceeb3bd7ae228015e3eb317a7d"
}
}
diff --git a/package.json b/package.json
index 55fe4b131e..6ad6676a9e 100644
--- a/package.json
+++ b/package.json
@@ -96,7 +96,7 @@
"src": {
"entry": [
"index.ts!",
- "{account-abstraction,accounts,actions,celo,chains,ens,experimental,experimental/erc7739,node,nonce,op-stack,siwe,utils,window,zksync}/index.ts!",
+ "{account-abstraction,accounts,actions,celo,chains,ens,experimental,experimental/erc7739,linea,node,nonce,op-stack,siwe,utils,window,zksync}/index.ts!",
"chains/utils.ts!"
],
"ignore": ["node/trustedSetups_cjs.ts"]
@@ -163,7 +163,7 @@
{
"name": "import * from 'viem/chains'",
"path": "./src/_esm/chains/index.js",
- "limit": "31.5 kB",
+ "limit": "38 kB",
"import": "*"
},
{
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6f637dc793..67496ccf56 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -156,7 +156,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -169,7 +169,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -182,7 +182,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -195,7 +195,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -214,7 +214,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -242,7 +242,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -264,7 +264,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -277,7 +277,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -296,7 +296,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -318,7 +318,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -331,7 +331,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -344,7 +344,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -357,7 +357,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -404,7 +404,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -432,7 +432,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.5.4)(zod@3.22.4)
+ version: 2.20.0(typescript@5.5.4)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -454,7 +454,7 @@ importers:
dependencies:
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
typescript:
specifier: ^5.0.3
@@ -473,7 +473,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -507,7 +507,7 @@ importers:
version: 18.2.0(react@18.2.0)
viem:
specifier: latest
- version: 2.19.1(typescript@5.4.2)(zod@3.22.4)
+ version: 2.20.0(typescript@5.4.2)(zod@3.22.4)
devDependencies:
'@types/react':
specifier: ^18.0.27
@@ -3225,7 +3225,6 @@ packages:
bun@1.1.12:
resolution: {integrity: sha512-NZzeZuZk7VwCs8VAXnXUHCPOlTS/IyHCscChtT1M1FLSwhBcVMsGVStYlXaaoqsinBKgp0CGJdhnJw2gR3NkDw==}
- cpu: [arm64, x64]
os: [darwin, linux, win32]
hasBin: true
@@ -6757,8 +6756,16 @@ packages:
vfile@6.0.1:
resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
- viem@2.19.1:
- resolution: {integrity: sha512-a0ca/ACEz3FRZB3OmiSfRUogWZGQh700wu7Pg3GmAWiGD+0PS9bVaWG67JQ+9azFZLq0BU/m0t2CeWd3xi8IzQ==}
+ viem@2.20.0:
+ resolution: {integrity: sha512-cM4vs81HnSNbfceI1MLkx4pCVzbVjl9xiNSv5SCutYjUyFFOVSPDlEyhpg2iHinxx1NM4Qne3END5eLT8rvUdg==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ viem@2.20.1:
+ resolution: {integrity: sha512-a/BSe25TSfkc423GTSKYl1O0ON2J5huoQeOLkylHT1WS8wh3JFqb8nfAq7vg+aZ+W06BCTn36bbi47yp4D92Cg==}
peerDependencies:
typescript: '>=5.0.4'
peerDependenciesMeta:
@@ -8447,7 +8454,7 @@ snapshots:
pino-http: 8.6.1
pino-pretty: 10.3.1
prom-client: 14.2.0
- viem: 2.19.1(typescript@5.5.2)(zod@3.22.4)
+ viem: 2.20.1(typescript@5.5.2)(zod@3.22.4)
yargs: 17.7.2
zod: 3.22.4
zod-validation-error: 1.5.0(zod@3.22.4)
@@ -14339,7 +14346,7 @@ snapshots:
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.2
- viem@2.19.1(typescript@5.4.2)(zod@3.22.4):
+ viem@2.20.0(typescript@5.4.2)(zod@3.22.4):
dependencies:
'@adraffy/ens-normalize': 1.10.0
'@noble/curves': 1.4.0
@@ -14357,37 +14364,37 @@ snapshots:
- utf-8-validate
- zod
- viem@2.19.1(typescript@5.5.2)(zod@3.22.4):
+ viem@2.20.0(typescript@5.5.4)(zod@3.22.4):
dependencies:
'@adraffy/ens-normalize': 1.10.0
'@noble/curves': 1.4.0
'@noble/hashes': 1.4.0
'@scure/bip32': 1.4.0
'@scure/bip39': 1.3.0
- abitype: 1.0.5(typescript@5.5.2)(zod@3.22.4)
+ abitype: 1.0.5(typescript@5.5.4)(zod@3.22.4)
isows: 1.0.4(ws@8.17.1)
webauthn-p256: 0.0.5
ws: 8.17.1
optionalDependencies:
- typescript: 5.5.2
+ typescript: 5.5.4
transitivePeerDependencies:
- bufferutil
- utf-8-validate
- zod
- viem@2.19.1(typescript@5.5.4)(zod@3.22.4):
+ viem@2.20.1(typescript@5.5.2)(zod@3.22.4):
dependencies:
'@adraffy/ens-normalize': 1.10.0
'@noble/curves': 1.4.0
'@noble/hashes': 1.4.0
'@scure/bip32': 1.4.0
'@scure/bip39': 1.3.0
- abitype: 1.0.5(typescript@5.5.4)(zod@3.22.4)
+ abitype: 1.0.5(typescript@5.5.2)(zod@3.22.4)
isows: 1.0.4(ws@8.17.1)
webauthn-p256: 0.0.5
ws: 8.17.1
optionalDependencies:
- typescript: 5.5.4
+ typescript: 5.5.2
transitivePeerDependencies:
- bufferutil
- utf-8-validate
diff --git a/site/pages/account-abstraction.mdx b/site/pages/account-abstraction.mdx
index 34f39c7fdd..da0b05e105 100644
--- a/site/pages/account-abstraction.mdx
+++ b/site/pages/account-abstraction.mdx
@@ -74,7 +74,7 @@ const bundlerClient = createBundlerClient({ // [!code focus]
```
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
[See `createBundlerClient` Docs](/account-abstraction/clients/bundler)
diff --git a/site/pages/account-abstraction/accounts/smart/toSmartAccount.md b/site/pages/account-abstraction/accounts/smart/toSmartAccount.md
index 03ccda1ab0..d1cdab9fee 100644
--- a/site/pages/account-abstraction/accounts/smart/toSmartAccount.md
+++ b/site/pages/account-abstraction/accounts/smart/toSmartAccount.md
@@ -2,7 +2,7 @@
description: Creates a Smart Account with a provided Account Implementation.
---
-# Custom
+# toSmartAccount
The `toSmartAccount` function allows you to create a Smart Account with a custom Account Implementation.
diff --git a/site/pages/account-abstraction/actions/bundler/estimateUserOperationGas.md b/site/pages/account-abstraction/actions/bundler/estimateUserOperationGas.md
index 3ff671d5c0..4f3661247d 100644
--- a/site/pages/account-abstraction/actions/bundler/estimateUserOperationGas.md
+++ b/site/pages/account-abstraction/actions/bundler/estimateUserOperationGas.md
@@ -48,7 +48,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
### Account Hoisting
diff --git a/site/pages/account-abstraction/actions/bundler/getChainId.md b/site/pages/account-abstraction/actions/bundler/getChainId.md
index 6929476bb0..c0ef023f1a 100644
--- a/site/pages/account-abstraction/actions/bundler/getChainId.md
+++ b/site/pages/account-abstraction/actions/bundler/getChainId.md
@@ -29,7 +29,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
## Returns
diff --git a/site/pages/account-abstraction/actions/bundler/getSupportedEntryPoints.md b/site/pages/account-abstraction/actions/bundler/getSupportedEntryPoints.md
index dc2c74b67e..48d7ea7773 100644
--- a/site/pages/account-abstraction/actions/bundler/getSupportedEntryPoints.md
+++ b/site/pages/account-abstraction/actions/bundler/getSupportedEntryPoints.md
@@ -29,7 +29,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
## Returns
diff --git a/site/pages/account-abstraction/actions/bundler/getUserOperation.md b/site/pages/account-abstraction/actions/bundler/getUserOperation.md
index b75b399b2e..85756879a3 100644
--- a/site/pages/account-abstraction/actions/bundler/getUserOperation.md
+++ b/site/pages/account-abstraction/actions/bundler/getUserOperation.md
@@ -32,7 +32,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
## Returns
diff --git a/site/pages/account-abstraction/actions/bundler/getUserOperationReceipt.md b/site/pages/account-abstraction/actions/bundler/getUserOperationReceipt.md
index 04aa6bc185..2b198cf4ba 100644
--- a/site/pages/account-abstraction/actions/bundler/getUserOperationReceipt.md
+++ b/site/pages/account-abstraction/actions/bundler/getUserOperationReceipt.md
@@ -39,7 +39,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
## Returns
diff --git a/site/pages/account-abstraction/actions/bundler/prepareUserOperation.md b/site/pages/account-abstraction/actions/bundler/prepareUserOperation.md
index cf318c8937..f1e6eec402 100644
--- a/site/pages/account-abstraction/actions/bundler/prepareUserOperation.md
+++ b/site/pages/account-abstraction/actions/bundler/prepareUserOperation.md
@@ -48,7 +48,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
### Account Hoisting
diff --git a/site/pages/account-abstraction/actions/bundler/sendUserOperation.md b/site/pages/account-abstraction/actions/bundler/sendUserOperation.md
index 73f7f63a9c..61037b0cbc 100644
--- a/site/pages/account-abstraction/actions/bundler/sendUserOperation.md
+++ b/site/pages/account-abstraction/actions/bundler/sendUserOperation.md
@@ -48,7 +48,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
### Account Hoisting
diff --git a/site/pages/account-abstraction/actions/bundler/waitForUserOperationReceipt.md b/site/pages/account-abstraction/actions/bundler/waitForUserOperationReceipt.md
index 24369a41ca..d106e5b7b6 100644
--- a/site/pages/account-abstraction/actions/bundler/waitForUserOperationReceipt.md
+++ b/site/pages/account-abstraction/actions/bundler/waitForUserOperationReceipt.md
@@ -39,7 +39,7 @@ export const bundlerClient = createBundlerClient({
:::
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
## Returns
diff --git a/site/pages/account-abstraction/clients/bundler.md b/site/pages/account-abstraction/clients/bundler.md
index 609f6ae5b2..461a212a69 100644
--- a/site/pages/account-abstraction/clients/bundler.md
+++ b/site/pages/account-abstraction/clients/bundler.md
@@ -27,7 +27,7 @@ const bundlerClient = createBundlerClient({ // [!code focus]
```
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
## Parameters
@@ -380,4 +380,4 @@ const bundlerClient = createBundlerClient({
} // [!code focus]
} // [!code focus]
})
-```
\ No newline at end of file
+```
diff --git a/site/pages/account-abstraction/guides/sending-user-operations.md b/site/pages/account-abstraction/guides/sending-user-operations.md
index 75d73934dd..ae5a16985e 100644
--- a/site/pages/account-abstraction/guides/sending-user-operations.md
+++ b/site/pages/account-abstraction/guides/sending-user-operations.md
@@ -93,7 +93,7 @@ const bundlerClient = createBundlerClient({ // [!code ++] // [!code focus]
```
:::info
-The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io) or another Bundler service.
+The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::
[See `createBundlerClient` Docs](/account-abstraction/clients/bundler)
diff --git a/site/pages/docs/accounts/local/mnemonicToAccount.md b/site/pages/docs/accounts/local/mnemonicToAccount.md
index 5e4fd1edb2..81d3b5e826 100644
--- a/site/pages/docs/accounts/local/mnemonicToAccount.md
+++ b/site/pages/docs/accounts/local/mnemonicToAccount.md
@@ -56,6 +56,7 @@ Available wordlists:
- `italian`
- `japanese`
- `korean`
+- `portuguese`
- `simplifiedChinese`
- `spanish`
- `traditionalChinese`
diff --git a/site/pages/docs/actions/public/verifyMessage.md b/site/pages/docs/actions/public/verifyMessage.md
index 4c32ccff3c..de0d9a9d7a 100644
--- a/site/pages/docs/actions/public/verifyMessage.md
+++ b/site/pages/docs/actions/public/verifyMessage.md
@@ -7,9 +7,9 @@ description: Verifies if a signed message was generated by the provided address.
Verify that a message was signed by the provided address.
:::info
-**Why should I use this over the [`verifyMessage`](../../utilities/verifyMessage.md) util?**
+**Why should I use this over the [`verifyMessage`](/docs/utilities/verifyMessage) util?**
-This Action supports verifying messages that were signed by either a Smart Contract Account or Externally Owned Account. The [`verifyMessage`](../../utilities/verifyMessage.md) util only supports Externally Owned Accounts. This is getting increasingly important as more wallets implement [Account Abstraction](https://eips.ethereum.org/EIPS/eip-4337).
+This Action supports verifying messages that were signed by either a Smart Contract Account or Externally Owned Account. The [`verifyMessage`](/docs/utilities/verifyMessage.md) util only supports Externally Owned Accounts. This is getting increasingly important as more wallets implement [Account Abstraction](https://eips.ethereum.org/EIPS/eip-4337).
:::
## Usage
diff --git a/site/pages/docs/actions/public/verifyTypedData.md b/site/pages/docs/actions/public/verifyTypedData.md
index 0d170a7ddc..38423f472a 100644
--- a/site/pages/docs/actions/public/verifyTypedData.md
+++ b/site/pages/docs/actions/public/verifyTypedData.md
@@ -7,9 +7,9 @@ description: Verifies a typed data signature
Verify that typed data was signed by the provided address.
:::info
-**Why should I use this over the [`verifyTypedData`](../../utilities/verifyTypedData.md) util?**
+**Why should I use this over the [`verifyTypedData`](/docs/utilities/verifyTypedData) util?**
-This Action supports verifying typed data that was signed by either a Smart Contract Account or Externally Owned Account (via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492)). The [`verifyTypedData`](../../utilities/verifyTypedData.md) util only supports Externally Owned Accounts. This is getting increasingly important as more wallets implement [Account Abstraction](https://eips.ethereum.org/EIPS/eip-4337).
+This Action supports verifying typed data that was signed by either a Smart Contract Account or Externally Owned Account (via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492)). The [`verifyTypedData`](/docs/utilities/verifyTypedData) util only supports Externally Owned Accounts. This is getting increasingly important as more wallets implement [Account Abstraction](https://eips.ethereum.org/EIPS/eip-4337).
:::
## Usage
diff --git a/site/pages/docs/contract/estimateContractGas.md b/site/pages/docs/contract/estimateContractGas.md
index f8d5dc87a5..b4c89da22b 100644
--- a/site/pages/docs/contract/estimateContractGas.md
+++ b/site/pages/docs/contract/estimateContractGas.md
@@ -92,7 +92,7 @@ export const wagmiAbi = [
inputs: [{ name: "owner", type: "uint32" }],
name: "mint",
outputs: [{ name: "", type: "uint32" }],
- stateMutability: "view",
+ stateMutability: "nonpayable",
type: "function",
},
...
diff --git a/site/pages/experimental/eip5792/sendCalls.mdx b/site/pages/experimental/eip5792/sendCalls.mdx
index fd9ecdb230..69feb5c866 100644
--- a/site/pages/experimental/eip5792/sendCalls.mdx
+++ b/site/pages/experimental/eip5792/sendCalls.mdx
@@ -101,6 +101,82 @@ export const walletClient = createWalletClient({
:::
+### Contract Calls
+
+The `calls` property also accepts **Contract Calls**, and can be used via the `abi`, `functionName`, and `args` properties.
+
+:::code-group
+
+```ts twoslash [example.ts]
+import { parseAbi } from 'viem'
+import { walletClient } from './config'
+
+const abi = parseAbi([
+ 'function approve(address, uint256) returns (bool)',
+ 'function transferFrom(address, address, uint256) returns (bool)',
+])
+
+const id = await walletClient.sendCalls({ // [!code focus:99]
+ calls: [
+ {
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
+ value: parseEther('1')
+ },
+ {
+ to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
+ abi,
+ functionName: 'approve',
+ args: [
+ '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
+ 100n
+ ],
+ },
+ {
+ to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
+ abi,
+ functionName: 'transferFrom',
+ args: [
+ '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
+ '0x0000000000000000000000000000000000000000',
+ 100n
+ ],
+ },
+ ],
+})
+```
+
+```ts twoslash [abi.ts] filename="abi.ts"
+export const wagmiAbi = [
+ // ...
+ {
+ inputs: [],
+ name: "mint",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ // ...
+] as const;
+```
+
+```ts [config.ts] filename="config.ts"
+import 'viem/window'
+import { createWalletClient, custom } from 'viem'
+import { walletActionsEip5792 } from 'viem/experimental'
+
+// Retrieve Account from an EIP-1193 Provider.
+const [account] = await window.ethereum!.request({
+ method: 'eth_requestAccounts'
+})
+
+export const walletClient = createWalletClient({
+ account,
+ transport: custom(window.ethereum!)
+}).extend(walletActionsEip5792())
+```
+
+:::
+
## Returns
`string`
diff --git a/site/pages/experimental/eip5792/writeContracts.mdx b/site/pages/experimental/eip5792/writeContracts.mdx
deleted file mode 100644
index 9727c81306..0000000000
--- a/site/pages/experimental/eip5792/writeContracts.mdx
+++ /dev/null
@@ -1,425 +0,0 @@
----
-description: Sign and broadcast a batch of write contract calls to the network.
----
-
-# writeContracts
-
-Requests for the wallet to sign and broadcast a batch of write contract calls (transactions) to the network.
-
-[Read more.](https://github.com/ethereum/EIPs/blob/815028dc634463e1716fc5ce44c019a6040f0bef/EIPS/eip-5792.md#wallet_sendcalls)
-
-:::warning[Warning]
-This is an experimental action that is not supported in most wallets. It is recommended to have a fallback mechanism if using this in production.
-:::
-
-## Usage
-
-:::code-group
-
-```ts twoslash [example.ts]
-import { parseAbi } from 'viem'
-import { account, walletClient } from './config'
-
-const abi = parseAbi([
- 'function approve(address, uint256) returns (bool)',
- 'function transferFrom(address, address, uint256) returns (bool)',
-])
-
-const id = await walletClient.writeContracts({
- account,
- contracts: [
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'approve',
- args: [
- '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
- 100n
- ],
- },
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'transferFrom',
- args: [
- '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
- '0x0000000000000000000000000000000000000000',
- 100n
- ],
- },
- ],
-})
-```
-
-```ts twoslash [config.ts] filename="config.ts"
-import 'viem/window'
-// ---cut---
-import { createWalletClient, custom } from 'viem'
-import { mainnet } from 'viem/chains'
-import { walletActionsEip5792 } from 'viem/experimental'
-
-export const walletClient = createWalletClient({
- chain: mainnet,
- transport: custom(window.ethereum!),
-}).extend(walletActionsEip5792())
-
-export const [account] = await walletClient.getAddresses()
-```
-
-:::
-
-Notes:
-
-- Internally calls [`sendCalls`](/experimental/eip5792/writeContracts)
-- `account` and `chain` are top level properties as all calls should be sent by the same account and chain.
-- [Read `wallet_sendCalls` on EIP-5792.](https://github.com/ethereum/EIPs/blob/815028dc634463e1716fc5ce44c019a6040f0bef/EIPS/eip-5792.md#wallet_sendcalls)
-
-### Account Hoisting
-
-If you do not wish to pass an `account` to every `writeContracts`, you can also hoist the Account on the Wallet Client (see `config.ts`).
-
-[Learn more](/docs/clients/wallet#account).
-
-:::code-group
-
-```ts twoslash [example.ts]
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi([
- 'function approve(address, uint256) returns (bool)',
- 'function transferFrom(address, address, uint256) returns (bool)',
-])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'approve',
- args: [
- '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
- 100n
- ],
- },
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'transferFrom',
- args: [
- '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC',
- '0x0000000000000000000000000000000000000000',
- 100n
- ],
- },
- ],
-})
-```
-
-```ts [config.ts] filename="config.ts"
-import 'viem/window'
-import { createWalletClient, custom } from 'viem'
-import { walletActionsEip5792 } from 'viem/experimental'
-
-// Retrieve Account from an EIP-1193 Provider.
-const [account] = await window.ethereum!.request({
- method: 'eth_requestAccounts'
-})
-
-export const walletClient = createWalletClient({
- account,
- transport: custom(window.ethereum!)
-}).extend(walletActionsEip5792())
-```
-
-:::
-
-## Returns
-
-`string`
-
-The identifier can be any arbitrary string. The only requirement is that for a given session, consumers should be able to call `getCallsStatus` with this identifier to retrieve a batch call status and call receipts.
-
-## Parameters
-
-### account
-
-- **Type:** `Account | Address`
-
-The Account to sign & broadcast the call from.
-
-Accepts a [JSON-RPC Account](/docs/clients/wallet#json-rpc-accounts).
-
-```ts twoslash
-import { parseAbi, mainnet } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // [!code focus]
- contracts: [
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'mint',
- args: [69n],
- },
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'mint',
- args: [420n],
- },
- ],
-})
-```
-
-### chain
-
-- **Type:** [`Chain`](/docs/glossary/types#chain)
-- **Default:** `walletClient.chain`
-
-The target chain to broadcast the calls.
-
-```ts twoslash
-import { parseAbi, mainnet } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- chain: mainnet, // [!code focus]
- contracts: [
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'mint',
- args: [69n],
- },
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- abi,
- functionName: 'mint',
- args: [420n],
- },
- ],
-})
-```
-
-### contracts
-
-- **Type:** `MulticallContracts[]`
-
-An array of write contract calls to be signed and broadcasted.
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [ // [!code focus]
- { // [!code focus]
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
- abi, // [!code focus]
- functionName: 'mint', // [!code focus]
- args: [69n], // [!code focus]
- }, // [!code focus]
- { // [!code focus]
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
- abi, // [!code focus]
- functionName: 'mint', // [!code focus]
- args: [420n], // [!code focus]
- }, // [!code focus]
- ],
-})
-```
-
-#### contracts.abi
-
-- **Type:** `Abi`
-
-The contract's ABI.
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- abi, // [!code focus]
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [69n],
- },
- {
- abi, // [!code focus]
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [420n],
- },
- ],
-})
-```
-
-#### contracts.address
-
-- **Type:** `Address`
-
-The contract address.
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
- abi,
- functionName: 'mint',
- args: [69n],
- },
- {
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
- abi,
- functionName: 'mint',
- args: [420n],
- },
- ],
-})
-```
-
-#### contracts.functionName
-
-- **Type:** `string`
-
-A function to extract from the ABI.
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint', // [!code focus]
- args: [69n],
- },
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint', // [!code focus]
- args: [420n],
- },
- ],
-})
-```
-
-#### contracts.args
-
-- **Type:** Inferred from ABI.
-
-Arguments to pass to function call.
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [69n], // [!code focus]
- },
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [420n], // [!code focus]
- },
- ],
-})
-```
-
-#### contracts.value
-
-- **Type:** `number`
-
-Value in wei sent with this call.
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [69n],
- },
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [420n],
- value: 69420n, // [!code focus]
- },
- ],
-})
-```
-
-### capabilities
-
-- **Type:** `WalletCapabilities`
-
-Capability metadata for the calls (e.g. specifying a paymaster).
-
-```ts twoslash
-import { parseAbi } from 'viem'
-import { walletClient } from './config'
-
-const abi = parseAbi(['function mint(uint256)'])
-
-const id = await walletClient.writeContracts({
- contracts: [
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [69n],
- },
- {
- abi,
- address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
- functionName: 'mint',
- args: [420n],
- },
- ],
- capabilities: { // [!code focus]
- paymasterService: { // [!code focus]
- url: 'https://...' // [!code focus]
- } // [!code focus]
- } // [!code focus]
-})
-```
-
diff --git a/site/pages/op-stack/utilities/opaqueDataToDepositData.md b/site/pages/op-stack/utilities/opaqueDataToDepositData.md
new file mode 100644
index 0000000000..1e28cecd7d
--- /dev/null
+++ b/site/pages/op-stack/utilities/opaqueDataToDepositData.md
@@ -0,0 +1,51 @@
+---
+description: Converts opaque data into a structured deposit data format.
+---
+
+# opaqueDataToDepositData
+
+Converts an opaque data into a structured deposit data object. This includes extracting and converting the `mint`, `value`, `gas`, `isCreation` flag, and `data` from a hex string.
+
+## Import
+
+```ts
+import { opaqueDataToDepositData } from "viem";
+```
+
+## Usage
+
+```ts
+import { opaqueDataToDepositData } from "viem";
+
+const opaqueData =
+ "0x00000000000000000000000000000000000000000000000000470DE4DF82000000000000000000000000000000000000000000000000000000470DE4DF82000000000000000186A00001";
+
+const depositData = opaqueDataToDepositData(opaqueData);
+// {
+// mint: 20000000000000000n,
+// value: 20000000000000000n,
+// gas: 100000n,
+// isCreation: false,
+// data: '0x01',
+// }
+```
+
+## Returns
+
+`OpaqueDataToDepositDataReturnType`
+
+An object containing the parsed deposit data.
+
+## Parameters
+
+### opaqueData
+
+- **Type:** `Hex`
+
+The opaque hex-encoded data.
+
+## Errors
+
+`OpaqueDataToDepositDataErrorType`
+
+An error type that includes potential slice, size, and generic errors encountered during the parsing process.
diff --git a/site/pages/zksync/accounts/toMultisigSmartAccount.md b/site/pages/zksync/accounts/toMultisigSmartAccount.md
new file mode 100644
index 0000000000..93e92ea678
--- /dev/null
+++ b/site/pages/zksync/accounts/toMultisigSmartAccount.md
@@ -0,0 +1,46 @@
+---
+description: Creates a multi-signature ZKsync Smart Account
+---
+
+# toMultisigSmartAccount (ZKsync)
+
+Creates a multi-signature [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and the Private Key of the owner.
+
+## Usage
+
+```ts twoslash
+import { toMultisigSmartAccount } from 'viem/zksync'
+
+const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266',
+ privateKeys: ['0x...', '0x...']
+})
+```
+
+## Parameters
+
+### address
+
+- **Type:** `Hex`
+
+Address of the deployed Account's Contract implementation.
+
+```ts
+const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus]
+ privateKeys: ['0x...', '0x...']
+})
+```
+
+### privateKeys
+
+- **Type:** `Hex[]`
+
+Private Keys of the owners.
+
+```ts
+const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKeys: ['0x...', '0x...'] // [!code focus]
+})
+```
\ No newline at end of file
diff --git a/site/pages/zksync/accounts/toSinglesigSmartAccount.md b/site/pages/zksync/accounts/toSinglesigSmartAccount.md
new file mode 100644
index 0000000000..ff94b3e80b
--- /dev/null
+++ b/site/pages/zksync/accounts/toSinglesigSmartAccount.md
@@ -0,0 +1,46 @@
+---
+description: Creates a single-signature ZKsync Smart Account
+---
+
+# toSinglesigSmartAccount (ZKsync)
+
+Creates a single-signature [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and the Private Key of the owner.
+
+## Usage
+
+```ts twoslash
+import { toSinglesigSmartAccount } from 'viem/zksync'
+
+const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266',
+ privateKey: '0x...'
+})
+```
+
+## Parameters
+
+### address
+
+- **Type:** `Hex`
+
+Address of the deployed Account's Contract implementation.
+
+```ts
+const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus]
+ privateKey: '0x...'
+})
+```
+
+### privateKey
+
+- **Type:** `Hex`
+
+Private Key of the owner.
+
+```ts
+const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKey: '0x...' // [!code focus]
+})
+```
\ No newline at end of file
diff --git a/site/pages/zksync/accounts/toSmartAccount.md b/site/pages/zksync/accounts/toSmartAccount.md
new file mode 100644
index 0000000000..dc9513920b
--- /dev/null
+++ b/site/pages/zksync/accounts/toSmartAccount.md
@@ -0,0 +1,53 @@
+---
+description: Creates a ZKsync Smart Account
+---
+
+# toSmartAccount (ZKsync)
+
+Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and a custom sign function.
+
+## Usage
+
+```ts twoslash
+import { toSmartAccount } from 'viem/zksync'
+
+const account = toSmartAccount({
+ address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266',
+ async sign({ hash }) {
+ // ... signing logic
+ return '0x...'
+ }
+})
+```
+
+## Parameters
+
+### address
+
+- **Type:** `Hex`
+
+Address of the deployed Account's Contract implementation.
+
+```ts
+const account = toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus]
+ async sign({ hash }) {
+ // ...
+ }
+})
+```
+
+### sign
+
+- **Type:** `({ hash: Hex }) => Hex`
+
+Custom sign function for the Smart Account.
+
+```ts
+const account = toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ async sign({ hash }) { // [!code focus]
+ // ... // [!code focus]
+ } // [!code focus]
+})
+```
\ No newline at end of file
diff --git a/site/sidebar.ts b/site/sidebar.ts
index 6907a65a78..8c4daad846 100644
--- a/site/sidebar.ts
+++ b/site/sidebar.ts
@@ -1242,10 +1242,6 @@ export const sidebar = {
text: 'showCallsStatus',
link: '/experimental/eip5792/showCallsStatus',
},
- {
- text: 'writeContracts',
- link: '/experimental/eip5792/writeContracts',
- },
],
},
],
@@ -1593,6 +1589,23 @@ export const sidebar = {
{ text: 'Chains', link: '/zksync/chains' },
],
},
+ {
+ text: 'Smart Accounts',
+ items: [
+ {
+ text: 'Singlesig',
+ link: '/zksync/accounts/toSinglesigSmartAccount',
+ },
+ {
+ text: 'Multisig',
+ link: '/zksync/accounts/toMultisigSmartAccount',
+ },
+ {
+ text: 'Custom',
+ link: '/zksync/accounts/toSmartAccount',
+ },
+ ],
+ },
{
text: 'EIP-712 Actions',
items: [
diff --git a/site/vercel.json b/site/vercel.json
index 5a023af28f..21e6d9c7ec 100644
--- a/site/vercel.json
+++ b/site/vercel.json
@@ -55,6 +55,10 @@
{
"source": "/:match/accounts/signTypedData",
"destination": "/:match/accounts/local/signTypedData"
+ },
+ {
+ "source": "/:match/experimental/eip5792/writeContracts",
+ "destination": "/:match/experimental/eip5792/sendCalls#contract-calls"
}
]
}
diff --git a/site/vocs.config.tsx b/site/vocs.config.tsx
index 3a103a988e..9cb10133f3 100644
--- a/site/vocs.config.tsx
+++ b/site/vocs.config.tsx
@@ -8,7 +8,7 @@ export default defineConfig({
// backgroundColor: '#3a393b',
// textColor: 'white',
// content:
- // 'Viem is participating in the Gitcoin Grants 20 round. Consider [supporting the project](https://explorer.gitcoin.co/#/round/42161/27/20). Thank you. 🙏',
+ // 'Viem is participating in Gitcoin Grants round 21. Consider [supporting the project](https://explorer.gitcoin.co/#/round/42161/389/73). Thank you. 🙏',
// },
baseUrl:
process.env.VERCEL_ENV === 'production'
@@ -45,7 +45,9 @@ export default defineConfig({
rootDir: '.',
search: {
boostDocument(documentId) {
- if (documentId.startsWith('pages/docs')) return 2
+ if (documentId.startsWith('pages/docs')) return 3
+ if (documentId.startsWith('pages/account-abstraction')) return 2
+ if (documentId.startsWith('pages/experimental')) return 2
return 1
},
},
@@ -221,6 +223,31 @@ export default defineConfig({
'https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/reservoir-light.svg',
},
],
+ [
+ {
+ name: 'Uniswap',
+ link: 'https://uniswap.org',
+ image:
+ 'https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/uniswap-light.svg',
+ },
+ {
+ name: 'Biconomy',
+ image:
+ 'https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/biconomy-light.svg',
+ link: 'https://biconomy.io',
+ },
+ {
+ name: 'Thirdweb',
+ image:
+ 'https://raw.githubusercontent.com/wevm/.github/main/content/sponsors/thirdweb-light.svg',
+ link: 'https://thirdweb.com',
+ },
+ {
+ name: '',
+ image: '',
+ link: 'https://github.com/sponsors/wevm',
+ },
+ ],
],
},
],
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index 8697858c80..3f7eebb281 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -1,5 +1,106 @@
# viem
+## 2.20.1
+
+### Patch Changes
+
+- [#2646](https://github.com/wevm/viem/pull/2646) [`50bde96b`](https://github.com/wevm/viem/commit/50bde96b39e4e2980e995f5288ea9b6a2f62a530) Thanks [@thobbyAk](https://github.com/thobbyAk)! - Added Botanix Testnet.
+
+- [#2644](https://github.com/wevm/viem/pull/2644) [`2eb817a2`](https://github.com/wevm/viem/commit/2eb817a25bfe0bfdb36df02c7907f38b889f474a) Thanks [@RobbyUitbeijerse](https://github.com/RobbyUitbeijerse)! - Added Sophon Testnet.
+
+- [#2656](https://github.com/wevm/viem/pull/2656) [`df905fe5`](https://github.com/wevm/viem/commit/df905fe5dd87e1a2a78494eba1845c6012606a51) Thanks [@Jabher](https://github.com/Jabher)! - Added Holesky API URL.
+
+- [#2654](https://github.com/wevm/viem/pull/2654) [`02415bce`](https://github.com/wevm/viem/commit/02415bcef331eeeee18f5e427766048b6fb33a96) Thanks [@lyszhang](https://github.com/lyszhang)! - Added Hashkey Chain testnet.
+
+- [#2657](https://github.com/wevm/viem/pull/2657) [`f31e93ec`](https://github.com/wevm/viem/commit/f31e93ec5ad65e551bb3a152f138aff3a157b9f6) Thanks [@jeanregisser](https://github.com/jeanregisser)! - Exported `portuguese` wordlist.
+
+- [#2643](https://github.com/wevm/viem/pull/2643) [`8ac740e9`](https://github.com/wevm/viem/commit/8ac740e9cec18a3138a771053eeb45397179885a) Thanks [@0oooooooo0](https://github.com/0oooooooo0)! - Added kaia chain.
+
+- [#2655](https://github.com/wevm/viem/pull/2655) [`4d874283`](https://github.com/wevm/viem/commit/4d8742836f53753e3ab1312c8aa3a66284253d22) Thanks [@RobbyUitbeijerse](https://github.com/RobbyUitbeijerse)! - Added multicall3 to Sophon Testnet.
+
+## 2.20.0
+
+### Minor Changes
+
+- [#2641](https://github.com/wevm/viem/pull/2641) [`89d11ed`](https://github.com/wevm/viem/commit/89d11edb558656bc3c97f0c410a448f99f92a1f4) Thanks [@jxom](https://github.com/jxom)! - **Experimental:** Deprecated `writeContracts`. Use `sendCalls` instead.
+
+- [#2641](https://github.com/wevm/viem/pull/2641) [`89d11ed`](https://github.com/wevm/viem/commit/89d11edb558656bc3c97f0c410a448f99f92a1f4) Thanks [@jxom](https://github.com/jxom)! - **Experimental:** Updated `sendCalls` to match the updated EIP-5792 spec (`chainId` per call).
+
+- [#2641](https://github.com/wevm/viem/pull/2641) [`89d11ed`](https://github.com/wevm/viem/commit/89d11edb558656bc3c97f0c410a448f99f92a1f4) Thanks [@jxom](https://github.com/jxom)! - **Experimental:** Updated `sendCalls` to also accept contract function interface.
+
+### Patch Changes
+
+- [#2638](https://github.com/wevm/viem/pull/2638) [`9cbd082`](https://github.com/wevm/viem/commit/9cbd0820533c278bb4e40ff390a7091a607152b3) Thanks [@jxom](https://github.com/jxom)! - Added \`nonceKeyManager\` as a property to \`toSmartAccount\`.
+
+- [#2638](https://github.com/wevm/viem/pull/2638) [`9cbd082`](https://github.com/wevm/viem/commit/9cbd0820533c278bb4e40ff390a7091a607152b3) Thanks [@jxom](https://github.com/jxom)! - Added ability to pass full-formed User Operations to `sendUserOperation` and `estimateUserOperationGas`.
+
+- [#2639](https://github.com/wevm/viem/pull/2639) [`9a1c6ab`](https://github.com/wevm/viem/commit/9a1c6abe6c89c444c8fc28cea1fc6ef9759ae53b) Thanks [@jxom](https://github.com/jxom)! - **OP Stack:** Tweaked proof submitter logic.
+
+## 2.19.9
+
+### Patch Changes
+
+- [#2598](https://github.com/wevm/viem/pull/2598) [`627274b`](https://github.com/wevm/viem/commit/627274b0cf70906d6d521f53e3290a87bcaee2b3) Thanks [@jxom](https://github.com/jxom)! - Added ZKsync `toSmartAccount`.
+
+- [#2636](https://github.com/wevm/viem/pull/2636) [`5f60093`](https://github.com/wevm/viem/commit/5f6009360eaa41caf7318deb832dae7484190b5b) Thanks [@saeta-eth](https://github.com/saeta-eth)! - Added support for `'evm_setAccountCode'` to `setCode` action.
+
+## 2.19.8
+
+### Patch Changes
+
+- [#2631](https://github.com/wevm/viem/pull/2631) [`b36cb2db`](https://github.com/wevm/viem/commit/b36cb2dbe7c83c36c54810839506399cf2882945) Thanks [@jxom](https://github.com/jxom)! - **OP Stack:** Handled case for `InvalidGameType` error on `getWithdrawalStatus`
+
+## 2.19.7
+
+### Patch Changes
+
+- [#2624](https://github.com/wevm/viem/pull/2624) [`46dd252`](https://github.com/wevm/viem/commit/46dd2523a96d8372b0d0cb5ffe56c613bf073048) Thanks [@holic](https://github.com/holic)! - Improved `writeContract` error handling.
+
+- [#2628](https://github.com/wevm/viem/pull/2628) [`a040bc4`](https://github.com/wevm/viem/commit/a040bc430293604cd8532c3f6349a56b2a5d366a) Thanks [@boavenn](https://github.com/boavenn)! - Added cronoszkEVM chain.
+
+- [`918bed5`](https://github.com/wevm/viem/commit/918bed5ee48b39b08d8ab8e879722358cc91ec56) Thanks [@jxom](https://github.com/jxom)! - Update 7702 implementation to be compatible with devnet3.
+
+- [#2629](https://github.com/wevm/viem/pull/2629) [`34093e1`](https://github.com/wevm/viem/commit/34093e12076639f110017cb5f9196884608eb76c) Thanks [@KONFeature](https://github.com/KONFeature)! - Exported `PaymasterRpcSchema`.
+
+- [#2625](https://github.com/wevm/viem/pull/2625) [`507eed7`](https://github.com/wevm/viem/commit/507eed7284c2ac6867fef850a0e2923b9078671e) Thanks [@qiwu7](https://github.com/qiwu7)! - Added B3 chain
+
+## 2.19.6
+
+### Patch Changes
+
+- [#2619](https://github.com/wevm/viem/pull/2619) [`ccaddcd9`](https://github.com/wevm/viem/commit/ccaddcd909b5f957f9b8352f7646f349402bb776) Thanks [@nialexsan](https://github.com/nialexsan)! - Added Multicall contract to Flow Testnet.
+
+- [#2620](https://github.com/wevm/viem/pull/2620) [`a8c78cb4`](https://github.com/wevm/viem/commit/a8c78cb4cbd5224259482114c6d65ce5b0b10f6b) Thanks [@jxom](https://github.com/jxom)! - Made `getNonce` optional on `SmartAccountImplementation`.
+
+- [#2614](https://github.com/wevm/viem/pull/2614) [`3749838f`](https://github.com/wevm/viem/commit/3749838fdd915ebccc56505ecd5a8047bfb8f38d) Thanks [@joshuanwankwo](https://github.com/joshuanwankwo)! - Added Curtis chain
+
+## 2.19.5
+
+### Patch Changes
+
+- [#2608](https://github.com/wevm/viem/pull/2608) [`cff2aac6`](https://github.com/wevm/viem/commit/cff2aac683f4e2e60e8ce84e3933f02b31311ed0) Thanks [@HighGecko](https://github.com/HighGecko)! - Added IOTA, IOTA Testnet and CHIPS chains.
+
+- [#2609](https://github.com/wevm/viem/pull/2609) [`bf0fe9c6`](https://github.com/wevm/viem/commit/bf0fe9c653697a2eadc16ec2ce53d149b10d68a8) Thanks [@CeoFred](https://github.com/CeoFred)! - Added AssetChain Testnet chain.
+
+## 2.19.4
+
+### Patch Changes
+
+- [`e708c5bd`](https://github.com/wevm/viem/commit/e708c5bd5dea9ee97fefaf6c4bf1d70080898851) Thanks [@jxom](https://github.com/jxom)! - Updated zkSync `getEip712Domain` name.
+
+- [#2606](https://github.com/wevm/viem/pull/2606) [`44cc5ecd`](https://github.com/wevm/viem/commit/44cc5ecd6bca2ee81ab2db0cd4ae273310f37302) Thanks [@ezynda3](https://github.com/ezynda3)! - Updated Taiko block explorer.
+
+- [`9aaa159f`](https://github.com/wevm/viem/commit/9aaa159f8e3f0aef45248368a2dd65a16e101c90) Thanks [@jxom](https://github.com/jxom)! - Removed redundant chain assertion for Local Accounts.
+
+## 2.19.3
+
+### Patch Changes
+
+- [#2595](https://github.com/wevm/viem/pull/2595) [`e022146`](https://github.com/wevm/viem/commit/e022146393bc3f3f315f2cf6a5b419adc1e5983d) Thanks [@gregfromstl](https://github.com/gregfromstl)! - Exported `universalSignatureValidatorAbi`.
+
+- [#2596](https://github.com/wevm/viem/pull/2596) [`cb127ab`](https://github.com/wevm/viem/commit/cb127ab7d1334c7a73dfdc1d22ba4e4e7fa868dc) Thanks [@danielsimao](https://github.com/danielsimao)! - Added Bob Sepolia chain.
+ Added OP Stack addresses to Bob chain.
+
## 2.19.2
### Patch Changes
diff --git a/src/README.md b/src/README.md
index bace303b34..be378b552b 100644
--- a/src/README.md
+++ b/src/README.md
@@ -1,3 +1,6 @@
+
+
+
@@ -222,6 +225,24 @@ Check out the following places for more viem-related content:
+
+
+
+
+
+
+
+
+
## Contributing
diff --git a/src/account-abstraction/accounts/implementations/toCoinbaseSmartAccount.ts b/src/account-abstraction/accounts/implementations/toCoinbaseSmartAccount.ts
index 66cabcbdce..7995fb62b6 100644
--- a/src/account-abstraction/accounts/implementations/toCoinbaseSmartAccount.ts
+++ b/src/account-abstraction/accounts/implementations/toCoinbaseSmartAccount.ts
@@ -1,4 +1,4 @@
-import { type Address, type TypedData, parseAbi } from 'abitype'
+import type { Address, TypedData } from 'abitype'
import {
type WebAuthnData,
parseSignature as parseP256Signature,
@@ -132,19 +132,6 @@ export async function toCoinbaseSmartAccount(
return { factory: factory.address, factoryData }
},
- async getNonce({ key = 0n } = {}) {
- const address = await this.getAddress()
- const nonce = await readContract(client, {
- abi: parseAbi([
- 'function getNonce(address, uint192) pure returns (uint256)',
- ]),
- address: entryPoint.address,
- functionName: 'getNonce',
- args: [address, key],
- })
- return nonce
- },
-
async getStubSignature() {
if (owner.type === 'webAuthn')
return '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000'
diff --git a/src/account-abstraction/accounts/implementations/toSoladySmartAccount.ts b/src/account-abstraction/accounts/implementations/toSoladySmartAccount.ts
index 9c9308d22b..204eadfc3a 100644
--- a/src/account-abstraction/accounts/implementations/toSoladySmartAccount.ts
+++ b/src/account-abstraction/accounts/implementations/toSoladySmartAccount.ts
@@ -1,4 +1,4 @@
-import { type Abi, type Address, type TypedData, parseAbi } from 'abitype'
+import type { Abi, Address, TypedData } from 'abitype'
import { parseAccount } from '../../../accounts/utils/parseAccount.js'
import { readContract } from '../../../actions/public/readContract.js'
@@ -34,6 +34,7 @@ export type ToSoladySmartAccountParameters<
}
| undefined
factoryAddress?: Address | undefined
+ getNonce?: SmartAccountImplementation['getNonce'] | undefined
owner: Address | Account
salt?: Hex | undefined
}
@@ -86,6 +87,7 @@ export async function toSoladySmartAccount<
version: '0.7',
},
factoryAddress = '0x5d82735936c6Cd5DE57cC3c1A799f6B2E6F933Df',
+ getNonce,
salt = '0x0',
} = parameters
@@ -103,6 +105,7 @@ export async function toSoladySmartAccount<
return toSmartAccount({
client,
entryPoint,
+ getNonce,
extend: { abi, factory },
@@ -144,19 +147,6 @@ export async function toSoladySmartAccount<
return { factory: factory.address, factoryData }
},
- async getNonce({ key = 0n } = {}) {
- const address = await this.getAddress()
- const nonce = await readContract(client, {
- abi: parseAbi([
- 'function getNonce(address, uint192) pure returns (uint256)',
- ]),
- address: entryPoint.address,
- functionName: 'getNonce',
- args: [address, key],
- })
- return nonce
- },
-
async getStubSignature() {
return '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
},
diff --git a/src/account-abstraction/accounts/toSmartAccount.test.ts b/src/account-abstraction/accounts/toSmartAccount.test.ts
index e677d8a143..d26d80a9e9 100644
--- a/src/account-abstraction/accounts/toSmartAccount.test.ts
+++ b/src/account-abstraction/accounts/toSmartAccount.test.ts
@@ -4,8 +4,9 @@ import { anvilMainnet } from '../../../test/src/anvil.js'
import { accounts } from '../../../test/src/constants.js'
import { deploySoladyAccount_07 } from '../../../test/src/utils.js'
import { mine, writeContract } from '../../actions/index.js'
-import { pad } from '../../utils/index.js'
+import { createNonceManager, pad } from '../../utils/index.js'
import { toSoladySmartAccount } from './implementations/toSoladySmartAccount.js'
+import { toSmartAccount } from './toSmartAccount.js'
const client = anvilMainnet.getClient({ account: true })
@@ -912,6 +913,40 @@ test('default', async () => {
`)
})
+test('args: nonceKeyManager', async () => {
+ const { factoryAddress } = await deploySoladyAccount_07()
+
+ const nonceKeyManager = createNonceManager({
+ source: {
+ get() {
+ return 69
+ },
+ set() {},
+ },
+ })
+
+ const implementation = await toSoladySmartAccount({
+ client,
+ factoryAddress,
+ owner: accounts[1].address,
+ })
+ const account = await toSmartAccount({
+ ...implementation,
+ async getNonce(parameters) {
+ return parameters!.key as bigint
+ },
+ nonceKeyManager,
+ })
+
+ const nonces = await Promise.all([
+ account.getNonce(),
+ account.getNonce(),
+ account.getNonce(),
+ ])
+
+ expect(nonces).toEqual([69n, 70n, 71n])
+})
+
test('return value: `isDeployed`', async () => {
const { factoryAddress } = await deploySoladyAccount_07()
@@ -946,7 +981,7 @@ test('return value: `getFactoryArgs`', async () => {
expect(await account.getFactoryArgs()).toMatchInlineSnapshot(`
{
- "factory": "0xd73bab8f06db28c87932571f87d0d2c0fdf13d94",
+ "factory": "0x82a9286db983093ff234cefcea1d8fa66382876b",
"factoryData": "0xf14ddffc00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000000000000",
}
`)
@@ -966,3 +1001,28 @@ test('return value: `getFactoryArgs`', async () => {
}
`)
})
+
+test('return value: `getNonce`', async () => {
+ const { factoryAddress } = await deploySoladyAccount_07()
+
+ const account = await toSoladySmartAccount({
+ client,
+ factoryAddress,
+ owner: accounts[1].address,
+ })
+ expect(await account.getNonce()).toBeDefined()
+})
+
+test('return value: `getNonce` (implementation override)', async () => {
+ const { factoryAddress } = await deploySoladyAccount_07()
+
+ const account = await toSoladySmartAccount({
+ client,
+ factoryAddress,
+ owner: accounts[1].address,
+ async getNonce() {
+ return 69n
+ },
+ })
+ expect(await account.getNonce()).toBe(69n)
+})
diff --git a/src/account-abstraction/accounts/toSmartAccount.ts b/src/account-abstraction/accounts/toSmartAccount.ts
index fec430171f..e3ca0c2a0f 100644
--- a/src/account-abstraction/accounts/toSmartAccount.ts
+++ b/src/account-abstraction/accounts/toSmartAccount.ts
@@ -1,6 +1,7 @@
-import type { Abi } from 'abitype'
+import { type Abi, parseAbi } from 'abitype'
import { getCode } from '../../actions/public/getCode.js'
+import { readContract } from '../../actions/public/readContract.js'
import type { Prettify } from '../../types/utils.js'
import { getAction } from '../../utils/getAction.js'
import { createNonceManager } from '../../utils/nonceManager.js'
@@ -30,21 +31,23 @@ export async function toSmartAccount<
>(
implementation: implementation,
): Promise> {
- const { extend, ...rest } = implementation
+ const {
+ extend,
+ nonceKeyManager = createNonceManager({
+ source: {
+ get() {
+ return Date.now()
+ },
+ set() {},
+ },
+ }),
+ ...rest
+ } = implementation
let deployed = false
const address = await implementation.getAddress()
- const nonceKeyManager = createNonceManager({
- source: {
- get() {
- return Date.now()
- },
- set() {},
- },
- })
-
return {
...extend,
...rest,
@@ -64,7 +67,19 @@ export async function toSmartAccount<
client: implementation.client,
}),
)
- return await implementation.getNonce({ ...parameters, key })
+
+ if (implementation.getNonce)
+ return await implementation.getNonce({ ...parameters, key })
+
+ const nonce = await readContract(implementation.client, {
+ abi: parseAbi([
+ 'function getNonce(address, uint192) pure returns (uint256)',
+ ]),
+ address: implementation.entryPoint.address,
+ functionName: 'getNonce',
+ args: [address, key],
+ })
+ return nonce
},
async isDeployed() {
if (deployed) return true
diff --git a/src/account-abstraction/accounts/types.ts b/src/account-abstraction/accounts/types.ts
index 5ed9e3342a..180380c110 100644
--- a/src/account-abstraction/accounts/types.ts
+++ b/src/account-abstraction/accounts/types.ts
@@ -5,6 +5,7 @@ import type { Client } from '../../clients/createClient.js'
import type { Hash, Hex, SignableMessage } from '../../types/misc.js'
import type { TypedDataDefinition } from '../../types/typedData.js'
import type { Assign, ExactPartial, UnionPartialBy } from '../../types/utils.js'
+import type { NonceManager } from '../../utils/nonceManager.js'
import type { EntryPointVersion } from '../types/entryPointVersion.js'
import type {
EstimateUserOperationGasReturnType,
@@ -88,9 +89,11 @@ export type SmartAccountImplementation<
* // 1n
* ```
*/
- getNonce: (
- parameters?: { key?: bigint | undefined } | undefined,
- ) => Promise
+ getNonce?:
+ | ((
+ parameters?: { key?: bigint | undefined } | undefined,
+ ) => Promise)
+ | undefined
/**
* Retrieves the User Operation "stub" signature for gas estimation.
*
@@ -100,6 +103,8 @@ export type SmartAccountImplementation<
* ```
*/
getStubSignature: () => Promise
+ /** Custom nonce key manager. */
+ nonceKeyManager?: NonceManager | undefined
/**
* Signs a hash via the Smart Account's owner.
*
@@ -184,6 +189,16 @@ export type SmartAccount<
{
/** Address of the Smart Account. */
address: Address
+ /**
+ * Retrieves the nonce of the Account.
+ *
+ * @example
+ * ```ts
+ * const nonce = await account.getNonce()
+ * // 1n
+ * ```
+ */
+ getNonce: NonNullable
/** Whether or not the Smart Account has been deployed. */
isDeployed: () => Promise
/** Type of account. */
diff --git a/src/account-abstraction/actions/bundler/estimateUserOperationGas.test.ts b/src/account-abstraction/actions/bundler/estimateUserOperationGas.test.ts
index 0b7e563c94..586c202a0e 100644
--- a/src/account-abstraction/actions/bundler/estimateUserOperationGas.test.ts
+++ b/src/account-abstraction/actions/bundler/estimateUserOperationGas.test.ts
@@ -1,4 +1,4 @@
-import { beforeEach, describe, expect, test, vi } from 'vitest'
+import { beforeEach, describe, expect, expectTypeOf, test, vi } from 'vitest'
import { ErrorsExample } from '../../../../contracts/generated.js'
import { wagmiContractConfig } from '../../../../test/src/abis.js'
import {
@@ -15,7 +15,9 @@ import { mine, writeContract } from '../../../actions/index.js'
import { http } from '../../../clients/transports/http.js'
import { pad, parseEther, parseGwei } from '../../../utils/index.js'
import { createPaymasterClient } from '../../clients/createPaymasterClient.js'
+import type { UserOperation } from '../../types/userOperation.js'
import { estimateUserOperationGas } from './estimateUserOperationGas.js'
+import { prepareUserOperation } from './prepareUserOperation.js'
const client = anvilMainnet.getClient({ account: true })
const bundlerClient = bundlerMainnet.getBundlerClient()
@@ -139,6 +141,39 @@ describe('entryPointVersion: 0.7', async () => {
`)
})
+ test('behavior: prepared user operation', async () => {
+ const request = {
+ ...(await prepareUserOperation(bundlerClient, {
+ account,
+ calls: [
+ {
+ to: '0x0000000000000000000000000000000000000000',
+ value: parseEther('1'),
+ },
+ ],
+ ...fees,
+ })),
+ account: undefined,
+ } as const
+
+ expectTypeOf(request).toMatchTypeOf()
+
+ expect(
+ await estimateUserOperationGas(bundlerClient, {
+ ...request,
+ entryPointAddress: account.entryPoint?.address,
+ }),
+ ).toMatchInlineSnapshot(`
+ {
+ "callGasLimit": 80000n,
+ "paymasterPostOpGasLimit": 0n,
+ "paymasterVerificationGasLimit": 0n,
+ "preVerificationGas": 51722n,
+ "verificationGasLimit": 259060n,
+ }
+ `)
+ })
+
test('error: insufficient funds', async () => {
await expect(() =>
estimateUserOperationGas(bundlerClient, {
@@ -160,7 +195,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761076688711039842254848
+ nonce: 30902162761095135455113551806464
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -198,7 +233,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761095135455113551806464
+ nonce: 30902162761113582199187261358080
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -235,7 +270,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761113582199187261358080
+ nonce: 30902162761132028943260970909696
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -273,7 +308,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761132028943260970909696
+ nonce: 30902162761150475687334680461312
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -314,7 +349,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761150475687334680461312
+ nonce: 30902162761168922431408390012928
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -355,7 +390,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761168922431408390012928
+ nonce: 30902162761187369175482099564544
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -396,7 +431,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761187369175482099564544
+ nonce: 30902162761205815919555809116160
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -437,7 +472,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761205815919555809116160
+ nonce: 30902162761224262663629518667776
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -478,7 +513,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761224262663629518667776
+ nonce: 30902162761242709407703228219392
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -521,7 +556,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761242709407703228219392
+ nonce: 30902162761261156151776937771008
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -564,7 +599,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761261156151776937771008
+ nonce: 30902162761279602895850647322624
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -642,7 +677,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0x
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761279602895850647322624
+ nonce: 30902162761298049639924356874240
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -689,7 +724,7 @@ describe('entryPointVersion: 0.7', async () => {
factoryData: 0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000002
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761298049639924356874240
+ nonce: 30902162761316496383998066425856
sender: 0xE911628bF8428C23f179a07b081325cAe376DE1f
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -723,6 +758,37 @@ describe('entryPointVersion: 0.6', async () => {
`)
})
+ test('behavior: prepared user operation', async () => {
+ const request = {
+ ...(await prepareUserOperation(bundlerClient, {
+ account,
+ calls: [
+ {
+ to: '0x0000000000000000000000000000000000000000',
+ value: parseEther('1'),
+ },
+ ],
+ ...fees,
+ })),
+ account: undefined,
+ } as const
+
+ expectTypeOf(request).toMatchTypeOf()
+
+ expect(
+ await estimateUserOperationGas(bundlerClient, {
+ ...request,
+ entryPointAddress: account.entryPoint.address,
+ }),
+ ).toMatchInlineSnapshot(`
+ {
+ "callGasLimit": 80000n,
+ "preVerificationGas": 55233n,
+ "verificationGasLimit": 258801n,
+ }
+ `)
+ })
+
test('error: aa13', async () => {
await expect(() =>
estimateUserOperationGas(bundlerClient, {
@@ -746,7 +812,7 @@ describe('entryPointVersion: 0.6', async () => {
initCode: 0x0000000000000000000000000000000000000000deadbeef
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761039795222892423151616
+ nonce: 30902162761058241966966132703232
paymasterAndData: 0x
sender: 0x6edf7db791fC4D438D4A683E857B2fE1a84947Ce
signature: 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
@@ -759,8 +825,8 @@ describe('entryPointVersion: 0.6', async () => {
test('error: account not defined', async () => {
await expect(() =>
+ // @ts-expect-error
estimateUserOperationGas(bundlerClient, {
- // @ts-expect-error
account: undefined,
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
...fees,
diff --git a/src/account-abstraction/actions/bundler/estimateUserOperationGas.ts b/src/account-abstraction/actions/bundler/estimateUserOperationGas.ts
index 38bc9c8633..24c6943374 100644
--- a/src/account-abstraction/actions/bundler/estimateUserOperationGas.ts
+++ b/src/account-abstraction/actions/bundler/estimateUserOperationGas.ts
@@ -10,7 +10,12 @@ import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
-import type { Assign, OneOf, Prettify } from '../../../types/utils.js'
+import type {
+ Assign,
+ MaybeRequired,
+ OneOf,
+ Prettify,
+} from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { getAction } from '../../../utils/getAction.js'
import type { SmartAccount } from '../../accounts/types.js'
@@ -55,26 +60,39 @@ export type EstimateUserOperationGasParameters<
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
-> = Assign<
- UserOperationRequest<_derivedVersion>,
- OneOf<{ calls: UserOperationCalls> } | { callData: Hex }> & {
- paymaster?:
- | Address
- | true
- | {
- /** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
- getPaymasterData?: PaymasterActions['getPaymasterData'] | undefined
- /** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
- getPaymasterStubData?:
- | PaymasterActions['getPaymasterStubData']
+> = GetSmartAccountParameter &
+ (
+ | UserOperation // Accept a full-formed User Operation.
+ | Assign<
+ // Accept a partially-formed User Operation (UserOperationRequest) to be filled.
+ UserOperationRequest<_derivedVersion>,
+ OneOf<
+ { calls: UserOperationCalls> } | { callData: Hex }
+ > & {
+ paymaster?:
+ | Address
+ | true
+ | {
+ /** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
+ getPaymasterData?:
+ | PaymasterActions['getPaymasterData']
+ | undefined
+ /** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
+ getPaymasterStubData?:
+ | PaymasterActions['getPaymasterStubData']
+ | undefined
+ }
| undefined
+ /** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
+ paymasterContext?: unknown | undefined
}
- | undefined
- /** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
- paymasterContext?: unknown | undefined
- }
-> &
- GetSmartAccountParameter
+ >
+ ) &
+ // Allow the EntryPoint address to be overridden, if no Account is provided, it will need to be required.
+ MaybeRequired<
+ { entryPointAddress?: Address },
+ _derivedAccount extends undefined ? true : false
+ >
export type EstimateUserOperationGasReturnType<
account extends SmartAccount | undefined = SmartAccount | undefined,
@@ -135,26 +153,28 @@ export async function estimateUserOperationGas<
calls
>,
): Promise> {
- const { account: account_ = client.account } = parameters
+ const { account: account_ = client.account, entryPointAddress } = parameters
- if (!account_) throw new AccountNotFoundError()
- const account = parseAccount(account_)
+ if (!account_ && !parameters.sender) throw new AccountNotFoundError()
+ const account = account_ ? parseAccount(account_) : undefined
- const request = await getAction(
- client,
- prepareUserOperation,
- 'prepareUserOperation',
- )({
- ...parameters,
- parameters: ['factory', 'nonce', 'paymaster', 'signature'],
- } as unknown as PrepareUserOperationParameters)
+ const request = account
+ ? await getAction(
+ client,
+ prepareUserOperation,
+ 'prepareUserOperation',
+ )({
+ ...parameters,
+ parameters: ['factory', 'nonce', 'paymaster', 'signature'],
+ } as unknown as PrepareUserOperationParameters)
+ : parameters
try {
const result = await client.request({
method: 'eth_estimateUserOperationGas',
params: [
formatUserOperationRequest(request as UserOperation),
- account.entryPoint.address,
+ (entryPointAddress ?? account?.entryPoint?.address)!,
],
})
return formatUserOperationGas(result) as EstimateUserOperationGasReturnType<
@@ -162,9 +182,10 @@ export async function estimateUserOperationGas<
accountOverride
>
} catch (error) {
+ const calls = (parameters as any).calls
throw getUserOperationError(error as BaseError, {
...(request as UserOperation),
- calls: parameters.calls,
+ ...(calls ? { calls } : {}),
})
}
}
diff --git a/src/account-abstraction/actions/bundler/sendUserOperation.test.ts b/src/account-abstraction/actions/bundler/sendUserOperation.test.ts
index 76cc1b220b..61f1700547 100644
--- a/src/account-abstraction/actions/bundler/sendUserOperation.test.ts
+++ b/src/account-abstraction/actions/bundler/sendUserOperation.test.ts
@@ -1,4 +1,4 @@
-import { beforeEach, describe, expect, test, vi } from 'vitest'
+import { beforeEach, describe, expect, expectTypeOf, test, vi } from 'vitest'
import { wagmiContractConfig } from '../../../../test/src/abis.js'
import {
createVerifyingPaymasterServer,
@@ -24,6 +24,8 @@ import { pad, parseEther, parseGwei } from '../../../utils/index.js'
import { toCoinbaseSmartAccount } from '../../accounts/implementations/toCoinbaseSmartAccount.js'
import { createBundlerClient } from '../../clients/createBundlerClient.js'
import { createPaymasterClient } from '../../clients/createPaymasterClient.js'
+import type { UserOperation } from '../../types/userOperation.js'
+import { prepareUserOperation } from './prepareUserOperation.js'
import { sendUserOperation } from './sendUserOperation.js'
const client = anvilMainnet.getClient({ account: true })
@@ -154,6 +156,53 @@ describe('entryPointVersion: 0.7', async () => {
await mine(client, { blocks: 1 })
})
+ test('behavior: prepared user operation', async () => {
+ const request = {
+ ...(await prepareUserOperation(bundlerClient, {
+ account,
+ calls: [
+ {
+ to: alice,
+ value: parseEther('1'),
+ },
+ {
+ to: bob,
+ value: parseEther('2'),
+ },
+ ],
+ ...fees,
+ })),
+ account: undefined,
+ } as const
+ const signature = await account.signUserOperation(request)
+
+ expectTypeOf(request).toMatchTypeOf()
+
+ const hash = await sendUserOperation(bundlerClient, {
+ ...request,
+ entryPointAddress: account.entryPoint.address,
+ signature,
+ })
+ expect(hash).toBeDefined()
+
+ await bundlerClient.request({ method: 'debug_bundler_sendBundleNow' })
+ await mine(client, { blocks: 1 })
+
+ expect(await getBalance(client, { address: alice })).toMatchInlineSnapshot(
+ '10001000000000000000000n',
+ )
+ expect(await getBalance(client, { address: bob })).toMatchInlineSnapshot(
+ '10002000000000000000000n',
+ )
+ expect(
+ await readContract(client, {
+ ...wagmiContractConfig,
+ functionName: 'ownerOf',
+ args: [69420451n],
+ }),
+ ).toBe(account.address)
+ })
+
test('error: no account', async () => {
await expect(() =>
// @ts-expect-error
@@ -289,7 +338,7 @@ describe('entryPointVersion: 0.7', async () => {
callGasLimit: 80000
maxFeePerGas: 15 gwei
maxPriorityFeePerGas: 2 gwei
- nonce: 30902162761076688711039842254848
+ nonce: 30902162761095135455113551806464
paymasterPostOpGasLimit: 0
paymasterVerificationGasLimit: 0
preVerificationGas: 50692
diff --git a/src/account-abstraction/actions/bundler/sendUserOperation.ts b/src/account-abstraction/actions/bundler/sendUserOperation.ts
index 9edb52ad17..030a535a26 100644
--- a/src/account-abstraction/actions/bundler/sendUserOperation.ts
+++ b/src/account-abstraction/actions/bundler/sendUserOperation.ts
@@ -7,7 +7,7 @@ import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Chain } from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
-import type { Assign, OneOf } from '../../../types/utils.js'
+import type { Assign, MaybeRequired, OneOf } from '../../../types/utils.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { getAction } from '../../../utils/getAction.js'
import type { SmartAccount } from '../../accounts/types.js'
@@ -47,27 +47,39 @@ export type SendUserOperationParameters<
>,
_derivedVersion extends
EntryPointVersion = DeriveEntryPointVersion<_derivedAccount>,
-> = Assign<
- UserOperationRequest<_derivedVersion>,
- OneOf<{ calls: UserOperationCalls> } | { callData: Hex }> & {
- paymaster?:
- | Address
- | true
- | {
- /** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
- getPaymasterData?: PaymasterActions['getPaymasterData'] | undefined
- /** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
- getPaymasterStubData?:
- | PaymasterActions['getPaymasterStubData']
+> = GetSmartAccountParameter &
+ (
+ | UserOperation // Accept a full-formed User Operation.
+ | Assign<
+ // Accept a partially-formed User Operation (UserOperationRequest) to be filled.
+ UserOperationRequest<_derivedVersion>,
+ OneOf<
+ { calls: UserOperationCalls> } | { callData: Hex }
+ > & {
+ paymaster?:
+ | Address
+ | true
+ | {
+ /** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */
+ getPaymasterData?:
+ | PaymasterActions['getPaymasterData']
+ | undefined
+ /** Retrieves paymaster-related User Operation properties to be used for gas estimation. */
+ getPaymasterStubData?:
+ | PaymasterActions['getPaymasterStubData']
+ | undefined
+ }
| undefined
+ /** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
+ paymasterContext?: unknown | undefined
}
- | undefined
- /** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */
- paymasterContext?: unknown
- }
-> &
- GetSmartAccountParameter
-
+ >
+ ) &
+ // Allow the EntryPoint address to be overridden, if no Account is provided, it will need to be required.
+ MaybeRequired<
+ { entryPointAddress?: Address },
+ _derivedAccount extends undefined ? true : false
+ >
export type SendUserOperationReturnType = Hex
export type SendUserOperationErrorType =
@@ -111,20 +123,21 @@ export async function sendUserOperation<
client: Client,
parameters: SendUserOperationParameters,
) {
- const { account: account_ = client.account } = parameters
+ const { account: account_ = client.account, entryPointAddress } = parameters
- if (!account_) throw new AccountNotFoundError()
- const account = parseAccount(account_)
+ if (!account_ && !parameters.sender) throw new AccountNotFoundError()
+ const account = account_ ? parseAccount(account_) : undefined
- const request = await getAction(
- client,
- prepareUserOperation,
- 'prepareUserOperation',
- )(parameters as unknown as PrepareUserOperationParameters)
+ const request = account
+ ? await getAction(
+ client,
+ prepareUserOperation,
+ 'prepareUserOperation',
+ )(parameters as unknown as PrepareUserOperationParameters)
+ : parameters
- const signature =
- parameters.signature ||
- (await account.signUserOperation(request as UserOperation))
+ const signature = (parameters.signature ||
+ (await account?.signUserOperation(request as UserOperation)))!
const rpcParameters = formatUserOperationRequest({
...request,
@@ -135,14 +148,18 @@ export async function sendUserOperation<
return await client.request(
{
method: 'eth_sendUserOperation',
- params: [rpcParameters, account.entryPoint.address],
+ params: [
+ rpcParameters,
+ (entryPointAddress ?? account?.entryPoint.address)!,
+ ],
},
{ retryCount: 0 },
)
} catch (error) {
+ const calls = (parameters as any).calls
throw getUserOperationError(error as BaseError, {
...(request as UserOperation),
- calls: parameters.calls,
+ ...(calls ? { calls } : {}),
signature,
})
}
diff --git a/src/accounts/index.test.ts b/src/accounts/index.test.ts
index 92fd6db4be..bb3be338d6 100644
--- a/src/accounts/index.test.ts
+++ b/src/accounts/index.test.ts
@@ -12,6 +12,7 @@ test('exports utils', () => {
"italian",
"japanese",
"korean",
+ "portuguese",
"simplifiedChinese",
"spanish",
"traditionalChinese",
diff --git a/src/accounts/index.ts b/src/accounts/index.ts
index 9f58cc626a..8b1a533754 100644
--- a/src/accounts/index.ts
+++ b/src/accounts/index.ts
@@ -8,6 +8,7 @@ export { wordlist as french } from '@scure/bip39/wordlists/french'
export { wordlist as italian } from '@scure/bip39/wordlists/italian'
export { wordlist as japanese } from '@scure/bip39/wordlists/japanese'
export { wordlist as korean } from '@scure/bip39/wordlists/korean'
+export { wordlist as portuguese } from '@scure/bip39/wordlists/portuguese'
export { wordlist as simplifiedChinese } from '@scure/bip39/wordlists/simplified-chinese'
export { wordlist as spanish } from '@scure/bip39/wordlists/spanish'
export { wordlist as traditionalChinese } from '@scure/bip39/wordlists/traditional-chinese'
diff --git a/src/accounts/privateKeyToAccount.test.ts b/src/accounts/privateKeyToAccount.test.ts
index 49aaa2097b..fea74b30e0 100644
--- a/src/accounts/privateKeyToAccount.test.ts
+++ b/src/accounts/privateKeyToAccount.test.ts
@@ -48,8 +48,8 @@ test('sign authorization', async () => {
"chainId": 1,
"contractAddress": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
"nonce": 0,
- "r": "0x623129c9fcc520bee4b19fbb5148b178d67e1c854d2baee0e64cd518aad5549f",
- "s": "0x17997fb5ef9d7521c09f0208b1082a9fecbeabdad90ef0a806a50d1b9c7b5d66",
+ "r": "0x1b633d8fa4b6822d010b17bdec4bd305eb024d302588edf0618fd04b26d686fd",
+ "s": "0x2633183f08457bbb7355b26280a122c7e6f5e38d5a23ece2cccf3068b6dd06ca",
"v": 27n,
"yParity": 0,
}
diff --git a/src/accounts/privateKeyToAccount.ts b/src/accounts/privateKeyToAccount.ts
index 70fa49066f..77182df937 100644
--- a/src/accounts/privateKeyToAccount.ts
+++ b/src/accounts/privateKeyToAccount.ts
@@ -66,7 +66,7 @@ export function privateKeyToAccount(
return signTransaction({ privateKey, transaction, serializer })
},
async signTypedData(typedData) {
- return signTypedData({ ...typedData, privateKey })
+ return signTypedData({ ...typedData, privateKey } as any)
},
})
diff --git a/src/accounts/utils/signAuthorization.test.ts b/src/accounts/utils/signAuthorization.test.ts
index b50a117596..23c5e0ebe2 100644
--- a/src/accounts/utils/signAuthorization.test.ts
+++ b/src/accounts/utils/signAuthorization.test.ts
@@ -58,7 +58,7 @@ test('args: to (hex)', async () => {
})
expect(signature).toMatchInlineSnapshot(
- `"0x623129c9fcc520bee4b19fbb5148b178d67e1c854d2baee0e64cd518aad5549f17997fb5ef9d7521c09f0208b1082a9fecbeabdad90ef0a806a50d1b9c7b5d661b"`,
+ `"0x1b633d8fa4b6822d010b17bdec4bd305eb024d302588edf0618fd04b26d686fd2633183f08457bbb7355b26280a122c7e6f5e38d5a23ece2cccf3068b6dd06ca1b"`,
)
expect(
await verifyAuthorization({
diff --git a/src/accounts/utils/signTransaction.test.ts b/src/accounts/utils/signTransaction.test.ts
index 67ca4d993c..ae1586751f 100644
--- a/src/accounts/utils/signTransaction.test.ts
+++ b/src/accounts/utils/signTransaction.test.ts
@@ -56,7 +56,7 @@ describe('eip7702', async () => {
privateKey: accounts[0].privateKey,
})
expect(signature).toMatchInlineSnapshot(
- `"0x04f9010e018203118080825208808080c0f8bcf85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a055f6b60208df4fec601dc838392e8512878d6d687889cbe2691787f6367fdd35a018af5c71f1d558a9167c47f67e49d1cde703e874c3ec1a5fb84d4a6abd5606f0f85b0a94fba3912ca04dd458c843e2ee08967fc04f3579c2c14580a039d02ee4d1a85269ed8470719ff4280912d7906f7d039af477994533784f8367a035169646947de890bbd468b60a5aba5b9b7fb7a08677f2389c3a9259d05e60f380a07489103f2133ff365733764c6aa9e30340bbbcca871283a38c7fe38f854d03e4a02f14f71225153db10143b7aba573e36c188e67f00453fbe31661f39746cfcd59"`,
+ `"0x04f9010b018203118080825208808080c0f8baf85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a401a0f6beafe7507f0c98ae9bc8d9e15d6b53c2f0714ccfc01663f658cb9f29caced4a00dc4dfc53537f8b09047eceb674c454acba020ce786d9004175f41669304dee0f85a0a94fba3912ca04dd458c843e2ee08967fc04f3579c24501a0c3fea6e0bd0f5743d6e2f2df8f1aa63ff262a6636ca96ac572da3ea5cd33344ca02a4f006f9a0cf7cd5f5528af86524c984b05a8446c8c124aede1d3531e91de9180a0c3d5845755debbe90b2fa6258ba04ec93c46bfb7b2657b66a11d8c7c5221ac939fbf44fdacf9e9f87b5074df6ba1f3f9c50c82d5b0be8e4e96686410ca405a30"`,
)
})
})
diff --git a/src/actions/ens/getEnsAvatar.test.ts b/src/actions/ens/getEnsAvatar.test.ts
index 372a520ecb..dd0f34e162 100644
--- a/src/actions/ens/getEnsAvatar.test.ts
+++ b/src/actions/ens/getEnsAvatar.test.ts
@@ -163,20 +163,20 @@ describe('args: gateways', async () => {
// uri: ipfs
record: 'ipfs://ipfs/Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio',
expected:
- 'https://cloudflare-ipfs.com/ipfs/Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio',
+ 'https://gateway.pinata.cloud/ipfs/Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio',
},
{
// uri: ipfs (no prefix)
record: 'Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio',
expected:
- 'https://cloudflare-ipfs.com/ipfs/Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio',
+ 'https://gateway.pinata.cloud/ipfs/Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio',
},
])('$record -> $expected', async ({ record, expected }) => {
await setEnsAvatar(record)
await expect(
getEnsAvatar(client, {
name: 'vitalik.eth',
- assetGatewayUrls: { ipfs: 'https://cloudflare-ipfs.com' },
+ assetGatewayUrls: { ipfs: 'https://gateway.pinata.cloud' },
}),
).resolves.toEqual(expected)
})
diff --git a/src/actions/public/estimateFeesPerGas.ts b/src/actions/public/estimateFeesPerGas.ts
index fe4e736a46..2ed70749c2 100644
--- a/src/actions/public/estimateFeesPerGas.ts
+++ b/src/actions/public/estimateFeesPerGas.ts
@@ -7,6 +7,7 @@ import {
type Eip1559FeesNotSupportedErrorType,
} from '../../errors/fee.js'
import type { ErrorType } from '../../errors/utils.js'
+import type { Account } from '../../types/account.js'
import type { Block } from '../../types/block.js'
import type {
Chain,
@@ -99,7 +100,7 @@ export async function internal_estimateFeesPerGas<
client: Client,
args: EstimateFeesPerGasParameters & {
block?: Block | undefined
- request?: PrepareTransactionRequestParameters | undefined
+ request?: PrepareTransactionRequestParameters | undefined
},
): Promise> {
const {
diff --git a/src/actions/public/estimateGas.test.ts b/src/actions/public/estimateGas.test.ts
index e7d61ad0fa..bb961940a7 100644
--- a/src/actions/public/estimateGas.test.ts
+++ b/src/actions/public/estimateGas.test.ts
@@ -82,7 +82,7 @@ test('args: authorizationList', async () => {
],
}),
}),
- ).toMatchInlineSnapshot('87132n')
+ ).toMatchInlineSnapshot('100168n')
})
test('args: blockNumber', async () => {
diff --git a/src/actions/public/estimateMaxPriorityFeePerGas.test.ts b/src/actions/public/estimateMaxPriorityFeePerGas.test.ts
index 52a23ec974..18eede5f89 100644
--- a/src/actions/public/estimateMaxPriorityFeePerGas.test.ts
+++ b/src/actions/public/estimateMaxPriorityFeePerGas.test.ts
@@ -31,7 +31,7 @@ test('fallback', async () => {
expect(await estimateMaxPriorityFeePerGas(client_1)).toBeDefined()
})
-test('args: chain `defaultPriorityFee` override', async () => {
+test('args: chain `priorityFee` override', async () => {
// value
const client_1 = createPublicClient({
transport: http(anvilMainnet.rpcUrl.http),
@@ -41,7 +41,7 @@ test('args: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: 69420n,
+ maxPriorityFeePerGas: 69420n,
},
},
}),
@@ -56,7 +56,7 @@ test('args: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: () => 69420n,
+ maxPriorityFeePerGas: () => 69420n,
},
},
}),
@@ -71,7 +71,7 @@ test('args: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: async () => 69420n,
+ maxPriorityFeePerGas: async () => 69420n,
},
},
}),
@@ -86,7 +86,7 @@ test('args: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: 0n,
+ maxPriorityFeePerGas: 0n,
},
},
}),
@@ -101,20 +101,50 @@ test('args: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: async () => 0n,
+ maxPriorityFeePerGas: async () => 0n,
},
},
}),
).toBe(0n)
+
+ // fallback
+ const client_6 = createPublicClient({
+ transport: http(anvilMainnet.rpcUrl.http),
+ })
+ expect(
+ await estimateMaxPriorityFeePerGas(client_6, {
+ chain: {
+ ...anvilMainnet.chain,
+ fees: {
+ maxPriorityFeePerGas: async () => null,
+ },
+ },
+ }),
+ ).toBeDefined()
+
+ // deprecated `defaultPriorityFee`
+ const client_7 = createPublicClient({
+ transport: http(anvilMainnet.rpcUrl.http),
+ })
+ expect(
+ await estimateMaxPriorityFeePerGas(client_7, {
+ chain: {
+ ...anvilMainnet.chain,
+ fees: {
+ defaultPriorityFee: async () => 69420n,
+ },
+ },
+ }),
+ ).toBe(69420n)
})
-test('client: chain `defaultPriorityFee` override', async () => {
+test('client: chain `priorityFee` override', async () => {
// value
const client_1 = createPublicClient({
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: 69420n,
+ maxPriorityFeePerGas: 69420n,
},
},
transport: http(),
@@ -126,7 +156,7 @@ test('client: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: () => 69420n,
+ maxPriorityFeePerGas: () => 69420n,
},
},
transport: http(),
@@ -138,7 +168,7 @@ test('client: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: async () => 69420n,
+ maxPriorityFeePerGas: async () => 69420n,
},
},
transport: http(),
@@ -150,7 +180,7 @@ test('client: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: 0n,
+ maxPriorityFeePerGas: 0n,
},
},
transport: http(),
@@ -162,7 +192,7 @@ test('client: chain `defaultPriorityFee` override', async () => {
chain: {
...anvilMainnet.chain,
fees: {
- defaultPriorityFee: async () => 0n,
+ maxPriorityFeePerGas: async () => 0n,
},
},
transport: http(),
diff --git a/src/actions/public/estimateMaxPriorityFeePerGas.ts b/src/actions/public/estimateMaxPriorityFeePerGas.ts
index 17445307c7..1dc0ed2c7c 100644
--- a/src/actions/public/estimateMaxPriorityFeePerGas.ts
+++ b/src/actions/public/estimateMaxPriorityFeePerGas.ts
@@ -86,19 +86,25 @@ export async function internal_estimateMaxPriorityFeePerGas<
},
): Promise {
const { block: block_, chain = client.chain, request } = args || {}
- if (typeof chain?.fees?.defaultPriorityFee === 'function') {
- const block = block_ || (await getAction(client, getBlock, 'getBlock')({}))
- return chain.fees.defaultPriorityFee({
- block,
- client,
- request,
- } as ChainFeesFnParameters)
- }
-
- if (typeof chain?.fees?.defaultPriorityFee !== 'undefined')
- return chain?.fees?.defaultPriorityFee
try {
+ const maxPriorityFeePerGas =
+ chain?.fees?.maxPriorityFeePerGas ?? chain?.fees?.defaultPriorityFee
+
+ if (typeof maxPriorityFeePerGas === 'function') {
+ const block =
+ block_ || (await getAction(client, getBlock, 'getBlock')({}))
+ const maxPriorityFeePerGas_ = await maxPriorityFeePerGas({
+ block,
+ client,
+ request,
+ } as ChainFeesFnParameters)
+ if (maxPriorityFeePerGas_ === null) throw new Error()
+ return maxPriorityFeePerGas_
+ }
+
+ if (typeof maxPriorityFeePerGas !== 'undefined') return maxPriorityFeePerGas
+
const maxPriorityFeePerGasHex = await client.request({
method: 'eth_maxPriorityFeePerGas',
})
diff --git a/src/actions/public/getTransaction.test.ts b/src/actions/public/getTransaction.test.ts
index 101481e3e2..aa17d4b751 100644
--- a/src/actions/public/getTransaction.test.ts
+++ b/src/actions/public/getTransaction.test.ts
@@ -222,32 +222,32 @@ test('gets transaction (eip7702)', async () => {
"chainId": 1,
"contractAddress": "0xfb6dab6200b8958c2655c3747708f82243d3f32e",
"nonce": 112,
- "r": "0x1ae3cd78a7f079fcb58415a5a1a647c9785c0a4d1993139f881b5659f7f88c6",
- "s": "0x5c9e71244dcbc2b197b76972817a4585af95e06d4cf7d2ec70c685ce3c586390",
- "yParity": 0,
+ "r": "0x9b837dd8fb66ebea939e6d7e35ea6e3888fbb197bd60dfe95e42b5564cf28da3",
+ "s": "0x645fe33f8be6e67ded51f1f65d68cda8cce7f76996bc34f7bf76921ce20c1ba8",
+ "yParity": 1,
},
],
"blockHash": null,
"blockNumber": 19868022n,
"chainId": 1,
"from": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
- "gas": 87312n,
+ "gas": 89812n,
"gasPrice": 8599866030n,
- "hash": "0x5c00e6b61f404b926d8ecfe9de5042be82dbff06d9df43651d710ecd09b0fba3",
+ "hash": "0xd43c161f2e6fbfbfeefc952fdfd8f6d6eba731b621c2cad1d54b9c716dd1b0e4",
"input": "0xa6d0ad61000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000",
"maxFeePerBlobGas": undefined,
"maxFeePerGas": 11392560424n,
"maxPriorityFeePerGas": 1000000000n,
"nonce": 112,
- "r": "0x9f6268ab14164a997a553089e993ab28bc7bdee62b147fb0a13dc18b9d485903",
- "s": "0x54a1d590b4dc376c59c0dbe833bec0801de77cc75c6d4d90af515b91643b91b6",
+ "r": "0xe215e0c9031b57f6a4e6000766d3c2a91d71b6171d47cff26554ba285bc19b3f",
+ "s": "0x350b3af55bb5079cc1eb09a53550b45b80599acddfb5a37e009471fd54fbb53",
"to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
"transactionIndex": 0,
"type": "eip7702",
"typeHex": "0x4",
- "v": 0n,
+ "v": 1n,
"value": 0n,
- "yParity": 0,
+ "yParity": 1,
}
`)
})
diff --git a/src/actions/public/getTransactionReceipt.test.ts b/src/actions/public/getTransactionReceipt.test.ts
index ae2e6efe45..32d691961b 100644
--- a/src/actions/public/getTransactionReceipt.test.ts
+++ b/src/actions/public/getTransactionReceipt.test.ts
@@ -364,7 +364,7 @@ test('chain w/ custom block type', async () => {
"transactionLogIndex": 10,
},
],
- "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logsBloom": "0x10880004000400000000000000000000000040000000020000000000000400120000000000100000000080000001000000000000000000000000000000804000004000101002040000000008000000200000000000000000001000000000080000840000020000000000100000000800000000020100000000000010000000000000000400000000000004000040000000000000280000000000002000000000000000004000120000000200000000000000000000000000000000200000800000001002008008000000000000000000000000200000800000000000000020001800000000000000000000001000000000000000000000000000000000000000",
"root": "0xc621ee95e2d4ab65ecf499805dba770b20297c64029816b18c618fc49fe3d748",
"status": "success",
"to": "0x54de43b6ba21a5553697a2b78338e046dd7e0278",
diff --git a/src/actions/test/setCode.ts b/src/actions/test/setCode.ts
index e215a5ebe7..8c3a3a8d1f 100644
--- a/src/actions/test/setCode.ts
+++ b/src/actions/test/setCode.ts
@@ -50,8 +50,14 @@ export async function setCode<
client: TestClient,
{ address, bytecode }: SetCodeParameters,
) {
- await client.request({
- method: `${client.mode}_setCode`,
- params: [address, bytecode],
- })
+ if (client.mode === 'ganache')
+ await client.request({
+ method: 'evm_setAccountCode',
+ params: [address, bytecode],
+ })
+ else
+ await client.request({
+ method: `${client.mode}_setCode`,
+ params: [address, bytecode],
+ })
}
diff --git a/src/actions/wallet/sendTransaction.test.ts b/src/actions/wallet/sendTransaction.test.ts
index 495d543122..c4093249bf 100644
--- a/src/actions/wallet/sendTransaction.test.ts
+++ b/src/actions/wallet/sendTransaction.test.ts
@@ -830,7 +830,7 @@ describe('local account', () => {
test('args: authorizationList', async () => {
const invoker = privateKeyToAccount(accounts[0].privateKey)
- const authority = privateKeyToAccount(accounts[1].privateKey)
+ const authority = privateKeyToAccount(accounts[9].privateKey)
const recipient = privateKeyToAccount(
'0x4a751f9ddcef30fd28648f415480f74eb418bd5145a56586a32e8c959c330742',
)
@@ -904,7 +904,7 @@ describe('local account', () => {
})
test('args: authorizationList (authority as invoker)', async () => {
- const authority = privateKeyToAccount(accounts[1].privateKey)
+ const authority = privateKeyToAccount(accounts[9].privateKey)
const recipient = privateKeyToAccount(
'0x4a751f9ddcef30fd28648f415480f74eb418bd5145a56586a32e8c959c330742',
)
@@ -1124,10 +1124,10 @@ describe('local account', () => {
])
expect((await getTransaction(client, { hash: hash_1 })).nonce).toBe(681)
- expect((await getTransaction(client, { hash: hash_2 })).nonce).toBe(113)
+ expect((await getTransaction(client, { hash: hash_2 })).nonce).toBe(112)
expect((await getTransaction(client, { hash: hash_3 })).nonce).toBe(682)
expect((await getTransaction(client, { hash: hash_4 })).nonce).toBe(683)
- expect((await getTransaction(client, { hash: hash_5 })).nonce).toBe(114)
+ expect((await getTransaction(client, { hash: hash_5 })).nonce).toBe(113)
const hash_6 = await sendTransaction(client, {
account: account_1,
diff --git a/src/actions/wallet/sendTransaction.ts b/src/actions/wallet/sendTransaction.ts
index 31021e177d..733babfac0 100644
--- a/src/actions/wallet/sendTransaction.ts
+++ b/src/actions/wallet/sendTransaction.ts
@@ -175,15 +175,6 @@ export async function sendTransaction<
try {
assertRequest(parameters as AssertRequestParameters)
- let chainId: number | undefined
- if (chain !== null) {
- chainId = await getAction(client, getChainId, 'getChainId')({})
- assertCurrentChain({
- currentChainId: chainId,
- chain,
- })
- }
-
const to = await (async () => {
// If `to` exists on the parameters, use that.
if (parameters.to) return parameters.to
@@ -204,6 +195,15 @@ export async function sendTransaction<
})()
if (account.type === 'json-rpc') {
+ let chainId: number | undefined
+ if (chain !== null) {
+ chainId = await getAction(client, getChainId, 'getChainId')({})
+ assertCurrentChain({
+ currentChainId: chainId,
+ chain,
+ })
+ }
+
const chainFormat = client.chain?.formatters?.transactionRequest?.format
const format = chainFormat || formatTransactionRequest
@@ -246,7 +246,6 @@ export async function sendTransaction<
authorizationList,
blobs,
chain,
- chainId,
data,
gas,
gasPrice,
diff --git a/src/actions/wallet/signTransaction.test.ts b/src/actions/wallet/signTransaction.test.ts
index 2aa174c6e6..f0110c4fbb 100644
--- a/src/actions/wallet/signTransaction.test.ts
+++ b/src/actions/wallet/signTransaction.test.ts
@@ -59,7 +59,7 @@ describe('eip7702', async () => {
...baseEip7702,
})
expect(signature).toMatchInlineSnapshot(
- `"0x04f8c50182031180808252089400000000000000000000000000000000000000008080c0f85ff85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a05140ccc73f785f9cfbe53f3fbbece6c3bd944669fab3ec95710d89c1acd9a67ea053a19bd48b26ec4dd88a31682061716ac4ae4c806d66704728cc11dba173d65701a0721ed11052bdd2347b2b3a5de7258c440b1b11d85ee890ec7fba00bc5b096d15a0780d9c23b62f9289b14bb010b62b2bf7d3129009bcb2740020425cb0930adcc9"`,
+ `"0x04f8c40182031180808252089400000000000000000000000000000000000000008080c0f85ef85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a08d7765afec6e09d93be91a1324f0dbbd6bcb96f4b37e8645a4c65d08a979ab69a070b81c53368b35a58af8630903c57d2b106842f9a081e3dc607c0c0cd990c77d01a0d2e4cf87a02ffd9a6cf0854ff5036b987847c562bf7a5f79fe2d322fba51e3eda002b72b1124ca8c2e43d8895601055cf4af7bf1cd0f757451483cc4dc5cdfb4a3"`,
)
})
@@ -70,7 +70,7 @@ describe('eip7702', async () => {
})
const signature = await signTransaction(client, request)
expect(signature).toMatchInlineSnapshot(
- `"0x04f8ce01820311843b9aca008502ae1107ec8252089400000000000000000000000000000000000000008080c0f85ff85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a05140ccc73f785f9cfbe53f3fbbece6c3bd944669fab3ec95710d89c1acd9a67ea053a19bd48b26ec4dd88a31682061716ac4ae4c806d66704728cc11dba173d65701a0dd7a61bcce42a949cc47981020c67fe5c38247501560753f26aa4f4a01cfe682a0214a76ccb14d8f4db68a8224840a3b377943e9bc51f10dc8f7b7f4bbf81fcc65"`,
+ `"0x04f8cd01820311843b9aca008502ae1107ec8252089400000000000000000000000000000000000000008080c0f85ef85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a08d7765afec6e09d93be91a1324f0dbbd6bcb96f4b37e8645a4c65d08a979ab69a070b81c53368b35a58af8630903c57d2b106842f9a081e3dc607c0c0cd990c77d01a00c0d98a5aa820287d07cd20b5b05a1a5c19f50ff3b0e700c795313522515a21fa00450425c5f5a9b6862d9c1f9513b6254d38391eee7d3147f67f9b7bb6d3b94c2"`,
)
})
})
diff --git a/src/actions/wallet/writeContract.test.ts b/src/actions/wallet/writeContract.test.ts
index bc4f801be6..5c954bc3dd 100644
--- a/src/actions/wallet/writeContract.test.ts
+++ b/src/actions/wallet/writeContract.test.ts
@@ -1,9 +1,13 @@
import { describe, expect, test, vi } from 'vitest'
-import { BatchCallInvoker, Payable } from '~contracts/generated.js'
+import {
+ BatchCallInvoker,
+ ErrorsExample,
+ Payable,
+} from '~contracts/generated.js'
import { wagmiContractConfig } from '~test/src/abis.js'
import { accounts } from '~test/src/constants.js'
-import { deploy, deployPayable } from '~test/src/utils.js'
+import { deploy, deployErrorExample, deployPayable } from '~test/src/utils.js'
import { anvilMainnet } from '../../../test/src/anvil.js'
import { privateKeyToAccount } from '../../accounts/privateKeyToAccount.js'
import { optimism } from '../../chains/index.js'
@@ -55,7 +59,7 @@ test('client chain mismatch', async () => {
functionName: 'mint',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
- [TransactionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
+ [ContractFunctionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
Current Chain ID: 1
Expected Chain ID: 10 – OP Mainnet
@@ -64,7 +68,13 @@ test('client chain mismatch', async () => {
from: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2
data: 0x1249c58b
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: mint()
+ sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ Docs: https://viem.sh/docs/contract/writeContract
Version: viem@x.y.z]
`)
})
@@ -81,14 +91,20 @@ test('no chain', async () => {
functionName: 'mint',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
- [TransactionExecutionError: No chain was provided to the request.
+ [ContractFunctionExecutionError: No chain was provided to the request.
Please provide a chain with the \`chain\` argument on the Action, or by supplying a \`chain\` to WalletClient.
Request Arguments:
from: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2
data: 0x1249c58b
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: mint()
+ sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ Docs: https://viem.sh/docs/contract/writeContract
Version: viem@x.y.z]
`)
})
@@ -118,7 +134,7 @@ describe('args: chain', () => {
chain: optimism,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
- [TransactionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
+ [ContractFunctionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
Current Chain ID: 1
Expected Chain ID: 10 – OP Mainnet
@@ -128,7 +144,13 @@ describe('args: chain', () => {
from: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2
data: 0x1249c58b
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: mint()
+ sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ Docs: https://viem.sh/docs/contract/writeContract
Version: viem@x.y.z]
`)
})
@@ -281,7 +303,10 @@ test('args: dataSuffix', async () => {
dataSuffix: '0x12345678',
})
expect(spy).toHaveBeenCalledWith({
- account: accounts[0].address,
+ account: {
+ address: accounts[0].address,
+ type: 'json-rpc',
+ },
data: '0x1249c58b12345678',
to: wagmiContractConfig.address,
})
@@ -381,7 +406,7 @@ test('w/ simulateContract (args chain mismatch)', async () => {
await expect(() =>
writeContract(client, request),
).rejects.toThrowErrorMatchingInlineSnapshot(`
- [TransactionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
+ [ContractFunctionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
Current Chain ID: 1
Expected Chain ID: 10 – OP Mainnet
@@ -391,7 +416,13 @@ test('w/ simulateContract (args chain mismatch)', async () => {
from: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2
data: 0x1249c58b
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: mint()
+ sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ Docs: https://viem.sh/docs/contract/writeContract
Version: viem@x.y.z]
`)
})
@@ -410,7 +441,7 @@ test('w/ simulateContract (client chain mismatch)', async () => {
await expect(() =>
writeContract(client, request),
).rejects.toThrowErrorMatchingInlineSnapshot(`
- [TransactionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
+ [ContractFunctionExecutionError: The current chain of the wallet (id: 1) does not match the target chain for the transaction (id: 10 – OP Mainnet).
Current Chain ID: 1
Expected Chain ID: 10 – OP Mainnet
@@ -419,7 +450,186 @@ test('w/ simulateContract (client chain mismatch)', async () => {
from: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2
data: 0x1249c58b
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: mint()
+ sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ Docs: https://viem.sh/docs/contract/writeContract
Version: viem@x.y.z]
`)
})
+
+describe('behavior: contract revert', () => {
+ test('revert', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'revertWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "revertWrite" reverted with the following reason:
+ This is a revert message
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: revertWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+
+ test('assert', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'assertWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "assertWrite" reverted with the following reason:
+ An \`assert\` condition failed.
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: assertWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+
+ test('overflow', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'overflowWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "overflowWrite" reverted with the following reason:
+ Arithmetic operation resulted in underflow or overflow.
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: overflowWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+
+ test('divide by zero', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'divideByZeroWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "divideByZeroWrite" reverted with the following reason:
+ Division or modulo by zero (e.g. \`5 / 0\` or \`23 % 0\`).
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: divideByZeroWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+
+ test('require', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'requireWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "requireWrite" reverted.
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: requireWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+
+ test('custom error: simple', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'simpleCustomWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "simpleCustomWrite" reverted.
+
+ Error: SimpleError(string message)
+ (bugger)
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: simpleCustomWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+
+ test('custom error: complex', async () => {
+ const { contractAddress } = await deployErrorExample()
+
+ await expect(() =>
+ writeContract(client, {
+ abi: ErrorsExample.abi,
+ address: contractAddress!,
+ functionName: 'complexCustomWrite',
+ account: privateKeyToAccount(accounts[0].privateKey),
+ }),
+ ).rejects.toMatchInlineSnapshot(`
+ [ContractFunctionExecutionError: The contract function "complexCustomWrite" reverted.
+
+ Error: ComplexError((address sender, uint256 bar), string message, uint256 number)
+ ({"sender":"0x0000000000000000000000000000000000000000","bar":"69"}, bugger, 69)
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: complexCustomWrite()
+ sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
+ Version: viem@x.y.z]
+ `)
+ })
+})
diff --git a/src/actions/wallet/writeContract.ts b/src/actions/wallet/writeContract.ts
index 44765980cc..af8f212019 100644
--- a/src/actions/wallet/writeContract.ts
+++ b/src/actions/wallet/writeContract.ts
@@ -1,8 +1,17 @@
import type { Abi } from 'abitype'
import type { Account } from '../../accounts/types.js'
+import {
+ type ParseAccountErrorType,
+ parseAccount,
+} from '../../accounts/utils/parseAccount.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
+import {
+ AccountNotFoundError,
+ type AccountNotFoundErrorType,
+} from '../../errors/account.js'
+import type { BaseError } from '../../errors/base.js'
import type { ErrorType } from '../../errors/utils.js'
import type { GetAccountParameter } from '../../types/account.js'
import type {
@@ -22,6 +31,10 @@ import {
type EncodeFunctionDataParameters,
encodeFunctionData,
} from '../../utils/abi/encodeFunctionData.js'
+import {
+ type GetContractErrorReturnType,
+ getContractError,
+} from '../../utils/errors/getContractError.js'
import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js'
import { getAction } from '../../utils/getAction.js'
import type { GetMutabilityAwareValue } from '../public/simulateContract.js'
@@ -81,7 +94,9 @@ export type WriteContractReturnType = SendTransactionReturnType
export type WriteContractErrorType =
| EncodeFunctionDataErrorType
- | SendTransactionErrorType
+ | AccountNotFoundErrorType
+ | ParseAccountErrorType
+ | GetContractErrorReturnType
| ErrorType
/**
@@ -156,20 +171,47 @@ export async function writeContract<
chainOverride
>,
): Promise {
- const { abi, address, args, dataSuffix, functionName, ...request } =
- parameters as WriteContractParameters
+ const {
+ abi,
+ account: account_ = client.account,
+ address,
+ args,
+ dataSuffix,
+ functionName,
+ ...request
+ } = parameters as WriteContractParameters
+
+ if (!account_)
+ throw new AccountNotFoundError({
+ docsPath: '/docs/contract/writeContract',
+ })
+ const account = parseAccount(account_)
+
const data = encodeFunctionData({
abi,
args,
functionName,
} as EncodeFunctionDataParameters)
- return getAction(
- client,
- sendTransaction,
- 'sendTransaction',
- )({
- data: `${data}${dataSuffix ? dataSuffix.replace('0x', '') : ''}`,
- to: address,
- ...request,
- })
+
+ try {
+ return await getAction(
+ client,
+ sendTransaction,
+ 'sendTransaction',
+ )({
+ data: `${data}${dataSuffix ? dataSuffix.replace('0x', '') : ''}`,
+ to: address,
+ account,
+ ...request,
+ })
+ } catch (error) {
+ throw getContractError(error as BaseError, {
+ abi,
+ address,
+ args,
+ docsPath: '/docs/contract/writeContract',
+ functionName,
+ sender: account.address,
+ })
+ }
}
diff --git a/src/chains/definitions/assetChainTestnet.ts b/src/chains/definitions/assetChainTestnet.ts
new file mode 100644
index 0000000000..c5b5062b93
--- /dev/null
+++ b/src/chains/definitions/assetChainTestnet.ts
@@ -0,0 +1,28 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const assetChainTestnet = /*#__PURE__*/ defineChain({
+ id: 42_421,
+ name: 'AssetChain Testnet',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'Real World Asset',
+ symbol: 'RWA',
+ },
+ rpcUrls: {
+ default: { http: ['https://enugu-rpc.assetchain.org'] },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Asset Chain Testnet Explorer',
+ url: 'https://scan-testnet.assetchain.org',
+ apiUrl: 'https://scan-testnet.assetchain.org/api',
+ },
+ },
+ testnet: true,
+ contracts: {
+ multicall3: {
+ address: '0x989F832D35988cb5e3eB001Fa2Fe789469EC31Ea',
+ blockCreated: 17177,
+ },
+ },
+})
diff --git a/src/chains/definitions/atletaOlympia.ts b/src/chains/definitions/atletaOlympia.ts
new file mode 100644
index 0000000000..9bdcdb576d
--- /dev/null
+++ b/src/chains/definitions/atletaOlympia.ts
@@ -0,0 +1,30 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const atletaOlympia = /*#__PURE__*/ defineChain({
+ id: 2340,
+ name: 'Atleta Olympia',
+ nativeCurrency: { decimals: 18, name: 'Atla', symbol: 'ATLA' },
+ rpcUrls: {
+ default: {
+ http: [
+ 'https://testnet-rpc.atleta.network:9944',
+ 'https://testnet-rpc.atleta.network',
+ ],
+ ws: ['wss://testnet-rpc.atleta.network:9944'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Atleta Olympia Explorer',
+ url: 'https://blockscout.atleta.network',
+ apiUrl: 'https://blockscout.atleta.network/api',
+ },
+ },
+ contracts: {
+ multicall3: {
+ address: '0x1472ec6392180fb84F345d2455bCC75B26577115',
+ blockCreated: 1076473,
+ },
+ },
+ testnet: true,
+})
diff --git a/src/chains/definitions/b3.ts b/src/chains/definitions/b3.ts
new file mode 100644
index 0000000000..4af7d78eff
--- /dev/null
+++ b/src/chains/definitions/b3.ts
@@ -0,0 +1,25 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+const sourceId = 8453 // base
+
+export const b3 = /*#__PURE__*/ defineChain({
+ id: 8333,
+ name: 'B3',
+ nativeCurrency: {
+ name: 'Ether',
+ symbol: 'ETH',
+ decimals: 18,
+ },
+ rpcUrls: {
+ default: {
+ http: ['https://mainnet-rpc.b3.fun/http'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Blockscout',
+ url: 'https://explorer.b3.fun',
+ },
+ },
+ sourceId,
+})
diff --git a/src/chains/definitions/b3Sepolia.ts b/src/chains/definitions/b3Sepolia.ts
index 74e4a16419..f832a571a9 100644
--- a/src/chains/definitions/b3Sepolia.ts
+++ b/src/chains/definitions/b3Sepolia.ts
@@ -12,7 +12,7 @@ export const b3Sepolia = /*#__PURE__*/ defineChain({
},
rpcUrls: {
default: {
- http: ['https://sepolia.b3.fun'],
+ http: ['https://sepolia.b3.fun/http'],
},
},
blockExplorers: {
diff --git a/src/chains/definitions/bob.ts b/src/chains/definitions/bob.ts
index 729bbac60a..12cdfa3369 100644
--- a/src/chains/definitions/bob.ts
+++ b/src/chains/definitions/bob.ts
@@ -1,6 +1,10 @@
+import { chainConfig } from '../../op-stack/chainConfig.js'
import { defineChain } from '../../utils/chain/defineChain.js'
+const sourceId = 1 // mainnet
+
export const bob = defineChain({
+ ...chainConfig,
id: 60808,
name: 'BOB',
nativeCurrency: {
@@ -16,15 +20,28 @@ export const bob = defineChain({
},
blockExplorers: {
default: {
- name: 'Blockscout',
+ name: 'BOB Explorer',
url: 'https://explorer.gobob.xyz',
},
},
contracts: {
+ ...chainConfig.contracts,
multicall3: {
- address: '0x63f8279bccDb75c0F38e0CD6B6A0c72a0a760FF9',
- blockCreated: 457045,
+ address: '0xcA11bde05977b3631167028862bE2a173976CA11',
+ blockCreated: 23131,
+ },
+ l2OutputOracle: {
+ [sourceId]: {
+ address: '0xdDa53E23f8a32640b04D7256e651C1db98dB11C1',
+ blockCreated: 4462615,
+ },
+ },
+ portal: {
+ [sourceId]: {
+ address: '0x8AdeE124447435fE03e3CD24dF3f4cAE32E65a3E',
+ blockCreated: 4462615,
+ },
},
},
- testnet: false,
+ sourceId,
})
diff --git a/src/chains/definitions/bobSepolia.ts b/src/chains/definitions/bobSepolia.ts
new file mode 100644
index 0000000000..d8eb765a4b
--- /dev/null
+++ b/src/chains/definitions/bobSepolia.ts
@@ -0,0 +1,52 @@
+import { chainConfig } from '../../op-stack/chainConfig.js'
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+const sourceId = 11_155_111 // sepolia
+
+export const bobSepolia = defineChain({
+ ...chainConfig,
+ id: 808813,
+ name: 'BOB Sepolia',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'ETH',
+ symbol: 'ETH',
+ },
+ rpcUrls: {
+ default: {
+ http: ['https://bob-sepolia.rpc.gobob.xyz'],
+ webSocket: ['wss://bob-sepolia.rpc.gobob.xyz'],
+ },
+ public: {
+ http: ['https://bob-sepolia.rpc.gobob.xyz'],
+ webSocket: ['wss://bob-sepolia.rpc.gobob.xyz'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'BOB Sepolia Explorer',
+ url: 'https://bob-sepolia.explorer.gobob.xyz',
+ },
+ },
+ contracts: {
+ ...chainConfig.contracts,
+ multicall3: {
+ address: '0xcA11bde05977b3631167028862bE2a173976CA11',
+ blockCreated: 35677,
+ },
+ l2OutputOracle: {
+ [sourceId]: {
+ address: '0x14D0069452b4AE2b250B395b8adAb771E4267d2f',
+ blockCreated: 4462615,
+ },
+ },
+ portal: {
+ [sourceId]: {
+ address: '0x867B1Aa872b9C8cB5E9F7755feDC45BB24Ad0ae4',
+ blockCreated: 4462615,
+ },
+ },
+ },
+ testnet: true,
+ sourceId,
+})
diff --git a/src/chains/definitions/botanixTestnet.ts b/src/chains/definitions/botanixTestnet.ts
new file mode 100644
index 0000000000..04e82fdce5
--- /dev/null
+++ b/src/chains/definitions/botanixTestnet.ts
@@ -0,0 +1,20 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const botanixTestnet = /*#__PURE__*/ defineChain({
+ id: 3636,
+ name: 'Botanix Testnet',
+ nativeCurrency: { name: 'Botanix', symbol: 'BTC', decimals: 18 },
+ rpcUrls: {
+ default: {
+ http: ['https://poa-node.botanixlabs.dev'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'blockscout',
+ url: 'https://blockscout.botanixlabs.dev',
+ apiUrl: 'https://blockscout.botanixlabs.dev',
+ },
+ },
+ testnet: true,
+})
diff --git a/src/chains/definitions/chips.ts b/src/chains/definitions/chips.ts
new file mode 100644
index 0000000000..29c1529e92
--- /dev/null
+++ b/src/chains/definitions/chips.ts
@@ -0,0 +1,19 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const chips = /*#__PURE__*/ defineChain({
+ id: 2882,
+ name: 'Chips Network',
+ network: 'CHIPS',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'IOTA',
+ symbol: 'IOTA',
+ },
+ rpcUrls: {
+ default: {
+ http: [
+ 'https://node.chips.ooo/wasp/api/v1/chains/iota1pp3d3mnap3ufmgqnjsnw344sqmf5svjh26y2khnmc89sv6788y3r207a8fn/evm',
+ ],
+ },
+ },
+})
diff --git a/src/chains/definitions/cronoszkEVM.ts b/src/chains/definitions/cronoszkEVM.ts
new file mode 100644
index 0000000000..1802326aa3
--- /dev/null
+++ b/src/chains/definitions/cronoszkEVM.ts
@@ -0,0 +1,20 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const cronoszkEVM = /*#__PURE__*/ defineChain({
+ id: 388,
+ name: 'Cronos zkEVM Mainnet',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'Cronos zkEVM CRO',
+ symbol: 'zkCRO',
+ },
+ rpcUrls: {
+ default: { http: ['https://mainnet.zkevm.cronos.org'] },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Cronos zkEVM (Mainnet) Chain Explorer',
+ url: 'https://explorer.zkevm.cronos.org',
+ },
+ },
+})
diff --git a/src/chains/definitions/curtis.ts b/src/chains/definitions/curtis.ts
new file mode 100644
index 0000000000..4124f71da7
--- /dev/null
+++ b/src/chains/definitions/curtis.ts
@@ -0,0 +1,19 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const curtis = /*#__PURE__*/ defineChain({
+ id: 33_111,
+ name: 'Curtis',
+ nativeCurrency: { name: 'ApeCoin', symbol: 'APE', decimals: 18 },
+ rpcUrls: {
+ default: {
+ http: ['https://rpc.curtis.apechain.com'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Curtis Explorer',
+ url: 'https://explorer.curtis.apechain.com',
+ },
+ },
+ testnet: true,
+})
diff --git a/src/chains/definitions/flowTestnet.ts b/src/chains/definitions/flowTestnet.ts
index d44ea37d67..58b79bcb9f 100644
--- a/src/chains/definitions/flowTestnet.ts
+++ b/src/chains/definitions/flowTestnet.ts
@@ -19,4 +19,10 @@ export const flowTestnet = /*#__PURE__*/ defineChain({
url: 'https://testnet.flowdiver.io',
},
},
+ contracts: {
+ multicall3: {
+ address: '0xca11bde05977b3631167028862be2a173976ca11',
+ blockCreated: 137518,
+ },
+ },
})
diff --git a/src/chains/definitions/hashkeyChainTestnet.ts b/src/chains/definitions/hashkeyChainTestnet.ts
new file mode 100644
index 0000000000..12940304c0
--- /dev/null
+++ b/src/chains/definitions/hashkeyChainTestnet.ts
@@ -0,0 +1,22 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const hashkeyTestnet = /*#__PURE__*/ defineChain({
+ id: 133,
+ name: 'HashKey Chain Testnet',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'HashKey EcoPoints',
+ symbol: 'HSK',
+ },
+ rpcUrls: {
+ default: {
+ http: ['https://hashkeychain-testnet.alt.technology'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'HashKey Chain Explorer',
+ url: 'https://hashkeychain-testnet-explorer.alt.technology',
+ },
+ },
+})
diff --git a/src/chains/definitions/holesky.ts b/src/chains/definitions/holesky.ts
index 3e944fd24b..7d395b376e 100644
--- a/src/chains/definitions/holesky.ts
+++ b/src/chains/definitions/holesky.ts
@@ -13,6 +13,7 @@ export const holesky = /*#__PURE__*/ defineChain({
default: {
name: 'Etherscan',
url: 'https://holesky.etherscan.io',
+ apiUrl: 'https://api-holesky.etherscan.io/api',
},
},
contracts: {
diff --git a/src/chains/definitions/immutableZkEvm.ts b/src/chains/definitions/immutableZkEvm.ts
index 2a569442e1..634a7de583 100644
--- a/src/chains/definitions/immutableZkEvm.ts
+++ b/src/chains/definitions/immutableZkEvm.ts
@@ -17,6 +17,7 @@ export const immutableZkEvm = /*#__PURE__*/ defineChain({
default: {
name: 'Immutable Explorer',
url: 'https://explorer.immutable.com',
+ apiUrl: 'https://explorer.immutable.com/api',
},
},
contracts: {
diff --git a/src/chains/definitions/iota.ts b/src/chains/definitions/iota.ts
new file mode 100644
index 0000000000..efcacb3886
--- /dev/null
+++ b/src/chains/definitions/iota.ts
@@ -0,0 +1,25 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const iota = /*#__PURE__*/ defineChain({
+ id: 8822,
+ name: 'IOTA EVM',
+ network: 'iotaevm',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'IOTA',
+ symbol: 'IOTA',
+ },
+ rpcUrls: {
+ default: {
+ http: ['https://json-rpc.evm.iotaledger.net'],
+ webSocket: ['wss://ws.json-rpc.evm.iotaledger.net'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Explorer',
+ url: 'https://explorer.evm.iota.org',
+ apiUrl: 'https://explorer.evm.iota.org/api',
+ },
+ },
+})
diff --git a/src/chains/definitions/iotaTestnet.ts b/src/chains/definitions/iotaTestnet.ts
new file mode 100644
index 0000000000..3309facdc8
--- /dev/null
+++ b/src/chains/definitions/iotaTestnet.ts
@@ -0,0 +1,26 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const iotaTestnet = /*#__PURE__*/ defineChain({
+ id: 1075,
+ name: 'IOTA EVM Testnet',
+ network: 'iotaevm-testnet',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'IOTA',
+ symbol: 'IOTA',
+ },
+ rpcUrls: {
+ default: {
+ http: ['https://json-rpc.evm.testnet.iotaledger.net'],
+ webSocket: ['wss://ws.json-rpc.evm.testnet.iotaledger.net'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Explorer',
+ url: 'https://explorer.evm.testnet.iotaledger.net',
+ apiUrl: 'https://explorer.evm.testnet.iotaledger.net/api',
+ },
+ },
+ testnet: true,
+})
diff --git a/src/chains/definitions/kaia.ts b/src/chains/definitions/kaia.ts
new file mode 100644
index 0000000000..0eb35b90d1
--- /dev/null
+++ b/src/chains/definitions/kaia.ts
@@ -0,0 +1,26 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const kaia = /*#__PURE__*/ defineChain({
+ id: 8_217,
+ name: 'Kaia',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'Kaia',
+ symbol: 'KAIA',
+ },
+ rpcUrls: {
+ default: { http: ['https://public-en.node.kaia.io'] },
+ },
+ blockExplorers: {
+ default: {
+ name: 'KaiaScope',
+ url: 'https://kaiascope.com',
+ },
+ },
+ contracts: {
+ multicall3: {
+ address: '0xcA11bde05977b3631167028862bE2a173976CA11',
+ blockCreated: 96002415,
+ },
+ },
+})
diff --git a/src/chains/definitions/kairos.ts b/src/chains/definitions/kairos.ts
new file mode 100644
index 0000000000..eab25abe3f
--- /dev/null
+++ b/src/chains/definitions/kairos.ts
@@ -0,0 +1,28 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const kairos = /*#__PURE__*/ defineChain({
+ id: 1_001,
+ name: 'Kairos Testnet',
+ network: 'kairos',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'Kairos KAIA',
+ symbol: 'KAIA',
+ },
+ rpcUrls: {
+ default: { http: ['https://public-en-kairos.node.kaia.io'] },
+ },
+ blockExplorers: {
+ default: {
+ name: 'KaiaScope',
+ url: 'https://kairos.kaiascope.com',
+ },
+ },
+ contracts: {
+ multicall3: {
+ address: '0xcA11bde05977b3631167028862bE2a173976CA11',
+ blockCreated: 123390593,
+ },
+ },
+ testnet: true,
+})
diff --git a/src/chains/definitions/linea.ts b/src/chains/definitions/linea.ts
index 109c6d29ad..cdae08e74c 100644
--- a/src/chains/definitions/linea.ts
+++ b/src/chains/definitions/linea.ts
@@ -1,6 +1,8 @@
+import { chainConfig } from '../../linea/chainConfig.js'
import { defineChain } from '../../utils/chain/defineChain.js'
export const linea = /*#__PURE__*/ defineChain({
+ ...chainConfig,
id: 59_144,
name: 'Linea Mainnet',
nativeCurrency: { name: 'Linea Ether', symbol: 'ETH', decimals: 18 },
diff --git a/src/chains/definitions/lineaSepolia.ts b/src/chains/definitions/lineaSepolia.ts
index c719794bf3..07d08d194e 100644
--- a/src/chains/definitions/lineaSepolia.ts
+++ b/src/chains/definitions/lineaSepolia.ts
@@ -1,6 +1,8 @@
+import { chainConfig } from '../../linea/chainConfig.js'
import { defineChain } from '../../utils/chain/defineChain.js'
export const lineaSepolia = /*#__PURE__*/ defineChain({
+ ...chainConfig,
id: 59_141,
name: 'Linea Sepolia Testnet',
nativeCurrency: { name: 'Linea Ether', symbol: 'ETH', decimals: 18 },
diff --git a/src/chains/definitions/sophonTestnet.ts b/src/chains/definitions/sophonTestnet.ts
new file mode 100644
index 0000000000..47a2bc3c2d
--- /dev/null
+++ b/src/chains/definitions/sophonTestnet.ts
@@ -0,0 +1,30 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const sophonTestnet = /*#__PURE__*/ defineChain({
+ id: 531_050_104,
+ name: 'Sophon Testnet',
+ nativeCurrency: {
+ decimals: 18,
+ name: 'Sophon',
+ symbol: 'SOPH',
+ },
+ rpcUrls: {
+ default: {
+ http: ['https://rpc.testnet.sophon.xyz'],
+ webSocket: ['wss://rpc.testnet.sophon.xyz/ws'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Sophon Block Explorer',
+ url: 'https://explorer.testnet.sophon.xyz',
+ },
+ },
+ contracts: {
+ multicall3: {
+ address: '0x83c04d112adedA2C6D9037bb6ecb42E7f0b108Af',
+ blockCreated: 15_642,
+ },
+ },
+ testnet: true,
+})
diff --git a/src/chains/definitions/taiko.ts b/src/chains/definitions/taiko.ts
index ab1ac9f8c3..f41a31f58c 100644
--- a/src/chains/definitions/taiko.ts
+++ b/src/chains/definitions/taiko.ts
@@ -17,8 +17,8 @@ export const taiko = /*#__PURE__*/ defineChain({
blockExplorers: {
default: {
name: 'Taikoscan',
- url: 'https://taikoscan.network',
- apiUrl: 'https://taikoscan.network/api',
+ url: 'https://taikoscan.io',
+ apiUrl: 'https://api.taikoscan.io/api',
},
},
contracts: {
diff --git a/src/chains/definitions/tron.ts b/src/chains/definitions/tron.ts
new file mode 100644
index 0000000000..2280378baa
--- /dev/null
+++ b/src/chains/definitions/tron.ts
@@ -0,0 +1,19 @@
+import { defineChain } from '../../utils/chain/defineChain.js'
+
+export const tron = /*#__PURE__*/ defineChain({
+ id: 728126428,
+ name: 'Tron',
+ nativeCurrency: { name: 'TRON', symbol: 'TRX', decimals: 6 },
+ rpcUrls: {
+ default: {
+ http: ['https://api.trongrid.io/jsonrpc'],
+ },
+ },
+ blockExplorers: {
+ default: {
+ name: 'Tronscan',
+ url: 'https://tronscan.org',
+ apiUrl: 'https://apilist.tronscanapi.com/api',
+ },
+ },
+})
diff --git a/src/chains/index.ts b/src/chains/index.ts
index 2e8ce8e5b2..061d826364 100644
--- a/src/chains/index.ts
+++ b/src/chains/index.ts
@@ -10,6 +10,7 @@ export { apexTestnet } from './definitions/apexTestnet.js'
export { arbitrum } from './definitions/arbitrum.js'
export { arbitrumGoerli } from './definitions/arbitrumGoerli.js'
export { arbitrumNova } from './definitions/arbitrumNova.js'
+export { assetChainTestnet } from './definitions/assetChainTestnet.js'
export { astar } from './definitions/astar.js'
export { astarZkEVM } from './definitions/astarZkEVM.js'
export { astarZkyoto } from './definitions/astarZkyoto.js'
@@ -17,12 +18,14 @@ export { arbitrumSepolia } from './definitions/arbitrumSepolia.js'
export { areonNetwork } from './definitions/areonNetwork.js'
export { areonNetworkTestnet } from './definitions/areonNetworkTestnet.js'
export { artelaTestnet } from './definitions/artelaTestnet.js'
+export { atletaOlympia } from './definitions/atletaOlympia.js'
export { aurora } from './definitions/aurora.js'
export { auroraTestnet } from './definitions/auroraTestnet.js'
export { auroria } from './definitions/auroria.js'
export { avalanche } from './definitions/avalanche.js'
export { avalancheFuji } from './definitions/avalancheFuji.js'
export { b3Sepolia } from './definitions/b3Sepolia.js'
+export { b3 } from './definitions/b3.js'
export { bahamut } from './definitions/bahamut.js'
export { base } from './definitions/base.js'
export { baseGoerli } from './definitions/baseGoerli.js'
@@ -41,8 +44,10 @@ export { bitTorrentTestnet } from './definitions/bitTorrentTestnet.js'
export { blast } from './definitions/blast.js'
export { blastSepolia } from './definitions/blastSepolia.js'
export { bob } from './definitions/bob.js'
+export { bobSepolia } from './definitions/bobSepolia.js'
export { boba } from './definitions/boba.js'
export { bobaSepolia } from './definitions/bobaSepolia.js'
+export { botanixTestnet } from './definitions/botanixTestnet.js'
export { bronos } from './definitions/bronos.js'
export { bronosTestnet } from './definitions/bronosTestnet.js'
export { bsc } from './definitions/bsc.js'
@@ -56,15 +61,18 @@ export { canto } from './definitions/canto.js'
export { celo } from './definitions/celo.js'
export { celoAlfajores } from './definitions/celoAlfajores.js'
export { chiliz } from './definitions/chiliz.js'
+export { chips } from './definitions/chips.js'
export { classic } from './definitions/classic.js'
export { confluxESpace } from './definitions/confluxESpace.js'
export { confluxESpaceTestnet } from './definitions/confluxESpaceTestnet.js'
export { coreDao } from './definitions/coreDao.js'
export { crab } from './definitions/crab.js'
export { cronos } from './definitions/cronos.js'
+export { cronoszkEVM } from './definitions/cronoszkEVM.js'
export { cronoszkEVMTestnet } from './definitions/cronoszkEVMTestnet.js'
export { cronosTestnet } from './definitions/cronosTestnet.js'
export { crossbell } from './definitions/crossbell.js'
+export { curtis } from './definitions/curtis.js'
export { cyber } from './definitions/cyber.js'
export { cyberTestnet } from './definitions/cyberTestnet.js'
export { darwinia } from './definitions/darwinia.js'
@@ -125,6 +133,7 @@ export { gravity } from './definitions/gravity.js'
export { ham } from './definitions/ham.js'
export { hardhat } from './definitions/hardhat.js'
export { harmonyOne } from './definitions/harmonyOne.js'
+export { hashkeyTestnet } from './definitions/hashkeyChainTestnet.js'
export { haqqMainnet } from './definitions/haqqMainnet.js'
export { haqqTestedge2 } from './definitions/haqqTestedge2.js'
export { hedera } from './definitions/hedera.js'
@@ -134,12 +143,18 @@ export { holesky } from './definitions/holesky.js'
export { immutableZkEvm } from './definitions/immutableZkEvm.js'
export { immutableZkEvmTestnet } from './definitions/immutableZkEvmTestnet.js'
export { inEVM } from './definitions/inEVM.js'
+export { iota } from './definitions/iota.js'
+export { iotaTestnet } from './definitions/iotaTestnet.js'
export { kakarotSepolia } from './definitions/kakarotSepolia.js'
export { kava } from './definitions/kava.js'
export { kavaTestnet } from './definitions/kavaTestnet.js'
export { kcc } from './definitions/kcc.js'
+/** @deprecated Use `kaia` instead. */
export { klaytn } from './definitions/klaytn.js'
+/** @deprecated Use `kairos` instead. */
export { klaytnBaobab } from './definitions/klaytnBaobab.js'
+export { kaia } from './definitions/kaia.js'
+export { kairos } from './definitions/kairos.js'
export { koi } from './definitions/koi.js'
export { kroma } from './definitions/kroma.js'
export { kromaSepolia } from './definitions/kromaSepolia.js'
@@ -264,6 +279,7 @@ export { skaleTitanTestnet } from './definitions/skale/titanTestnet.js'
export { sketchpad } from './definitions/sketchpad.js'
export { songbird } from './definitions/songbird.js'
export { songbirdTestnet } from './definitions/songbirdTestnet.js'
+export { sophonTestnet } from './definitions/sophonTestnet.js'
export { spicy } from './definitions/spicy.js'
export { shardeumSphinx } from './definitions/shardeumSphinx.js'
export { shibarium } from './definitions/shibarium.js'
@@ -284,6 +300,7 @@ export { telosTestnet } from './definitions/telosTestnet.js'
export { tenet } from './definitions/tenet.js'
export { thaiChain } from './definitions/thaiChain.js'
export { thunderTestnet } from './definitions/thunderTestnet.js'
+export { tron } from './definitions/tron.js'
export { unreal } from './definitions/unreal.js'
export { vechain } from './definitions/vechain.js'
export { wanchain } from './definitions/wanchain.js'
diff --git a/src/errors/node.ts b/src/errors/node.ts
index 3b7f59970b..45299e1348 100644
--- a/src/errors/node.ts
+++ b/src/errors/node.ts
@@ -150,7 +150,8 @@ export type InsufficientFundsErrorType = InsufficientFundsError & {
name: 'InsufficientFundsError'
}
export class InsufficientFundsError extends BaseError {
- static nodeMessage = /insufficient funds/
+ static nodeMessage =
+ /insufficient funds|exceeds transaction sender account balance/
constructor({ cause }: { cause?: BaseError | undefined } = {}) {
super(
[
diff --git a/src/errors/version.ts b/src/errors/version.ts
index 37a900b108..f37d9ff3fb 100644
--- a/src/errors/version.ts
+++ b/src/errors/version.ts
@@ -1 +1 @@
-export const version = '2.19.2'
+export const version = '2.20.1'
diff --git a/src/experimental/eip5792/actions/sendCalls.test.ts b/src/experimental/eip5792/actions/sendCalls.test.ts
index 223c3a7e73..22d27ce76b 100644
--- a/src/experimental/eip5792/actions/sendCalls.test.ts
+++ b/src/experimental/eip5792/actions/sendCalls.test.ts
@@ -1,43 +1,108 @@
import { expect, test } from 'vitest'
+import { wagmiContractConfig } from '../../../../test/src/abis.js'
import { anvilMainnet } from '../../../../test/src/anvil.js'
import { accounts } from '../../../../test/src/constants.js'
-import { mainnet } from '../../../chains/index.js'
-import { createClient } from '../../../clients/createClient.js'
+import { reset } from '../../../actions/index.js'
+import { type Chain, mainnet } from '../../../chains/index.js'
+import { type Client, createClient } from '../../../clients/createClient.js'
+import type { Transport } from '../../../clients/transports/createTransport.js'
import { custom } from '../../../clients/transports/custom.js'
import { RpcRequestError } from '../../../errors/request.js'
+import type { WalletCallReceipt } from '../../../types/eip1193.js'
+import type { Hex } from '../../../types/misc.js'
import { getHttpRpcClient, parseEther } from '../../../utils/index.js'
+import { uid } from '../../../utils/uid.js'
import { sendCalls } from './sendCalls.js'
-const getClient = ({
+type Uid = string
+type TxHashes = Hex[]
+const calls = new Map()
+
+const testClient = anvilMainnet.getClient()
+
+const getClient = ({
+ chain,
onRequest,
-}: { onRequest({ method, params }: any): void }) =>
+}: {
+ chain?: chain | undefined
+ onRequest({ method, params }: any): void
+}): Client =>
createClient({
+ chain,
transport: custom({
async request({ method, params }) {
- if (method !== 'wallet_sendCalls') return
-
onRequest({ method, params })
const rpcClient = getHttpRpcClient(anvilMainnet.rpcUrl.http)
- for (const call of params[0].calls) {
- const { error } = await rpcClient.request({
- body: {
- method: 'eth_sendTransaction',
- params: [call],
- id: 0,
- },
- })
- if (error)
- throw new RpcRequestError({
- body: { method, params },
- error,
- url: anvilMainnet.rpcUrl.http,
+
+ if (method === 'wallet_getCallsStatus') {
+ const hashes = calls.get(params[0])
+ if (!hashes) return { status: 'PENDING', receipts: [] }
+ const receipts = await Promise.all(
+ hashes.map(async (hash) => {
+ const { result, error } = await rpcClient.request({
+ body: {
+ method: 'eth_getTransactionReceipt',
+ params: [hash],
+ id: 0,
+ },
+ })
+ if (error)
+ throw new RpcRequestError({
+ body: { method, params },
+ error,
+ url: anvilMainnet.rpcUrl.http,
+ })
+ if (!result) throw new Error('receipt not found')
+ return {
+ blockHash: result.blockHash,
+ blockNumber: result.blockNumber,
+ gasUsed: result.gasUsed,
+ logs: result.logs,
+ status: result.status,
+ transactionHash: result.transactionHash,
+ } satisfies WalletCallReceipt
+ }),
+ )
+ return { status: 'CONFIRMED', receipts }
+ }
+
+ if (method === 'wallet_sendCalls') {
+ const hashes = []
+ for (const call of params[0].calls) {
+ const callResult = await rpcClient.request({
+ body: {
+ method: 'eth_call',
+ params: [{ ...call, from: params[0].from }],
+ id: 0,
+ },
})
+ if (callResult.error) throw new Error(callResult.error.message)
+
+ const { result, error } = await rpcClient.request({
+ body: {
+ method: 'eth_sendTransaction',
+ params: [{ ...call, from: params[0].from }],
+ id: 0,
+ },
+ })
+ if (error)
+ throw new RpcRequestError({
+ body: { method, params },
+ error,
+ url: anvilMainnet.rpcUrl.http,
+ })
+ hashes.push(result)
+ }
+ const uid_ = uid()
+ calls.set(uid_, hashes)
+ return uid_
}
- return '0xdeadbeef'
+
+ return null
},
}),
- })
+ }) as never
test('default', async () => {
const requests: unknown[] = []
@@ -48,8 +113,14 @@ test('default', async () => {
},
})
+ await reset(testClient, {
+ blockNumber: 16280770n,
+ jsonRpcUrl: anvilMainnet.forkUrl,
+ })
+
const id_ = await sendCalls(client, {
account: accounts[0].address,
+ chain: mainnet,
calls: [
{
to: accounts[1].address,
@@ -63,29 +134,145 @@ test('default', async () => {
to: accounts[3].address,
value: parseEther('100'),
},
+ {
+ abi: wagmiContractConfig.abi,
+ functionName: 'mint',
+ to: wagmiContractConfig.address,
+ },
+ {
+ abi: wagmiContractConfig.abi,
+ functionName: 'mint',
+ to: wagmiContractConfig.address,
+ },
],
+ })
+
+ expect(id_).toBeDefined()
+ expect(requests).toMatchInlineSnapshot(`
+ [
+ [
+ {
+ "calls": [
+ {
+ "chainId": "0x1",
+ "data": undefined,
+ "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
+ "value": "0xde0b6b3a7640000",
+ },
+ {
+ "chainId": "0x1",
+ "data": undefined,
+ "to": "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc",
+ "value": undefined,
+ },
+ {
+ "chainId": "0x1",
+ "data": "0xcafebabe",
+ "to": "0x90f79bf6eb2c4f870365e785982e1f101e93b906",
+ "value": "0x56bc75e2d63100000",
+ },
+ {
+ "chainId": "0x1",
+ "data": "0x1249c58b",
+ "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
+ "value": undefined,
+ },
+ {
+ "chainId": "0x1",
+ "data": "0x1249c58b",
+ "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
+ "value": undefined,
+ },
+ ],
+ "capabilities": undefined,
+ "chainId": "0x1",
+ "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
+ "version": "1.0",
+ },
+ ],
+ ]
+ `)
+})
+
+test('behavior: chain on client', async () => {
+ const requests: unknown[] = []
+
+ const client = getClient({
chain: mainnet,
+ onRequest({ params }) {
+ requests.push(params)
+ },
})
- expect(id_).toMatchInlineSnapshot(`"0xdeadbeef"`)
+ await reset(testClient, {
+ blockNumber: 16280770n,
+ jsonRpcUrl: anvilMainnet.forkUrl,
+ })
+
+ const id_ = await sendCalls(client, {
+ account: accounts[0].address,
+ calls: [
+ {
+ to: accounts[1].address,
+ value: parseEther('1'),
+ },
+ {
+ to: accounts[2].address,
+ },
+ {
+ data: '0xcafebabe',
+ to: accounts[3].address,
+ value: parseEther('100'),
+ },
+ {
+ abi: wagmiContractConfig.abi,
+ functionName: 'mint',
+ to: wagmiContractConfig.address,
+ },
+ {
+ abi: wagmiContractConfig.abi,
+ functionName: 'mint',
+ to: wagmiContractConfig.address,
+ },
+ ],
+ })
+
+ expect(id_).toBeDefined()
expect(requests).toMatchInlineSnapshot(`
[
[
{
"calls": [
{
+ "chainId": "0x1",
+ "data": undefined,
"to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
"value": "0xde0b6b3a7640000",
},
{
+ "chainId": "0x1",
+ "data": undefined,
"to": "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc",
"value": undefined,
},
{
+ "chainId": "0x1",
"data": "0xcafebabe",
"to": "0x90f79bf6eb2c4f870365e785982e1f101e93b906",
"value": "0x56bc75e2d63100000",
},
+ {
+ "chainId": "0x1",
+ "data": "0x1249c58b",
+ "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
+ "value": undefined,
+ },
+ {
+ "chainId": "0x1",
+ "data": "0x1249c58b",
+ "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
+ "value": undefined,
+ },
],
"capabilities": undefined,
"chainId": "0x1",
@@ -107,18 +294,25 @@ test('error: no chain', async () => {
})
await expect(() =>
- // @ts-expect-error
sendCalls(client, {
account: accounts[0].address,
calls: [
{
+ chainId: 1,
to: accounts[1].address,
value: parseEther('1'),
},
+ {
+ chain: mainnet,
+ to: accounts[1].address,
+ value: parseEther('1'),
+ },
+ // @ts-expect-error
{
to: accounts[2].address,
value: parseEther('10'),
},
+ // @ts-expect-error
{
data: '0xcafebabe',
to: accounts[3].address,
@@ -138,6 +332,7 @@ test('error: no account', async () => {
const requests: unknown[] = []
const client = getClient({
+ chain: mainnet,
onRequest({ params }) {
requests.push(params)
},
@@ -176,6 +371,7 @@ test('error: insufficient funds', async () => {
const requests: unknown[] = []
const client = getClient({
+ chain: mainnet,
onRequest({ params }) {
requests.push(params)
},
diff --git a/src/experimental/eip5792/actions/sendCalls.ts b/src/experimental/eip5792/actions/sendCalls.ts
index 8c6d2d7652..8f06f1c5df 100644
--- a/src/experimental/eip5792/actions/sendCalls.ts
+++ b/src/experimental/eip5792/actions/sendCalls.ts
@@ -1,3 +1,4 @@
+import type { AbiStateMutability, Address, Narrow } from 'abitype'
import { parseAccount } from '../../../accounts/utils/parseAccount.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
@@ -6,13 +7,16 @@ import type { BaseError } from '../../../errors/base.js'
import { ChainNotFoundError } from '../../../errors/chain.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
-import type { Chain, GetChainParameter } from '../../../types/chain.js'
+import type { Chain, DeriveChain } from '../../../types/chain.js'
+import type { ContractFunctionParameters } from '../../../types/contract.js'
import type {
WalletCapabilities,
WalletSendCallsParameters,
} from '../../../types/eip1193.js'
import type { Hex } from '../../../types/misc.js'
-import type { OneOf } from '../../../types/utils.js'
+import type { GetMulticallContractParameters } from '../../../types/multicall.js'
+import type { MaybeRequired, OneOf, Prettify } from '../../../types/utils.js'
+import { encodeFunctionData } from '../../../utils/abi/encodeFunctionData.js'
import type { RequestErrorType } from '../../../utils/buildRequest.js'
import { numberToHex } from '../../../utils/encoding/toHex.js'
import { getTransactionError } from '../../../utils/errors/getTransactionError.js'
@@ -21,23 +25,17 @@ export type SendCallsParameters<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
+ calls extends readonly unknown[] = readonly unknown[],
+ //
+ _chain extends Chain | undefined = DeriveChain,
> = {
- calls: OneOf<
- | {
- to: Hex
- data?: Hex | undefined
- value?: bigint | undefined
- }
- | {
- data: Hex
- }
- >[]
+ chain?: chainOverride | Chain | undefined
+ calls: Calls, _chain>
capabilities?:
| WalletSendCallsParameters[number]['capabilities']
| undefined
version?: WalletSendCallsParameters[number]['version'] | undefined
-} & GetAccountParameter &
- GetChainParameter
+} & GetAccountParameter
export type SendCallsReturnType = string
@@ -76,16 +74,16 @@ export type SendCallsErrorType = RequestErrorType | ErrorType
* })
*/
export async function sendCalls<
+ const calls extends readonly unknown[],
chain extends Chain | undefined,
account extends Account | undefined = undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client,
- parameters: SendCallsParameters,
+ parameters: SendCallsParameters,
): Promise {
const {
account: account_ = client.account,
- calls,
capabilities,
chain = client.chain,
version = '1.0',
@@ -97,7 +95,27 @@ export async function sendCalls<
})
const account = parseAccount(account_)
- if (!chain) throw new ChainNotFoundError()
+ const calls = parameters.calls.map((call_: unknown) => {
+ const call = call_ as Call
+
+ const chainId = call.chain?.id ?? call.chainId ?? chain?.id
+ if (!chainId) throw new ChainNotFoundError()
+
+ const data = call.abi
+ ? encodeFunctionData({
+ abi: call.abi,
+ functionName: call.functionName,
+ args: call.args,
+ })
+ : call.data
+
+ return {
+ chainId: numberToHex(chainId),
+ data,
+ to: call.to,
+ value: call.value ? numberToHex(call.value) : undefined,
+ }
+ })
try {
return await client.request(
@@ -105,10 +123,7 @@ export async function sendCalls<
method: 'wallet_sendCalls',
params: [
{
- calls: calls.map((call) => ({
- ...call,
- value: call.value ? numberToHex(call.value) : undefined,
- })) as any,
+ calls,
capabilities,
chainId: numberToHex(chain!.id),
from: account.address,
@@ -126,3 +141,74 @@ export async function sendCalls<
})
}
}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+type RawCall = { data?: Hex; to?: Address; value?: bigint }
+
+type Call<
+ chain extends Chain | undefined = Chain | undefined,
+ contractFunctionParameters = Omit,
+> = OneOf<
+ | (contractFunctionParameters & {
+ to: Address
+ value?: bigint | undefined
+ })
+ | RawCall
+> &
+ OneOf<
+ | MaybeRequired<
+ { chain?: Chain | undefined },
+ chain extends Chain ? false : true
+ >
+ | MaybeRequired<{ chainId?: number }, chain extends Chain ? false : true>
+ >
+
+type Calls<
+ calls extends readonly unknown[],
+ chain extends Chain | undefined,
+ ///
+ result extends readonly any[] = [],
+> = calls extends readonly [] // no calls, return empty
+ ? readonly []
+ : calls extends readonly [infer call] // one call left before returning `result`
+ ? readonly [
+ ...result,
+ Prettify<
+ Call<
+ chain,
+ Omit<
+ GetMulticallContractParameters,
+ 'address'
+ >
+ >
+ >,
+ ]
+ : calls extends readonly [infer call, ...infer rest] // grab first call and recurse through `rest`
+ ? Calls<
+ [...rest],
+ chain,
+ [
+ ...result,
+ Prettify<
+ Call<
+ chain,
+ Omit<
+ GetMulticallContractParameters,
+ 'address'
+ >
+ >
+ >,
+ ]
+ >
+ : readonly unknown[] extends calls
+ ? calls
+ : // If `calls` is *some* array but we couldn't assign `unknown[]` to it, then it must hold some known/homogenous type!
+ // use this to infer the param types in the case of Array.map() argument
+ calls extends readonly (infer call extends Call<
+ chain,
+ Omit
+ >)[]
+ ? readonly Prettify[]
+ : // Fallback
+ readonly Call>[]
diff --git a/src/experimental/eip5792/actions/writeContracts.test.ts b/src/experimental/eip5792/actions/writeContracts.test.ts
index 9e254abadb..94ae7e88a9 100644
--- a/src/experimental/eip5792/actions/writeContracts.test.ts
+++ b/src/experimental/eip5792/actions/writeContracts.test.ts
@@ -139,16 +139,19 @@ test('default', async () => {
{
"calls": [
{
+ "chainId": "0x1",
"data": "0x1249c58b",
"to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
"value": undefined,
},
{
+ "chainId": "0x1",
"data": "0x1249c58b",
"to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
"value": undefined,
},
{
+ "chainId": "0x1",
"data": "0x1249c58b",
"to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
"value": undefined,
diff --git a/src/experimental/eip5792/actions/writeContracts.ts b/src/experimental/eip5792/actions/writeContracts.ts
index d2889071d7..3f94f66c48 100644
--- a/src/experimental/eip5792/actions/writeContracts.ts
+++ b/src/experimental/eip5792/actions/writeContracts.ts
@@ -50,6 +50,8 @@ export type WriteContractsErrorType =
| ErrorType
/**
+ * @deprecated Use {@link sendCalls} instead. See https://viem.sh/experimental/eip5792/sendCalls#contract-calls.
+ *
* Requests for the wallet to sign and broadcast a batch of write contract calls (transactions) to the network.
*
* - Docs: https://viem.sh/experimental/eip5792/writeContracts
diff --git a/src/experimental/eip7702/utils/hashAuthorization.test.ts b/src/experimental/eip7702/utils/hashAuthorization.test.ts
index 1923ed9173..d354fbae6a 100644
--- a/src/experimental/eip7702/utils/hashAuthorization.test.ts
+++ b/src/experimental/eip7702/utils/hashAuthorization.test.ts
@@ -10,7 +10,7 @@ test('default', () => {
nonce: 40,
}),
).toMatchInlineSnapshot(
- `"0xf4b2818a2452d296afd2a3434f490930548bf91f29196463ab563a9998e698e1"`,
+ `"0x5919da563810a99caf657d42bd10905adbd28b3b89b8a4577efa471e5e4b3914"`,
)
expect(
@@ -20,7 +20,7 @@ test('default', () => {
nonce: 420,
}),
).toMatchInlineSnapshot(
- `"0x62d076dc1afee955cc80d50d192d8d60ea68b5b3b871de7dda3eba3f1544b3c3"`,
+ `"0x9aeacccc1b8571dfc4fb4ba734dbde6e94d6c0188484413585144a755c359aac"`,
)
})
@@ -35,38 +35,38 @@ test('args: to', () => {
).toMatchInlineSnapshot(
`
Uint8Array [
- 190,
- 250,
- 208,
- 44,
- 87,
- 218,
- 191,
- 180,
- 93,
- 163,
- 101,
- 147,
- 149,
- 12,
+ 144,
+ 51,
+ 238,
+ 19,
+ 215,
+ 246,
+ 3,
+ 204,
+ 179,
+ 170,
+ 41,
+ 75,
+ 60,
+ 94,
+ 113,
+ 124,
+ 15,
+ 193,
+ 57,
102,
- 164,
- 115,
- 81,
- 67,
- 100,
- 239,
- 142,
- 22,
- 81,
- 0,
- 165,
- 98,
- 117,
- 147,
- 192,
- 153,
- 209,
+ 229,
+ 8,
+ 229,
+ 140,
+ 203,
+ 54,
+ 237,
+ 73,
+ 228,
+ 251,
+ 177,
+ 230,
]
`,
)
@@ -81,38 +81,38 @@ test('args: to', () => {
).toMatchInlineSnapshot(
`
Uint8Array [
- 98,
- 208,
- 118,
- 220,
- 26,
- 254,
- 233,
- 85,
- 204,
- 128,
- 213,
- 13,
- 25,
- 45,
- 141,
- 96,
+ 154,
234,
- 104,
- 181,
- 179,
- 184,
+ 204,
+ 204,
+ 27,
+ 133,
113,
+ 223,
+ 196,
+ 251,
+ 75,
+ 167,
+ 52,
+ 219,
222,
- 125,
- 218,
- 62,
- 186,
- 63,
- 21,
- 68,
- 179,
- 195,
+ 110,
+ 148,
+ 214,
+ 192,
+ 24,
+ 132,
+ 132,
+ 65,
+ 53,
+ 133,
+ 20,
+ 74,
+ 117,
+ 92,
+ 53,
+ 154,
+ 172,
]
`,
)
diff --git a/src/experimental/eip7702/utils/hashAuthorization.ts b/src/experimental/eip7702/utils/hashAuthorization.ts
index fab6571570..6b544a3fbd 100644
--- a/src/experimental/eip7702/utils/hashAuthorization.ts
+++ b/src/experimental/eip7702/utils/hashAuthorization.ts
@@ -48,11 +48,7 @@ export function hashAuthorization(
const hash = keccak256(
concatHex([
'0x05',
- toRlp([
- numberToHex(chainId),
- contractAddress,
- [nonce ? numberToHex(nonce) : '0x'],
- ]),
+ toRlp([numberToHex(chainId), contractAddress, numberToHex(nonce)]),
]),
)
if (to === 'bytes') return hexToBytes(hash) as HashAuthorizationReturnType
diff --git a/src/experimental/eip7702/utils/serializeAuthorizationList.test.ts b/src/experimental/eip7702/utils/serializeAuthorizationList.test.ts
index d1a3c923a3..4d7983b023 100644
--- a/src/experimental/eip7702/utils/serializeAuthorizationList.test.ts
+++ b/src/experimental/eip7702/utils/serializeAuthorizationList.test.ts
@@ -22,9 +22,7 @@ test('default', () => {
[
"0x1",
"0x0000000000000000000000000000000000000000",
- [
- "0x",
- ],
+ "0x0",
"0x",
"0x",
"0x",
@@ -48,9 +46,7 @@ test('default', () => {
[
"0x1",
"0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
- [
- "0x45",
- ],
+ "0x45",
"0x1",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
@@ -82,9 +78,7 @@ test('default', () => {
[
"0x1",
"0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
- [
- "0x45",
- ],
+ "0x45",
"0x1",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
@@ -92,9 +86,7 @@ test('default', () => {
[
"0x45",
"0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
- [
- "0x1a4",
- ],
+ "0x1a4",
"0x",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
diff --git a/src/experimental/eip7702/utils/serializeAuthorizationList.ts b/src/experimental/eip7702/utils/serializeAuthorizationList.ts
index cc174ff47e..99d2fcd007 100644
--- a/src/experimental/eip7702/utils/serializeAuthorizationList.ts
+++ b/src/experimental/eip7702/utils/serializeAuthorizationList.ts
@@ -24,7 +24,7 @@ export function serializeAuthorizationList(
serializedAuthorizationList.push([
toHex(chainId),
contractAddress,
- [nonce ? toHex(nonce) : '0x'],
+ toHex(nonce),
...toYParitySignatureArray({}, signature),
])
}
diff --git a/src/index.test.ts b/src/index.test.ts
index 38a8491cb2..584b24c187 100644
--- a/src/index.test.ts
+++ b/src/index.test.ts
@@ -42,11 +42,11 @@ test('exports', () => {
"erc20Abi_bytes32",
"erc721Abi",
"erc4626Abi",
+ "universalSignatureValidatorAbi",
"zeroAddress",
"deploylessCallViaBytecodeBytecode",
"deploylessCallViaFactoryBytecode",
"universalSignatureValidatorByteCode",
- "universalSignatureValidatorAbi",
"etherUnits",
"gweiUnits",
"weiUnits",
diff --git a/src/index.ts b/src/index.ts
index ea9cf00833..51816b3f27 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -467,11 +467,14 @@ export type {
} from './actions/wallet/writeContract.js'
export type {
Chain,
+ ChainConfig,
ChainContract,
+ ChainEstimateFeesPerGasFn,
ChainFees,
ChainFeesFnParameters,
ChainFormatter,
ChainEstimateFeesPerGasFnParameters,
+ ChainMaxPriorityFeePerGasFn,
DeriveChain,
GetChainParameter,
ChainFormatters,
@@ -1049,6 +1052,7 @@ export type {
ProviderConnectInfo,
ProviderMessage,
PublicRpcSchema,
+ PaymasterRpcSchema,
NetworkSync,
RpcSchema,
RpcSchemaOverride,
diff --git a/src/jsr.json b/src/jsr.json
index 5c3c83ff1f..2768a189e3 100644
--- a/src/jsr.json
+++ b/src/jsr.json
@@ -1,6 +1,6 @@
{
"name": "@wevm/viem",
- "version": "2.19.2",
+ "version": "2.20.1",
"exports": {
".": "./index.ts",
"./accounts": "./accounts/index.ts",
diff --git a/src/linea/actions/estimateGas.test.ts b/src/linea/actions/estimateGas.test.ts
new file mode 100644
index 0000000000..b8a01acabe
--- /dev/null
+++ b/src/linea/actions/estimateGas.test.ts
@@ -0,0 +1,56 @@
+import { expect, test } from 'vitest'
+
+import { accounts } from '../../../test/src/constants.js'
+import { createClient } from '../../clients/createClient.js'
+import { http } from '../../clients/transports/http.js'
+import { parseEther } from '../../utils/unit/parseEther.js'
+import { lineaSepolia } from '../chains.js'
+import { estimateGas } from './estimateGas.js'
+
+const client = createClient({
+ chain: lineaSepolia,
+ transport: http(),
+})
+
+test('default', async () => {
+ const { baseFeePerGas, gasLimit, priorityFeePerGas } = await estimateGas(
+ client,
+ {
+ account: '0x0000000000000000000000000000000000000000',
+ to: '0x0000000000000000000000000000000000000000',
+ value: parseEther('0.0001'),
+ },
+ )
+ expect(baseFeePerGas).toBeGreaterThan(0n)
+ expect(gasLimit).toBe(21000n)
+ expect(priorityFeePerGas).toBeGreaterThan(0n)
+})
+
+test('error: insufficient balance', async () => {
+ await expect(() =>
+ estimateGas(client, {
+ account: accounts[0].address,
+ to: '0x0000000000000000000000000000000000000000',
+ value: parseEther('0.0001'),
+ }),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
+ [CallExecutionError: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.
+
+ This error could arise when the account does not have enough funds to:
+ - pay for the total gas fee,
+ - pay for the value to send.
+
+ The cost of the transaction is calculated as \`gas * gas fee + value\`, where:
+ - \`gas\` is the amount of gas needed for transaction to execute,
+ - \`gas fee\` is the gas fee,
+ - \`value\` is the amount of ether to send to the recipient.
+
+ Raw Call Arguments:
+ from: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+ to: 0x0000000000000000000000000000000000000000
+ value: 0.0001 ETH
+
+ Details: transaction up-front cost 0x5af31cfe9880 exceeds transaction sender account balance 0x0
+ Version: viem@x.y.z]
+ `)
+})
diff --git a/src/linea/actions/estimateGas.ts b/src/linea/actions/estimateGas.ts
new file mode 100644
index 0000000000..5c9f282f26
--- /dev/null
+++ b/src/linea/actions/estimateGas.ts
@@ -0,0 +1,137 @@
+import type { Account } from '../../accounts/types.js'
+import { parseAccount } from '../../accounts/utils/parseAccount.js'
+import type { EstimateGasParameters as EstimateGasParameters_base } from '../../actions/public/estimateGas.js'
+import {
+ type PrepareTransactionRequestParameters,
+ prepareTransactionRequest,
+} from '../../actions/wallet/prepareTransactionRequest.js'
+import type { Client } from '../../clients/createClient.js'
+import type { Transport } from '../../clients/transports/createTransport.js'
+import { AccountNotFoundError } from '../../errors/account.js'
+import type { BaseError } from '../../errors/base.js'
+import type { GetAccountParameter } from '../../types/account.js'
+import type { Chain } from '../../types/chain.js'
+import type { TransactionRequest } from '../../types/transaction.js'
+import type { Filter } from '../../types/utils.js'
+import { numberToHex } from '../../utils/encoding/toHex.js'
+import { getCallError } from '../../utils/errors/getCallError.js'
+import { extract } from '../../utils/formatters/extract.js'
+import { formatTransactionRequest } from '../../utils/formatters/transactionRequest.js'
+import {
+ type AssertRequestParameters,
+ assertRequest,
+} from '../../utils/transaction/assertRequest.js'
+import type { LineaRpcSchema } from '../types/rpc.js'
+
+export type EstimateGasParameters<
+ chain extends Chain | undefined = Chain | undefined,
+ account extends Account | undefined = Account | undefined,
+> = EstimateGasParameters_base & GetAccountParameter
+
+export type EstimateGasReturnType = {
+ gasLimit: bigint
+ baseFeePerGas: bigint
+ priorityFeePerGas: bigint
+}
+
+/**
+ * Estimates the gas and fees per gas necessary to complete a transaction without submitting it to the network.
+ *
+ * @param client - Client to use
+ * @param parameters - {@link EstimateGasParameters}
+ * @returns A gas estimate and fees per gas (in wei). {@link EstimateGasReturnType}
+ *
+ * @example
+ * import { createPublicClient, http, parseEther } from 'viem'
+ * import { linea } from 'viem/chains'
+ * import { estimateGas } from 'viem/linea'
+ *
+ * const client = createPublicClient({
+ * chain: linea,
+ * transport: http(),
+ * })
+ * const gasEstimate = await estimateGas(client, {
+ * account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
+ * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
+ * value: 0n,
+ * })
+ */
+export async function estimateGas<
+ chain extends Chain | undefined,
+ account extends Account | undefined,
+>(
+ client: Client,
+ args: EstimateGasParameters,
+): Promise {
+ const { account: account_ = client.account } = args
+
+ if (!account_) throw new AccountNotFoundError()
+ const account = parseAccount(account_)
+
+ try {
+ const {
+ accessList,
+ blockNumber,
+ blockTag,
+ data,
+ gas,
+ gasPrice,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ nonce,
+ to,
+ value,
+ ...rest
+ } =
+ account?.type === 'local'
+ ? ((await prepareTransactionRequest(
+ client,
+ args as PrepareTransactionRequestParameters,
+ )) as EstimateGasParameters)
+ : args
+
+ const blockNumberHex = blockNumber ? numberToHex(blockNumber) : undefined
+ const block = blockNumberHex || blockTag
+
+ assertRequest(args as AssertRequestParameters)
+
+ const chainFormat = client.chain?.formatters?.transactionRequest?.format
+ const format = chainFormat || formatTransactionRequest
+
+ const request = format({
+ // Pick out extra data that might exist on the chain's transaction request type.
+ ...extract(rest, { format: chainFormat }),
+ from: account?.address,
+ accessList,
+ data,
+ gas,
+ gasPrice,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ nonce,
+ to,
+ value,
+ } as TransactionRequest)
+
+ type LineaEstimateGasSchema = Filter<
+ LineaRpcSchema,
+ { Method: 'linea_estimateGas' }
+ >[0]
+ const { baseFeePerGas, gasLimit, priorityFeePerGas } =
+ await client.request({
+ method: 'linea_estimateGas',
+ params: block ? [request, block] : [request],
+ })
+ return {
+ baseFeePerGas: BigInt(baseFeePerGas),
+ gasLimit: BigInt(gasLimit),
+ priorityFeePerGas: BigInt(priorityFeePerGas),
+ }
+ } catch (err) {
+ throw getCallError(err as BaseError, {
+ ...args,
+ account,
+ chain: client.chain,
+ })
+ }
+}
diff --git a/src/linea/chainConfig.test.ts b/src/linea/chainConfig.test.ts
new file mode 100644
index 0000000000..3a48b58d97
--- /dev/null
+++ b/src/linea/chainConfig.test.ts
@@ -0,0 +1,67 @@
+import { describe, expect, test, vi } from 'vitest'
+import { address } from '../../test/src/constants.js'
+import { internal_estimateFeesPerGas } from '../actions/public/estimateFeesPerGas.js'
+import { createClient } from '../clients/createClient.js'
+import { http } from '../clients/transports/http.js'
+import { parseEther } from '../utils/index.js'
+import * as estimateGas from './actions/estimateGas.js'
+import * as chainConfig from './chainConfig.js'
+import { lineaSepolia } from './chains.js'
+
+const client = createClient({
+ chain: lineaSepolia,
+ transport: http(),
+})
+
+describe('estimateFeesPerGas', () => {
+ test('default', async () => {
+ const spy = vi.spyOn(estimateGas, 'estimateGas')
+ const { maxFeePerGas, maxPriorityFeePerGas } =
+ await internal_estimateFeesPerGas(client, {
+ request: {
+ account: address.burn,
+ to: address.burn,
+ value: parseEther('0.0001'),
+ },
+ })
+ expect(spy).toBeCalledWith(client, {
+ account: address.burn,
+ to: address.burn,
+ value: parseEther('0.0001'),
+ })
+ expect(maxFeePerGas).toBeDefined()
+ expect(maxPriorityFeePerGas).toBeDefined()
+ })
+
+ test('behavior: `estimateFeesPerGas` returns null', async () => {
+ vi.spyOn(estimateGas, 'estimateGas').mockRejectedValueOnce(new Error())
+ const spy = vi.spyOn(chainConfig.chainConfig.fees, 'estimateFeesPerGas')
+ const { maxFeePerGas, maxPriorityFeePerGas } =
+ await internal_estimateFeesPerGas(client, {
+ request: {
+ account: address.burn,
+ to: address.burn,
+ value: parseEther('0.0001'),
+ },
+ })
+ expect(spy).toHaveReturnedWith(null)
+ expect(maxFeePerGas).toBeDefined()
+ expect(maxPriorityFeePerGas).toBeDefined()
+ })
+
+ test('behavior: `maxPriorityFeePerGas` returns null', async () => {
+ vi.spyOn(estimateGas, 'estimateGas').mockRejectedValue(new Error())
+ const spy = vi.spyOn(chainConfig.chainConfig.fees, 'maxPriorityFeePerGas')
+ const { maxFeePerGas, maxPriorityFeePerGas } =
+ await internal_estimateFeesPerGas(client, {
+ request: {
+ account: address.burn,
+ to: address.burn,
+ value: parseEther('0.0001'),
+ },
+ })
+ expect(spy).toHaveReturnedWith(null)
+ expect(maxFeePerGas).toBeDefined()
+ expect(maxPriorityFeePerGas).toBeDefined()
+ })
+})
diff --git a/src/linea/chainConfig.ts b/src/linea/chainConfig.ts
new file mode 100644
index 0000000000..bffbb769c7
--- /dev/null
+++ b/src/linea/chainConfig.ts
@@ -0,0 +1,53 @@
+import type { ChainConfig, ChainEstimateFeesPerGasFn } from '../types/chain.js'
+import { estimateGas } from './actions/estimateGas.js'
+
+export const chainConfig = {
+ fees: {
+ estimateFeesPerGas,
+ async maxPriorityFeePerGas({ block, client, request }) {
+ const response = await estimateFeesPerGas({
+ block,
+ client,
+ multiply: (x) => x,
+ request,
+ type: 'eip1559',
+ })
+ // Returning `null` will trigger the base `estimateMaxPriorityFeePerGas` to perform
+ // fallback mechanisms to estimate priority fee.
+ if (!response?.maxPriorityFeePerGas) return null
+ return response.maxPriorityFeePerGas
+ },
+ },
+} as const satisfies ChainConfig
+
+///////////////////////////////////////////////////////////////////////////
+// Internal
+///////////////////////////////////////////////////////////////////////////
+
+async function estimateFeesPerGas({
+ client,
+ multiply,
+ request,
+ type,
+}: Parameters[0]): ReturnType {
+ try {
+ const response = await estimateGas(client, {
+ ...request,
+ account: request?.account!,
+ })
+ const { priorityFeePerGas: maxPriorityFeePerGas } = response
+
+ const baseFeePerGas = multiply(BigInt(response.baseFeePerGas))
+ const maxFeePerGas = baseFeePerGas + maxPriorityFeePerGas
+
+ if (type === 'legacy') return { gasPrice: maxFeePerGas }
+ return {
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ }
+ } catch {
+ // Returning `null` will trigger the base `estimateFeesPerGas` to perform
+ // fallback mechanisms to estimate fees.
+ return null
+ }
+}
diff --git a/src/linea/chains.ts b/src/linea/chains.ts
new file mode 100644
index 0000000000..a305d0fd6b
--- /dev/null
+++ b/src/linea/chains.ts
@@ -0,0 +1,3 @@
+// biome-ignore lint/performance/noBarrelFile: entrypoint module
+export { linea } from '../chains/definitions/linea.js'
+export { lineaSepolia } from '../chains/definitions/lineaSepolia.js'
diff --git a/src/linea/index.ts b/src/linea/index.ts
new file mode 100644
index 0000000000..4e3abe2331
--- /dev/null
+++ b/src/linea/index.ts
@@ -0,0 +1,8 @@
+// biome-ignore lint/performance/noBarrelFile: entrypoint module
+export {
+ estimateGas,
+ type EstimateGasParameters,
+ type EstimateGasReturnType,
+} from './actions/estimateGas.js'
+
+export { linea, lineaSepolia } from './chains.js'
diff --git a/src/linea/package.json b/src/linea/package.json
new file mode 100644
index 0000000000..ef7e268f2e
--- /dev/null
+++ b/src/linea/package.json
@@ -0,0 +1,6 @@
+{
+ "type": "module",
+ "types": "../_types/linea/index.d.ts",
+ "module": "../_esm/linea/index.js",
+ "main": "../_cjs/linea/index.js"
+}
diff --git a/src/linea/types/rpc.ts b/src/linea/types/rpc.ts
new file mode 100644
index 0000000000..d61570d600
--- /dev/null
+++ b/src/linea/types/rpc.ts
@@ -0,0 +1,28 @@
+import type { BlockNumber, BlockTag } from '../../types/block.js'
+import type { Hex } from '../../types/misc.js'
+import type {
+ RpcStateOverride,
+ RpcTransactionRequest,
+} from '../../types/rpc.js'
+
+export type LineaRpcSchema = [
+ {
+ Method: 'linea_estimateGas'
+ Parameters?:
+ | [transaction: RpcTransactionRequest]
+ | [
+ transaction: RpcTransactionRequest,
+ block: Hex | BlockNumber | BlockTag,
+ ]
+ | [
+ transaction: RpcTransactionRequest,
+ block: BlockNumber | BlockTag,
+ stateOverride: RpcStateOverride,
+ ]
+ ReturnType: {
+ gasLimit: Hex
+ baseFeePerGas: Hex
+ priorityFeePerGas: Hex
+ }
+ },
+]
diff --git a/src/op-stack/abis.ts b/src/op-stack/abis.ts
index 6d887bdcf8..9d4dc02f2c 100644
--- a/src/op-stack/abis.ts
+++ b/src/op-stack/abis.ts
@@ -913,101 +913,119 @@ export const portal2Abi = [
stateMutability: 'nonpayable',
type: 'constructor',
},
- { inputs: [], name: 'BadTarget', type: 'error' },
- { inputs: [], name: 'CallPaused', type: 'error' },
- { inputs: [], name: 'GasEstimation', type: 'error' },
- { inputs: [], name: 'LargeCalldata', type: 'error' },
- { inputs: [], name: 'OutOfGas', type: 'error' },
- { inputs: [], name: 'SmallGasLimit', type: 'error' },
- { inputs: [], name: 'Unauthorized', type: 'error' },
{
- anonymous: false,
- inputs: [
- { indexed: false, internalType: 'uint8', name: 'version', type: 'uint8' },
- ],
- name: 'Initialized',
- type: 'event',
+ stateMutability: 'payable',
+ type: 'receive',
},
{
- anonymous: false,
- inputs: [
- { indexed: true, internalType: 'address', name: 'from', type: 'address' },
- { indexed: true, internalType: 'address', name: 'to', type: 'address' },
+ inputs: [],
+ name: 'balance',
+ outputs: [
{
- indexed: true,
internalType: 'uint256',
- name: 'version',
+ name: '',
type: 'uint256',
},
- {
- indexed: false,
- internalType: 'bytes',
- name: 'opaqueData',
- type: 'bytes',
- },
],
- name: 'TransactionDeposited',
- type: 'event',
+ stateMutability: 'view',
+ type: 'function',
},
{
- anonymous: false,
inputs: [
{
- indexed: true,
- internalType: 'bytes32',
- name: 'withdrawalHash',
- type: 'bytes32',
+ internalType: 'contract IDisputeGame',
+ name: '_disputeGame',
+ type: 'address',
},
- { indexed: false, internalType: 'bool', name: 'success', type: 'bool' },
],
- name: 'WithdrawalFinalized',
- type: 'event',
+ name: 'blacklistDisputeGame',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
},
{
- anonymous: false,
inputs: [
{
- indexed: true,
internalType: 'bytes32',
- name: 'withdrawalHash',
+ name: '_withdrawalHash',
type: 'bytes32',
},
- { indexed: true, internalType: 'address', name: 'from', type: 'address' },
- { indexed: true, internalType: 'address', name: 'to', type: 'address' },
- ],
- name: 'WithdrawalProven',
- type: 'event',
- },
- {
- inputs: [
{
- internalType: 'contract IDisputeGame',
- name: '_disputeGame',
+ internalType: 'address',
+ name: '_proofSubmitter',
type: 'address',
},
],
- name: 'blacklistDisputeGame',
+ name: 'checkWithdrawal',
outputs: [],
- stateMutability: 'nonpayable',
+ stateMutability: 'view',
type: 'function',
},
{
inputs: [
- { internalType: 'bytes32', name: '_withdrawalHash', type: 'bytes32' },
- { internalType: 'address', name: '_proofSubmitter', type: 'address' },
+ {
+ internalType: 'address',
+ name: '_to',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_mint',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint64',
+ name: '_gasLimit',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bool',
+ name: '_isCreation',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes',
+ name: '_data',
+ type: 'bytes',
+ },
],
- name: 'checkWithdrawal',
+ name: 'depositERC20Transaction',
outputs: [],
- stateMutability: 'view',
+ stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
- { internalType: 'address', name: '_to', type: 'address' },
- { internalType: 'uint256', name: '_value', type: 'uint256' },
- { internalType: 'uint64', name: '_gasLimit', type: 'uint64' },
- { internalType: 'bool', name: '_isCreation', type: 'bool' },
- { internalType: 'bytes', name: '_data', type: 'bytes' },
+ {
+ internalType: 'address',
+ name: '_to',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint64',
+ name: '_gasLimit',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bool',
+ name: '_isCreation',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes',
+ name: '_data',
+ type: 'bytes',
+ },
],
name: 'depositTransaction',
outputs: [],
@@ -1016,10 +1034,20 @@ export const portal2Abi = [
},
{
inputs: [
- { internalType: 'contract IDisputeGame', name: '', type: 'address' },
+ {
+ internalType: 'contract IDisputeGame',
+ name: '',
+ type: 'address',
+ },
],
name: 'disputeGameBlacklist',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
@@ -1039,7 +1067,13 @@ export const portal2Abi = [
{
inputs: [],
name: 'disputeGameFinalityDelaySeconds',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
@@ -1054,12 +1088,36 @@ export const portal2Abi = [
inputs: [
{
components: [
- { internalType: 'uint256', name: 'nonce', type: 'uint256' },
- { internalType: 'address', name: 'sender', type: 'address' },
- { internalType: 'address', name: 'target', type: 'address' },
- { internalType: 'uint256', name: 'value', type: 'uint256' },
- { internalType: 'uint256', name: 'gasLimit', type: 'uint256' },
- { internalType: 'bytes', name: 'data', type: 'bytes' },
+ {
+ internalType: 'uint256',
+ name: 'nonce',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'target',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
],
internalType: 'struct Types.WithdrawalTransaction',
name: '_tx',
@@ -1075,18 +1133,46 @@ export const portal2Abi = [
inputs: [
{
components: [
- { internalType: 'uint256', name: 'nonce', type: 'uint256' },
- { internalType: 'address', name: 'sender', type: 'address' },
- { internalType: 'address', name: 'target', type: 'address' },
- { internalType: 'uint256', name: 'value', type: 'uint256' },
- { internalType: 'uint256', name: 'gasLimit', type: 'uint256' },
- { internalType: 'bytes', name: 'data', type: 'bytes' },
+ {
+ internalType: 'uint256',
+ name: 'nonce',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'target',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
],
internalType: 'struct Types.WithdrawalTransaction',
name: '_tx',
type: 'tuple',
},
- { internalType: 'address', name: '_proofSubmitter', type: 'address' },
+ {
+ internalType: 'address',
+ name: '_proofSubmitter',
+ type: 'address',
+ },
],
name: 'finalizeWithdrawalTransactionExternalProof',
outputs: [],
@@ -1094,16 +1180,34 @@ export const portal2Abi = [
type: 'function',
},
{
- inputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
name: 'finalizedWithdrawals',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'guardian',
- outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
@@ -1138,23 +1242,51 @@ export const portal2Abi = [
{
inputs: [],
name: 'l2Sender',
- outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
{
- inputs: [{ internalType: 'uint64', name: '_byteCount', type: 'uint64' }],
+ inputs: [
+ {
+ internalType: 'uint64',
+ name: '_byteCount',
+ type: 'uint64',
+ },
+ ],
name: 'minimumGasLimit',
- outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
+ outputs: [
+ {
+ internalType: 'uint64',
+ name: '',
+ type: 'uint64',
+ },
+ ],
stateMutability: 'pure',
type: 'function',
},
{
inputs: [
- { internalType: 'bytes32', name: '_withdrawalHash', type: 'bytes32' },
+ {
+ internalType: 'bytes32',
+ name: '_withdrawalHash',
+ type: 'bytes32',
+ },
],
name: 'numProofSubmitters',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
@@ -1162,9 +1294,21 @@ export const portal2Abi = [
inputs: [],
name: 'params',
outputs: [
- { internalType: 'uint128', name: 'prevBaseFee', type: 'uint128' },
- { internalType: 'uint64', name: 'prevBoughtGas', type: 'uint64' },
- { internalType: 'uint64', name: 'prevBlockNum', type: 'uint64' },
+ {
+ internalType: 'uint128',
+ name: 'prevBaseFee',
+ type: 'uint128',
+ },
+ {
+ internalType: 'uint64',
+ name: 'prevBoughtGas',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'prevBlockNum',
+ type: 'uint64',
+ },
],
stateMutability: 'view',
type: 'function',
@@ -1172,24 +1316,50 @@ export const portal2Abi = [
{
inputs: [],
name: 'paused',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'proofMaturityDelaySeconds',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
- { internalType: 'bytes32', name: '', type: 'bytes32' },
- { internalType: 'uint256', name: '', type: 'uint256' },
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
],
name: 'proofSubmitters',
- outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
@@ -1197,34 +1367,78 @@ export const portal2Abi = [
inputs: [
{
components: [
- { internalType: 'uint256', name: 'nonce', type: 'uint256' },
- { internalType: 'address', name: 'sender', type: 'address' },
- { internalType: 'address', name: 'target', type: 'address' },
- { internalType: 'uint256', name: 'value', type: 'uint256' },
- { internalType: 'uint256', name: 'gasLimit', type: 'uint256' },
- { internalType: 'bytes', name: 'data', type: 'bytes' },
- ],
- internalType: 'struct Types.WithdrawalTransaction',
- name: '_tx',
- type: 'tuple',
+ {
+ internalType: 'uint256',
+ name: 'nonce',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'target',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct Types.WithdrawalTransaction',
+ name: '_tx',
+ type: 'tuple',
+ },
+ {
+ internalType: 'uint256',
+ name: '_disputeGameIndex',
+ type: 'uint256',
},
- { internalType: 'uint256', name: '_disputeGameIndex', type: 'uint256' },
{
components: [
- { internalType: 'bytes32', name: 'version', type: 'bytes32' },
- { internalType: 'bytes32', name: 'stateRoot', type: 'bytes32' },
+ {
+ internalType: 'bytes32',
+ name: 'version',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'stateRoot',
+ type: 'bytes32',
+ },
{
internalType: 'bytes32',
name: 'messagePasserStorageRoot',
type: 'bytes32',
},
- { internalType: 'bytes32', name: 'latestBlockhash', type: 'bytes32' },
+ {
+ internalType: 'bytes32',
+ name: 'latestBlockhash',
+ type: 'bytes32',
+ },
],
internalType: 'struct Types.OutputRootProof',
name: '_outputRootProof',
type: 'tuple',
},
- { internalType: 'bytes[]', name: '_withdrawalProof', type: 'bytes[]' },
+ {
+ internalType: 'bytes[]',
+ name: '_withdrawalProof',
+ type: 'bytes[]',
+ },
],
name: 'proveWithdrawalTransaction',
outputs: [],
@@ -1233,8 +1447,16 @@ export const portal2Abi = [
},
{
inputs: [
- { internalType: 'bytes32', name: '', type: 'bytes32' },
- { internalType: 'address', name: '', type: 'address' },
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
],
name: 'provenWithdrawals',
outputs: [
@@ -1243,7 +1465,11 @@ export const portal2Abi = [
name: 'disputeGameProxy',
type: 'address',
},
- { internalType: 'uint64', name: 'timestamp', type: 'uint64' },
+ {
+ internalType: 'uint64',
+ name: 'timestamp',
+ type: 'uint64',
+ },
],
stateMutability: 'view',
type: 'function',
@@ -1251,19 +1477,65 @@ export const portal2Abi = [
{
inputs: [],
name: 'respectedGameType',
- outputs: [{ internalType: 'GameType', name: '', type: 'uint32' }],
+ outputs: [
+ {
+ internalType: 'GameType',
+ name: '',
+ type: 'uint32',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'respectedGameTypeUpdatedAt',
- outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
+ outputs: [
+ {
+ internalType: 'uint64',
+ name: '',
+ type: 'uint64',
+ },
+ ],
stateMutability: 'view',
type: 'function',
},
{
- inputs: [{ internalType: 'GameType', name: '_gameType', type: 'uint32' }],
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint8',
+ name: '_decimals',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_name',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_symbol',
+ type: 'bytes32',
+ },
+ ],
+ name: 'setGasPayingToken',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'GameType',
+ name: '_gameType',
+ type: 'uint32',
+ },
+ ],
name: 'setRespectedGameType',
outputs: [],
stateMutability: 'nonpayable',
@@ -1273,7 +1545,11 @@ export const portal2Abi = [
inputs: [],
name: 'superchainConfig',
outputs: [
- { internalType: 'contract SuperchainConfig', name: '', type: 'address' },
+ {
+ internalType: 'contract SuperchainConfig',
+ name: '',
+ type: 'address',
+ },
],
stateMutability: 'view',
type: 'function',
@@ -1282,7 +1558,11 @@ export const portal2Abi = [
inputs: [],
name: 'systemConfig',
outputs: [
- { internalType: 'contract SystemConfig', name: '', type: 'address' },
+ {
+ internalType: 'contract SystemConfig',
+ name: '',
+ type: 'address',
+ },
],
stateMutability: 'view',
type: 'function',
@@ -1290,11 +1570,280 @@ export const portal2Abi = [
{
inputs: [],
name: 'version',
- outputs: [{ internalType: 'string', name: '', type: 'string' }],
- stateMutability: 'view',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'pure',
type: 'function',
},
- { stateMutability: 'payable', type: 'receive' },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'contract IDisputeGame',
+ name: 'disputeGame',
+ type: 'address',
+ },
+ ],
+ name: 'DisputeGameBlacklisted',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint8',
+ name: 'version',
+ type: 'uint8',
+ },
+ ],
+ name: 'Initialized',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'GameType',
+ name: 'newGameType',
+ type: 'uint32',
+ },
+ {
+ indexed: true,
+ internalType: 'Timestamp',
+ name: 'updatedAt',
+ type: 'uint64',
+ },
+ ],
+ name: 'RespectedGameTypeSet',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'version',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: 'opaqueData',
+ type: 'bytes',
+ },
+ ],
+ name: 'TransactionDeposited',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'withdrawalHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: false,
+ internalType: 'bool',
+ name: 'success',
+ type: 'bool',
+ },
+ ],
+ name: 'WithdrawalFinalized',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'withdrawalHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ ],
+ name: 'WithdrawalProven',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'withdrawalHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'proofSubmitter',
+ type: 'address',
+ },
+ ],
+ name: 'WithdrawalProvenExtension1',
+ type: 'event',
+ },
+ {
+ inputs: [],
+ name: 'AlreadyFinalized',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'BadTarget',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'Blacklisted',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'CallPaused',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'ContentLengthMismatch',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'EmptyItem',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'GasEstimation',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'InvalidDataRemainder',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'InvalidDisputeGame',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'InvalidGameType',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'InvalidHeader',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'InvalidMerkleProof',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'InvalidProof',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'LargeCalldata',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'NoValue',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'NonReentrant',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'OnlyCustomGasToken',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'OutOfGas',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'ProposalNotValidated',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'SmallGasLimit',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'TransferFailed',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'Unauthorized',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'UnexpectedList',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'UnexpectedString',
+ type: 'error',
+ },
+ {
+ inputs: [],
+ name: 'Unproven',
+ type: 'error',
+ },
] as const
export const portalAbi = [
diff --git a/src/op-stack/actions/getTimeToFinalize.ts b/src/op-stack/actions/getTimeToFinalize.ts
index b997dca74d..4839f4e476 100644
--- a/src/op-stack/actions/getTimeToFinalize.ts
+++ b/src/op-stack/actions/getTimeToFinalize.ts
@@ -128,11 +128,18 @@ export async function getTimeToFinalize<
return { period: Number(period), seconds, timestamp }
}
+ const numProofSubmitters = await readContract(client, {
+ abi: portal2Abi,
+ address: portalAddress,
+ functionName: 'numProofSubmitters',
+ args: [withdrawalHash],
+ }).catch(() => 1n)
+
const proofSubmitter = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'proofSubmitters',
- args: [withdrawalHash, 0n],
+ args: [withdrawalHash, numProofSubmitters - 1n],
}).catch(() => undefined)
const [[_disputeGameProxy, proveTimestamp], proofMaturityDelaySeconds] =
diff --git a/src/op-stack/actions/getWithdrawalStatus.ts b/src/op-stack/actions/getWithdrawalStatus.ts
index 5f2d62e6de..014eaf32fc 100644
--- a/src/op-stack/actions/getWithdrawalStatus.ts
+++ b/src/op-stack/actions/getWithdrawalStatus.ts
@@ -197,11 +197,18 @@ export async function getWithdrawalStatus<
return seconds > 0 ? 'waiting-to-finalize' : 'ready-to-finalize'
}
+ const numProofSubmitters = await readContract(client, {
+ abi: portal2Abi,
+ address: portalAddress,
+ functionName: 'numProofSubmitters',
+ args: [withdrawal.withdrawalHash],
+ }).catch(() => 1n)
+
const proofSubmitter = await readContract(client, {
abi: portal2Abi,
address: portalAddress,
functionName: 'proofSubmitters',
- args: [withdrawal.withdrawalHash, 0n],
+ args: [withdrawal.withdrawalHash, numProofSubmitters - 1n],
}).catch(() => withdrawal.sender)
const [disputeGameResult, checkWithdrawalResult, finalizedResult] =
@@ -238,6 +245,7 @@ export async function getWithdrawalStatus<
if (error.cause instanceof ContractFunctionRevertedError) {
const errorMessage = error.cause.data?.args?.[0]
if (
+ errorMessage === 'OptimismPortal: invalid game type' ||
errorMessage === 'OptimismPortal: withdrawal has not been proven yet' ||
errorMessage ===
'OptimismPortal: withdrawal has not been proven by proof submitter address yet'
@@ -251,6 +259,9 @@ export async function getWithdrawalStatus<
errorMessage === 'OptimismPortal: output proposal in air-gap'
)
return 'waiting-to-finalize'
+
+ if (error.cause.data?.errorName === 'InvalidGameType')
+ return 'ready-to-prove'
}
throw checkWithdrawalResult.reason
}
diff --git a/src/op-stack/actions/proveWithdrawal.test.ts b/src/op-stack/actions/proveWithdrawal.test.ts
index 95dff29968..7cae530483 100644
--- a/src/op-stack/actions/proveWithdrawal.test.ts
+++ b/src/op-stack/actions/proveWithdrawal.test.ts
@@ -138,7 +138,7 @@ test('error: small gas', async () => {
gas: 69n,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
- [TransactionExecutionError: The amount of gas (69) provided for the transaction is too low.
+ [ContractFunctionExecutionError: The amount of gas (69) provided for the transaction is too low.
Request Arguments:
chain: Ethereum (Local) (id: 1)
@@ -146,7 +146,14 @@ test('error: small gas', async () => {
to: 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed
data: 0x4870496f00000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000011b1000000000000000000000000000000000000000000000000000000000000000092822c772346d9c8ad1c28885a736de9189a4523e0c79639831a4eed651d837f04091eb8ad4eb056aff2749c1d17b1ed1a0cdcd44f9d7a539ffd56e4b2b4e1f867319b70138527b1087a535099cf8a4db4692ca7cee16b7a3ebd950408ed610a00000000000000000000000000000000000000000000000000000000000003800001000000000000000000000000000000000000000000000000000000002d49000000000000000000000000420000000000000000000000000000000000000700000000000000000000000025ace71c97b33cc4729cf772ae268934f7ab5fa1000000000000000000000000000000000000000000000000002e2f6e5e148000000000000000000000000000000000000000000000000000000000000004638800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001a4d764ad0b0001000000000000000000000000000000000000000000000000000000002d49000000000000000000000000420000000000000000000000000000000000001000000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be1000000000000000000000000000000000000000000000000002e2f6e5e148000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a41635f5fd000000000000000000000000bcce5f55dfda11600e48e91598ad0f8645466142000000000000000000000000bcce5f55dfda11600e48e91598ad0f8645466142000000000000000000000000000000000000000000000000002e2f6e5e148000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000214f90211a07dd255038ced20e27bd9c823d53dff05dab9b56f47efec4d1373c6af4fef5989a0a72a37936fd968f361c541a4d5374d3862f0c3e6125b095158982b8eca440da0a0bb0cf61ec3b7954fa2f09263e712f1b9ed681dcab015932294512ad8b288b90da0d91dbd89baf8206e4952fa0f183cb76fee20ad8ab6d0c200053c4ed3e64d1b32a0ab94dcdd454eb74ece1d1fc6bee90a2cc9468a9be3500d774328cced5dd136dca0fa51148073d2fd37bea62e78e31d2f29f95bacdd72ee987ecbbce8fa98433681a0a7ad5e83e3e1b8d2e85dc249a99fbcff1673cf55c9915c879ac139ac0bf26dd5a0c5766a7cdac0498fd865d735ab9085a3a4163e2338c422e33c4a6b4d1ad0e9afa073b391a00e484b6729f77c11aa424511c8618c34a23dddbfc0fe0265dc4eb4cfa01b50892e8e4ecbfd5bc0cf53604f6f23cd706dc79719972d62be28e627ae287ca05658e252128bfb8ff743644e9610400bebc0264a5a1511469e9d35088c601437a06b5301e3158ca1288125e03b486e99d23baaa5706858ed39488854e197de81bba098a631fd53cccaaafc3b2e217a85cc4243fec1de269dab5135dede898a56993da0b72713ea8fc5cf977ed19945cd080171f40ea428fe438b99ffa70e086f2e38a1a0be5a3eadca25a2ed6a9b09f8d4203ad9bf76627a241756240cb241a5d7bfd422a0453403dd41482ab5064f94fa2d2e96182fb8b3b9f85473d300689c63f46f2441800000000000000000000000000000000000000000000000000000000000000000000000000000000000000214f90211a0a0ab3e1601d6549af5c32a5c38fef42f0866f24fc16ce70f5d6119733b32ce1ba0f92fcd0021c63ab9136e6df54db3ab735dc267ca8e5725c1c127c107c0d3fc83a0efeb2ad3839ca38f72fbb877c158ade23d7d62b49971abbbe874ae4d44298cdba0d5435ee90a9a1f4c369a0228fcef2d70f8e45d11bdb7bd7b823eb2e265824ac1a033a919415c4532290ca370f71492f352c9c729e4012955d461d525cb8b0faa3ca05cc9620184e8a396cacd0c4f76d25fd4ef80831b3717bfe1d89a0753c544a46fa059baaf0e765b6ff0c9488f7afa06071b9eda81a13b2db2762287c26623f8b27ca04735c39e8cd3d267e6e91f867c2b7120c86ff0c98c4cccd67dfa634f0870f6eda02a80ec76fee519323b8b553e071480d28ca693f95a21b82a48c70adc8155dcbba07809ffaff9ca0875ef1f6e1f84584e2fc79fc2054a47c150aea03f02dabf5cfaa00775eb64cd0add1462a1d0d762424f60fd5faed324f51d48d709ed564cc6d494a011c3b1c19b83e86d587900cfe3a3e2d8534a5c705bad96e68ef3ca0126cbe6a0a0459db754e27d481108d0bfe242f492f06e317437700d860d8a171b961acb3be1a058c7be7e1965ecae30b844bf8676adc851d6af299c6bccaf0856bec60776b01fa05a5ddb72a2a98858c0b4120d728947537bdcc9ab061c4b8da0f684576822bebca07e38f091de1b9e0fcf00f9fdfdec18eb34bfd3996c88329c4cd2d916dbf29cdc800000000000000000000000000000000000000000000000000000000000000000000000000000000000000214f90211a091e1c27400a43c5a5c197c19c9f9762fa99615f751f093ec268dcde5a0e46363a07c4dff1acc35fa31805556cda36b9618263faecf49a5b287b97fe39a13408c8da03c37d2c5a2f388350546e74c4593236849543f6476aa70f034d88ecc43e1d190a0abaf9651fa71053aa953bdc895c75969f82ed3569d9f001a7f7be66a92b1e6c9a04dfc96da68c1d49908f89f5a9bed4f65c514d1e2193ff1126f9700952e4daceda0ceb6d263009c644f0a348d951e12185bafe238e985432fb5a0deb94fa9a3b2b3a0eb493209507df91c53c45366178061b03226000cf2a8c4ef88fc4e809ae82cd0a064006be53d6f88a198080f749ffb1d6743843c63d3e6f6e395f103c572c73796a0466c8bea652668720b53de631bc1d16935bfaa85c730f6f7d19fcbe4704ab047a0c2792da5608db91851be4114546902cb4cbebea053665b1329c1e73f24e72d48a05fdd0ade55a0571d508274576bcd7a2ced913e77534ff267b3e60368b2ee95c5a0b574398c5e6640048b26a7ca2298105f027dd3561652a1f1fa9ba1c01ed0057fa0d1a98317c3dee409a6178771fc67378e3a14197f4f9f9e5aed1c6b05584d3f48a0e9abf8d9df852a45a5310777b46fbdfa0876e83063a94bc91096c4d5bb8385dba0f831723d52c0b60b61bb830c9a33c9f9b2d83830c3ed570e5da44ae6ee80a953a0333636ac068b435c011fd4e7d30dc52a8bbaf8e9d861a95eee4d3e512ff839c58000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b4f901b1a0e480ad00d97a48b6ecdbf027135399615123578f3de7e572259000b946f4c87080a09d2298b1328a8afd6b47f0bd57a1332011ab02614a86ef6b544baf61e425ba9ea05713276bc96f85c79bb9f4e4ef517d5bafe56db6775fd27f757981fe94846aa4a02f787118beba540f07c1fd3b488628ee0fa47694aa5eb1d86405ff25b3d6f66b80a09a628c00eebfe343a8f4a7072aa6ee968eea22a6dde4ac3a29d65bdaae854758a01ffd70ab795cbc879376990fad07f95ec2bf6dc9a51ae3603bfd5f321dc7474aa0cf82883dc01744467fa15bec5689b559b70aa63c6d341548676605e927a102cba0fdb90a7114f2137e15ac8915bf54727cd5d0dead26962eefe4ab2499ec6b5c65a00909bea4f700704cda454c330e2a88f73ebd6a7d7e8fef4204397e154953de99a0bbab7f75e0804aaee0f2761a49579f08820eb074f5ff9320ff5f48383975079880a0b3663141987995925fed9ef86f8fc02a44a42136645714891831ccdf1e08c68ea00a23286f92dbcd146255c6c2cc880990cbd694894653701169c07f6098d9573da0d8a58420dc5d85d4150c2bc6fcae28eb3a843d92aaba1274161e12554c389b8d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000022e19f20418ffb24ba711dfecd671b4054aa2e87efe3d10484b88078ceef79373c6001000000000000000000000000000000000000000000000000000000000000
gas: 69
-
+
+ Contract Call:
+ address: 0x0000000000000000000000000000000000000000
+ function: proveWithdrawalTransaction((uint256 nonce, address sender, address target, uint256 value, uint256 gasLimit, bytes data), uint256 _l2OutputIndex, (bytes32 version, bytes32 stateRoot, bytes32 messagePasserStorageRoot, bytes32 latestBlockhash), bytes[] _withdrawalProof)
+ args: ({"data":"0xd764ad0b0001000000000000000000000000000000000000000000000000000000002d49000000000000000000000000420000000000000000000000000000000000001000000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be1000000000000000000000000000000000000000000000000002e2f6e5e148000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a41635f5fd000000000000000000000000bcce5f55dfda11600e48e91598ad0f8645466142000000000000000000000000bcce5f55dfda11600e48e91598ad0f8645466142000000000000000000000000000000000000000000000000002e2f6e5e1480000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","gasLimit":"287624","nonce":"1766847064778384329583297500742918515827483896875618958121606201292631369","sender":"0x4200000000000000000000000000000000000007","target":"0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1","value":"13000000000000000"}, 4529, {"latestBlockhash":"0x67319b70138527b1087a535099cf8a4db4692ca7cee16b7a3ebd950408ed610a","messagePasserStorageRoot":"0x04091eb8ad4eb056aff2749c1d17b1ed1a0cdcd44f9d7a539ffd56e4b2b4e1f8","stateRoot":"0x92822c772346d9c8ad1c28885a736de9189a4523e0c79639831a4eed651d837f","version":"0x0000000000000000000000000000000000000000000000000000000000000000"}, ["0xf90211a07dd255038ced20e27bd9c823d53dff05dab9b56f47efec4d1373c6af4fef5989a0a72a37936fd968f361c541a4d5374d3862f0c3e6125b095158982b8eca440da0a0bb0cf61ec3b7954fa2f09263e712f1b9ed681dcab015932294512ad8b288b90da0d91dbd89baf8206e4952fa0f183cb76fee20ad8ab6d0c200053c4ed3e64d1b32a0ab94dcdd454eb74ece1d1fc6bee90a2cc9468a9be3500d774328cced5dd136dca0fa51148073d2fd37bea62e78e31d2f29f95bacdd72ee987ecbbce8fa98433681a0a7ad5e83e3e1b8d2e85dc249a99fbcff1673cf55c9915c879ac139ac0bf26dd5a0c5766a7cdac0498fd865d735ab9085a3a4163e2338c422e33c4a6b4d1ad0e9afa073b391a00e484b6729f77c11aa424511c8618c34a23dddbfc0fe0265dc4eb4cfa01b50892e8e4ecbfd5bc0cf53604f6f23cd706dc79719972d62be28e627ae287ca05658e252128bfb8ff743644e9610400bebc0264a5a1511469e9d35088c601437a06b5301e3158ca1288125e03b486e99d23baaa5706858ed39488854e197de81bba098a631fd53cccaaafc3b2e217a85cc4243fec1de269dab5135dede898a56993da0b72713ea8fc5cf977ed19945cd080171f40ea428fe438b99ffa70e086f2e38a1a0be5a3eadca25a2ed6a9b09f8d4203ad9bf76627a241756240cb241a5d7bfd422a0453403dd41482ab5064f94fa2d2e96182fb8b3b9f85473d300689c63f46f244180","0xf90211a0a0ab3e1601d6549af5c32a5c38fef42f0866f24fc16ce70f5d6119733b32ce1ba0f92fcd0021c63ab9136e6df54db3ab735dc267ca8e5725c1c127c107c0d3fc83a0efeb2ad3839ca38f72fbb877c158ade23d7d62b49971abbbe874ae4d44298cdba0d5435ee90a9a1f4c369a0228fcef2d70f8e45d11bdb7bd7b823eb2e265824ac1a033a919415c4532290ca370f71492f352c9c729e4012955d461d525cb8b0faa3ca05cc9620184e8a396cacd0c4f76d25fd4ef80831b3717bfe1d89a0753c544a46fa059baaf0e765b6ff0c9488f7afa06071b9eda81a13b2db2762287c26623f8b27ca04735c39e8cd3d267e6e91f867c2b7120c86ff0c98c4cccd67dfa634f0870f6eda02a80ec76fee519323b8b553e071480d28ca693f95a21b82a48c70adc8155dcbba07809ffaff9ca0875ef1f6e1f84584e2fc79fc2054a47c150aea03f02dabf5cfaa00775eb64cd0add1462a1d0d762424f60fd5faed324f51d48d709ed564cc6d494a011c3b1c19b83e86d587900cfe3a3e2d8534a5c705bad96e68ef3ca0126cbe6a0a0459db754e27d481108d0bfe242f492f06e317437700d860d8a171b961acb3be1a058c7be7e1965ecae30b844bf8676adc851d6af299c6bccaf0856bec60776b01fa05a5ddb72a2a98858c0b4120d728947537bdcc9ab061c4b8da0f684576822bebca07e38f091de1b9e0fcf00f9fdfdec18eb34bfd3996c88329c4cd2d916dbf29cdc80","0xf90211a091e1c27400a43c5a5c197c19c9f9762fa99615f751f093ec268dcde5a0e46363a07c4dff1acc35fa31805556cda36b9618263faecf49a5b287b97fe39a13408c8da03c37d2c5a2f388350546e74c4593236849543f6476aa70f034d88ecc43e1d190a0abaf9651fa71053aa953bdc895c75969f82ed3569d9f001a7f7be66a92b1e6c9a04dfc96da68c1d49908f89f5a9bed4f65c514d1e2193ff1126f9700952e4daceda0ceb6d263009c644f0a348d951e12185bafe238e985432fb5a0deb94fa9a3b2b3a0eb493209507df91c53c45366178061b03226000cf2a8c4ef88fc4e809ae82cd0a064006be53d6f88a198080f749ffb1d6743843c63d3e6f6e395f103c572c73796a0466c8bea652668720b53de631bc1d16935bfaa85c730f6f7d19fcbe4704ab047a0c2792da5608db91851be4114546902cb4cbebea053665b1329c1e73f24e72d48a05fdd0ade55a0571d508274576bcd7a2ced913e77534ff267b3e60368b2ee95c5a0b574398c5e6640048b26a7ca2298105f027dd3561652a1f1fa9ba1c01ed0057fa0d1a98317c3dee409a6178771fc67378e3a14197f4f9f9e5aed1c6b05584d3f48a0e9abf8d9df852a45a5310777b46fbdfa0876e83063a94bc91096c4d5bb8385dba0f831723d52c0b60b61bb830c9a33c9f9b2d83830c3ed570e5da44ae6ee80a953a0333636ac068b435c011fd4e7d30dc52a8bbaf8e9d861a95eee4d3e512ff839c580","0xf901b1a0e480ad00d97a48b6ecdbf027135399615123578f3de7e572259000b946f4c87080a09d2298b1328a8afd6b47f0bd57a1332011ab02614a86ef6b544baf61e425ba9ea05713276bc96f85c79bb9f4e4ef517d5bafe56db6775fd27f757981fe94846aa4a02f787118beba540f07c1fd3b488628ee0fa47694aa5eb1d86405ff25b3d6f66b80a09a628c00eebfe343a8f4a7072aa6ee968eea22a6dde4ac3a29d65bdaae854758a01ffd70ab795cbc879376990fad07f95ec2bf6dc9a51ae3603bfd5f321dc7474aa0cf82883dc01744467fa15bec5689b559b70aa63c6d341548676605e927a102cba0fdb90a7114f2137e15ac8915bf54727cd5d0dead26962eefe4ab2499ec6b5c65a00909bea4f700704cda454c330e2a88f73ebd6a7d7e8fef4204397e154953de99a0bbab7f75e0804aaee0f2761a49579f08820eb074f5ff9320ff5f48383975079880a0b3663141987995925fed9ef86f8fc02a44a42136645714891831ccdf1e08c68ea00a23286f92dbcd146255c6c2cc880990cbd694894653701169c07f6098d9573da0d8a58420dc5d85d4150c2bc6fcae28eb3a843d92aaba1274161e12554c389b8d80","0xe19f20418ffb24ba711dfecd671b4054aa2e87efe3d10484b88078ceef79373c6001"])
+ sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
+
+ Docs: https://viem.sh/docs/contract/writeContract
Details: intrinsic gas too low
Version: viem@x.y.z]
`)
diff --git a/src/package.json b/src/package.json
index 07db113af9..ef265fcdc7 100644
--- a/src/package.json
+++ b/src/package.json
@@ -1,7 +1,7 @@
{
"name": "viem",
"description": "TypeScript Interface for Ethereum",
- "version": "2.19.2",
+ "version": "2.20.1",
"type": "module",
"main": "./_cjs/index.js",
"module": "./_esm/index.js",
@@ -70,6 +70,11 @@
"import": "./_esm/experimental/erc7739/index.js",
"default": "./_cjs/experimental/erc7739/index.js"
},
+ "./linea": {
+ "types": "./_types/linea/index.d.ts",
+ "import": "./_esm/linea/index.js",
+ "default": "./_cjs/linea/index.js"
+ },
"./node": {
"types": "./_types/node/index.d.ts",
"import": "./_esm/node/index.js",
diff --git a/src/types/chain.ts b/src/types/chain.ts
index 4eeb91d269..3caeef921b 100644
--- a/src/types/chain.ts
+++ b/src/types/chain.ts
@@ -58,7 +58,18 @@ export type Chain<
sourceId?: number | undefined
/** Flag for test networks */
testnet?: boolean | undefined
+} & ChainConfig
+/////////////////////////////////////////////////////////////////////
+// Config
+/////////////////////////////////////////////////////////////////////
+
+export type ChainConfig<
+ formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
+ custom extends Record | undefined =
+ | Record
+ | undefined,
+> = {
/** Custom chain data. */
custom?: custom | undefined
/** Modifies how fees are derived. */
@@ -70,7 +81,51 @@ export type Chain<
}
/////////////////////////////////////////////////////////////////////
-// Config
+// Fees
+/////////////////////////////////////////////////////////////////////
+
+export type ChainFeesFnParameters<
+ formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
+> = {
+ /** The latest block. */
+ block: Prettify<
+ FormattedBlock & { formatters: formatters }>
+ >
+ client: Client
+ /**
+ * A transaction request. This value will be undefined if the caller
+ * is outside of a transaction request context (e.g. a direct call to
+ * the `estimateFeesPerGas` Action).
+ */
+ request?:
+ | PrepareTransactionRequestParameters<
+ Omit & { formatters: formatters },
+ Account | undefined,
+ undefined
+ >
+ | undefined
+}
+
+export type ChainEstimateFeesPerGasFnParameters<
+ formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
+> = {
+ /** A function to multiply the base fee based on the `baseFeeMultiplier` value. */
+ multiply: (x: bigint) => bigint
+ /** The type of fees to return. */
+ type: FeeValuesType
+} & ChainFeesFnParameters
+
+export type ChainEstimateFeesPerGasFn<
+ formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
+> = (
+ args: ChainEstimateFeesPerGasFnParameters,
+) => Promise
+
+export type ChainMaxPriorityFeePerGasFn<
+ formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
+> = (
+ args: ChainFeesFnParameters,
+) => Promise | bigint | null
export type ChainFees<
formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
@@ -90,23 +145,27 @@ export type ChainFees<
*
* Overrides the return value in the [`estimateMaxPriorityFeePerGas` Action](/docs/actions/public/estimateMaxPriorityFeePerGas).
*/
+ maxPriorityFeePerGas?:
+ | bigint
+ | ChainMaxPriorityFeePerGasFn
+ | undefined
+ /** @deprecated Use `maxPriorityFeePerGas` instead. */
defaultPriorityFee?:
| bigint
- | ((args: ChainFeesFnParameters) => Promise | bigint)
+ | ChainMaxPriorityFeePerGasFn
| undefined
/**
* Allows customization of fee per gas values (e.g. `maxFeePerGas`/`maxPriorityFeePerGas`).
*
* Overrides the return value in the [`estimateFeesPerGas` Action](/docs/actions/public/estimateFeesPerGas).
*/
- estimateFeesPerGas?:
- | bigint
- | ((
- args: ChainEstimateFeesPerGasFnParameters,
- ) => Promise)
- | undefined
+ estimateFeesPerGas?: ChainEstimateFeesPerGasFn | undefined
}
+/////////////////////////////////////////////////////////////////////
+// Formatters
+/////////////////////////////////////////////////////////////////////
+
export type ChainFormatters = {
/** Modifies how the Block structure is formatted & typed. */
block?: ChainFormatter<'block'> | undefined
@@ -123,6 +182,10 @@ export type ChainFormatter = {
type: type
}
+/////////////////////////////////////////////////////////////////////
+// Serializers
+/////////////////////////////////////////////////////////////////////
+
export type ChainSerializers<
formatters extends ChainFormatters | undefined = undefined,
///
@@ -140,42 +203,9 @@ export type ChainSerializers<
| undefined
}
-/////////////////////////////////////////////////////////////////////
-// Parameters
-
-export type ChainFeesFnParameters<
- formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
-> = {
- /** The latest block. */
- block: Prettify<
- FormattedBlock & { formatters: formatters }>
- >
- client: Client
- /**
- * A transaction request. This value will be undefined if the caller
- * is outside of a transaction request context (e.g. a direct call to
- * the `estimateFeesPerGas` Action).
- */
- request?:
- | PrepareTransactionRequestParameters<
- Omit & { formatters: formatters },
- Account | undefined,
- undefined
- >
- | undefined
-}
-
-export type ChainEstimateFeesPerGasFnParameters<
- formatters extends ChainFormatters | undefined = ChainFormatters | undefined,
-> = {
- /** A function to multiply the base fee based on the `baseFeeMultiplier` value. */
- multiply: (x: bigint) => bigint
- /** The type of fees to return. */
- type: FeeValuesType
-} & ChainFeesFnParameters
-
/////////////////////////////////////////////////////////////////////
// Utils
+/////////////////////////////////////////////////////////////////////
export type ExtractChainFormatterExclude<
chain extends Chain | undefined,
@@ -228,6 +258,7 @@ export type GetChainParameter<
/////////////////////////////////////////////////////////////////////
// Constants
+/////////////////////////////////////////////////////////////////////
type ChainBlockExplorer = {
name: string
diff --git a/src/types/eip1193.ts b/src/types/eip1193.ts
index bfdd6c0e7a..d45779a1b3 100644
--- a/src/types/eip1193.ts
+++ b/src/types/eip1193.ts
@@ -207,18 +207,15 @@ export type WalletSendCallsParameters<
quantity extends Quantity | bigint = Quantity,
> = [
{
- calls: OneOf<
- | {
- to: Address
- data?: Hex | undefined
- value?: quantity | undefined
- }
- | {
- data: Hex
- }
- >[]
+ calls: readonly {
+ chainId?: chainId | undefined
+ to?: Address | undefined
+ data?: Hex | undefined
+ value?: quantity | undefined
+ }[]
capabilities?: capabilities | undefined
- chainId: chainId
+ /** @deprecated Use `chainId` on `calls` instead. */
+ chainId?: chainId | undefined
from: Address
version: string
},
@@ -1335,7 +1332,7 @@ export type TestRpcSchema = [
* @link https://ganache.dev/#evm_setAccountBalance
*/
{
- Method: `evm_setAccountBalance`
+ Method: 'evm_setAccountBalance'
Parameters: [
/** The address of the target account. */
address: Address,
@@ -1344,12 +1341,26 @@ export type TestRpcSchema = [
]
ReturnType: void
},
+ /**
+ * @description Modifies the bytecode stored at an account's address.
+ * @link https://ganache.dev/#evm_setAccountCode
+ */
+ {
+ Method: 'evm_setAccountCode'
+ Parameters: [
+ /** The address of the contract. */
+ address: Address,
+ /** Data bytecode. */
+ data: string,
+ ]
+ ReturnType: void
+ },
/**
* @description Enables or disables, based on the single boolean argument, the automatic mining of new blocks with each new transaction submitted to the network.
* @link https://hardhat.org/hardhat-network/docs/reference#evm_setautomine
*/
{
- Method: `evm_setAutomine`
+ Method: 'evm_setAutomine'
Parameters: [boolean]
ReturnType: void
},
@@ -1367,7 +1378,7 @@ export type TestRpcSchema = [
* @link https://github.com/trufflesuite/ganache/blob/ef1858d5d6f27e4baeb75cccd57fb3dc77a45ae8/src/chains/ethereum/ethereum/RPC-METHODS.md#evm_increasetime
*/
{
- Method: `evm_increaseTime`
+ Method: 'evm_increaseTime'
Parameters: [seconds: Quantity]
ReturnType: Quantity
},
diff --git a/src/utils/transaction/serializeTransaction.test.ts b/src/utils/transaction/serializeTransaction.test.ts
index 240dcd4b00..2dcb5943b1 100644
--- a/src/utils/transaction/serializeTransaction.test.ts
+++ b/src/utils/transaction/serializeTransaction.test.ts
@@ -65,7 +65,7 @@ describe('eip7702', () => {
const serialized = serializeTransaction(baseEip7702)
assertType(serialized)
expect(serialized).toMatchInlineSnapshot(
- `"0x04f8e5018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8bcf85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85b0a940000000000000000000000000000000000000000c14501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
+ `"0x04f8e3018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8baf85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85a0a9400000000000000000000000000000000000000004501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
)
expect(parseTransaction(serialized)).toEqual({
...baseEip7702,
@@ -81,7 +81,7 @@ describe('eip7702', () => {
yParity: 1,
}),
).toMatchInlineSnapshot(
- `"0x04f90128018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8bcf85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85b0a940000000000000000000000000000000000000000c14501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe01a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
+ `"0x04f90126018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8baf85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85a0a9400000000000000000000000000000000000000004501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe01a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
)
expect(
serializeTransaction(
@@ -94,7 +94,7 @@ describe('eip7702', () => {
},
),
).toMatchInlineSnapshot(
- `"0x04f90128018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8bcf85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85b0a940000000000000000000000000000000000000000c14501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe80a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
+ `"0x04f90126018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8baf85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85a0a9400000000000000000000000000000000000000004501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe80a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
)
expect(
serializeTransaction(
@@ -107,7 +107,7 @@ describe('eip7702', () => {
},
),
).toMatchInlineSnapshot(
- `"0x04f90128018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8bcf85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85b0a940000000000000000000000000000000000000000c14501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe80a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
+ `"0x04f90126018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8baf85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85a0a9400000000000000000000000000000000000000004501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe80a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
)
expect(
serializeTransaction(
@@ -120,7 +120,7 @@ describe('eip7702', () => {
},
),
).toMatchInlineSnapshot(
- `"0x04f90128018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8bcf85d0194fba3912ca04dd458c843e2ee08967fc04f3579c2c38201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85b0a940000000000000000000000000000000000000000c14501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe01a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
+ `"0x04f90126018203118080809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0f8baf85c0194fba3912ca04dd458c843e2ee08967fc04f3579c28201a480a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fef85a0a9400000000000000000000000000000000000000004501a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe01a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fea060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe"`,
)
})
})
diff --git a/src/zksync/accounts/toMultisigSmartAccount.test.ts b/src/zksync/accounts/toMultisigSmartAccount.test.ts
new file mode 100644
index 0000000000..873f6595d5
--- /dev/null
+++ b/src/zksync/accounts/toMultisigSmartAccount.test.ts
@@ -0,0 +1,83 @@
+import { expect, test } from 'vitest'
+
+import { accounts, typedData } from '~test/src/constants.js'
+
+import { parseEther, parseGwei } from '../../utils/index.js'
+import { toMultisigSmartAccount } from './toMultisigSmartAccount.js'
+
+test('default', () => {
+ expect(
+ toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKeys: [accounts[0].privateKey, accounts[1].privateKey],
+ }),
+ ).toMatchInlineSnapshot(`
+ {
+ "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
+ "experimental_signAuthorization": undefined,
+ "nonceManager": undefined,
+ "sign": [Function],
+ "signMessage": [Function],
+ "signTransaction": [Function],
+ "signTypedData": [Function],
+ "source": "smartAccountZksync",
+ "type": "local",
+ }
+ `)
+})
+
+test('sign', async () => {
+ const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKeys: [accounts[0].privateKey, accounts[1].privateKey],
+ })
+ expect(
+ await account.sign({
+ hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68',
+ }),
+ ).toMatchInlineSnapshot(
+ `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`,
+ )
+})
+
+test('sign message', async () => {
+ const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKeys: [accounts[0].privateKey, accounts[1].privateKey],
+ })
+ expect(
+ await account.signMessage({ message: 'hello world' }),
+ ).toMatchInlineSnapshot(
+ `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`,
+ )
+})
+
+test('sign transaction', async () => {
+ const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKeys: [accounts[0].privateKey, accounts[1].privateKey],
+ })
+ expect(
+ await account.signTransaction({
+ chainId: 1,
+ maxFeePerGas: parseGwei('20'),
+ gas: 21000n,
+ to: accounts[1].address,
+ value: parseEther('1'),
+ }),
+ ).toMatchInlineSnapshot(
+ `"0x71f8cc80808504a817c8008252089470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000000180800194f39fd6e51aad88f6f4ce6ab8827279cfffb9226682c350c0b882f40a2d2ae9638056cafbe9083c7125edc8555e0e715db0984dd859a5c6dfac5720f36fd0b32bef4d6d75c62f220e59c5fb60c244ca3b361e750985ee5c3a09311c4e5f2cafb92b15ff828d4f8fb34cd3058355428586539495fd0075919cb3cd0d0d33b1e617948f2938f0be2895e4eb4a58a4bcd1a57874ed2c2d235424cf03271bc0"`,
+ )
+})
+
+test('sign typed data', async () => {
+ const account = toMultisigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKeys: [accounts[0].privateKey, accounts[1].privateKey],
+ })
+ expect(
+ await account.signTypedData({ ...typedData.basic, primaryType: 'Mail' }),
+ ).toMatchInlineSnapshot(
+ `"0x32f3d5975ba38d6c2fba9b95d5cbed1febaa68003d3d588d51f2de522ad54117760cfc249470a75232552e43991f53953a3d74edf6944553c6bef2469bb9e5921b7f65cd2428f35d93b843da90d299ccabe834c0068b0d4035112ad1345abf962b579854cad5911d725fc1000e6e4ebfb4c41e233f66153857b5c017cb3115879a1c"`,
+ )
+})
diff --git a/src/zksync/accounts/toMultisigSmartAccount.ts b/src/zksync/accounts/toMultisigSmartAccount.ts
new file mode 100644
index 0000000000..4a1d329497
--- /dev/null
+++ b/src/zksync/accounts/toMultisigSmartAccount.ts
@@ -0,0 +1,37 @@
+import type { Address } from 'abitype'
+
+import { sign } from '../../accounts/utils/sign.js'
+import type { Hex } from '../../types/misc.js'
+import { concatHex } from '../../utils/index.js'
+import type { ZksyncSmartAccount } from '../types/account.js'
+import { toSmartAccount } from './toSmartAccount.js'
+
+export type ToMultisigSmartAccountParameters = {
+ /** Address of the deployed Account's Contract implementation. */
+ address: Address
+ /** Array of Private Keys belonging to the owners. */
+ privateKeys: readonly Hex[]
+}
+
+/**
+ * Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts)
+ * from a Contract Address and an array of Private Keys belonging to the owners.
+ */
+export function toMultisigSmartAccount(
+ parameters: ToMultisigSmartAccountParameters,
+): ZksyncSmartAccount {
+ const { address, privateKeys } = parameters
+
+ return toSmartAccount({
+ address,
+ async sign({ hash }) {
+ return concatHex(
+ await Promise.all(
+ privateKeys.map((privateKey) =>
+ sign({ hash, privateKey, to: 'hex' }),
+ ),
+ ),
+ )
+ },
+ })
+}
diff --git a/src/zksync/accounts/toSinglesigSmartAccount.test.ts b/src/zksync/accounts/toSinglesigSmartAccount.test.ts
new file mode 100644
index 0000000000..ff020eb48a
--- /dev/null
+++ b/src/zksync/accounts/toSinglesigSmartAccount.test.ts
@@ -0,0 +1,83 @@
+import { expect, test } from 'vitest'
+
+import { accounts, typedData } from '~test/src/constants.js'
+
+import { parseEther, parseGwei } from '../../utils/index.js'
+import { toSinglesigSmartAccount } from './toSinglesigSmartAccount.js'
+
+test('default', () => {
+ expect(
+ toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKey: accounts[0].privateKey,
+ }),
+ ).toMatchInlineSnapshot(`
+ {
+ "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
+ "experimental_signAuthorization": undefined,
+ "nonceManager": undefined,
+ "sign": [Function],
+ "signMessage": [Function],
+ "signTransaction": [Function],
+ "signTypedData": [Function],
+ "source": "smartAccountZksync",
+ "type": "local",
+ }
+ `)
+})
+
+test('sign', async () => {
+ const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKey: accounts[0].privateKey,
+ })
+ expect(
+ await account.sign({
+ hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68',
+ }),
+ ).toMatchInlineSnapshot(
+ `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`,
+ )
+})
+
+test('sign message', async () => {
+ const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKey: accounts[0].privateKey,
+ })
+ expect(
+ await account.signMessage({ message: 'hello world' }),
+ ).toMatchInlineSnapshot(
+ `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`,
+ )
+})
+
+test('sign transaction', async () => {
+ const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKey: accounts[0].privateKey,
+ })
+ expect(
+ await account.signTransaction({
+ chainId: 1,
+ maxFeePerGas: parseGwei('20'),
+ gas: 21000n,
+ to: accounts[1].address,
+ value: parseEther('1'),
+ }),
+ ).toMatchInlineSnapshot(
+ `"0x71f88b80808504a817c8008252089470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000000180800194f39fd6e51aad88f6f4ce6ab8827279cfffb9226682c350c0b841f40a2d2ae9638056cafbe9083c7125edc8555e0e715db0984dd859a5c6dfac5720f36fd0b32bef4d6d75c62f220e59c5fb60c244ca3b361e750985ee5c3a09311cc0"`,
+ )
+})
+
+test('sign typed data', async () => {
+ const account = toSinglesigSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ privateKey: accounts[0].privateKey,
+ })
+ expect(
+ await account.signTypedData({ ...typedData.basic, primaryType: 'Mail' }),
+ ).toMatchInlineSnapshot(
+ `"0x32f3d5975ba38d6c2fba9b95d5cbed1febaa68003d3d588d51f2de522ad54117760cfc249470a75232552e43991f53953a3d74edf6944553c6bef2469bb9e5921b"`,
+ )
+})
diff --git a/src/zksync/accounts/toSinglesigSmartAccount.ts b/src/zksync/accounts/toSinglesigSmartAccount.ts
new file mode 100644
index 0000000000..9c053727ee
--- /dev/null
+++ b/src/zksync/accounts/toSinglesigSmartAccount.ts
@@ -0,0 +1,30 @@
+import type { Address } from 'abitype'
+
+import { sign } from '../../accounts/utils/sign.js'
+import type { Hex } from '../../types/misc.js'
+import type { ZksyncSmartAccount } from '../types/account.js'
+import { toSmartAccount } from './toSmartAccount.js'
+
+export type ToSinglesigSmartAccountParameters = {
+ /** Address of the deployed Account's Contract implementation. */
+ address: Address
+ /** Private Key of the owner. */
+ privateKey: Hex
+}
+
+/**
+ * Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts)
+ * from a Contract Address and a Private Key belonging to the owner.
+ */
+export function toSinglesigSmartAccount(
+ parameters: ToSinglesigSmartAccountParameters,
+): ZksyncSmartAccount {
+ const { address, privateKey } = parameters
+
+ return toSmartAccount({
+ address,
+ async sign({ hash }) {
+ return sign({ hash, privateKey, to: 'hex' })
+ },
+ })
+}
diff --git a/src/zksync/accounts/toSmartAccount.test.ts b/src/zksync/accounts/toSmartAccount.test.ts
new file mode 100644
index 0000000000..23cc09bc3d
--- /dev/null
+++ b/src/zksync/accounts/toSmartAccount.test.ts
@@ -0,0 +1,94 @@
+import { expect, test } from 'vitest'
+
+import { accounts, typedData } from '~test/src/constants.js'
+
+import { sign as sign_ } from '../../accounts/index.js'
+import type { Hex } from '../../types/misc.js'
+import { concatHex, parseEther, parseGwei } from '../../utils/index.js'
+import { toSmartAccount } from './toSmartAccount.js'
+
+async function sign({ hash }: { hash: Hex }) {
+ const privateKeys = [accounts[0].privateKey, accounts[1].privateKey]
+ return concatHex(
+ await Promise.all(
+ privateKeys.map((privateKey) => sign_({ hash, privateKey, to: 'hex' })),
+ ),
+ )
+}
+
+test('default', () => {
+ expect(
+ toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ sign,
+ }),
+ ).toMatchInlineSnapshot(`
+ {
+ "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
+ "experimental_signAuthorization": undefined,
+ "nonceManager": undefined,
+ "sign": [Function],
+ "signMessage": [Function],
+ "signTransaction": [Function],
+ "signTypedData": [Function],
+ "source": "smartAccountZksync",
+ "type": "local",
+ }
+ `)
+})
+
+test('sign', async () => {
+ const account = toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ sign,
+ })
+ expect(
+ await account.sign({
+ hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68',
+ }),
+ ).toMatchInlineSnapshot(
+ `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`,
+ )
+})
+
+test('sign message', async () => {
+ const account = toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ sign,
+ })
+ expect(
+ await account.signMessage({ message: 'hello world' }),
+ ).toMatchInlineSnapshot(
+ `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`,
+ )
+})
+
+test('sign transaction', async () => {
+ const account = toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ sign,
+ })
+ expect(
+ await account.signTransaction({
+ chainId: 1,
+ maxFeePerGas: parseGwei('20'),
+ gas: 21000n,
+ to: accounts[1].address,
+ value: parseEther('1'),
+ }),
+ ).toMatchInlineSnapshot(
+ `"0x71f8cc80808504a817c8008252089470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000000180800194f39fd6e51aad88f6f4ce6ab8827279cfffb9226682c350c0b882f40a2d2ae9638056cafbe9083c7125edc8555e0e715db0984dd859a5c6dfac5720f36fd0b32bef4d6d75c62f220e59c5fb60c244ca3b361e750985ee5c3a09311c4e5f2cafb92b15ff828d4f8fb34cd3058355428586539495fd0075919cb3cd0d0d33b1e617948f2938f0be2895e4eb4a58a4bcd1a57874ed2c2d235424cf03271bc0"`,
+ )
+})
+
+test('sign typed data', async () => {
+ const account = toSmartAccount({
+ address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+ sign,
+ })
+ expect(
+ await account.signTypedData({ ...typedData.basic, primaryType: 'Mail' }),
+ ).toMatchInlineSnapshot(
+ `"0x32f3d5975ba38d6c2fba9b95d5cbed1febaa68003d3d588d51f2de522ad54117760cfc249470a75232552e43991f53953a3d74edf6944553c6bef2469bb9e5921b7f65cd2428f35d93b843da90d299ccabe834c0068b0d4035112ad1345abf962b579854cad5911d725fc1000e6e4ebfb4c41e233f66153857b5c017cb3115879a1c"`,
+ )
+})
diff --git a/src/zksync/accounts/toSmartAccount.ts b/src/zksync/accounts/toSmartAccount.ts
new file mode 100644
index 0000000000..092e42c0f3
--- /dev/null
+++ b/src/zksync/accounts/toSmartAccount.ts
@@ -0,0 +1,63 @@
+import type { Address } from 'abitype'
+
+import { toAccount } from '../../accounts/toAccount.js'
+import type { ErrorType } from '../../errors/utils.js'
+import type { Hash, Hex } from '../../types/misc.js'
+import { keccak256 } from '../../utils/index.js'
+import { hashMessage } from '../../utils/signature/hashMessage.js'
+import { hashTypedData } from '../../utils/signature/hashTypedData.js'
+import { serializeTransaction } from '../serializers.js'
+import type { ZksyncSmartAccount } from '../types/account.js'
+import type { ZksyncTransactionSerializableEIP712 } from '../types/transaction.js'
+
+export type ToSmartAccountParameters = {
+ /** Address of the deployed Account's Contract implementation. */
+ address: Address
+ /** Function to sign a hash. */
+ sign: (parameters: { hash: Hash }) => Promise
+}
+
+export type ToSmartAccountErrorType = ErrorType
+
+/**
+ * Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts)
+ * from a Contract Address and a custom sign function.
+ */
+export function toSmartAccount(
+ parameters: ToSmartAccountParameters,
+): ZksyncSmartAccount {
+ const { address, sign } = parameters
+
+ const account = toAccount({
+ address,
+ sign,
+ async signMessage({ message }) {
+ return sign({
+ hash: hashMessage(message),
+ })
+ },
+ async signTransaction(transaction) {
+ const signableTransaction = {
+ ...transaction,
+ from: this.address!,
+ } as ZksyncTransactionSerializableEIP712
+
+ return serializeTransaction({
+ ...signableTransaction,
+ customSignature: await sign({
+ hash: keccak256(serializeTransaction(signableTransaction)),
+ }),
+ })
+ },
+ async signTypedData(typedData) {
+ return sign({
+ hash: hashTypedData(typedData),
+ })
+ },
+ })
+
+ return {
+ ...account,
+ source: 'smartAccountZksync',
+ } as ZksyncSmartAccount
+}
diff --git a/src/zksync/actions/signEip712Transaction.test.ts b/src/zksync/actions/signEip712Transaction.test.ts
index fba555ebd0..045c8fd74a 100644
--- a/src/zksync/actions/signEip712Transaction.test.ts
+++ b/src/zksync/actions/signEip712Transaction.test.ts
@@ -25,7 +25,7 @@ test('default', async () => {
...base,
}),
).toMatchInlineSnapshot(
- `"0x71f8c880808080808000820144808082014494000000000000000000000000000000000000000082c350c0b841edc8fb1e839969b2072865653798be4b4e6ea7181ec97c5e32867bad5838224e1d247fb2f2f07c1bc70bf789d43404771ac496f4fc57c5e6398c1fa6fe6f62861cf85b94fd9ae5ebb0f6656f4b77a0e99dcbc5138d54b0bab8448c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"`,
+ `"0x71f8c880808080808000820144808082014494000000000000000000000000000000000000000082c350c0b841bb509f381d29a038bd2f700bd6a1f1138edfd7a3cf7234c13a03b01a023a30aa53e6bd5e6a50fdcdcf74587c9395b8a314690abbc85aadab5ebcb7678994eacf1bf85b94fd9ae5ebb0f6656f4b77a0e99dcbc5138d54b0bab8448c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"`,
)
})
diff --git a/src/zksync/actions/signTransaction.test.ts b/src/zksync/actions/signTransaction.test.ts
index db5561eb4e..cfd6372ef8 100644
--- a/src/zksync/actions/signTransaction.test.ts
+++ b/src/zksync/actions/signTransaction.test.ts
@@ -30,7 +30,7 @@ test('eip712', async () => {
...eip712,
}),
).toMatchInlineSnapshot(
- `"0x71f8c880808080808000820144808082014494000000000000000000000000000000000000000082c350c0b841edc8fb1e839969b2072865653798be4b4e6ea7181ec97c5e32867bad5838224e1d247fb2f2f07c1bc70bf789d43404771ac496f4fc57c5e6398c1fa6fe6f62861cf85b94fd9ae5ebb0f6656f4b77a0e99dcbc5138d54b0bab8448c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"`,
+ `"0x71f8c880808080808000820144808082014494000000000000000000000000000000000000000082c350c0b841bb509f381d29a038bd2f700bd6a1f1138edfd7a3cf7234c13a03b01a023a30aa53e6bd5e6a50fdcdcf74587c9395b8a314690abbc85aadab5ebcb7678994eacf1bf85b94fd9ae5ebb0f6656f4b77a0e99dcbc5138d54b0bab8448c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"`,
)
})
diff --git a/src/zksync/formatters.test.ts b/src/zksync/formatters.test.ts
index f8e0fe6ed4..2b4208b32f 100644
--- a/src/zksync/formatters.test.ts
+++ b/src/zksync/formatters.test.ts
@@ -321,7 +321,7 @@ describe('block', () => {
"hash": "0x51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f",
"l1BatchNumber": 1n,
"l1BatchTimestamp": 1676384542n,
- "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logsBloom": "0x000000200004000800000100000000004001800000004000008008000400000008000000000000011400000000100000200000000010000000000000000000000001000000200400000021200000420200010000800000000000000000000000000000000200000000040000010808000000008000004001000000000000004000000810000400000000000000000000000001000000800000001000208000000080a0000001000000000000800500030440000010040000002000010000000080000020008100000000200000040000085000000552000000010000000020004802000000100000000400000000000010000080000000000200000010004000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
@@ -1397,7 +1397,7 @@ describe('transaction receipt', () => {
"transactionLogIndex": 9,
},
],
- "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logsBloom": "0x00000020000400000000000000000000400100000000000000800000040000000000000000000000100000000010000020000000001000000000000000000000000000000020040000002100000042000001000000000000000000000000000000000000020000000000000001000800000000800000400000000000000000400000081000000000000000000000000000000100000080000000100020800000008080000001000000000000800400030440000010000000002000010000000080000020008100000000200000040000085000000552000000010000000020000802000000100000000000000000000010000080000000000200000010004000",
"root": "0x51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f",
"status": "success",
"to": "0x0000000000000000000000000000000000008006",
diff --git a/src/zksync/index.ts b/src/zksync/index.ts
index 1130eb820b..08d9df95cd 100644
--- a/src/zksync/index.ts
+++ b/src/zksync/index.ts
@@ -1,4 +1,18 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint module
+export {
+ type ToSmartAccountErrorType,
+ type ToSmartAccountParameters,
+ toSmartAccount,
+} from './accounts/toSmartAccount.js'
+export {
+ type ToMultisigSmartAccountParameters,
+ toMultisigSmartAccount,
+} from './accounts/toMultisigSmartAccount.js'
+export {
+ type ToSinglesigSmartAccountParameters,
+ toSinglesigSmartAccount,
+} from './accounts/toSinglesigSmartAccount.js'
+
export {
type DeployContractErrorType,
type DeployContractParameters,
@@ -131,6 +145,7 @@ export {
export { serializeTransaction } from './serializers.js'
+export type { ZksyncSmartAccount } from './types/account.js'
export type {
/** @deprecated Use `ZksyncBlock` instead */
ZksyncBlock as ZkSyncBlock,
diff --git a/src/zksync/types/account.ts b/src/zksync/types/account.ts
new file mode 100644
index 0000000000..2c880ffa78
--- /dev/null
+++ b/src/zksync/types/account.ts
@@ -0,0 +1,5 @@
+import type { CustomSource, LocalAccount } from '../../accounts/types.js'
+
+export type ZksyncSmartAccount = LocalAccount<'smartAccountZksync'> & {
+ sign: NonNullable
+}
diff --git a/src/zksync/utils/getEip712Domain.test.ts b/src/zksync/utils/getEip712Domain.test.ts
index bd4e87dbc4..0b5f67c93d 100644
--- a/src/zksync/utils/getEip712Domain.test.ts
+++ b/src/zksync/utils/getEip712Domain.test.ts
@@ -39,7 +39,7 @@ test('default', () => {
{
"domain": {
"chainId": 324,
- "name": "zksync",
+ "name": "zkSync",
"version": "2",
},
"message": {
diff --git a/src/zksync/utils/getEip712Domain.ts b/src/zksync/utils/getEip712Domain.ts
index 239703e82b..77e7d53d3a 100644
--- a/src/zksync/utils/getEip712Domain.ts
+++ b/src/zksync/utils/getEip712Domain.ts
@@ -21,7 +21,7 @@ export const getEip712Domain: EIP712DomainFn<
return {
domain: {
- name: 'zksync',
+ name: 'zkSync',
version: '2',
chainId: transaction.chainId,
},
diff --git a/test/src/constants.ts b/test/src/constants.ts
index 58eff9bcb3..e15f76e48d 100644
--- a/test/src/constants.ts
+++ b/test/src/constants.ts
@@ -42,6 +42,8 @@ export const accounts = [
{
address: '0xa0ee7a142d267c1f36714e4a8f75612f20a79720',
balance: 10000000000000000000000n,
+ privateKey:
+ '0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6',
},
] as const