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
fromusing the address derived from the ECDSA private key. - Populates
nonceviaprovider.getTransactionCount(tx.from, "pending"). - Populates
gasLimitviaprovider.estimateGas(tx). Iftx.fromis not EOA, the estimation is done with address derived from the ECDSA private key. - Populates
chainIdviaprovider.getNetwork(). - Populates
typewithutils.EIP712_TX_TYPE. - Populates
valueby converting tobigintif set, otherwise to0n. - Populates
datawith0x. - Populates
customDatawith{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.SigningKey | The ECDSA private key. |
provider | Provider | The 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
| Parameter | Type | Description |
|---|---|---|
tx | BytesLike | The transaction that needs to be populated. |
secret | string[] or ethers.SigningKey[] | The list of the ECDSA private keys used for populating the transaction. |
provider | Provider | The 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
| Parameter | Type | Description |
|---|---|---|
payload | BytesLike | The payload that needs to be signed. |
secret | string or ethers.SigningKey | The 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);
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
| Parameter | Type | Description |
|---|---|---|
payload | BytesLike | The payload that needs to be signed. |
secret | string[] 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]);