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 Matter Labs team 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)