Possible to Integrate IOS app with ERC20 token - ios

Is is possible at this point to integrate an IOS app with an ERC20 token on the ethereum network.
There seems to be a library called web3.swift that allows for integration with Ethereum. Maybe this is the way to go but don't know if it is ready for a production app.
In addition, there seem to be some online courses on Swift and blockchain such as this one from Lynda and this one from Udemy and some tutorials on blockchain integration such as this from AppCoda and this one which uses the Tierion blockchain as a service. In fact, AWS, Azure and so forth all seem to offer blockchain as a service. Amazon offers managed blockchain with Ethereum.
However, I haven't seen anything that specifically addresses how to integrate an IOS app with Ethereum. And if it's done on the back end by AWS does this defeat the purpose of using a blockchain to avoid centralization on a server?
The use case I am examining would be to view the number of tokens you have and enable users to spend tokens on services. The tokens would reside however on a blockchain.
Thanks for any advice on whether this is even possible at this stage and, if so, how to approach developing it.

New transaction
Infura service might help here. Once you create your account and setup a project - you will be provided with nodes (for main-net and some test-nets) that can perform write operations on a specific Ethereum network.
Here is the code written in Swift and Web3.swift that might help:
func send(sender: String,
receiver: String,
senderSecret: String,
tokenContractAddress: String,
amount: Int,
completion: #escaping Result<Transaction, Error>) {
let web3 = Web3(rpcURL: "YOUR_INFURA_NODE_ID_GOES_HERE")
do {
let privateKey = try EthereumPrivateKey(hexPrivateKey: senderSecret)
let senderWallet = try EthereumAddress(hex: sender, eip55: true)
let receiverWallet = try EthereumAddress(hex: receiver, eip55: true)
let contract = web3.eth.Contract(
type: GenericERC20Contract.self,
address: try EthereumAddress(hex: tokenContractAddress, eip55: true)
)
firstly {
return web3.eth.getTransactionCount(address: privateKey.address, block: .latest)
}.compactMap { nonce in
guard let tx = contract
.transfer(to: receiverWallet, value: BigUInt(amount))
.createTransaction(
nonce: nonce,
from: senderWallet,
value: 0,
gas: 100000,
gasPrice: EthereumQuantity(quantity: 21.gwei)
) else { return nil }
return try tx.sign(with: privateKey, chainId: 3)
}.then { tx in
return web3.eth.sendRawTransaction(transaction: tx!)
}.done { hash in
let tx = Transaction(hash: hash)
completion.set(.success(tx))
}.catch { error in
completion.set(.failure(error))
}
} catch {
completion.set(.failure(error))
}
}
Open information
If there is no need to initiate transactions and you just want to work with public information like token supply/holder balances/etc. - you can try some open API like blockscout to get the data you need.

Related

AWS Amplify iOS SDK : FederatedSignIn Failed to retrieve authorization token on Amplify.API.post

