Skip to content

Instantly share code, notes, and snippets.

@danielnordh
Last active March 25, 2022 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielnordh/d0e174b66ebd3af6ddbf07b48a91813b to your computer and use it in GitHub Desktop.
Save danielnordh/d0e174b66ebd3af6ddbf07b48a91813b to your computer and use it in GitHub Desktop.
USDC transferWithAuthorization call failing on zkSync, but working on Ropsten
// See comment below with code example
@danielnordh
Copy link
Author

danielnordh commented Mar 21, 2022

I have code for signing a transaction containing USDC transferWithAuthorization data. It works successfully on Ropsten on a Dart client using web3Dart. I am also able to make a USDC transfer from the same account on zkSync 2.0 Alpha on that client.

I am not however able to get the same transferWithAuthorization code to work on zkSync, where I get:
RPCError: got code 3 with msg "Execution error"

Why is it working on Ropsten but not on zkSync?

Code that works on Ropsten for transferWithAuthorization, but not on zkSync

final transaction = Web3Dart.Transaction.callContract(
    contract: usdcContract,
    function: usdcContract.function('transferWithAuthorization'),
    parameters: [
      Web3Dart.EthereumAddress.fromHex(user.pubKey!),
      toAddress,
      amount,
      BigInt.from(validAfter),
      BigInt.from(validBefore),
      nonce.bytes,
      BigInt.from(sigParams.v),
      bigIntToUint8List(sigParams.r),
      bigIntToUint8List(sigParams.s),
    ],
);

final signedTx = await ethClient.signTransaction(
    wallet.privateKey, 
    transaction,
    chainId: config.chain.chainId, 
    fetchChainIdFromNetworkId: false);

Code that works on zkSync for transfer

final transaction = Web3Dart.Transaction.callContract(
            contract: usdcContract,
            function: usdcContract.function('transfer'),
            parameters: [toAddress, amount],
);

final txHash = await ethClient.sendTransaction(
          wallet.privateKey,
          transaction,
          chainId: null,
          fetchChainIdFromNetworkId: true);

(Note the difference in chainId and fetchChainIdFromNetworkId settings, I have tried using the ones that work for zkSync transfer but that does not help)

@danielnordh
Copy link
Author

Testing the same thing in Node with web3, and the below code I get the following error:

code: 104,
message: 'Cannot estimate transaction: VM execution resulted in a revert: VM Error.'

Node code

var transfer = usdcInstance.methods
    .transferWithAuthorization(fromAddress, toAddress, amount, validAfter, validBefore, nonce, sigParams.v, sigParams.r, sigParams.s);
var encodedABI = transfer.encodeABI();
console.log("EncodeABI: " + encodedABI);
var tx = {
    from: fromAddress,
    to: usdcAddressZKSync,
    chainId: 280,
    gasLimit: web3.utils.toHex(90000),
    gasPrice: web3.utils.toHex(150e9),
    data: encodedABI
  };

web3.eth.accounts.signTransaction(tx, privateKey).then(signed => {
    var result = web3.eth.sendSignedTransaction(signed.rawTransaction);

    result.on('confirmation', (confirmationNumber, receipt) => {
      console.log('confirmation: ' + confirmationNumber);
    });

    result.on('transactionHash', hash => {
      console.log('hash');
      console.log(hash);
    });

    result.on('receipt', receipt => {
      console.log('reciept');
      console.log(receipt);
    });

    result.on('error', console.error);
  });

@danielnordh
Copy link
Author

Currently thinking this flow is wrong anyway.
I think I need to pass all the parameters to the server and create the transaction there instead of signing on client.

@danielnordh
Copy link
Author

danielnordh commented Mar 21, 2022

Having changed the flow to pass the parameters to the server and signing the transaction there for submitting, I get the same result:

Ropsten - works
zkSync - does not work, "Returned error: Execution error"

@danielnordh
Copy link
Author

Here is the current server code that works for Ropsten, but not for zkSync:

