flutter web3主链,合约操作

引用的依赖

import 'package:web3dart/web3dart.dart';
import 'dart:math';
import 'package:http/http.dart';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;
const String contarctChainaddress =
    '0x1daEAaa139f801dA23153a381B78ec68D1551071'; //跨链合约地址
const String rpcUrl = 'https://mainnet.infura.io/v3/'; //默认rpc节点,这个需要自己更改,当前为以太坊主网
const int gcdecimals = 18;

首先初始化web3,创建client

 static Wbe3Api wbe3api;
 static Web3Client client;
 static DeployedContract contract;
 static String _contractAddress;
 static int decimals;
//使用单例模式,
Future<Wbe3Api> getInstances() async {
    try {
      if (wbe3api == null) {
        wbe3api = Wbe3Api();
      }
      if (client == null) {
        client = new Web3Client(rpcUrl, Client());
      }
      return wbe3api;
    } catch (error) {
      return null;
    }
  }

主链币种操作:

1.获取主链的余额,这里面要用到主链的精度,也就是小数位

 //获取主链余额
  Future<String> getBalance(String address) async {
    try {
      EtherAmount amount =
          await client.getBalance(EthereumAddress.fromHex(address));
      BigInt available = amount.getInWei;
      String blance = (available / BigInt.from(pow(10, gcdecimals))).toString();
      print("=====" + blance);
      await client.dispose();
      return formatFour(blance);
    } catch (err) {
      print(' 余额错误: ${err.toString()}');
      return formatFour('0.00');
    }
  }
//格式化小数点
String formatFour(String values) {
    double value = double.tryParse(values) / pow(10, gcdecimals);
    String newvalue = value.toStringAsFixed(8);
    return newvalue.substring(0, newvalue.indexOf('.') + 7);
  }

2.主链的交易
首先获取矿工费

 //获取主链矿工费,如果矿工费给的不够高,那就无法交易
  Future<String> getdefaultEthfee() async {
    EtherAmount gasprice = await client.getGasPrice();
    print("==" + _tofee(BigInt.from(21000), gasprice));
    return _tofee(BigInt.from(21000), gasprice);
  }
  /**
   * 获取手续费
   * gaslimit :最小gas,合约需要算出来,主链币则默认为21000
   * gasprice:gas价格
   */
  String _tofee(BigInt gaslimit, EtherAmount gasprice) {
    var fee = gaslimit * gasprice.getInWei;
    var result = fee / BigInt.from(pow(10, gcdecimals));
    return result.toString();
  }

之后发起交易

  //判断以太坊地址是否正确
  Future<bool> getIsGCAddress(String maddress) async {
    try {
      EthereumAddress address = EthereumAddress.fromHex(maddress);
      return true;
    } catch (e) {
      return false;
    }
  }

  /**
   * 发起普通交易
   * fromaddress 发送地址
   * toaddress  接收地址
   * privatekey 私钥
   * fee  手续费
   * value  数量
   */
  Future<String> signETHTransaction(String fromaddress, String toaddress,
      String privatekey, String fee, String value) async {
    try {
      final credentials = EthPrivateKey.fromHex(privatekey);
      EthereumAddress from = EthereumAddress.fromHex(fromaddress);
      final receiver = EthereumAddress.fromHex(toaddress);
      EtherAmount gasprice = await client.getGasPrice();
      final networkId = await client.getNetworkId();
      BigInt amount = tokenInt(value, gcdecimals);
      BigInt gaslimit = BigInt.from(double.parse(fee) * pow(10, 9));
      print("gaslimit ====" + gaslimit.toString());
      print("GCstart ====");
      var transaction = Transaction(
        to: receiver,
        gasPrice: gasprice,
        maxGas: gaslimit.toInt(),
        value: EtherAmount.fromUnitAndValue(EtherUnit.wei, amount),
      );
      var txHash = await client.sendTransaction(
        credentials,
        transaction,
        chainId: networkId,
      );
      print('transferhash====' + txHash);
      await client.dispose();
      return txHash;
    } catch (error) {
      return error;
    }
  }

 /**
   * 通过精度格式化 传入的数量
   * value 数量
   * decimals 精度(保留小数位)
   */
  BigInt tokenInt(String value, int decimals) {
    if (value == null) {
      return BigInt.zero;
    }
    double v = 0;
    try {
      if (value.contains(',') || value.contains('.')) {
        v = NumberFormat(",##0.${"0" * decimals}").parse(value);
      } else {
        v = double.parse(value);
      }
    } catch (err) {
      print('Fmt.tokenInt() error: ${err.toString()}');
    }
    return BigInt.from(v * pow(10, decimals));
  }

