Introducing: Web3.swift for Ethereum iOS Development

Mercury Protocol
mercuryprotocol
Published in
7 min readFeb 13, 2018

Dev Highlights This Week

  • Developing new UI + features for Dust wallet
  • Additional stability fixes for Dust iOS (pending release)
  • Created and pushed Web3 and Geth wrapper utility in swift
    (used in Dust wallet)
  • Example app showcasing how to use above framework in iOS app
  • Targeting latest Android SDK, Oreo, with latest patch (Android)
  • Upgraded to target latest Google Play services libraries
  • Identified and fixed a few memory leaks in the app, should improve performance and help with any out of memory crashes (Android)
  • Cleaning and stripping old assets from app, reduced APK size by ~10 MB (pending Android release)
  • Added a synchronized call in account creation rewards to ensure Rinkeby ETH reward transaction
  • Implemented an Amazon SQS deadletter queue for GMT rewards

Introduction

You may already be familiar with our “How To” series which documents how to utilize existing technology in the blockchain community. This post marks the start of a new series of blog posts “Introducing” never before seen technology discovered by the talented Mercury team.

The iOS team here at Mercury Protocol is very happy to announce that we have open sourced the framework web3.swift. Mercury’s web3.swift provides native iOS developers an API to encode and sign transactions client side. This gives clients the choice to send to the blockchain directly or send it elsewhere to avoid mobile devices acting as Ethereum peers.

Mercury’s web3.swift library gives the blockchain community and app developers an easier way to use Swift for Ethereum: Account Management, Transaction Encoding, and Signing.

This library is similar to web3.js for Web and web3j for Java and Android.

Prerequisites

Install CocoaPods using Homebrew by entering the following in a terminal:

$ brew install CocoaPods

Library Installation

First, create an Xcode iOS app project called Web3SwiftExample in your home directory. To integrate web3.swift into your Xcode project using CocoaPods, first create a podfile.

$ cd ~/Web3SwiftExample
$ pod init

Open the newly created Podfile with a text editor, and paste in the following in the pods section:

pod 'web3swift', :git => 'https://github.com/MercuryProtocol/web3.swift.git', :branch => 'master'

Your podfile should look like this:

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'Web3SwiftExample' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for Web3SwiftExample
pod 'web3swift', :git => 'https://github.com/MercuryProtocol/web3.swift.git', :branch => 'master'
end

Then, run the following command:

$ pod update
$ pod install

Open the newly created .xcworkspacefile

Disclaimer: This is a work in progress, which doesn’t support full features of web3 or geth. In the coming months, we will add more to the library and open-source more of the code.

Problems and Solutions

  1. Why use Geth?
    Time was critical and we had to build solution to showcase integration of Mercury Protocol in Dust. This saved a lot of time without having to write a lot of code from the ground up. Also, it is the official library hence new changes and patches can be immediately integrated in Dust as soon as they are released instead of relying on third party vendors.
  2. Why not connect to blockchain directly from iOS?
    We wanted to keep iOS and Android clients as light as possible. Hence, we created a Mercury server which is responsible for business logic instead of writing everything “on the client side.”
  3. How to support Int256 on iOS?
    Out of the box Swift doesn’t support Int256. This is big problem if you want to represent values in wei. We had two options either use GethBigInt provided by Geth or use the BigInt Swift framework. We chose to use GethBigInt and wrote Swift Extensions to add additional functionalities. We might end up using BigInt in the future if we do decide to move away from using Geth.
  4. How to fix issues with the debugger?
    The simulator debugger might crash which can be fixed by removing arm7 from supporting architectures. You can find more information about this here.

How to use web3.swift?

We have created an example application showcasing how to use this framework here. This simple application generates default accounts and shows how to sign transactions. But let’s go through a few examples here to showcase how to use web3.swift in iOS.

Example: Create an Ethereum Account

1. Import libraries
Note: If you have compilation issues see “Fixing compiler issues” below

import web3swift
import Geth

2. Choose your namespace and password

let configA = EthAccountConfiguration(namespace: "walletA", password: "qwerty")

3. Call launch with configuration to create a keystore and account

let (keystoreA, accountA): (GethKeyStore?,GethAccount?) = EthAccountCoordinator.default.launch(configA)

keystoreA : The encrypted private and public key for wallet A
accountA : An Ethereum account

Similar to AlamoFire, the most recently loaded wallet is used for all operations such as changing password and signing.

Example: Change Your Password

To change the password of the most recently loaded account, do the following which returns Bool for success/failure

let result = EthAccountCoordinator.default.updatePassword("qwerty", newPassphrase: "qwerty1234!")

Or you can pass in the account you want to change

do {
try keystoreA.update(accountA, passphrase: "qwerty", newPassphrase: "qwerty1234!")
} catch {
print("Update password failed")
}

Example: Send 50 GMT from Wallet B to Wallet A

1. Create wallet B

let configB = EthAccountConfiguration(namespace: "walletB", password: "12345")let (keystoreB, accountB): (GethKeyStore?,GethAccount?) = EthAccountCoordinator.default.launch(configB)

2. Start with the smart contract function and arguments
Sending GMT utilizes the ERC-20 “transfer” function

