How to Integrate with the Ethereum Blockchain with Magic

How to Integrate with the Ethereum Blockchain with Magic

JavaScript

Installation

To interact with the Ethereum blockchain, you can use either ethers.js or web3.js libraries with Magic.

note

If you are already familiar with Ethereum application development, you can skip straight to our kitchen sink examples:

👉 Ethers.js Example
👉 Web3.js Example

Ethers.js

NPM

npm install --save ethers

Yarn

yarn add ethers

CDN

<script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"></script>

Web3.js

NPM

npm install --save web3

Yarn

yarn add web3

CDN

<script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>

Initializing Provider

important

Ethereum provider is only supported in magic-sdk@1.0.1 or later versions.

Ethers.js

ES Modules/TypeScript

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; // Test key defaults to "rinkeby", live key defaults to "mainnet" const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider);

CommonJS

const { Magic } = require('magic-sdk'); const ethers = require('ethers'); // Test key defaults to "rinkeby", live key defaults to "mainnet" const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider);

Web3.js

ES Modules/TypeScript

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; // Test key defaults to "rinkeby", live key defaults to "mainnet" const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // Or window.web3 = ...

CommonJS

const { Magic } = require('magic-sdk'); const Web3 = require('web3'); // Test key defaults to "rinkeby", live key defaults to "mainnet" const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // Or window.web3 = ...

Use Different Networks

Choose Different Testnet

const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: 'ropsten', // Supports "rinkeby", "ropsten", "kovan" });

Configure Custom Nodes

const customNodeOptions = { rpcUrl: 'http://127.0.0.1:7545', // Your own node URL chainId: 1011, // Your own node's chainId }; // Setting network to localhost blockchain const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions, });

Get User Info

Ethers.js

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); // Get user's Ethereum public address const address = await signer.getAddress(); // Get user's balance in ether const balance = ethers.utils.formatEther( await provider.getBalance(userAddress), // Balance is in wei );

Web3.js

note

Example is using web3@1.2.0 or later version.

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const address = (await web3.eth.getAccounts())[0]; // Get user's balance in ether const balance = web3.utils.fromWei( await web3.eth.getBalance(address), // Balance is in wei );

Send Transaction

Ethers.js

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const destination = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D'; const amount = ethers.utils.parseEther('1.0'); // Convert 1 ether to wei // Submit transaction to the blockchain const tx = await signer.sendTransaction({ to: destination, value: amount, }); // Wait for transaction to be mined const receipt = await tx.wait();

Web3.js

note

Example is using web3@1.2.0 or later version.

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const destination = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D'; const amount = web3.utils.toWei(1); // Convert 1 ether to wei // Submit transaction to the blockchain and wait for it to be mined const receipt = await web3.eth.sendTransaction({ from: fromAddress, to: destination, value: amount, });

Sign Message

Ethers.js

Personal Sign

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const originalMessage = 'YOUR_MESSAGE'; const signedMessage = await signer.signMessage(originalMessage);

Sign Typed Data v1

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = [ { type: 'string', name: 'fullName', value: 'John Doe', }, { type: 'uint32', name: 'userId', value: '1234', }, ]; const params = [originalMessage, fromAddress]; const method = 'eth_signTypedData'; const signedMessage = await signer.provider.send(method, params);