合约查询和操作:
1.判断合约地址

 /**
   * 判断是否合约地址 
   * contractaddress 合约地址
   */
  Future<bool> getIsContractAddress(String contractaddress) async {
    if (contractaddress.length != 42) {
      return false;
    } else {
      EthereumAddress address = EthereumAddress.fromHex(contractaddress);
      var respons = await client.getCode(address);
      print("respons ====" + respons.toString());
      return respons.length > 0 ? true : false;
    }
  }

2.读取合约abi

 /**
   * 设置全局合约,读取abi
   */
  setContaract(String contractaddress) async {
    _contractAddress = contractaddress;
    contract = await fromAssets(
        'images/contract.json', _contractAddress);
    if (contract != null) {
      var result = await getContractInfo('decimals');
      decimals = int.parse(result.toString());
      print("==精度获取==" + decimals.toString());
    }
  }

  /**
   * 将合约格式化
   */
   Future<DeployedContract> fromAssets(
      String path, String contractAddress) async {
    final contractJson =
        jsonDecode(await rootBundle.loadString(path));
    return DeployedContract(ContractAbi.fromJson(jsonEncode(contractJson['abi']),contractJson['contractName'] as String),
        EthereumAddress.fromHex(contractAddress));
  }

abi为json,需要在yaml文件中读取

 assets:
    - images/home/
    - images/assets/
    - images/contract.json

abi为格式

{
  "contractName": "TargaryenCoin",
  "abi": [
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "owner",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "spender",
          "type": "address"
        }
      ],
      "name": "allowance",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "decimals",
      "outputs": [
        {
          "internalType": "uint8",
          "name": "",
          "type": "uint8"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "name",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
   
    {
      "inputs": [],
      "name": "symbol",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "to",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "value",
          "type": "uint256"
        }
      ],
      "name": "transfer",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
  
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        }
      ],
      "name": "exchange",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "withdraw",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
 {
      "inputs": [
        {
          "internalType": "address",
          "name": "owner",
          "type": "address"
        }
      ],
      "name": "balanceOf",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    }
   
  ]
}

3.获取合约基本参数

 /**
   * 读取合约基本信息decimals和symbol
   */
  Future<Map> adppContaractDecimals(String contractaddress) async {
    int dappDecimals = 18;
    // contract = await fromAssets(
    //     'images/contract.json', contractaddress);
    try {
      var dec = await getContractInfo('decimals');
      var type = await getContractInfo('symbol');
      dappDecimals = int.parse(dec.toString());
      print("adpp精度获取==" + dappDecimals.toString());
      return {'decimals': dappDecimals, 'coin': type};
    } catch (error) {
      return {'decimals': dappDecimals, 'coin': "ETH"};
    }
  }

/**
   * 获取合约基本信息
   * functionname 方法名
   * decimals:合约精度,小数位
   * name 名字
   * symbol  和名字一致
   */
  Future<String> getContractInfo(String functionname) async {
    try {
      final response = await client.call(
        contract: contract,
        function: contract.function(functionname),
        params: [],
      );
      print(response.first.toString());
      return response.first.toString();
    } catch (error) {
      print(error);
      return error.toString();
    }
  }

4.获取合约余额,切记只能执行abi里面的方法

//获取合约余额
  Future<String> getTokenBalance(String madress) async {
    EthereumAddress adress = EthereumAddress.fromHex(madress);
    final response = await client.call(
      contract: contract,
      function: contract.function('balanceOf'),
      params: [adress],
    );
    print(response.toString());
    String blance =
        (response.first / BigInt.from(pow(10, decimals))).toString();
    print('====' + blance);
    return formatFour(blance);
  }

