swiftblockchainethereumsolidityzk-snark

How to create a ZkSync account?


The ZkSync documentation states that a deposit has to be made from an existing Ethereum account to a non-existing ZkSync account counterfactually in order to create a new account.

However, the method that is used to make the deposit already takes a ZkSync account address as a parameter to specify the recipient:

/// @notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
/// @param _zkSyncAddress The receiver Layer 2 address
function depositETH(address _zkSyncAddress) external payable {
    require(_zkSyncAddress != SPECIAL_ACCOUNT_ADDRESS, "P");
    require(msg.value > 0, "M"); // Zero-value deposits are forbidden by zkSync rollup logic
    requireActive();
    registerDeposit(0, SafeCast.toUint128(msg.value), _zkSyncAddress);
}

How do we pass a ZkSync account address if that's the very thing we're trying to achieve?

If you take a look at one of examples the ZkSync team provided for the Swift SDK:

let ethereumProvider = try self.wallet.createEthereumProvider(web3: Web3.InfuraRinkebyWeb3())

firstly {
    ethereumProvider.deposit(token: .ETH,
                             amount: amount,
                             userAddress: wallet.address) /// Ethereum wallet address is provided
}.done { (_) in
    self.present(UIAlertController.for(message: "Successfully deposited"), animated: true, completion: nil)
}.catch { (error) in
    self.present(UIAlertController.for(error: error), animated: true, completion: nil)
}

The parameter for the deposit is the address of the sender which is the account on the Ethereum network, not ZkSync.

Source

func depositETH(address: EthereumAddress, value: BigUInt) -> Promise<TransactionSendingResult> {
    guard let tx = self.contract.write("depositETH",
                                       parameters: [address] as [AnyObject], // here the parameter passed for the depositETH method of the smart contract is the address of the sender's account, which is the Ethereum account.
                                       transactionOptions: createOptions(value: value)) else {
        return Promise(error: ZkSyncContractError.invalidParameters)
    }

    return tx.sendPromise()
}

If you execute above code, you get the following error:

enter image description here

This mysterious error seems to indicate a connection error according to the web3swift source code, since it seems to say error 1.

public enum Web3Error: Error {
    case transactionSerializationError
    case connectionError
    case dataError
    case walletError
    case inputError(desc:String)
    case nodeError(desc:String)
    case processingError(desc:String)
    case keystoreError(err:AbstractKeystoreError)
    case generalError(err:Error)
    case unknownError

So what address do you have to pass to the deposit method in order to create a new ZkSync account? Is the Swift SDK example correct by passing a sender's address of an Ethereum account? If so, what is the source of the error?

Update

The sequence of the deposit process in the Swift SDK's example code goes something like following:

  1. A wallet is created
guard let wallet = try? DefaultWallet<ChangePubKeyECDSA, DefaultEthSigner>(ethSigner: ethSigner,
                                                                           zkSigner: zkSigner,
                                                                           provider: provider) else {
    fatalError()
}
  1. The wallet is passed to a depositing view controller and is used to send a deposit.
let ethereumProvider = try self.wallet.createEthereumProvider(web3: Web3.InfuraRinkebyWeb3())

firstly {
    ethereumProvider.deposit(token: .ETH,
                             amount: amount,
                             userAddress: wallet.address)
}.done { (_) in
    self.present(UIAlertController.for(message: "Successfully deposited"), animated: true, completion: nil)
}.catch { (error) in
    self.present(UIAlertController.for(error: error), animated: true, completion: nil)
}

But, if my understanding if correct, the userAddress parameter in the deposit method is supposed to be the recipient's address. wallet.address, however, is the address of the sender.

public var address: String {
    self.ethSigner.address
}

Prior to calling the deposit method, I generated a new Ethereum address to send the funds to in hopes that it could generate a new account in Zksync, but still got the same error.

KeysService().getWalletPrivateKey(password: password, completion: { [weak self] privateKey, error in
    if let error = error {
        print(error)
    }
    
    if let privateKey = privateKey {
        do {

            guard let newWallet = self?.createZKWallet(.rinkeby, privateKey: privateKey) else { return }
            let ethereumProvider = try newWallet.createEthereumProvider(web3: Web3.InfuraRinkebyWeb3())

            firstly {
                ethereumProvider.deposit(token: .ETH,
                                         amount: amount,
                                         userAddress: newWallet.address)
            }.done { (_) in
                self?.present(UIAlertController.for(message: "Successfully deposited"), animated: true, completion: nil)
            }.catch { (error) in
                self?.present(UIAlertController.for(error: error), animated: true, completion: nil)
            }
        } catch {
            self?.present(UIAlertController.for(error: error), animated: true, completion: nil)
        }
    }
})

Solution

  • You need to pass a new address (recipient address). Can't send from a 'non-existing' address as you state.

    You can 'create' the address by generating it randomly from a private key, or by copying it if you know the address already.

    Related to your problem, the example app uses an account that currently doesn't have any funds on Ethereum (0x46a23e25df9a0f6c18729dda9ad1af3b6a131160) (see screenshot). I've sent a small amount and it will work now.

    enter image description here