Sign Typed Data v3

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { 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!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v3'; const signedMessage = await signer.provider.send(method, params);

Sign Typed Data v4

/* Sign Typed Data v4 adds support for arrays and recursive data types. Otherwise, it works the same as Sign Typed Data v3. */ import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { 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!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v4'; const signedMessage = await signer.provider.send(method, params);

Web3.js

note

The following examples assume web3@>=1.2.0

Personal Sign

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const originalMessage = 'YOUR_MESSAGE'; const signedMessage = await web3.eth.personal.sign(originalMessage, fromAddress);

Sign Typed Data v1

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const originalMessage = [ { type: 'string', name: 'fullName', value: 'John Doe', }, { type: 'uint32', name: 'userId', value: '1234', }, ]; const params = [originalMessage, fromAddress]; const method = 'eth_signTypedData'; const signedMessage = await web3.currentProvider.sendAsync({ id: 1, method, params, fromAddress, });

Sign Typed Data v3

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const originalMessage = { 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!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v3'; const signedMessage = await web3.currentProvider.sendAsync({ id: 1, method, params, fromAddress, });

Sign Typed Data v4

/* Sign Typed Data v4 adds support for arrays and recursive data types. Otherwise, it works the same as Sign Typed Data v3. */ import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const originalMessage = { 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!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v4'; const signedMessage = await web3.currentProvider.sendAsync({ id: 1, method, params, fromAddress, });

Smart Contract

Solidity Contract

In this example, we'll be demonstrating how to use Magic with ethers.js or web3.js 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; } }

Ethers.js

Deploy Contract

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const 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"}]'; const contractByteCode = '0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032'; const contractFactory = new ethers.ContractFactory(contractABI, contractByteCode, signer); // Deploy contract with "Hello World!" in the constructor const contract = await contractFactory.deploy('Hello World!'); // Wait for deployment to finish const receipt = await contract.deployed();

Read From Contract

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const 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"}]'; const contractAddress = '0x8b211dfebf490a648f6de859dfbed61fa22f35e0'; const contract = new ethers.Contract(contractAddress, contractABI, signer); // Read message from smart contract const message = await contract.message();

Write to Contract

import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const 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"}]'; const contractAddress = '0x8b211dfebf490a648f6de859dfbed61fa22f35e0'; const contract = new ethers.Contract(contractAddress, contractABI, signer); // Send transaction to smart contract to update message const tx = await contract.update('NEW_MESSAGE'); // Wait for transaction to finish const receipt = await tx.wait();

Web3.js

note

Example is using web3@1.2.0 or later version.

Deploy Contract

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const 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"}]'; const contractByteCode = '0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032'; const contract = new web3.eth.Contract(JSON.parse(contractABI)); // Deploy contract with "Hello World!" in the constructor and wait to finish const contractInstance = await contract .deploy({ data: contractByteCode, arguments: ['Hello World!'], }) .send({ from: fromAddress, });

Read From Contract

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const 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"}]'; const contractAddress = '0x8b211dfebf490a648f6de859dfbed61fa22f35e0'; const contract = new web3.eth.Contract(JSON.parse(contractABI), contractAddress); // Read message from smart contract const message = await contract.methods.message().call();

Write to Contract

import { Magic } from 'magic-sdk'; import Web3 from 'web3'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const web3 = new Web3(magic.rpcProvider); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = (await web3.eth.getAccounts())[0]; const 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"}]'; const contractAddress = '0x8b211dfebf490a648f6de859dfbed61fa22f35e0'; const contract = new web3.eth.Contract(JSON.parse(contractABI), contractAddress); // Send transaction to smart contract to update message and wait to finish const receipt = await contract.methods.update('NEW_MESSAGE').send({ from: fromAddress });

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

note

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 // Test key defaults to "rinkeby", live key defaults to "mainnet" 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 }
important

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 URL
  • chainId : 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) } } }

Android

Installation

To interact with the Ethereum blockchain, Magic Android SDK integrates Web3j as sub dependency.

Add the following dependencies in build.gradle

