How to create a ZkSync account? - swift

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:
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:
A wallet is created
guard let wallet = try? DefaultWallet<ChangePubKeyECDSA, DefaultEthSigner>(ethSigner: ethSigner,
zkSigner: zkSigner,
provider: provider) else {
fatalError()
}
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)
}
}
})

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.

to create a zkSync account we need to interact with our wallet somehow: f.e.: We can make a deposit from any decentralized wallet L1→L2 and it will mean that account was created or make a transfer to the existing address l2→l2 and it will be also workable. But to work with this funds we will need activate our L2 - make CPK, pay some fee. Otherwise we will need to do alternative withdraw.

Related

Firebase with Swift, sendSignInLinkToEmail succeeds with email not registered

I'm implementing sign-in via email link and I have it sending the email, but I'm using an email that has not been registered yet. Is there a way to detect if the email is already registered before calling sendSignInLinkToEmail? No error was reported from the call. TIA!
Nabil's answer led me to finding the function fetchSignInMethods, which was just the thing I needed. Posted here for anyone else looking for this.
Auth.auth().fetchSignInMethods(forEmail: email, completion: {
methods, error in
if methods == nil {
self.showAlert(title: "This email is not registered, please create an account")
return
}
})
Maybe this function can help you
Auth.auth().fetchProviders(forEmail: emailAddress, completion: {
error in
if let error = error {
print(error.localizedDescription)
} else {
//
}
})
So that if the email doesn't exist it'll return an error otherwise you can run the sendSignInLinkToEmail function

How to handle user after registration?

I need to register my user's with both phone authentication and email authentication. I have successfully done both. However, with the way I've structured my code, it's a bit flimsy.
After a user registers with their phone number, they are automatically signed in. But before they gain access to the application I need them to register with their email as well but I can't do so until they are signed in with their phone.
Group {
if (self.session.session != nil) {
if user?.metadata.creationDate != user?.metadata.lastSignInDate {
AppView()
} else {
EmailRegisterView()
}
} else {
OnBoardingView()
}
}.onAppear(perform: {
session.listen()
})
With the code I have, if the user for some reason logs out of their account on the same day that they've created it, they will be taken to the EmailRegisterView() again which will cause problems.
Is there another way to redirect users to the EmailRegisterView() only if they are new users, and once they have finished there, send them to the AppView()?
Here is my code for the phone authentication...
func phoneRegister () {
let credential = PhoneAuthProvider.provider().credential(withVerificationID: self.verificationCode, verificationCode: self.code)
Auth.auth().signIn(with: credential) { (result, error) in
if error != nil{
self.alertMessage = (error?.localizedDescription)!
self.alert.toggle()
return
}
UserDefaults.standard.set(true, forKey: "status")
NotificationCenter.default.post(name: NSNotification.Name("statusChange"), object: nil)
}
}
Looks like you can add a key to user default and check the value every time the user open the app.
If the userdefault bool key, let's say IsUserRegistered, doesn't exist or is set to false, then you navigate to login page/register page and set the key to true. Otherwise, if the the key is set to true, you just display the home view.

Microsoft Azure mobile SDK custom provider login IOS

I have followed this tutorial to implement a login with custom provider on an Azure mobile app. The backend works perfectly but when I try to login to my new custom controller I'm not able to do it. It's possible to implement it with Xamarin and also with java Android but is no way to do it with Objective C or Swift.
The object MSClient on Microsoft Azure mobile SDK only has two login implementations.
I have tried both but without luck, the callback always returns an empty client.
I also have tried to store the token created by own API use it for login call but without luck again.
Here is my Swift code:
let client = MSClient(applicationURLString: "https://myApp.azurewebsites.net")
client.login(withProvider: "custom", urlScheme: "myApp", parameters: ["username": "pau", "password": "123456"], controller: self, animated: true) {user, error in
print("USER", user)
print("ERROR", error)
}
We found a solution, it's really easy but the msclient documentation is not clear enough. You just need to pass whatever you need (i.e: username,password) as dictionary in token parameter.
Client.msClient.login(withProvider: "auth", token: params, completion: {user, error in
print("USER", user)
print("ERROR", error)
print("USER ID", user?.userId)
print("TOKEN", user?.mobileServiceAuthenticationToken)
if let user: MSUser = user {
guard let username: String = user.userId else { return }
guard let token: String = user.mobileServiceAuthenticationToken else { return }
Client.username = username
Client.msClient.currentUser = user
completion(true)
}else {
completion(false)
}
})

How to check if user needs to re-authenticate using Firebase Authentication