if let walletAAccountAddress: GethAddress = accountA?.getAddress() {
let amount = GethBigInt.bigInt(valueInEther:50)!
let transferFunction = EthFunction(name: "transfer", inputParameters: [walletAAccountAddress, amount])
}

2. Encode the “transfer” function
In order to execute a function on the blockchain we must encode the function name and its parameters following Ethereum rules.

let encodedTransferFunction = web3swift.encode(transferFunction)
print("\(encodedTransferFunction.toHexString())")

Your output should look like this:

c5e23ba100000000000000000000000000b51e934abfb2e159101014ba4b8c0f1b66dcea000000000000000000000000000000000000000000000002b5e3af16b1880000

3. Sign the transaction
You must include the standard Ethereum parameters: contract address, nonce, gasLimit and gasPrice. It can be used to generate JSON or RLPdump. Sign() uses the most recently loaded account which is account B.

let nonce: Int64 = 4 // Update this to valid nonce
let gasPrice = GethNewBigInt(20000000000)!
let gasLimit = GethNewBigInt(4300000)!
let contractAddress = "0xb3Bd49E28f8F832b8d1E246106991e546c323502"
var addressError: NSError? = nil
let gethContractAddress: GethAddress! = GethNewAddressFromHex(contractAddress, &addressError)
let signedTx = web3swift.sign(address: gethContractAddress, encodedFunctionData: encodedTransferFunction, nonce: nonce, gasLimit: gasLimit, gasPrice: gasPrice)

4. Create RLP dump
RLP is the main encoding method used to serialize objects in Ethereum. We used this to generate signed Transaction Data which can be transmitted to server.

let signedTxData = try! signedTx?.encodeRLP()
print("\(signedTxData!.toHexString())\n\(signedTxData!.bytes)")

Your output should look like this:

f8aa048504a817c80083419ce094b3bd49e28f8f832b8d1e246106991e546c32350280b844c5e23ba1000000000000000000000000e110ebb757b26f2acd89069077b80bdae22615ee000000000000000000000000000000000000000000000002b5e3af16b18800001ca02c76a6c029d3377fdead1a72111ddaf804fcf8e02d85afc8370656483ac8306aa048e96ae739e6e0f34627bfe391e4532431b033e6917e17b4546363ec87438158 [248, 170, 4, 133, 4, 168, 23, 200, 0, 131, 65, 156, 224, 148, 179, 189, 73, 226, 143, 143, 131, 43, 141, 30, 36, 97, 6, 153, 30, 84, 108, 50, 53, 2, 128, 184, 68, 197, 226, 59, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 16, 235, 183, 87, 178, 111, 42, 205, 137, 6, 144, 119, 184, 11, 218, 226, 38, 21, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 181, 227, 175, 22, 177, 136, 0, 0, 28, 160, 44, 118, 166, 192, 41, 211, 55, 127, 222, 173, 26, 114, 17, 29, 218, 248, 4, 252, 248, 224, 45, 133, 175, 200, 55, 6, 86, 72, 58, 200, 48, 106, 160, 72, 233, 106, 231, 57, 230, 224, 243, 70, 39, 191, 227, 145, 228, 83, 36, 49, 176, 51, 230, 145, 126, 23, 180, 84, 99, 99, 236, 135, 67, 129, 88]

5. Convert to Base64
We convert signed transaction data to a base64 encoded string which can be sent to a server. You can use libraries like Alamofire to send this signed transaction to your own servers.

let base64SignedTx = signedTxData.base64EncodedString()let parameters: Parameters = [
"signedTx" : base64SignedTx,
]
Alamofire.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default)

6. On the server side this can be decoded and sent to the blockchain
(Web3J Java Example)

try {
byte[] signedTxBytes = base64SignedTx.getBytes("UTF-8");
byte[] decoded = Base64.getDecoder().decode(signedTxBytes);
String hexValue = Numeric.toHexString(signedTransaction);
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
return ethSendTransaction.getTransactionHash();
} catch (UnsupportedEncodingException ex) {
logger.error("Cannot decode signed tx", ex);
throw new Exception("Contract Failed");
}

Congrats! You’ve made it through a brief overview on how to use web3.swift, a Geth wrapper for Ethereum.

Troubleshooting: Fixing Compilation Issues
1. The Geth framework doesn’t support bitcode hence you should disable bitcode support

  • Go to “Build Settings”
  • Search “Bitcode” (case sensitive)
  • Disable it by setting it to No

2. In case you get no such module web3swift error

  • Go to “Build Settings”
  • Search “Framework Search Paths” (case sensitive)
  • Double click on <Multiple values>
  • Click the “+”
  • Add $(SRCROOT) and set it to recursive

Looking Ahead

  1. We are internally working on removing the Geth framework dependency and moving towards building everything on Swift/Objective C.
  2. Continue developing thorough documentation for the iOS library.
  3. Full Mercury wallet example showcasing communication with Mercury server.
  4. Add support for external providers (i.e. Rinkeby).

Share your thoughts with us in any of the community channels linked below!

Connect

Slack
Telegram
Twitter
Reddit
Facebook
LinkedIn
GitHub

Learn more about the Mercury Protocol
Read the Mercury Protocol whitepaper
Follow +mercuryprotocol on Dust

--

--