SmartAccount Utilities

Utilities for signing and populating transactions

The SmartAccount Utilities provide functions for signing payloads and populating transactions using ECDSA and multiple ECDSA keys. These utilities offer flexibility and ease of use for developers working with smart accounts in the ZKsync Era.

Functions

PopulateTransactionECDSA

Populates missing properties meant for signing using an ECDSA private key:

  • Populates tx.From using the address derived from the ECDSA private key.
  • Populates tx.Nonce via client.NonceAt().
  • Populates tx.Gas via client.EstimateGasL2(). If tx.From is not EOA, the estimation is done with address derived from the ECDSA private key.
  • Populates tx.GasFeeCap via client.SuggestGasPrice().
  • Populates tx.GasTipCap with 0 if is not set.
  • Populates tx.ChainID via client.ChainID().
  • Populates tx.Data with "0x".
  • Populates tx.Meta.GasPerPubdata with utils.DefaultGasPerPubdataLimit.

Expects the secret to be ECDSA private in hex format.

Inputs

ParameterTypeDescription
ctxcontext.ContextContext.
tx*zkTypes.TransactionThe transaction that needs to be populated.
secretstringThe ECDSA private key.
client*clients.ClientThe client which fetches data from the network.
var PopulateTransactionECDSA TransactionBuilder = func(ctx context.Context, tx *zkTypes.Transaction, secret interface{}, client *clients.Client) error

Examples

privateKey := os.Getenv("PRIVATE_KEY")
client, err := clients.DialBase(ZkSyncEraProvider)
if err != nil {
  log.Panic(err)
}
defer client.Close()

tx := zkTypes.Transaction{
  To:      &Address2,
  Value:   big.NewInt(7_000_000_000),
  From:    &Address1,
}

err = accounts.PopulateTransactionECDSA(context.Background(), &tx, privateKey, client)
if err != nil {
  log.Panic(err)
}

PopulateTransactionMultipleECDSA

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. Expects the secret to be a slice of ECDSA private in hex format.

Inputs

ParameterTypeDescription
ctxcontext.ContextContext.
tx*zkTypes.TransactionThe transaction that needs to be populated.
secret[]stringThe list of the ECDSA private keys.
client*clients.ClientThe client which fetches data from the network.
var PopulateTransactionMultipleECDSA TransactionBuilder = func(ctx context.Context, tx *zkTypes.Transaction, secret interface{}, client *clients.Client) error

Examples

privateKey1 := os.Getenv("PRIVATE_KEY1")
privateKey2 := os.Getenv("PRIVATE_KEY2")
client, err := clients.DialBase(ZkSyncEraProvider)
if err != nil {
  log.Panic(err)
}
defer client.Close()

tx := zkTypes.Transaction{
  To:      &Address2,
  Value:   big.NewInt(7_000_000_000),
  From:    &Address1,
}

err = accounts.PopulateTransactionECDSA(context.Background(), &tx, privateKey, client)
if err != nil {
  log.Panic(err)
}

SignPayloadWithECDSA

Implementation of PayloadSigner which signs the payload using an ECDSA private key.

Inputs

ParameterTypeDescription
ctxcontext.ContextContext.
payload[]byteThe payload that needs to be signed.
secretstringThe ECDSA private key.
client*clients.ClientNot used and should be nil.
var SignPayloadWithECDSA PayloadSigner = func(ctx context.Context, payload []byte, secret interface{}, client *clients.Client) ([]byte, error)

Examples

Sign EIP712 transaction hash.

privateKey := os.Getenv("PRIVATE_KEY")
client, err := clients.DialBase(ZkSyncEraProvider)
if err != nil {
  log.Panic(err)
}
defer client.Close()

chainId, err := client.ChainID(context.Background())
if err != nil {
  log.Panic(err)
}

tx := zkTypes.Transaction{
  Nonce:     big.NewInt(0),
  GasTipCap: big.NewInt(0),
  GasFeeCap: big.NewInt(1_000_000_000),
  Gas:       big.NewInt(1_000_000_000),
  To:        &Address2,
  Value:     big.NewInt(7_000_000_000),
  Data:      hexutil.Bytes{},
  ChainID:   chainId,
  From:      &Address1,
  Meta: &zkTypes.Eip712Meta{
    GasPerPubdata: utils.NewBig(utils.DefaultGasPerPubdataLimit.Int64()),
  },
}

domain := eip712.ZkSyncEraEIP712Domain(chainId.Int64())

eip712Msg, err := tx.EIP712Message()
if err != nil {
  log.Panic(err)
}

hash, _, err := apitypes.TypedDataAndHash(apitypes.TypedData{
  Types: apitypes.Types{
    tx.EIP712Type():     tx.EIP712Types(),
    domain.EIP712Type(): domain.EIP712Types(),
  },
  PrimaryType: tx.EIP712Type(),
  Domain:      domain.EIP712Domain(),
  Message:     eip712Msg,
 })
if err != nil {
  log.Panic(err)
}

signature, err := accounts.SignPayloadWithECDSA(context.Background(), hash, privateKey, nil)
if err != nil {
  log.Panic(err)
}

Sign message hash.

privateKey := os.Getenv("PRIVATE_KEY")
signature, err := accounts.SignPayloadWithECDSA(context.Background(), ethAccounts.TextHash([]byte("Hello World!")), privateKey, nil)
if err != nil {
  log.Panic(err)
}

