How to Integrate with the Ethereum Blockchain using Flutter

How to Integrate with the Ethereum Blockchain using Flutter

#Installation

To interact with the Ethereum blockchain, Magic Flutter SDK embeds web3dart as sub dependency. No more extra dependency is needed.

#Initialization

The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic publishable key.

Dart
01import 'package:magic_sdk/magic_sdk.dart';
02
03void main() {
04  runApp(const MyApp());
05
06  Magic.instance = Magic("YOUR_PUBLISHABLE_KEY");
07}
Dart
01import 'package:magic_sdk/magic_sdk.dart';
02import 'package:magic_sdk/modules/web3/magic_credential.dart';
03import 'package:web3dart/web3dart.dart';
04
05class Web3Page extends StatefulWidget {
06  const Web3Page({Key? key}) : super(key: key);
07
08  
09  State<Web3Page> createState() => _Web3PageState();
10}
11
12class _Web3PageState extends State<Web3Page> {
13  final magic = Magic.instance;
14
15  final client = Web3Client.custom(Magic.instance.provider);
16  final credential = MagicCredential(Magic.instance.provider);
17
18  
19  Widget build(BuildContext context) {
20    //Your page
21  }
22}

#Use Different Networks

#Testnet

Goerli Block Explorer: https://goerli.etherscan.io

Goerli Testnet Faucet: https://goerlifaucet.com

Dart
01import 'package:magic_sdk/magic_sdk.dart';
02import 'package:magic_sdk/modules/web3/eth_network.dart';
03
04void main() {
05  runApp(const MyApp());
06
07  Magic.instance = Magic.eth("YOUR_PUBLISHABLE_KEY", network: EthNetwork.goerli);
08}

#Custom Node

You can allow specific URLs to interact with the Magic SDK, such as a custom RPC URL to send transactions to your node. The Content Security Policy (CSP) of a browser dictates what resources can be loaded. If you're used a dedicated wallet, you can update the policy in the settings page of the dashboard with your custom URL. If you're using a universal wallet, please reach out to support to get your URL added.

note

Note: the use of a custom node will require the RPC URL to the project's Content Security Policy from your Magic dashboard. Refer to the CSP documentation.

Dart
01import 'package:magic_sdk/magic_sdk.dart';
02
03void main() {
04  runApp(const MyApp());
05
06  Magic.instance = Magic.custom("YOUR_PUBLISHABLE_KEY", rpcUrl: "https://your.own.node", chainId: 1011); // your own node
07}
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 the mobile device / simulator

#Magic Credential

web3dart supports custodial design where the library will ask for user's private key to sign the payloads.

To integrate web3dart in non-custodial, We recommend you to use MagicCredential to optimize security.

Dart
01class MagicCredential {
02
03  Future<EthereumAddress> getAccount()
04  Future<String> personalSign({required Uint8List payload})
05  Future<String> ethSign({required Uint8List payload})
06  Future<String> signTypedDataLegacy({required Map payload})
07  Future<String> signTypedData({required Map payload})
08  Future<String> sendTransaction(Transaction transaction)
09}
important

You will need to call MagicCredential getAccount() before other signing functions to get the public address that will be used from the current authenticated user.

#Common Methods

#Send Transaction

Dart
01import 'package:flutter/cupertino.dart';
02import 'package:flutter/material.dart';
03import 'package:magic_sdk/magic_sdk.dart';
04import 'package:magic_sdk/modules/web3/magic_credential.dart';
05
06class _Web3PageState extends State<Web3Page> {
07  final magic = Magic.instance;
08
09  final credential = MagicCredential(Magic.instance.provider);
10  final client = Web3Client.custom(Magic.instance.provider);
11
12  
13  Widget build(BuildContext context) {
14    return Scaffold(
15        body: Center(
16            child:
17            Column(mainAxisAlignment: MainAxisAlignment.center, children: [
18
19              /// send Transaction
20                ElevatedButton(
21                  onPressed: () async {
22                    var hash = await client.sendTransaction(
23                      credential,
24                      Transaction(
25                        to: EthereumAddress.fromHex(
26                            '0x01568bf1c1699bb9d58fac67f3a487b28ab4ab2d'),
27                        gasPrice: EtherAmount.inWei(BigInt.two),
28                        maxGas: 100000,
29                        value: EtherAmount.fromUnitAndValue(EtherUnit.gwei, 2),
30                      ),
31                    );
32                    debugPrint("transaction, $hash");
33                  },
34                  child: const Text('sendTransaction'),
35                ),
36            ])));
37  }
38}

