Smart Account Utilities
This module provides utilities for signing transactions with ECDSA private keys in the ZKsync ecosystem.
Functions
populateTransactionECDSA
Populates missing properties meant for signing using an ECDSA private key:
- Populates
from
using the address derived from the ECDSA private key. - Populates
nonce
viaprovider.getTransactionCount(tx.from, "pending")
. - Populates
gasLimit
viaprovider.estimateGas(tx)
. Iftx.from
is not EOA, the estimation is done with address derived from the ECDSA private key. - Populates
chainId
viaprovider.getNetwork()
. - Populates
type
withutils.EIP712_TX_TYPE
. - Populates
value
by converting tobigint
if set, otherwise to0n
. - Populates
data
with0x
. - Populates
customData
with{factoryDeps=[], gasPerPubdata=utils.DEFAULT_GAS_PER_PUBDATA_LIMIT}
.
Inputs
Parameter | Type | Description |
---|---|---|
tx | BytesLike | The transaction that needs to be populated. |
secret | string or ethers.utils.SigningKey | The ECDSA private key. |
provider | Provider | The provider is used to fetch data from the network if it is required for signing. |
const populateTransactionECDSA: TransactionBuilder = async (tx, secret: string | utils.SigningKey, provider)
Example
import { Provider, types, utils } from "zksync-ethers";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const populatedTx = await utils.populateTransactionECDSA(
{
chainId: 270,
to: "<RECEIVER>",
value: 7_000_000_000,
},
PRIVATE_KEY,
provider
);
populateTransactionMultisigECDSA
Populates missing properties meant for signing using multiple ECDSA private keys. It
uses populateTransactionECDSA
, where the address of the first ECDSA key is
set as the secret
argument.
Inputs
Parameter | Type | Description |
---|---|---|
tx | BytesLike | The transaction that needs to be populated. |
secret | string[] or ethers.utils.SigningKey[] | The list of the ECDSA private keys used for populating the transaction. |
provider | Provider | The provider is used to fetch data from the network if it is required for signing. |
const populateTransactionMultisigECDSA: TransactionBuilder = async (tx, secret: string[] | SigningKey[], provider)
Example
import { Provider, types, utils } from "zksync-ethers";
const PRIVATE_KEY1 = "<PRIVATE_KEY1>";
const PRIVATE_KEY2 = "<PRIVATE_KEY2>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const populatedTx = await utils.populateTransactionMultisigECDSA(
{
chainId: 270,
to: "<RECEIVER>",
value: 7_000_000_000,
},
[PRIVATE_KEY1, PRIVATE_KEY2],
provider
);
signPayloadWithECDSA
Signs the payload
using an ECDSA private key.
Inputs
Parameter | Type | Description |
---|---|---|
payload | BytesLike | The payload that needs to be signed. |
secret | string or ethers.utils.SigningKey | The ECDSA private key. |
export const signPayloadWithECDSA: PayloadSigner = async (payload, secret: string | utils.SigningKey)
Examples
Sign EIP712 transaction hash.
import { EIP712Signer, types, utils } from "zksync-ethers";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const tx: types.TransactionRequest = {
chainId: 270,
from: ADDRESS,
to: "<RECEIVER>",
value: 7_000_000_000,
};
const txHash = EIP712Signer.getSignedDigest(tx);
const signature = await utils.signPayloadWithECDSA(txHash, PRIVATE_KEY);
Sign message hash.
import { utils } from "zksync-ethers";
import { ethers } from "ethers";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const message = "Hello World!";
const messageHash = ethers.utils.hashMessage(message);
const signature = await utils.signPayloadWithECDSA(messageHash, PRIVATE_KEY);
Sign typed data hash.
import { utils } from "zksync-ethers";
import { ethers } from "ethers";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const typedDataHash = ethers.utils._TypedDataEncoder.hash(
{ name: "Example", version: "1", chainId: 270 },
{
Person: [
{ name: "name", type: "string" },
{ name: "age", type: "uint8" },
],
},
{ name: "John", age: 30 }
);
const signature = await utils.signPayloadWithECDSA(typedDataHash, PRIVATE_KEY);
signPayloadWithMultipleECDSA
Signs the payload
using multiple ECDSA private keys. The signature is generated by concatenating signatures
created by signing with each key individually. The length of the resulting signature should be secrets.length * 65 + 2
.
Inputs
Parameter | Type | Description |
---|---|---|
payload | BytesLike | The payload that needs to be signed. |
secret | string[] or ethers.utils.SigningKey[] | The list of the ECDSA private keys. |
const signPayloadWithMultipleECDSA: PayloadSigner = async (payload, secret: string[] | utils.SigningKey[])
Examples
Sign EIP712 transaction hash.
import { EIP712Signer, types, utils } from "zksync-ethers";
const PRIVATE_KEY1 = "<PRIVATE_KEY1>";
const PRIVATE_KEY2 = "<PRIVATE_KEY2>";
const tx: types.TransactionRequest = {
chainId: 270,
from: ADDRESS,
to: "<RECEIVER>",
value: 7_000_000_000,
};
const txHash = EIP712Signer.getSignedDigest(tx);
const signature = await utils.signPayloadWithMultipleECDSA(txHash, [PRIVATE_KEY1, PRIVATE_KEY2]);
Sign message hash.
import { utils } from "zksync-ethers";
import { ethers } from "ethers";
const PRIVATE_KEY1 = "<PRIVATE_KEY1>";
const PRIVATE_KEY2 = "<PRIVATE_KEY2>";
const message = "Hello World!";
const messageHash = ethers.utils.hashMessage(message);
const signature = await utils.signPayloadWithMultipleECDSA(messageHash, [PRIVATE_KEY1, PRIVATE_KEY2]);
Sign typed data hash.
import { utils } from "zksync-ethers";
import { ethers } from "ethers";
const PRIVATE_KEY1 = "<PRIVATE_KEY1>";
const PRIVATE_KEY2 = "<PRIVATE_KEY2>";
const typedDataHash = ethers.utils._TypedDataEncoder.hash(
{ name: "Example", version: "1", chainId: 270 },
{
Person: [
{ name: "name", type: "string" },
{ name: "age", type: "uint8" },
],
},
{ name: "John", age: 30 }
);
const signature = await utils.signPayloadWithMultipleECDSA(typedDataHash, [PRIVATE_KEY1, PRIVATE_KEY2]);