How to Integrate with the Ethereum Blockchain with Magic in iOS
How to Integrate with the Ethereum Blockchain with Magic in iOS
#Installation
To interact with the Ethereum blockchain, Magic iOS SDK embeds Web3.swift
as sub dependency. No more extra dependency is needed.
#Initializing Provider
Following example is using Swift 5 with XCode 11. IOS demo will be open-sourced soon.
// AppDelegate.swift
import MagicSDK
import UIKit
@UIApplicationMain
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// assign the newly created Magic instance to shared property
Magic.shared = Magic("YOUR_PUBLISHABLE_API_KEY");
return true
}
// ViewController.swift
import UIKit
import MagicSDK
import MagicSDK_Web3
class Web3ViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
}
#Use Different Networks
#Choose Different Testnet
// AppDelegate.swift
import MagicSDK
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// assign to Magic singleton
Magic.shared = Magic("YOUR_PUBLISHABLE_API_KEY", EthNetwork.rinkeby);
return true
}
#Configure Custom Nodes
// AppDelegate.swift
import MagicSDK
// assign to Magic singleton
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let config = CustomNodeConfiguration(rpcUrl: "https://alchemy.io", chainId: 1)
Magic.shared = Magic(apiKey: "API_KEY", customNode: config);
return true
}
Do not set the custom nodes to local IP address (E.x. "http://127.0.0.1"). Local IP will point to the network environment inside mobile device / simulator
#Associated Class
CustomNodeConfiguration(rpcUrl: String, chainId: Int?)
rpcUrl
:Your own node URLchainId
: Your own node's chainId
EthNetwork
public enum EthNetwork: String {
case mainnet
case kovan
case rinkeby
case ropsten
}
#Get User Info
import MagicSDK
import MagicSDK_Web3
import PromiseKit
class Web3ViewController: UIViewController {
var web3 = Web3(provider: Magic.shared.rpcProvider)
// ⭐️ After user is successfully authenticated
@IBOutlet weak var accountLabel: UILabel!
func getAccount() {
firstly {
// Get user's Ethereum public address
web3.eth.accounts()
}.done { accounts -> Void in
if let account = accounts.first {
// Set to UILa
self.accountLabel.text = account.hex(eip55: false)
} else {
print("No Account Found")
}
}.catch { error in
print("Error loading accounts and balance: \(error)")
}
}
}
#Send Transaction
import MagicSDK
import MagicSDK_Web3
class Web3ViewController: UIViewController {
var web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
// ⭐️ After user is successfully authenticated
func sendTransaction() {
guard let account = self.account else { return }
// Construct a transaction
let transaction = EthereumTransaction(
from: account, // from Get User Info section
to: EthereumAddress(hexString: "0xE0cef4417a772512E6C95cEf366403839b0D6D6D"),
value: EthereumQuantity(quantity: 1.gwei)
)
// Submit transaction to the blockchain
web3.eth.sendTransaction(transaction: transaction).done { (transactionHash) in
print(transactionHash.hex())
}.catch { error in
print(error.localizedDescription)
}
}
}
#Sign Message
Magic iOS SDK extends the functionality from Web3.swift to allow developers to sign Typed Data. Make sure to import MagicSDK
while using these functions.
#Eth Sign
import MagicSDK
import MagicSDK_Web3
import PromiseKit
class ViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
func ethSign() {
guard let account = self.account else { return }
let message = try! EthereumData("Hello World".data(using: .utf8)!)
web3.eth.sign(from: account, message: message).done({ result in
print(result.hex())
})
}
}
#Sign Typed Data Legacy (V1)
import MagicSDK
import MagicSDK_Web3
import PromiseKit
class Web3ViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
func SignTypedDataLegacy() {
guard let account = self.account else { return }
let payload = EIP712TypedDataLegacyFields(type: "string", name: "Hello from Magic Labs", value: "This message will be signed by you")
web3.eth.signTypedDataLegacy(account: account, data: [payload]).done({ result in
print(result.hex())
})
}
}
#Sign Typed Data (EIP 712)
import MagicSDK
import MagicSDK_Web3
class Web3ViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
func SignTypedData() {
guard let account = self.account else { return }
do {
let json = """
{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"verifyingContract","type":"address"}],"Greeting":[{"name":"contents","type":"string"}]},"primaryType":"Greeting","domain":{"name":"Magic","version":"1","verifyingContract":"0xE0cef4417a772512E6C95cEf366403839b0D6D6D"},"message":{"contents":"Hello, from Magic!"}}
""".data(using: .utf8)!
let typedData = try JSONDecoder().decode(EIP712TypedData.self, from: json)
web3.eth.signTypedData(account: account, data: typedData).done({ result in
print(result.hex())
})
} catch {
print(error.localizedDescription)
}
}
}
#Smart Contract
In this example, we'll be demonstrating how to use Magic with Web3.swift to interact with Solidity smart contracts. The simple Hello World contract allows anyone to read and write a message to it.
pragma solidity ^0.5.10;
contract HelloWorld {
string public message;
constructor(string memory initMessage) public {
message = initMessage;
}
function update(string memory newMessage) public {
message = newMessage;
}
}
#Deploy Contract
import MagicSDK
import MagicSDK_Web3
class Web3ViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
// ⭐️ After user is successfully authenticated
func deployContract() {
guard let account = self.account else { return }
do {
let contractABI = """
[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
""".data(using: .utf8)!
let contractByteCode = try EthereumData("0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032")
/// Create Contract instance
let contract = try web3.eth.Contract(json: contractABI, abiKey: nil, address: nil)
/// Deploy contract
guard let invocation = contract.deploy(byteCode: contractByteCode) else { return }
invocation.send(from: self.account!, gas: 1025256, gasPrice: 0) { (hash, error) in
print(hash?.hex() ?? "Missing Hash")
print(error?.localizedDescription ?? "Error")
}
} catch {
print(error.localizedDescription)
}
}
}
#Read From Contract
import MagicSDK
import MagicSDK_Web3
class MagicViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
// ⭐️ After user is successfully authenticated
func getMessage() {
do {
/// Construct contract instance
let contractABI = """
[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
""".data(using: .utf8)!
let contract = try web3.eth.Contract(json: contractABI, abiKey: nil, address: EthereumAddress(ethereumValue: "0x8b211dfebf490a648f6de859dfbed61fa22f35e0"))
/// contract call
contract["message"]?().call() { response, error in
if let response = response, let message = response[""] as? String {
print(message.description)
} else {
print(error?.localizedDescription ?? "Failed to get response")
}
}
} catch {
/// Error handling
print(error.localizedDescription)
}
}
}
#Write to Contract
import MagicSDK
import MagicSDK_Web3
class MagicViewController: UIViewController {
let web3 = Web3(provider: Magic.shared.rpcProvider)
var account: EthereumAddress?
// ⭐️ After user is successfully authenticated
func writeMessage() {
guard let account = self.account else { return }
do {
/// contract instance
let contractABI = """
[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
""".data(using: .utf8)!
let contract = try web3.eth.Contract(json: contractABI, abiKey: nil, address: EthereumAddress(ethereumValue: "0x8b211dfebf490a648f6de859dfbed61fa22f35e0"))
/// contract call
guard let transaction = contract["update"]?("NEW_MESSAGE").createTransaction(
nonce: 0,
from: account,
value: 0,
gas: EthereumQuantity(150000),
gasPrice: EthereumQuantity(quantity: 21.gwei)
) else { return }
web3.eth.sendTransaction(transaction: transaction).done({ txHash in
print(txHash.hex())
}).catch{ error in
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
}
}