• 区块链钱包开发,第三周总结 (货币精度计算 超大数运算)


    这周主要做了 ETH钱包:(1)钱包列表展示钱包价值(2)在钱包内发起一个Transation交易 (3)获取交易详情

    当前热钱包部分, 对于以上三个功能最大的需求功能,最大阻碍是精度问题和超大数的的基本运算

    一.钱包价值展示

    参考imToken, imToken主做ETH钱包三年多,相当专业,有太多的地方值得我们学习。

    首页部分头部是钱包内主币和代币换算成实体货币数量的总价值,列表部分是当前钱包内主币和选中代币集合,展示了币的图标、名称、数量、兑换成实体货币价值。

    关键是数量这里:
    ETH 有很多单位,基本使用统计如下:

    单位Unit 以最小单位Wei为基准的进制(Wei  数量) 使用频率
    Wei 1           wei 经常
    KWei 10 * 3    wei  
    MWei 10 * 6    wei  
    GWei 10 * 9    wei 经常
    microether 10 * 12  wei  
    milliether 10 * 15  wei  
    ether 10 * 18  wei 经常

    二 需要运算的地方:

    发起一笔转账时的进制转换,和gas 费用。

      Gas limit 是用户愿意为执行某个操作或确认交易支付的最大Gas量(最少21,000) 单位个(即对应的是ETH的个数)

      计算 花费应该   gas fee = Gas Limit * Gas Price

      这个是给矿工的佣金

      第一步就是把gas price 从Gwei 转为 ETH 再 * limit

      Gas Limit*Gas Price

     eg:  1Gwei≈0.00000002 ETH,所以佣金最少为0.00000002*21000=0.00042ETH

     

    (1)用户输入转账ETH个数 单位:ether    

    和服务端约定上传一律用最小单位,这里需要做一次进制转换  即:1 ether = 10 * 18 wei

    如果交易稍大就会超过 iOS 中精度了,更别提运算了。因为有Web3 ,提供 BigDecimal方案处理超大数的精度处理,iOS 这边,完全可以自己写超大数的加减乘除,也可以选择一些成熟的第三 ,这里我使用了一个框架web3swift

           web3swift,完全支持在iOS 客户端上实施冷钱包,并发起交易的整个过程 通过  Infura 节点(测试/主网)。具体参考(1)上介绍

          所以,上一篇文章,我使用trust keystore 能完成创建钱包,导入钱包,校验钱包的操作。今天换成web3swift,主要是符合我的需求和持续化的迭代设计。

         至此,超大数,精度浮点数运算 进制转换使用swift 框架 web3swift来解决

        使用举例:

    //
    //  FIREnterUnitManager.swift
    //  AkeyWallet
    //
    //  Created by HF on 2018/7/14.
    //  Copyright © 2018年 Fir.im. All rights reserved.
    //
    
    import Foundation
    import BigInt
    import web3swift
    
    @objc public class FIREnterUnitManager:NSObject {
     
        //从ETH 转换为 Wei
        public static func getWeiBigStringFrom(ethString:String) throws -> String {
            let eth = Web3.Utils.parseToBigUInt(ethString, units: .eth);//当前是eth
            if (nil == eth) {
                return ""
            } else {
                let balString =  Web3.Utils.formatToEthereumUnits(eth!, toUnits: .wei, decimals: 0)
                return balString!
            }
        }
        //从wei 到 eth
        public static func getETHFrom(weiString:String) throws -> String {
            let weiBig = BigUInt(weiString);
            let balString =  Web3.Utils.formatToEthereumUnits(weiBig!, toUnits: .eth, decimals: 8)
            return balString!;
        }//从当前进制到wei
        public static func getWeiFromUnit(unitString:String,decimal:Int) throws -> String {
            let balance = Web3.Utils.parseToBigUInt(unitString, decimals: decimal)
            let balString =  Web3.Utils.formatToEthereumUnits(balance!, toUnits: .wei, decimals: 8)
            return balString!;
        }//wei乘法
        public static func getMultiWei(wei1:String,wei2:String) throws -> String {
            let weiBig1 = BigUInt(wei1);
            let weiBig2 = BigUInt(wei2);
            let wei = weiBig1! * weiBig2!;
            let balString =  Web3.Utils.formatToEthereumUnits(wei, toUnits: .wei, decimals: 8)
            return balString!;
        }
        //wei加法
        public func getSumWei(wei1:String,wei2:String) throws -> String {
            let weiBig1 = BigUInt(wei1);
            let weiBig2 = BigUInt(wei2);
            let wei = weiBig1! + weiBig2!;
            let balString =  Web3.Utils.formatToEthereumUnits(wei, toUnits: .wei, decimals: 8)
            return balString!;
        }}

    (2)有一些非超大数,比如一些显示,虚拟币、实体币,求和,汇率计算什么的,也可以选择上面的方法算,但是我的风格是按需处理,不是超大数,就是正常的金融数据运算,自己写了一个工具类。

          正常的浮点经度进行金融数据运算,会丢精度。苹果针对浮点型计算时存在精度计算误差的问题,提供NSDecimalNumber的计算类

          参考(2),自己完善一下边界处理

          NSDecimalNumber 有 NSRoundingMode 类型,我使用四舍五入类型 NSRoundPlain ,请参考者按需处理

    //
    //  NSDecimalNumber+FIRDeimalTool.h
    //  AkeyWallet
    //
    //  Created by HF on 2018/7/15.
    //  Copyright © 2018年 Fir.im. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    typedef NS_ENUM(NSInteger, calculationType) {
        Add,
        Subtract,
        Multiply,
        Divide
    };
    
    @interface NSDecimalNumber (FIRDeimalTool)
    
    //自定义加减乘除
    +(NSDecimalNumber *)aDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber1 type:(calculationType)type anotherDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber2 andDecimalNumberHandler:(NSDecimalNumberHandler *)handler;
    
    //小数比较
    +(NSComparisonResult)aDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber1 compareAnotherDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber2;
    
    /**
     小数位数保留
    
     @param str1 小数
     @param scale 保留位数
     @return 结果
     */
    +(NSString *)stringWithDecimalNumber:(NSDecimalNumber *)str1 scale:(NSInteger)scale;
    
    extern NSComparisonResult StrNumCompare(id str1,id str2);
    
    extern NSDecimalNumber *handlerDecimalNumber(id strOrNum,NSRoundingMode mode,int scale);
    
    
    extern NSComparisonResult FIRCompare(id strOrNum1,id strOrNum2);
    
    //加减乘除
    extern NSDecimalNumber *FIRAdd(id strOrNum1,id strOrNum2);
    extern NSDecimalNumber *FIRSub(id strOrNum1,id strOrNum2);
    extern NSDecimalNumber *FIRMul(id strOrNum1,id strOrNum2);
    extern NSDecimalNumber *FIRDiv(id strOrNum1,id strOrNum2);
    //比大小
    extern NSDecimalNumber *FIRMin(id strOrNum1,id strOrNum2);
    extern NSDecimalNumber *FIRMax(id strOrNum1,id strOrNum2);
    //定义运算结果小数取舍模态
    extern NSDecimalNumber *FIRAdd_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale);
    extern NSDecimalNumber *FIRSub_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale);
    extern NSDecimalNumber *FIRMul_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale);
    extern NSDecimalNumber *FIRDiv_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale);
    extern NSDecimalNumber *FIRMin_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale);
    extern NSDecimalNumber *FIRMax_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale);
    /**
     字符串小数舍去末尾0
     
     @param stringOrNumber 字符串小数/Number/decimalNumer
     @return 舍去末尾0
     */
    +(NSString *)getStringDecimalWithoutEndZero:(id)stringOrNumber;
    
    @end
    //
    //  NSDecimalNumber+FIRDeimalTool.m
    //  AkeyWallet
    //
    //  Created by HF on 2018/7/15.
    //  Copyright © 2018年 Fir.im. All rights reserved.
    //
    
    #import "NSDecimalNumber+FIRDeimalTool.h"
    
    @implementation NSDecimalNumber (FIRDeimalTool)
    
    +(NSDecimalNumber *)aDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber1 type:(calculationType)type anotherDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber2 andDecimalNumberHandler:(NSDecimalNumberHandler *)handler{
        if (!stringOrNumber2 || !stringOrNumber1) {
            NSAssert(NO, @"输入正确类型");
            return nil;
        }
        NSDecimalNumber *one;
        NSDecimalNumber *another;
        NSDecimalNumber *returnNum;
        if ([stringOrNumber1 isKindOfClass:[NSString class]]) {
            one = [NSDecimalNumber decimalNumberWithString:stringOrNumber1];
        }else if([stringOrNumber1 isKindOfClass:[NSDecimalNumber class]]){
            one = stringOrNumber1;
        }else if ([stringOrNumber1 isKindOfClass:[NSNumber class]]){
            one = [NSDecimalNumber decimalNumberWithDecimal:[stringOrNumber1 decimalValue]];
        }else{
            NSAssert(NO, @"输入正确类型");
            return nil;
        }
        
        if ([stringOrNumber2 isKindOfClass:[NSString class]]) {
            another = [NSDecimalNumber decimalNumberWithString:stringOrNumber2];
        }else if([stringOrNumber2 isKindOfClass:[NSDecimalNumber class]]){
            another = stringOrNumber2;
        }else if ([stringOrNumber2 isKindOfClass:[NSNumber class]]){
            another = [NSDecimalNumber decimalNumberWithDecimal:[stringOrNumber2 decimalValue]];
        }else{
            NSAssert(NO, @"输入正确类型");
            return nil;
        }
        //排除NaN
        if (isnan(one.doubleValue) || isinf(one.doubleValue)) one = [NSDecimalNumber decimalNumberWithString:@"0"];
        if (isnan(another.doubleValue) || isinf(another.doubleValue)) another = [NSDecimalNumber decimalNumberWithString:@"0"];
        //
        if (type == Add) {
            returnNum = [one decimalNumberByAdding:another];
        }else if (type == Subtract){
            returnNum  = [one decimalNumberBySubtracting:another];
        }else if (type == Multiply){
            returnNum = [one decimalNumberByMultiplyingBy:another];
        }else if (type == Divide){
            
            if ([NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:another compareAnotherDecimalNumberWithStringOrNumberOrDecimalNumber:@(0)] == 0) {
                returnNum = nil;
            }else
                returnNum = [one decimalNumberByDividingBy:another];
        }else{
            returnNum = nil;
        }
        if (returnNum) {
            if (handler) {
                return [returnNum decimalNumberByRoundingAccordingToBehavior:handler];
            }else{
                return returnNum;
            }
        }else{
            NSAssert(NO, @"输入正确类型");
            return nil;
        }
    }
    
    +(NSComparisonResult)aDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber1 compareAnotherDecimalNumberWithStringOrNumberOrDecimalNumber:(id)stringOrNumber2{
        if (!stringOrNumber2 || !stringOrNumber1) {
            NSAssert(NO, @"输入正确类型");
            return -404;
        }
        NSDecimalNumber *one;
        NSDecimalNumber *another;
        if ([stringOrNumber1 isKindOfClass:[NSString class]]) {
            one = [NSDecimalNumber decimalNumberWithString:stringOrNumber1];
        }else if([stringOrNumber1 isKindOfClass:[NSDecimalNumber class]]){
            one = stringOrNumber1;
        }else if ([stringOrNumber1 isKindOfClass:[NSNumber class]]){
            one = [NSDecimalNumber decimalNumberWithDecimal:[stringOrNumber1 decimalValue]];
        }else{
            NSAssert(NO, @"输入正确类型");
            return -404;
        }
        
        if ([stringOrNumber2 isKindOfClass:[NSString class]]) {
            another = [NSDecimalNumber decimalNumberWithString:stringOrNumber2];
        }else if([stringOrNumber2 isKindOfClass:[NSDecimalNumber class]]){
            another = stringOrNumber2;
        }else if ([stringOrNumber2 isKindOfClass:[NSNumber class]]){
            another = [NSDecimalNumber decimalNumberWithDecimal:[stringOrNumber2 decimalValue]];
        }else{
            NSAssert(NO, @"输入正确类型");
            return -404;
        }
        //排除NaN
        if (isnan(one.doubleValue) || isinf(one.doubleValue)) one = [NSDecimalNumber decimalNumberWithString:@"0"];
        if (isnan(another.doubleValue) || isinf(another.doubleValue)) another = [NSDecimalNumber decimalNumberWithString:@"0"];
        //
        return [one compare:another];
    }
    
    +(NSString *)stringWithDecimalNumber:(NSDecimalNumber *)str1 scale:(NSInteger)scale{
        if (!str1) {
            return @"";
        }
        NSString *str = [NSString stringWithFormat:@"%@",str1];
        if (str && str.length) {
            if ([str rangeOfString:@"."].length == 1) {//有小数点
                NSArray *arr = [str componentsSeparatedByString:@"."];
                if (scale > 0) {
                    NSInteger count = [arr[1] length];
                    for (NSInteger i = count; i<scale; i++) {
                        str = [str stringByAppendingString:@"0"];
                    }
                    return str;
                }else{
                    return arr[0];
                }
            }else{//没有小数点
                if ([str rangeOfString:@"."].length) {
                    return @"";
                }
                if (scale > 0) {
                    str = [str stringByAppendingString:@"."];
                    for (int i = 0; i<scale; i++) {
                        str = [str stringByAppendingString:@"0"];
                    }
                    return str;
                }else{
                    return str;
                }
            }
        }else{
            return @"";
        }
    }
    
    NSComparisonResult StrNumCompare(id str1,id str2){
        return [NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:str1 compareAnotherDecimalNumberWithStringOrNumberOrDecimalNumber:str2];
    }
    
    NSDecimalNumber *handlerDecimalNumber(id strOrNum,NSRoundingMode mode,int scale){
        if (!strOrNum || strOrNum == nil) {
            NSLog(@"输入正确类型");
            return nil;
        }else{
            NSDecimalNumber *one;
            if ([strOrNum isKindOfClass:[NSString class]]) {
                one = [NSDecimalNumber decimalNumberWithString:strOrNum];
            }else if([strOrNum isKindOfClass:[NSDecimalNumber class]]){
                one = strOrNum;
            }else if ([strOrNum isKindOfClass:[NSNumber class]]){
                one = [NSDecimalNumber decimalNumberWithDecimal:[strOrNum decimalValue]];
            }else{
                NSLog(@"输入正确的类型");
                return nil;
            }
            //排除NaN
            if (isnan(one.doubleValue) || isinf(one.doubleValue)) one = [NSDecimalNumber decimalNumberWithString:@"0"];
            //
            NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:mode scale:scale raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
            return  [one decimalNumberByRoundingAccordingToBehavior:handler];
        }
    }
    
    
    NSDecimalNumber *FIRAdd(id strOrNum1,id strOrNum2){
        return [NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum1 type:Add anotherDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum2 andDecimalNumberHandler:nil];
    }
    
    NSDecimalNumber *FIRSub(id strOrNum1,id strOrNum2){
        return [NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum1 type:Subtract anotherDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum2 andDecimalNumberHandler:nil];
    }
    NSDecimalNumber *FIRMul(id strOrNum1,id strOrNum2){
        return [NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum1 type:Multiply anotherDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum2 andDecimalNumberHandler:nil];
    }
    
    NSDecimalNumber *FIRDiv(id strOrNum1,id strOrNum2){
        return [NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum1 type:Divide anotherDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum2 andDecimalNumberHandler:nil];
    }
    
    NSComparisonResult FIRCompare(id strOrNum1,id strOrNum2){
        return [NSDecimalNumber aDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum1 compareAnotherDecimalNumberWithStringOrNumberOrDecimalNumber:strOrNum2];
    }
    
    NSDecimalNumber *FIRMin(id strOrNum1,id strOrNum2){
        return FIRCompare(strOrNum1, strOrNum2) > 0 ? strOrNum2 : strOrNum1;
    }
    NSDecimalNumber *FIRMax(id strOrNum1,id strOrNum2){
        return FIRCompare(strOrNum1, strOrNum2) > 0 ? strOrNum1 : strOrNum2;
    }
    NSDecimalNumber *FIRAdd_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale){
        return handlerDecimalNumber(FIRAdd(strOrNum1, strOrNum2), mode, scale);
    }
    NSDecimalNumber *FIRSub_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale){
        return handlerDecimalNumber(FIRSub(strOrNum1, strOrNum2), mode, scale);
    }
    NSDecimalNumber *FIRMul_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale){
        return handlerDecimalNumber(FIRMul(strOrNum1, strOrNum2), mode, scale);
    }
    NSDecimalNumber *FIRDiv_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale){
        return handlerDecimalNumber(FIRDiv(strOrNum1, strOrNum2), mode, scale);
    }
    
    
    NSDecimalNumber *FIRMin_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale){
        return handlerDecimalNumber(FIRMin(strOrNum1, strOrNum2), mode, scale);
    }
    NSDecimalNumber *FIRMax_handler(id strOrNum1,id strOrNum2,NSRoundingMode mode,int scale){
        return handlerDecimalNumber(FIRMax(strOrNum1, strOrNum2), mode, scale);
    }
    
    +(NSString *)getStringDecimalWithoutEndZero:(id)stringOrNumber
    {
        NSDecimalNumber *num = FIRAdd(stringOrNumber, @"0");
        return [NSString stringWithFormat:@"%@",num];
    }
    
    @end

    参考:

    (1) https://github.com/BANKEX/web3swift

    (2)https://github.com/CodeJCSON/NSDecimalNumber 

    待续

  • 相关阅读:
    批量修改文件的名字
    字节码指令以及操作数栈的分析
    字节码文件的分析
    类加载器详解
    类的加载-连接-初始化
    电商订单ElasticSearch同步解决方案--使用logstash
    springboot整合Mangodb实现crud,高级查询,分页,排序,简单聚合
    mongodb安装教程(亲测有效)
    Azure : 通过 SendGrid 发送邮件
    用java实现删除目录
  • 原文地址:https://www.cnblogs.com/someonelikeyou/p/9326670.html
Copyright © 2020-2023  润新知