dependencies { implementation 'link.magic:magic-android:0.3.0' implementation 'org.web3j:core:4.6.0-android' // Not required implementation 'org.web3j:geth:4.6.0-android' // Only for personal Sign }

Initializing Provider

note

The following example is using Kotlin 1.3. Android demo will be open-sourced soon. You may use Android Studio to convert Java to Kotlin or vice versa.

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") web3j = Web3j.build(magic.rpcProvider) gethWeb3j = Geth.build(magic.rpcProvider) } }

Use Different Networks

Choose Different Testnet

magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY", Magic.Network.Mainnet)

Configure Custom Nodes

magic = Magic(this, "##YOUR_PUBLISHABLE_API_KEY", CustomNodeConfiguration("https://alchemy.io"))
important

Do not set the custom nodes to local IP address (E.x. "http://127.0.0.1"\), because local IP will point to the network environment inside mobile device / simulator. Try accessible IP address in the same Wifi/Internet Environment (E.x. "http://10.0.0.93:3000"\)

Associated Class

CustomNodeConfiguration(rpcUrl: String, chainId: Int?)

  • rpcUrl :Your own node URL
  • chainId : Your own node's chainId

Magic.EthNetwork

enum class EthNetwork { Mainnet, Kovan, Rinkeby, Ropsten }

Get User Info

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") web3j = Web3j.build(magic.rpcProvider) } // ⭐️ After user is successfully authenticated fun getAccount(){ try { val accounts = web3j.ethAccounts().sendAsync() accounts.whenComplete { accRepsonse: EthAccounts?, error: Throwable? -> if (error != null) { Log.e("MagicError", error.localizedMessage) } if (accRepsonse != null && !accRepsonse.hasError()) { account = accRepsonse.accounts[0] Log.d("Magic", "Your address is $account") } } } catch (e: Exception) { Log.e("Error", e.localizedMessage) } } }

Send Transaction

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") web3j = Web3j.build(magic.rpcProvider) } // ⭐️ After user is successfully authenticated fun sendTransaction(v: View) { try { val value: BigInteger = Convert.toWei("0.5", Convert.Unit.ETHER).toBigInteger() val transaction = createEtherTransaction(account, BigInteger("1"), BigInteger("21000"), BigInteger("21000"), account, value) val receipt = web3j.ethSendTransaction(transaction).send() Log.d("Transaction complete: " + receipt.transactionHash) } catch (e: Exception) { Log.e("Error", e.localizedMessage) } } }

Sign Message

Magic Android SDK extends the functionality from Web3j to allow developers to sign Typed Data. You may find it in magic.web3jSigExt

Personal Sign

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // ⭐️ After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") web3j = Web3j.build(magic.rpcProvider) gethWeb3j = Geth.build(magic.rpcProvider) } fun personSign(view: View) { val message = "Hello from Magic!!!" val personalSign: PersonalSign = gethWeb3j.personalSign( message, account, "password") .send() Log.d("Magic", "Signed Message: " + personalSign.signedMessage) // Recover Message val recovered = gethWeb3j.personalEcRecover(message, personalSign.signedMessage).send() Log.d("Magic", "Recovered Address: " + recovered.recoverAccountId) } }

Sign TypedData Legacy (V1)

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // ⭐️ After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") } // Sign with EIP712 Data Field fun signTypedDataLegacy(v: View) { val list = listOf( EIP712TypedDataLegacyFields("string", "Hello from Magic", "This message will be signed by you"), EIP712TypedDataLegacyFields("uint32", "Here is a number", "90210") ) val signature = magic.web3jSigExt.signTypedDataLegacy(account, list).send() Log.d("Magic", signature.result) } // Sign with JSON String fun signTypedDataLegacyJson(v: View) { val jsonString = "[{\"type\":\"string\",\"name\":\"Hello from Magic\",\"value\":\"This message will be signed by you\"},{\"type\":\"uint32\",\"name\":\"Here is a number\",\"value\":\"90210\"}]" val signature = magic.web3jSigExt.signTypedDataLegacy(account, jsonString).send() Log.d("Magic", signature.result) } }

Sign Typed Data v3

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // ⭐️ After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") } fun signTypedData(v: View) { val jsonString = "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Order\":[{\"name\":\"makerAddress\",\"type\":\"address\"},{\"name\":\"takerAddress\",\"type\":\"address\"},{\"name\":\"feeRecipientAddress\",\"type\":\"address\"},{\"name\":\"senderAddress\",\"type\":\"address\"},{\"name\":\"makerAssetAmount\",\"type\":\"uint256\"},{\"name\":\"takerAssetAmount\",\"type\":\"uint256\"},{\"name\":\"makerFee\",\"type\":\"uint256\"},{\"name\":\"takerFee\",\"type\":\"uint256\"},{\"name\":\"expirationTimeSeconds\",\"type\":\"uint256\"},{\"name\":\"salt\",\"type\":\"uint256\"},{\"name\":\"makerAssetData\",\"type\":\"bytes\"},{\"name\":\"takerAssetData\",\"type\":\"bytes\"}]},\"domain\":{\"name\":\"0x Protocol\",\"version\":\"2\",\"verifyingContract\":\"0x35dd2932454449b14cee11a94d3674a936d5d7b2\"},\"message\":{\"exchangeAddress\":\"0x35dd2932454449b14cee11a94d3674a936d5d7b2\",\"senderAddress\":\"0x0000000000000000000000000000000000000000\",\"makerAddress\":\"0x338be8514c1397e8f3806054e088b2daf1071fcd\",\"takerAddress\":\"0x0000000000000000000000000000000000000000\",\"makerFee\":\"0\",\"takerFee\":\"0\",\"makerAssetAmount\":\"97500000000000\",\"takerAssetAmount\":\"15000000000000000\",\"makerAssetData\":\"0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c\",\"takerAssetData\":\"0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570\",\"salt\":\"1553722433685\",\"feeRecipientAddress\":\"0xa258b39954cef5cb142fd567a46cddb31a670124\",\"expirationTimeSeconds\":\"1553808833\"},\"primaryType\":\"Order\"}" val signature = magic.web3jSigExt.signTypedData(account, jsonString).send() Log.d("Magic", "Signature: " + signature.result) } }

Sign Typed Data v4

class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // ⭐️ After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") } fun signTypedDataV4(v: View) { val jsonString = "{\"domain\":{\"chainId\":1,\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}]},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}" val signature = magic.web3jSigExt.signTypedDataV4(account, jsonString).send() Log.d("Magic", "Signature: " + signature.result) } }

Smart Contract

Solidity Contract

In this example, we'll be demonstrating how to use Magic with Web3j 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; } }

Create a Kotlin/Java Contract Class from ABI

Web3j supports the auto-generation of smart contract function wrappers in Java from Solidity ABI files.

To get started, you must have two files

  • ABI JSON file <Contract>.json
  • ByteCode file <Contract>.bin

Install web3j cli-tool

$ curl -L https://get.web3j.io | sh

You may need to install a JDK to support this library

After it has been installed to your computer, you may run the following command to check

$ web3j version

Create the contract class

$ web3j solidity generate -a=./path/to/<Contract>.json -b=./path/to/<Contract>.bin -o=/output/path/ -p={packageName}

You’ll find a Contract.java file created in your output directory above. Put this file in your project, and no more changes are needed.

For more detail about this section. Please check the following link
https://web3j.readthedocs.io/en/latest/smart_contracts.html#solidity-smart-contract-wrappers

Contract Functions

When deploying contract or building contract using web3j library, Magic offers MagicTxnManager class as a default TransactionManager that helps you to avoid dealing with private keys or credentials that Contract class requires.

Deploy Contract

import link.magic.demo.contract.Contract // This is the contract class you created above class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j // ⭐️ After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY") web3j = Web3j.build(magic.rpcProvider) } fun deployContract(view: View) { try { val price = BigInteger.valueOf(22000000000L) val limit = BigInteger.valueOf(4300000) val gasProvider = StaticGasProvider(price, limit) val contract = Contract.deploy( web3j, account?.let { MagicTxnManager(web3j, it) }, gasProvider, "HELLO_WORLD_FROM_ANDROID" ).send() Log.d("Magic", "Deploy to" + contract.contractAddress) } catch (e: Exception) { Log.e("E", "error", e) } } }

Read From Contract

fun contractRead(view: View) { try { val price = BigInteger.valueOf(22000000000L) val limit = BigInteger.valueOf(4300000) val gasProvider = StaticGasProvider(price, limit) // Contract in Rinkeby testnet val contract = ExampleContract.load("0x6a2d321a3679b1b3c8a19b84e41abd11763a8ab5", web3j, account?.let { MagicTxnManager(web3j, it) }, gasProvider) if (contract.isValid) { val ethCall = contract.message().send() Log.d("Magic", ethCall.toString()) } else { throw Error("contract not valid") } } catch (e: Exception) { Log.e("E", "error", e) } }

Write to Contract

fun contractWrite(view: View) { try { val price = BigInteger.valueOf(22000000000L) val limit = BigInteger.valueOf(4300000) val gasProvider = StaticGasProvider(price, limit) // Contract in Rinkeby testnet val contract = ExampleContract.load("0x6a2d321a3679b1b3c8a19b84e41abd11763a8ab5", web3j, account?.let { MagicTxnManager(web3j, it) }, gasProvider) if (contract.isValid) { val ethCall = contract.update("NEW_MESSAGE_FROM_ANDROID").send() Log.d("Magic", ethCall.toString()) } else { throw Error("contract not valid") } } catch (e: Exception) { Log.e("E", "error", e) } }