ZKsync Era Features
While ZKsync Era is mostly Web3-compatible, it has some differences compared to Ethereum. The major of those are:
- Account abstraction support (accounts may have near-arbitrary validation logic, and also paymaster support is enabled).
- Deployment transactions require the contracts' bytecode to be passed in a separate field.
- The fee system is somewhat different.
These require us to extend standard Ethereum transactions with new custom fields. Such extended transactions are called EIP-712 transactions since EIP-712 is used to sign them. You can look at the internal structure of the EIP-712 transactions in the ZKsync documentation.
EIP-712 support
The following objects provides support for utilizing ZKsync features:
Encoding paymaster params
While the paymaster feature by itself does not impose any limitations on values of the paymasterInput,
the ZKsync protocol endorses certain types of
paymaster flows
that are processable by EOAs.
ZKsync SDK provides a utility method that can be used to get the correctly formed PaymasterParams object:
GetPaymasterParams.
See in action
If you want to call the method setGreeting of a contract called greeter, this would look the following way,
while paying fees with the
testnet paymaster:
PrivateKey := os.Getenv("PRIVATE_KEY")
ZkSyncEraProvider := "https://testnet.era.zksync.dev"
TokenAddress := common.HexToAddress("<Token address>")
GreeterAddress := common.HexToAddress("<Greeter contract address>")
ReceiptAddress := common.HexToAddress("<Receipt address>")
client, err := clients.Dial(ZkSyncEraProvider)
if err != nil {
log.Panic(err)
}
defer client.Close()
ethClient, err := ethclient.Dial(EthereumProvider)
if err != nil {
log.Panic(err)
}
defer ethClient.Close()
// Create wallet
wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), client, ethClient)
if err != nil {
log.Panic(err)
}
abi, err := greeter.GreeterMetaData.GetAbi()
if err != nil {
log.Panic(err)
}
// Encode transfer function from token contract
calldata, err := abi.Pack("setGreeting")
if err != nil {
log.Panic(err)
}
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Panic(err)
}
gas, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
From: wallet.Address(),
To: ReceiptAddress,
Data: calldata,
})
if err != nil {
log.Panic(err)
}
testnetPaymaster, err := client.TestnetPaymaster(context.Background())
if err != nil {
log.Panic(err)
}
// Create paymaster parameters with encoded paymaster input
paymasterParams, err := utils.GetPaymasterParams(
PaymasterAddress,
&zkTypes.ApprovalBasedPaymasterInput{
Token: testnetPaymaster,
MinimalAllowance: new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gas)),
InnerInput: []byte{},
})
if err != nil {
log.Panic(err)
}
hash, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{
To: &TokenAddress,
Data: calldata,
Meta: &types.Eip712Meta{
PaymasterParams: paymasterParams,
},
})
if err != nil {
log.Panic(err)
}
_, err = client.WaitMined(context.Background(), hash)
if err != nil {
log.Panic(err)
}
fmt.Println("Tx: ", hash)