zksync-sdk / zksync2-go

zksync2-go is a geth library adapted to work with the zkSync Era.
Apache License 2.0
87 stars 36 forks source link

Cannot decode EIP712 transaction encoded by zksync-ethers JS library #49

Closed ro-tex closed 3 months ago

ro-tex commented 4 months ago

šŸ› Bug Report for zksync2-go Go SDK

šŸ“ Description

We are trying to create an EIP712 transaction in one application (in TypeScript) and send it to another (in Go) for signing. The transaction is successfully created and transmitted but cannot be unmarshalled into a types.Transaction712 struct.

Question:
What is the prescribed way for decoding a zksync-ethers types.Transaction into a zksync2-go types.Transaction712?

Issue:
We cannot do the above transformation because the Go library is using a Meta field, while the TS library is using a customData field.

Our approach to solving this:
We tried to use geth's rlp.Decode but we are losing some data fields.

    b, err := hex.DecodeString(strings.TrimPrefix(rlpTx, "0x"))
    if err != nil {
        log.Fatal(err)
    }
    var txn types.Transaction712
    err = rlp.DecodeBytes(b[1:], &txn)
    if err != nil {
        log.Println("error:", err) // We get and error here, even though we're also getting a useful value.
    }
    fmt.Printf("txn: %+v\n", txn)

šŸ”„ Reproduction Steps

  1. Step 1
  2. Step 2
  3. ...

šŸ¤” Expected Behavior

Describe what you expected to happen.

šŸ˜Æ Current Behavior

Describe what actually happened.

šŸ–„ļø Environment

šŸ“‹ Additional Context

Add any other context about the problem here. If applicable, add screenshots to help explain.

šŸ“Ž Log Output

Paste any relevant log output here.
danijelTxFusion commented 3 months ago

@ro-tex Here is an example how to serialize tx in zksync-ethers and parse it using zksync2-go.

zksync-ethers

const tx: types.TransactionLike = {
    type: 113,
    nonce: 0,
    maxPriorityFeePerGas: 0n,
    maxFeePerGas: 0n,
    gasLimit: 0n,
    to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
    value: 1_000_000n,
    data: '0x',
    chainId: 270n,
    from: '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049',
    customData: {
        gasPerPubdata: 50_000n,
        factoryDeps: [],
        customSignature: '0x307837373262396162343735386435636630386637643732303161646332653534383933616532376263666562323162396337643666643430393766346464653063303166376630353332323866346636643838653662663334333436343931343135363761633930363632306661653832633239333339393062353563613336363162',
        paymasterParams: {
            paymaster: '0xa222f0c183AFA73a8Bc1AFb48D34C88c9Bf7A174',
            paymasterInput: ethers.getBytes('0x949431dc000000000000000000000000841c43fa5d8fffdb9efe3358906f7578d8700dd4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000'),
        }
    },
    hash: '0xc0ba55587423e1ef281b06a9d684b481365897f37a6ad611d7619b1b7e0bc908',
 };

const serializedTx = utils.serializeEip712(tx);
console.log(serializedTx)

Output:

0x71f901628080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c0b884307837373262396162343735386435636630386637643732303161646332653534383933616532376263666562323162396337643666643430393766346464653063303166376630353332323866346636643838653662663334333436343931343135363761633930363632306661653832633239333339393062353563613336363162f89b94a222f0c183afa73a8bc1afb48d34c88c9bf7a174b884949431dc000000000000000000000000841c43fa5d8fffdb9efe3358906f7578d8700dd4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000

zksync2-go


import (
  // omit other imports
  zkTypes "github.com/zksync-sdk/zksync2-go/types"
)

serializedTx := "0x71f901628080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c0b884307837373262396162343735386435636630386637643732303161646332653534383933616532376263666562323162396337643666643430393766346464653063303166376630353332323866346636643838653662663334333436343931343135363761633930363632306661653832633239333339393062353563613336363162f89b94a222f0c183afa73a8bc1afb48d34c88c9bf7a174b884949431dc000000000000000000000000841c43fa5d8fffdb9efe3358906f7578d8700dd4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"

type zkSyncTxRLP struct {
    Nonce                uint64
    MaxPriorityFeePerGas *big.Int
    MaxFeePerGas         *big.Int
    GasLimit             *big.Int
    To                   *common.Address `rlp:"nil"` // nil means contract creation
    Value                *big.Int
    Data                 hexutil.Bytes
    // zkSync part
    ChainID1 *big.Int // legacy
    Empty1   string   // legacy
    Empty2   string   // legacy
    ChainID2 *big.Int
    From     *common.Address
    // Meta fields   *Meta
    GasPerPubdata   *big.Int
    FactoryDeps     []hexutil.Bytes
    CustomSignature hexutil.Bytes
    PaymasterParams *PaymasterParams `rlp:"nil"`
}
var decodedTx zkSyncTxRLP
err := rlp.DecodeBytes(input[1:], &decodedTx)
if err != nil {
  log.Fatal(err)
}