5.合约交易
(1)矿工费,合约的任何交易都要手续费

 //转出手续费
  Future<String> getTransferFee(
      String fromaddress, String toaddress, String value) async {
    BigInt amount = tokenInt(value, decimals);
    EthereumAddress receiver = EthereumAddress.fromHex(toaddress);
    return await getCommonFee(fromaddress, "transfer", [receiver, amount]);
  }

  //兑换手续费
  Future<void> getexchangeFee(
      String fromaddress, String toaddress, String value) async {
    BigInt amount = tokenInt(value, decimals);
    await getCommonFee(fromaddress, "exchange", [amount]);
  }

/*
   * 获取合约手续费通用方法
   * fromaddress 发送地址
   * functionname 方法名
   * parameters  合约参数
   */
  Future<String> getCommonFee(
      String from, String functionname, List<dynamic> parameters) async {
    EthereumAddress fromaddress = EthereumAddress.fromHex(from);
    final gasprice = await client.getGasPrice();
    print('gasprice' + gasprice.toString());
    var transaction = Transaction.callContract(
        contract: contract,
        function: contract.function(functionname),
        parameters: parameters,
        gasPrice: gasprice,
        from: fromaddress);
    print('检测gaslimit');
    var gaslimit = BigInt.from(21000);
    try {
      gaslimit = await client.estimateGas(
          sender: fromaddress,
          to: EthereumAddress.fromHex(_contractAddress),
          data: transaction.data,
          value: EtherAmount.zero());
      print('gaslimit ====' + gaslimit.toString());
      return _tofee(gaslimit, gasprice);
    } catch (error) {
      print(error.toString());
      return 'error' + error.toString();
    }
  }

(2)发起合约交易,转账和兑换

/**
   * 发起合约转账
   * fromaddress 发送地址
   * toaddress  接收地址
   * privatekey 私钥
   * value  数量
   */
  Future<String> tokenTransfer(String fromaddress, String toaddress,
      String privatekey, String fee, String value) async {
    BigInt amount = tokenInt(value, decimals);
    EthereumAddress receiver = EthereumAddress.fromHex(toaddress);
    return await signContractTransaction(
        fromaddress, privatekey, fee, "transfer", [receiver, amount]);
  }

  /**
   * 发起兑换交易
   * fromaddress 发送地址
   * privatekey 私钥
   * value  数量
   */
  Future<void> tokenExchange(
      String fromaddress, String privatekey, String fee, String value) async {
    BigInt amount = tokenInt(value, decimals);
    await signContractTransaction(
        fromaddress, privatekey, fee, "exchange", [amount]);
  }

/*
   * 发起合约交易
   * fromaddress 发送地址
   * privatekey 私钥
   * functionname 合約调用用方法
   * parameters  合約参數
   */
  Future<String> signContractTransaction(String from, String privatekey,
      String fee, String functionname, List<dynamic> parameters) async {
    try {
      EthereumAddress fromaddress = EthereumAddress.fromHex(from);
      final credentials = EthPrivateKey.fromHex(privatekey);
      final networkId = await client.getNetworkId();
      final gasprice = await client.getGasPrice();
      BigInt gaslimit = BigInt.from(double.parse(fee) * pow(10, 9));
      print("gaslimit ====" + gaslimit.toString());
      var transaction = Transaction.callContract(
          contract: contract,
          function: contract.function(functionname),
          parameters: parameters,
          from: fromaddress,
          gasPrice: gasprice,
          maxGas: gaslimit.toInt());
      print("开始交易");
      var txHash = await client.sendTransaction(
        credentials,
        transaction,
        chainId: networkId,
      );
      print('hash====' + txHash);
      await client.dispose();
      return txHash;
    } catch (error) {
      print(error);
      return error;
    }
  }

  /**
   * 获取交易状态
   * txHash 交易hash
   */
  Future<bool> getTranferstate(String txHash) async {
    try {
      var transactionReceipt = await client.getTransactionReceipt(txHash);
      if (transactionReceipt != null) {
        print("交易状态: " + transactionReceipt.status.toString());
        await client.dispose();
        return transactionReceipt.status;
      }
      return false;
    } catch (error) {
      print("状态error: " + error.toString());
      return false;
    }
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容