I've been working with the Amplify SDK to get federatedSignIn working with my iOS app with "Sign in with Apple" and Cognito to eventually make calls to API Gateway / Lambda functions.
TL;DR : My access token does not appear to be "automatically included in outbound requests" to my API as per the last paragraph of this section of the docs : Cognito User pool authorization
I have successfully authenticated using the tutorial found here Authentication Getting Started and other various Youtube videos on the Amazon Web Services channel.
Upon successful sign in through Apple I'm given an ASAuthorizationAppleIDCredential object. This contains the user field (token) which I pass to the Amplify.Auth class using the following Swift code :
func signIn (with userId: String)
{
guard
let plugin = try? Amplify.Auth.getPlugin(for: AWSCognitoAuthPlugin().key),
let authPlugin = plugin as? AWSCognitoAuthPlugin,
case .awsMobileClient (let client) = authPlugin.getEscapeHatch()
else
{
return
}
client.federatedSignIn(providerName: AuthProvider.signInWithApple.rawValue, token: userId) { (state, error) in
if let unwrappedError = error
{
print (unwrappedError)
}
else if let unwrappedState = state
{
print ("Successful federated sign in:", unwrappedState)
}
}
}
All appears to be successful and to double check I use the following bit of code to ensure I'm authorized :
func getCredentialsState (for userId:String)
{
let provider = ASAuthorizationAppleIDProvider()
provider.getCredentialState(forUserID: userId) { (credentialsState, error) in
if let unwrappedError = error
{
print (unwrappedError)
}
switch credentialsState
{
case .authorized:
print ("User Authorized")
case .notFound, .revoked:
print ("User Unauthenticated")
case .transferred:
print ("User Needs Transfer")
#unknown default:
print ("User Handle new use cases")
}
}
}
In the console I see "User Authorized" so everything appears to be working well.
However when I then go to make a call to Amplify.API.post I get the following error:
[Amplify] AWSMobileClient Event listener - signedOutFederatedTokensInvalid
Failed APIError: Failed to retrieve authorization token.
Caused by:
AuthError: Session expired could not fetch cognito tokens
Recovery suggestion: Invoke Auth.signIn to re-authenticate the user
My function for doing the POST is as follows :
func postTest ()
{
let message = #"{'message": "my Test"}"#
let request = RESTRequest (path: "/test", body: message.data(using: .utf8))
Amplify.API.post (request:request)
{
result in switch result
{
case .success(let data):
let str = String (decoding: data, as: UTF8.self)
print ("Success \(str)")
case .failure(let apiError):
print ("Failed", apiError)
}
}
}`
I then went into the API Gateway UI and changed the generated Method Request on my resource from AWS IAM to my Cognito User Pool Authorizer thinking this was the issue. I also changed the awsAPIPlugin authorizationType to "AMAZON_COGNITO_USER_POOLS" in my amplifyconfiguration.json file. This unfortunately did not have any affect.
I've seen posts such as this issue User is not created in Cognito User pool for users logging in with Google federated login #1937 where people discuss the problem of having to to use a web ui to bring up the social sign in. I understand that Apple will reject your app sometimes for this. Therefore this is not a solution.
I then found this post which seems to resolve the issue however this appears to use the old version of the SDK? Get JWT Token using federatedSignIn #1276
I'm not great with Swift (I'm still an Objective C expert, but am slowly learning Swift) so I'm uncertain which path to go here and whether this is actually a solution? It does seem to be quite more complicated than the function I have that does my POST? The RESTRequest does seem to be a simple and easy solution but I'm uncertain how to pass it the Authorization token (or even how to get the token if it is needed here).
However, everything I've read about the SDK is that the authorization should be handled automatically in the background according the docs in my first link above. Specifically pointed out, again, here : Cognito User pool authorization. The last paragraph here states 👍
With this configuration, your access token will automatically be included in outbound requests to your API, as an Authorization header.
Therefore, what am I missing here as this does not appear to automatically include my access token to my outbound requests to my API?

How to create a "challenge" for my Cloud Functions server

I'm trying use Apple's new DeviceCheck API to verify that network calls in my app are actually coming from an uncompromised version of my app.
Documentation
After successfully verifying a key’s attestation, your server can
require the app to assert its legitimacy for any or all future server
requests. The app does this by signing the request. In the app, first
obtain a unique, one-time challenge from the server. You use a
challenge here, like for attestation, to avoid replay attacks. Then
combine the challenge with the server request to create a hash:
let challenge = <# A string from your server #>
let request = [ "action": "getGameLevel",
"levelId": "1234",
"challenge": challenge ]
guard let clientData = try? JSONEncoder().encode(request) else { return }
let clientDataHash = Data(SHA256.hash(data: clientData))
Use this hash and the key identifier that you generated earlier to
create an assertion object by calling the
generateAssertion(_:clientDataHash:completionHandler:) method:
service.generateAssertion(keyId, clientDataHash: clientDataHash) { assertion, error in
guard error == nil else { /* Handle the error. */ }
// Send the assertion and request to your server.
}
I'm trying to add this assertion functionality to my Swift function, which is a helper function that calls a Firebase Cloud Function.
I want the assertion object to be passed as data to the Cloud Function, to verify that the Cloud Function is being called from an uncompromised version of my app:
func callFunction(name: String, data: [String:Any?], completion: #escaping (HTTPSCallableResult?, Error?)->()){
var functions = Functions.functions()
functions.httpsCallable(name).call(data){ (result, error) in
completion(result, error)
}
}
(Example of callFunction() being used below):
let data: [String:Any?] = [
"gameId": self.game?.id,
"answer": answer,
"answeredAt": Date().millisecondsSince1970
]
callFunction(name: "answerQuestion", data: data){ res, err in
print("Submitted answer: \(res.debugDescription) | Error: \(err)")
if let err = err {
self.game?.question?.state = .initial
}
}
To generate the assertion object to send to my server (cloud function), it requires me to generate a challenge as stated above. However I'm not sure how to generate this challenge.
Apple says it should be "A string from your server". But I'm not sure what the string should be. Is it meant to be a dynamic string based on the user's UID? A Base64-encoded string of the user ID and a static secret string? And when I try to retrieve this string from the server, the user will just be able to read it as they can see incoming network JSON (I presume I would retrieve the string with a Cloud Function call) - so it seems pointless as it's not a secret string anymore?
Any idea how I can make the challenge work securely?
The point of the challenge is to avoid replay attacks, so it can be any randomised string. A UUID would be fine. It doesn't need to be a secret.
The challenge string is combined with the transaction data and a hash is generated. You send the hash to and you send that to generateAssertion and receive the assertion object. You then send this to your server along with the request data.
Now your server can combine the received request data with the challenge (which it knows, since it sent it to the client initially), generate the same hash and validate the attestation.
The server-side attestation article provides detail on the challenge data:
Provide a Challenge
Every time your app needs to communicate attestation data to your server, the app first asks the server for a unique, one-time challenge. App Attest integrates this challenge into the objects that it provides, and that your app sends back to your server for validation. This makes it harder for an attacker to implement a replay attack.
When asked for a challenge, provide your app with a randomized data value, and remember the value for use when verifying the corresponding attestation or assertion objects sent by the client. How you use the challenge data depends on the kind of object that you need to validate.

Create token for bank account information Stripe

I am trying to create an ios app which safely collects a user's bank account information (with the intention of paying the user) using Stripe. Stripe recommends that I collect the bank information in an instance of STPBankAccountParams. This is not too bad:
var bankAccount = STPBankAccountParams()
bankAccount.routingNumber = routingNumber
bankAccount.accountNumber = accountNumber
...
Stripe then recommends that you tokenize the bankAccount for security purposes before sending to backend. They recommend you use this function:
func createToken(withBankAccount bankAccount: STPBankAccountParams, completion: STPTokenCompletionBlock? = nil)
The documentation on this function is a bit sparse: Docs
I am not sure how to run this function in my code. I want to use this function and get the token, but I lack understanding on how to do that in code. I want to run something like:
token = createToken(withBankAccount: bankAccount)
But of course that and other things I have tried have not worked yet. Does anyone have experience running the createTokenWithBankAccount() function in Stripe?
MadProgrammer's answer was very close, but did not actually work. I did talk with a representative from Stripe. For reference, he recommended the following code, which seems to work:
STPAPIClient.shared().createToken(withBankAccount: bankAccount) { (token, error) in
if let error = error {
print(error)
}
else {
print(token)
}
}
STPTokenCompletionBlock is closure (or callback), when the function completes (what ever task it was doing), it will call this block, passing you a STPToken or a Error. You would use something like
createToken(withBankAccount: bankAccount) { (token, error) in
// your code to check the token and error
}
This is a pretty common pattern and I suggest you take a look at something like Swift Programming Language: Closures

Using API-Key in Vapor

I'm developing a simple web API with Vapor. To give more context, I'm newbie in backend development.
The consumer of the API is going to be an iOS app. Currently, I don't need the users to sign up to use the app. And I would like to keep it like that.
On the other hand, I would like to have some authentication to avoid that anyone could use the API I'm developing.
Looking for information I've found how implement authentication. But the examples I've seen are based on creating users in the backend for each user of the app. What I don't want to do. I would like to use an api-key as we do normally when we use third-party api's.
How could I have "api-key authentication" with Vapor ??
Or, should I just create an unique user/password that it's shared by all the users of the iOS app (that use the API) and then use basic or token authentication?
Thank you very much!
Carlos
One way around this is to create a fake token and use either the TokenAuthenticationMiddleware or more likely a custom middleware that checks the incoming token.
However, be aware that there is nothing stopping anyone from inspecting the traffic coming from your app to view the token and then using that to access your API.
Following Tim idea and an example from the book Server Side with Vapor (by the Raywenderlich.com Tutorial Team) I've created this custom middleware that makes the work:
final class SecretMiddleware: Middleware {
let secret: String
init(secret: String) {
self.secret = secret
}
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> {
guard let bearerAuthorization = request.http.headers.bearerAuthorization else {
throw Abort(.unauthorized, reason: "Missing token")
}
guard bearerAuthorization.token == secret else {
throw Abort(.unauthorized, reason: "Wrong token")
}
return try next.respond(to: request)
}
}
extension SecretMiddleware: ServiceType {
static func makeService(for worker: Container) throws -> SecretMiddleware {
let secret: String
switch worker.environment {
case .development:
secret = "foo"
default:
guard let envSecret = Environment.get("SECRET") else {
let reason = "No SECRET set on environment."
throw Abort(.internalServerError, reason: reason)
}
secret = envSecret
}
return SecretMiddleware(secret: secret)
}
}

how to send verification code by sms in swift 2

i build a register form for my app and i need to send the user a verifiation code by sms in order to complete the registration proccess.
i tried to use MFMessageComposeViewController but its open the dialog sms on the device so the user can see the code.
i also checked the web for 3party of sending sms but there is a problem with the country code. i know its posible becuse whatsapp do it to confirm the user.
what it the right way to do it?
this is the topic the i tried:
Sending SMS in iOS with Swift
The best way to achieve this is by creating some views for allowing the user to enter the phone number with the country code which can be used by a server to send a request for initiating the OTP verification. To achieve this you need to:
Create View Controllers.
Upload Phone Number and Country code to the server.
Validate the requests by verifying the OTP.
As mentioned by Dan, you can use Digits in Fabric for that purpose, and create custom views for a great UX.
On the other hand, you can also use a service called as SendOTP from MSG91 - you can use it for internal testing and development ideas as they provide you with 5,000 free OTP SMS. The service has a complete set of APIs which you can implement on the backend as well on the app front. Also, they provide a framework so that you don't need to create the views, but only presentViewController and call delegate methods for knowing what happened during the verification process - such as Cancelled or Verified or Failed.
Swift implementation of the same looks like this:
class OTPFrame: UIViewController, sendOTPAuthenticationViewControllerDelegate {
func loadOTPFramework() {
SendOTP.sharedManager().startWithApiId("yourAppID")
let frameworkPath: NSString = NSBundle.mainBundle().privateFrameworksPath!
let frameworkBundlePath: NSString = frameworkPath.stringByAppendingPathComponent("SendOTPFramework.framework")
let frameworkBundle : NSBundle
= NSBundle(path: frameworkBundlePath as String)!
let authenticationViewController: AuthenticationViewController = AuthenticationViewController(nibName: "AuthenticationViewController", bundle: frameworkBundle)
authenticationViewController.delegate = self self.presentViewController(authenticationViewController, animated: true, completion: nil)
}
func authenticationisSuccessfulForMobileNumber(mobNo: String!, withCountryCode countryCode: String!) {
print("Success")
}
func canceledAuthentication() {
print("Cancelled")
}
func authenticationisFailedForMobileNumber(mobNo: String!, withCountryCode countryCode: String!) {
print("Failed")
}
}
Disclaimer: I, in no way, endorse the services mentioned above - you are free to choose whatever you like.
Thank You!
I would give digits a try! It's part of the Twitter Fabric package and it's very simple to use. The user enters their phone number and Fabric takes care of validating the number.

Resources