Sign typed data hash.

privateKey := os.Getenv("PRIVATE_KEY")
client, err := clients.DialBase(ZkSyncEraProvider)
if err != nil {
  log.Panic(err)
}
defer client.Close()

chainId, err := client.ChainID(context.Background())
if err != nil {
  log.Panic(err)
}

hash, _, err := apitypes.TypedDataAndHash(apitypes.TypedData{
  Domain: apitypes.TypedDataDomain{
   Name:    "Example",
   Version: "1",
   ChainId: math.NewHexOrDecimal256(chainId.Int64()),
  },
  Types: apitypes.Types{
   "Person": []apitypes.Type{
    {Name: "name", Type: "string"},
    {Name: "age", Type: "uint8"},
   },
   "EIP712Domain": []apitypes.Type{
    {Name: "name", Type: "string"},
    {Name: "version", Type: "string"},
    {Name: "chainId", Type: "uint256"},
   },
  },
  PrimaryType: "Person",
  Message: apitypes.TypedDataMessage{
   "name": "John",
   "age":  hexutil.EncodeUint64(30),
  },
 })
if err != nil {
  log.Panic(err)
}

signature, err := accounts.SignPayloadWithECDSA(context.Background(), hash, privateKey, nil)
if err != nil {
  log.Panic(err)
}

SignPayloadWithMultipleECDSA

Implementation of PayloadSigner which 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 is len(secret) * 65.

Inputs

ParameterTypeDescription
ctxcontext.ContextContext.
payload[]byteThe payload that needs to be signed.
secret[]stringThe list of the ECDSA private keys.
client*clients.ClientNot used and should be nil.
var SignPayloadWithMultipleECDSA PayloadSigner = func(ctx context.Context, payload []byte, secret interface{}, client *clients.Client) ([]byte, error)

Examples

Sign EIP712 transaction hash.

privateKey1 := os.Getenv("PRIVATE_KEY1")
privateKey2 := os.Getenv("PRIVATE_KEY2")
client, err := clients.DialBase(ZkSyncEraProvider)
if err != nil {
  log.Panic(err)
}
defer client.Close()

chainId, err := client.ChainID(context.Background())
if err != nil {
  log.Panic(err)
}

tx := zkTypes.Transaction{
  Nonce:     big.NewInt(0),
  GasTipCap: big.NewInt(0),
  GasFeeCap: big.NewInt(1_000_000_000),
  Gas:       big.NewInt(1_000_000_000),
  To:        &Address2,
  Value:     big.NewInt(7_000_000_000),
  Data:      hexutil.Bytes{},
  ChainID:   big.NewInt(270),
  From:      &Address1,
  Meta: &zkTypes.Eip712Meta{
    GasPerPubdata: utils.NewBig(utils.DefaultGasPerPubdataLimit.Int64()),
  },
}

domain := eip712.ZkSyncEraEIP712Domain(chainId.Int64())

eip712Msg, err := tx.EIP712Message()
if err != nil {
  log.Panic(err)
}

hash, _, err := apitypes.TypedDataAndHash(apitypes.TypedData{
  Types: apitypes.Types{
    tx.EIP712Type():     tx.EIP712Types(),
    domain.EIP712Type(): domain.EIP712Types(),
  },
  PrimaryType: tx.EIP712Type(),
  Domain:      domain.EIP712Domain(),
  Message:     eip712Msg,
 })
if err != nil {
  log.Panic(err)
}

signature, err := accounts.SignPayloadWithMultipleECDSA(context.Background(), hash, []string{privateKey1, privateKey2}, nil)
if err != nil {
  log.Panic(err)
}

Sign message hash.

privateKey1 := os.Getenv("PRIVATE_KEY1")
privateKey2 := os.Getenv("PRIVATE_KEY2")
signature, err := accounts.SignPayloadWithMultipleECDSA(context.Background(), hash, []string{privateKey1, privateKey2}, nil)
if err != nil {
  log.Panic(err)
}

Sign typed data hash.

privateKey1 := os.Getenv("PRIVATE_KEY1")
privateKey2 := os.Getenv("PRIVATE_KEY2")
client, err := clients.DialBase(ZkSyncEraProvider)
if err != nil {
  log.Panic(err)
}
defer client.Close()

chainId, err := client.ChainID(context.Background())
if err != nil {
  log.Panic(err)
}


hash, _, err := apitypes.TypedDataAndHash(apitypes.TypedData{
  Domain: apitypes.TypedDataDomain{
   Name:    "Example",
   Version: "1",
   ChainId: math.NewHexOrDecimal256(chainId.Int64()),
  },
  Types: apitypes.Types{
   "Person": []apitypes.Type{
    {Name: "name", Type: "string"},
    {Name: "age", Type: "uint8"},
   },
   "EIP712Domain": []apitypes.Type{
    {Name: "name", Type: "string"},
    {Name: "version", Type: "string"},
    {Name: "chainId", Type: "uint256"},
   },
  },
  PrimaryType: "Person",
  Message: apitypes.TypedDataMessage{
   "name": "John",
   "age":  hexutil.EncodeUint64(30),
  },
 })
if err != nil {
  log.Panic(err)
}

signature, err := accounts.SignPayloadWithMultipleECDSA(context.Background(), hash, []string{privateKey1, privateKey2}, nil)
if err != nil {
  log.Panic(err)
}

Made with ❤️ by the ZKsync Community