#Sign Message

Magic Credential offers non-custodial signing apis

#Eth Sign

Dart
01import 'package:flutter/cupertino.dart';
02import 'package:flutter/material.dart';
03import 'package:magic_sdk/magic_sdk.dart';
04import 'package:magic_sdk/modules/web3/magic_credential.dart';
05
06class _Web3PageState extends State<Web3Page> {
07  final magic = Magic.instance;
08
09  final credential = MagicCredential(Magic.instance.provider);
10
11  
12  Widget build(BuildContext context) {
13    return Scaffold(
14        body: Center(
15            child:
16            Column(mainAxisAlignment: MainAxisAlignment.center, children: [
17
18              /// eth sign
19              ElevatedButton(
20                onPressed: () async {
21                  List<int> list = utf8.encode("hello world");
22                  Uint8List payload = Uint8List.fromList(list);
23                  var signature = await credential.ethSign(payload: payload);
24                  debugPrint(context, "eth_sign signature, $signature");
25                },
26                child: const Text('eth sign'),
27              ),
28            ])));
29  }
30}

#Sign Typed Data Legacy (V1)

Dart
01import 'package:flutter/cupertino.dart';
02import 'package:flutter/material.dart';
03import 'package:magic_sdk/magic_sdk.dart';
04import 'package:magic_sdk/modules/web3/magic_credential.dart';
05
06class _Web3PageState extends State<Web3Page> {
07  final magic = Magic.instance;
08
09  final credential = MagicCredential(Magic.instance.provider);
10
11  
12  Widget build(BuildContext context) {
13    return Scaffold(
14        body: Center(
15            child:
16            Column(mainAxisAlignment: MainAxisAlignment.center, children: [
17
18              /// Sign Typed Data V1
19              ElevatedButton(
20                onPressed: () async {
21                  var payload = {
22                    "type": "string",
23                    "name": "Hello from Magic Labs",
24                    "value": "This message will be assigned by you"
25                  };
26                  var signature = await credential.signTypedDataLegacy(payload: payload);
27                  debugPrint(context, "sign Typed Data V1 signature, $signature");
28                },
29                child: const Text('sign Typed Data V1'),
30              ),
31            ])));
32  }
33}

#Sign Typed Data (EIP 712)

Dart
01import 'package:flutter/cupertino.dart';
02import 'package:flutter/material.dart';
03import 'package:magic_sdk/magic_sdk.dart';
04import 'package:magic_sdk/modules/web3/magic_credential.dart';
05
06class _Web3PageState extends State<Web3Page> {
07  final magic = Magic.instance;
08
09  final credential = MagicCredential(Magic.instance.provider);
10
11  
12  Widget build(BuildContext context) {
13    return Scaffold(
14        body: Center(
15            child:
16            Column(mainAxisAlignment: MainAxisAlignment.center, children: [
17
18              /// Sign Typed Data V3
19              ElevatedButton(
20                onPressed: () async {
21                  var signature =
22                  await credential.signTypedData(payload: signTypedDataV3Payload);
23                  showResult(context, "sign Typed Data V3 signature, ${(signature)}");
24                },
25                child: const Text('sign Typed Data V3'),
26              ),
27            ])));
28  }
29}
30
31var signTypedDataV3Payload = {
32  "types": {
33    "EIP712Domain": [
34      {"name": "name", "type": "string"},
35      {"name": "version", "type": "string"},
36      {"name": "verifyingContract", "type": "address"}
37    ],
38    "Order": [
39      {"name": "makerAddress", "type": "address"},
40      {"name": "takerAddress", "type": "address"},
41      {"name": "feeRecipientAddress", "type": "address"},
42      {"name": "senderAddress", "type": "address"},
43      {"name": "makerAssetAmount", "type": "uint256"},
44      {"name": "takerAssetAmount", "type": "uint256"},
45      {"name": "makerFee", "type": "uint256"},
46      {"name": "takerFee", "type": "uint256"},
47      {"name": "expirationTimeSeconds", "type": "uint256"},
48      {"name": "salt", "type": "uint256"},
49      {"name": "makerAssetData", "type": "bytes"},
50      {"name": "takerAssetData", "type": "bytes"}
51    ]
52  },
53  "domain": {
54    "name": "0x Protocol",
55    "version": "2",
56    "verifyingContract": "0x35dd2932454449b14cee11a94d3674a936d5d7b2"
57  },
58  "message": {
59    "exchangeAddress": "0x35dd2932454449b14cee11a94d3674a936d5d7b2",
60    "senderAddress": "0x0000000000000000000000000000000000000000",
61    "makerAddress": "0x338be8514c1397e8f3806054e088b2daf1071fcd",
62    "takerAddress": "0x0000000000000000000000000000000000000000",
63    "makerFee": "0",
64    "takerFee": "0",
65    "makerAssetAmount": "97500000000000",
66    "takerAssetAmount": "15000000000000000",
67    "makerAssetData":
68    "0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c",
69    "takerAssetData":
70    "0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570",
71    "salt": "1553722433685",
72    "feeRecipientAddress": "0xa258b39954cef5cb142fd567a46cddb31a670124",
73    "expirationTimeSeconds": "1553808833"
74  },
75  "primaryType": "Order"
76};

#Get User Info

Dart
01import 'package:flutter/cupertino.dart';
02import 'package:flutter/material.dart';
03import 'package:magic_sdk/magic_sdk.dart';
04import 'package:magic_sdk/modules/web3/magic_credential.dart';
05
06class _Web3PageState extends State<Web3Page> {
07  final magic = Magic.instance;
08
09  final credential = MagicCredential(Magic.instance.provider);
10
11  
12  Widget build(BuildContext context) {
13    return Scaffold(
14        body: Center(
15            child:
16            Column(mainAxisAlignment: MainAxisAlignment.center, children: [
17              /// get account
18              ElevatedButton(
19                onPressed: () async {
20                  var cred = await credential.getAccount();
21                  debugPrint("account, ${cred.hex}");
22                },
23                child: const Text('getAccount'),
24              ),
25            ])));
26  }
27}

#Smart Contract

In this example, we'll be demonstrating how to use Magic with web3dart to interact with Solidity smart contracts. The simple Hello World contract allows anyone to read and write a message to it.

Javascript
01pragma solidity ^0.5.10;
02
03contract HelloWorld {
04
05  string public message;
06
07  constructor(string memory initMessage) public {
08    message = initMessage;
09  }
10
11  function update(string memory newMessage) public {
12    message = newMessage;
13  }
14}
Dart
01class TestContract {
02  static final deployedAddress =
03  EthereumAddress.fromHex("0x8b211dfebf490a648f6de859dfbed61fa22f35e0");
04  static 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"}]';
05  static const byteCode =
06      "0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032";
07}

#Deploy Contract

Dart
01final credential = MagicCredential(Magic.instance.provider);
02final client = Web3Client.custom(Magic.instance.provider);
03
04var list = utf8.encode(TestContract.byteCode);
05Uint8List payload = Uint8List.fromList(list);
06final Transaction transaction = Transaction(
07                to: null,
08                from: credential.address,
09                data: payload,
10                maxGas: 2000000);
11final String transactionId =
12                await client.sendTransaction(credential, transaction);

#Read from Contract

Dart
01final credential = MagicCredential(Magic.instance.provider);
02final client = Web3Client.custom(Magic.instance.provider);
03
04final contract = DeployedContract(
05    ContractAbi.fromJson(TestContract.contractAbi, ''),
06    TestContract.deployedAddress);
07final messageFunction = contract.function('message');
08var message = await client.call(
09contract: contract, function: messageFunction, params: []);
10debugPrint("Contract Read Message, $message");
11},

#Write to Contract

Dart
01final credential = MagicCredential(Magic.instance.provider);
02final client = Web3Client.custom(Magic.instance.provider);
03
04final contract = DeployedContract(
05                ContractAbi.fromJson(TestContract.contractAbi, ''),
06                TestContract.deployedAddress);
07final updateFunction = contract.function('update');
08var transactionId = await client.sendTransaction(
09                credential,
10                Transaction.callContract(
11                    contract: contract,
12                    function: updateFunction,
13                    parameters: ["NEW_MESSAGE"]));

#Resources