I am using Firebase to log in users into my app, but when I am adding the capability to manage their account like changing their email, password and so on. The documentation says that if the user have not recently signed in they need to re-authenticate, but my question is: How can I check if the user have signed in recently or not? According to the docs the error will return FIRAuthErrorCodeCredentialTooOld, but how can I check this?
Swift 3
I had to do this yesterday when trying to delete a user. One thing to note is FIRAuthErrorCodeCredentialTooOld is now FIRAuthErrorCode.errorCodeRequiresRecentLogin
What I did was trigger a UIView to ask for log in details if that error is thrown. Since I was using email and password, that's what I collected from the user in my example.
private func deleteUser() {
//get the current user
guard let currentUser = FIRAuth.auth()?.currentUser else { return }
currentUser.delete { (error) in
if error == nil {
//currentUser is deleted
} else {
//this gets the error code
guard let errorCode = FIRAuthErrorCode(rawValue: error!._code) else { return }
if errorCode == FIRAuthErrorCode.errorCodeRequiresRecentLogin {
//create UIView to get user login information
let loginView = [yourLoginUIViewController]
self.present(loginView, animated: true, completion: nil)
}
}
}
Once I had the login information I ran this function to reauthenticate the user. In my case I ran it the loginView in the above code if the login in was successful:
func reauthenticateUserWith(email: String, password: String) {
FIRAuth.auth()?.signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
//display UIView to delete user again
let deleteUserView = deleteUserView()
present(deleteUserView, animated: true, completion: nil)
} else {
//handle error
print(error!.localizedDescription)
}
}
}
The deleteUserView in my case calls deleteUser() on a button tap from the user. You can also use UIAlertController in place of the custom UIViews, but that's up to you.
Hope this helps.
Update for current Swift 5
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
let authErr = AuthErrorCode(rawValue: error.code)
if authErr == .requiresRecentLogin {
// reauthenticate
}
// other error
} else {
// delete success
}
}
According to the documents, there is currently no way to check FIRAuthErrorCodeCredentialTooOld other than going through the deleting of the account or the other sensitive cases mentioned.
If you are like me and ended up here because you are trying to figure out how to handle removing someone from Auth and removing other user data in Cloud Firestore, Realtime Database, and/or Cloud Storage, then there is a better solution.
Check out the Delete User Data Extension from Firebase to handle this. In short, when a user profile is deleted from Auth, you can use this also to delete data associated with the uid from those other Firebase data storage tools.

Customer does not have linked source with id

I keep getting the following error when viewing stripe test trasactions:
{
"error": {
"type": "invalid_request_error",
"message": "Customer cus_9tW8Cf0Xvm9lRv does not have a linked source with ID tok_19Zj5rAANnhOmz4ex3ri2jtW.",
"param": "source",
"code": "missing"
}
}
my swift code to call the api is as follows:
#IBAction func payButtonWasPressed() {
stripeCard = STPCardParams()
let expirationDate = self.cardExpiryTextField.text!.components(separatedBy: "/")
let expMonth = UInt(expirationDate[0])
let expYear = UInt(expirationDate[1])
stripeCard.number = self.cardNumberTextField.text
stripeCard.cvc = self.cardCVVTextField.text
stripeCard.expMonth = expMonth!
stripeCard.expYear = expYear!
STPAPIClient.shared().createToken(withCard: stripeCard, completion: { (token, error) -> Void in
if error != nil {
self.handleError(error! as NSError)
return
}
self.chargeUsingToken(token!)
})
}
func handleError(_ error: NSError) {
UIAlertView(title: "Please Try Again",
message: error.localizedDescription,
delegate: nil,
cancelButtonTitle: "OK").show()
}
func chargeUsingToken(_ token: STPToken) {
let URL = "https://splitterstripeserver.herokuapp.com/charge"
let params = ["source": token.tokenId,
"stripe_token": token.tokenId,
"amount": total] as [String : Any]
let manager = AFHTTPSessionManager()
manager.post(URL, parameters: params, success: { (operation, responseObject) -> Void in
if let response = responseObject as? [String: String] {
UIAlertView(title: response["status"],
message: response["message"],
delegate: nil,
cancelButtonTitle: "OK").show()
}
}) { (operation, error) -> Void in
self.handleError(error as NSError)
}
}
and I am using the example backend found on the stripe github
On my app I get a pop-up saying Request failed: payment required (402). I have tried a lot of different things but can't seem to get successful response. I cant see what im doing wrong and need a fresh pair of eyes. Any help would be great. Thanks
Once you create the card token with Stripe Checkout or Stripe.js, you send it to your server where you use it to create a customer via the API. That card becomes "saved" with that customer and you can then charge it in the future. If you want to add another card to the same customer you would use the Create Card API to pass the new token and add that card.
When you create a charge, you pass the customer id (cus_XXX) in the customer parameter to charge the default card. If you want to charge a specific card, you would pass the card id (card_YYY) in the source parameter along with the customer.
At the moment, you are trying to pass a customer id and a card token which is not supported. You'll need to save that card on the customer first and then pass the new card id in the source parameter as mentioned above.