tx := new(zkTypes.Transaction712)
tx.Nonce = new(big.Int).SetUint64(decodedTx.Nonce)
tx.GasTipCap = decodedTx.MaxPriorityFeePerGas
tx.GasFeeCap = decodedTx.MaxFeePerGas
tx.Gas = decodedTx.GasLimit
tx.To = decodedTx.To
tx.Value = decodedTx.Value
tx.Data = decodedTx.Data
tx.ChainID = decodedTx.ChainID2
tx.From = decodedTx.From
tx.Meta = &Eip712Meta{
    GasPerPubdata:   (*hexutil.Big)(decodedTx.GasPerPubdata),
    CustomSignature: decodedTx.CustomSignature,
    FactoryDeps:     decodedTx.FactoryDeps,
    PaymasterParams: decodedTx.PaymasterParams,
}
 fmt.Printf("%+v\n", tx)

Output:

{Nonce:+0 GasTipCap:+0 GasFeeCap:+0 Gas:+0 To:0xa61464658AfeAf65CccaaFD3a512b69A83B77618 Value:+1000000 Data:0x AccessList:[] ChainID:+270 From:0x36615Cf349d7F6344891B1e7CA7C72883F5dc049 Meta:0xc00007ff80}
github-actions[bot] commented 3 months ago

:tada: This issue has been resolved in version 0.5.0 :tada:

The release is available on GitHub release

Your semantic-release bot :package::rocket:

danijelTxFusion commented 3 months ago

From version 0.5.0, the Transaciton712.Decode() method can be utilized for decoding serialized transaction.

zksync-ethers

const tx: types.TransactionLike = {
    type: 113,
    nonce: 0,
    maxPriorityFeePerGas: 0n,
    maxFeePerGas: 0n,
    gasLimit: 0n,
    to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
    value: 1_000_000n,
    data: '0x',
    chainId: 270n,
    from: '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049',
    customData: {
        gasPerPubdata: 50_000n,
        factoryDeps: [],
        customSignature: '0x307837373262396162343735386435636630386637643732303161646332653534383933616532376263666562323162396337643666643430393766346464653063303166376630353332323866346636643838653662663334333436343931343135363761633930363632306661653832633239333339393062353563613336363162',
        paymasterParams: {
            paymaster: '0xa222f0c183AFA73a8Bc1AFb48D34C88c9Bf7A174',
            paymasterInput: ethers.getBytes('0x949431dc000000000000000000000000841c43fa5d8fffdb9efe3358906f7578d8700dd4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000'),
        }
    },
    hash: '0xc0ba55587423e1ef281b06a9d684b481365897f37a6ad611d7619b1b7e0bc908',
 };

const serializedTx = utils.serializeEip712(tx);
console.log(serializedTx)

Output:

0x71f901628080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c0b884307837373262396162343735386435636630386637643732303161646332653534383933616532376263666562323162396337643666643430393766346464653063303166376630353332323866346636643838653662663334333436343931343135363761633930363632306661653832633239333339393062353563613336363162f89b94a222f0c183afa73a8bc1afb48d34c88c9bf7a174b884949431dc000000000000000000000000841c43fa5d8fffdb9efe3358906f7578d8700dd4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000

zksync2-go


import (
  // omit other imports
  zkTypes "github.com/zksync-sdk/zksync2-go/types"
)

serializedTx := "0x71f901628080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c0b884307837373262396162343735386435636630386637643732303161646332653534383933616532376263666562323162396337643666643430393766346464653063303166376630353332323866346636643838653662663334333436343931343135363761633930363632306661653832633239333339393062353563613336363162f89b94a222f0c183afa73a8bc1afb48d34c88c9bf7a174b884949431dc000000000000000000000000841c43fa5d8fffdb9efe3358906f7578d8700dd4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"

tx := new(zkTypes.Transaction712)
err := tx.Decode(serializedTx)
if err != nil {
   log.Fatal(err)
}
fmt.Printf("%+v\n", tx)

Output:

{Nonce:+0 GasTipCap:+0 GasFeeCap:+0 Gas:+0 To:0xa61464658AfeAf65CccaaFD3a512b69A83B77618 Value:+1000000 Data:0x AccessList:[] ChainID:+270 From:0x36615Cf349d7F6344891B1e7CA7C72883F5dc049 Meta:0xc00007ff80}