// Create transaction
  var transfer = usdcInstance.methods.transferWithAuthorization(fromAddress, toAddress, amount, validAfter, validBefore, nonce, sigParams.v, sigParams.r, sigParams.s);
  var encodedABI = transfer.encodeABI();

  var tx = {
    from: fromAddress,
    to: usdcAddress,
    chainId: chainId,
    gasLimit: web3.utils.toHex(90000),
    gasPrice: web3.utils.toHex(192e9),
    data: encodedABI
  };

  // Sign and send transaction
  chainWeb3.eth.accounts.signTransaction(tx, config.PK, function (err, signedTx) {
    if (!err) {
      console.log("Signed Tx: " + signedTx);
      chainWeb3.eth.sendSignedTransaction(signedTx.rawTransaction, function (err, hash) {
        if (!err) {
          console.log("Signed Tx sent to blockchain: " + hash);
          response.statusCode = 200;
          response.end(JSON.stringify({"hash": hash }));
        } else {
          console.log("Error sending signed Tx to blockchain: " + err)
          response.statusCode = 400;
          response.end("Error!");
        }
      });
    } else {
      console.log("Error signing Tx: " + err)
      response.statusCode = 400;
      response.end("Error!");
    }
  });

@danielnordh
Copy link
Author

For reference, I'm using:

chainId - 280,
usdcContract address - '0xd35cceead182dcee0f148ebac9447da2c4d449c4', (I'm using v2 of the ABI)

https://zksync2-testnet.zksync.dev
wss://zksync2-testnet.zksync.dev/ws

@danielnordh
Copy link
Author

danielnordh commented Mar 22, 2022

Tried the same thing with Ethers.js instead of Web3.js, but same thing:

Error: processing response error (body="{"jsonrpc":"2.0","error":{"code":3,"message":"Execution error","data":{"code":104,"message":"Cannot estimate transaction: VM execution resulted in a revert: VM Error."}},"id":53}\n", error={"code":3,"data":{"code":104,"message":"Cannot estimate transaction: VM execution resulted in a revert: VM Error."}}, requestBody="{"method":"eth_sendRawTransaction","params":["0xf9018d0785174876e800830f424094d35cceead182dcee0f148ebac9447da2c4d449c480b90124e3ee160e000000000000000000000000d9d3768621774c3c357b9b5fcb23b5735ea2c7ae00000000000000000000000033eecb4a75c9241eea0190e5dcdfb76d2ed6761300000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006239c7bbe7348e305d9e8f9ea286a33097e413507739d6039f188f6eddb647a490bbbdde000000000000000000000000000000000000000000000000000000000000001c72a7a861f5394e6a6b83d201241a51bb9c45f1560a87cdcd245293adbcd4cb9837f7ef4c69dd9a1533c3ee7cf10121d0389e9fbafe3e71ef687c03899be12c13820253a0e95c7f809f092daec4769400f9f374c0338a93f0e6a84fc001c24ed119274b23a03bdd3ae65aaa024fc42086516b1b4a5605bd91ac8bd5e4bedb80c1b113afbdea"],"id":53,"jsonrpc":"2.0"}", requestMethod="POST", url="https://zksync2-testnet.zksync.dev", code=SERVER_ERROR, version=web/5.6.0)

@danielnordh
Copy link
Author

danielnordh commented Mar 22, 2022

Ethers code:

// transferWithAuthorization (fails)
var txHash = await contractWithSigner.transferWithAuthorization(fromAddress, toAddress, amount, validAfter, validBefore, nonce, sigParams.v, sigParams.r, sigParams.s, {gasPrice: ethers.utils.parseUnits('100', 'gwei'), gasLimit: 1000000});
console.log("txHash:" + JSON.stringify(txHash, null, 2));

// transfer (succeeds)
let txHash2 = await contractWithSigner.transfer(toAddress, amount);
console.log("txHash2:" + JSON.stringify(txHash2, null, 2));

@danielnordh
Copy link
Author

Turns out the issue is that the 'USDC' contract currently deployed on the zkSync 2.0 Alpha testnet is only a standard ERC20 contract and does NOT contain the transferWithAuthorization function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment