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.
01import 'package:magic_sdk/magic_sdk.dart';
02
03void main() {
04 runApp(const MyApp());
05
06 Magic.instance = Magic("YOUR_PUBLISHABLE_KEY");
07}
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
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: 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.
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}
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.
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}
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
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
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)
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)
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
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.
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}
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
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
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
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"]));