Smart Account Utilities

Utilities for signing payloads and populating transactions with ECDSA and multiple ECDSA keys.

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 via provider.getTransactionCount(tx.from, "pending").
  • Populates gasLimit via provider.estimateGas(tx). If tx.from is not EOA, the estimation is done with address derived from the ECDSA private key.
  • Populates chainId via provider.getNetwork().
  • Populates type with utils.EIP712_TX_TYPE.
  • Populates value by converting to bigint if set, otherwise to 0n.
  • Populates data with 0x.
  • Populates customData with {factoryDeps=[], gasPerPubdata=utils.DEFAULT_GAS_PER_PUBDATA_LIMIT}.

Inputs

ParameterTypeDescription
txBytesLikeThe transaction that needs to be populated.
secretstring or ethers.SigningKeyThe ECDSA private key.
providerProviderThe provider which fetches data from the network.
const populateTransactionECDSA: TransactionBuilder = async (tx, secret: string | 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

ParameterTypeDescription
txBytesLikeThe transaction that needs to be populated.
secretstring[] or ethers.SigningKey[]The list of the ECDSA private keys used for populating the transaction.
providerProviderThe provider which fetches data from the network.
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

ParameterTypeDescription
payloadBytesLikeThe payload that needs to be signed.
secretstring or ethers.SigningKeyThe ECDSA private key.
const signPayloadWithECDSA: PayloadSigner = async (payload, secret: string | 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 result = await utils.signPayloadWithECDSA(txHash, PRIVATE_KEY);
For development and testing, it is recommended to use burner wallets. Avoid using real private keys to prevent security risks.

Sign message hash.

import { utils } from "zksync-ethers";
import { hashMessage } from "ethers";

const PRIVATE_KEY = "<PRIVATE_KEY>";

const message = "Hello World!";
const messageHash = hashMessage(message);

const result = await utils.signPayloadWithECDSA(messageHash, PRIVATE_KEY);

Sign typed data hash.

import { utils } from "zksync-ethers";
import { TypedDataEncoder } from "ethers";

const PRIVATE_KEY = "<PRIVATE_KEY>";

const typedDataHash = TypedDataEncoder.hash(
  { name: "Example", version: "1", chainId: 270 },
  {
    Person: [
      { name: "name", type: "string" },
      { name: "age", type: "uint8" },
    ],
  },
  { name: "John", age: 30 }
);
const result = 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

ParameterTypeDescription
payloadBytesLikeThe payload that needs to be signed.
secretstring[] or ethers.SigningKey[]The list of the ECDSA private keys.
const signPayloadWithMultipleECDSA: PayloadSigner = async (payload, secret: string[] | 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 result = await utils.signPayloadWithMultipleECDSA(typedDataHash, [PRIVATE_KEY1, PRIVATE_KEY2]);

Sign message hash.

import { utils } from "zksync-ethers";
import { hashMessage } from "ethers";

const PRIVATE_KEY1 = "<PRIVATE_KEY1>";
const PRIVATE_KEY2 = "<PRIVATE_KEY2>";

const message = "Hello World!";
const messageHash = hashMessage(message);

const result = await utils.signPayloadWithMultipleECDSA(typedDataHash, [PRIVATE_KEY1, PRIVATE_KEY2]);

Sign typed data hash.

import { utils } from "zksync-ethers";
import { TypedDataEncoder } from "ethers";

const PRIVATE_KEY1 = "<PRIVATE_KEY1>";
const PRIVATE_KEY2 = "<PRIVATE_KEY2>";

const typedDataHash = TypedDataEncoder.hash(
  { name: "Example", version: "1", chainId: 270 },
  {
    Person: [
      { name: "name", type: "string" },
      { name: "age", type: "uint8" },
    ],
  },
  { name: "John", age: 30 }
);
const result = await utils.signPayloadWithMultipleECDSA(typedDataHash, [PRIVATE_KEY1, PRIVATE_KEY2]);

Made with ❤️ by the ZKsync Community