FISCO BCOS——SmartDev-Contract——RewardPoint积分模板合约案例分析

一、合约场景分析

在区块链的业务方案中,积分系统是非常常见的一种场景。
基于智能合约的积分系统,支持多家机构发行、用户注册、积分消费、积分销户、用户销户等多种功能。

典型的积分场景包括了以下角色:

  • 发行者:发行积分。
  • 用户:获得和消费积分,还可实现积分的转账。
  • 管理者:积分系统的管理者。

积分系统中包括了以下功能:

  • 创建积分系统
  • 注册:用户注册会员,只有经过注册的用户才能获得和消费积分。
  • 销户: 用户销户。只有积分为0的用户才能正常销户。
  • 添加发行者: 发行者具有发行积分的权利。
  • 发行积分: 支持定向发送到某个积分账户。
  • 删除发行者: 发行者可以注销自身的发行积分的权利。
  • 转账: 支持用户积分的互转和消费等。
  • 积分销毁: 用户可以销毁自己的积分。

源码库:

https://github.com/WeBankBlockchain/SmartDev-Contract

源码分析者:

github:Blockchain_Key

源码链接:

https://github.com/WeBankBlockchain/SmartDev-Contract/tree/master/contracts/business_template/reward_point

二、合约场景说明

三、基础库合约介绍(无单独使用)

1. 角色库合约

LibRoles.sol

pragma solidity ^0.4.25;

library LibRoles {
    
    
    struct Role {
    
    
        mapping (address => bool) bearer;
    }

    function add(Role storage role, address account) internal {
    
    
        require(!has(role, account), "Roles: account already has role");
        role.bearer[account] = true;
    }

    function remove(Role storage role, address account) internal {
    
    
        require(has(role, account), "Roles: account does not have role");
        role.bearer[account] = false;
    }

    function has(Role storage role, address account) internal view returns (bool) {
    
    
        require(account != address(0), "Roles: account is the zero address");
        return role.bearer[account];
    }
}

(1)功能说明

本合约作为库合约,进行角色增,删,查操作

(2)接口说明

  • add(Role storage role, address account):添加角色
  • remove(Role storage role, address account):删除角色
  • has(Role storage role, address account) internal view returns (bool):查询角色,有返回true,没有返回false

(3)使用说明

作为库合约,不能直接部署,必须由其他合约引入使用

2.运算库合约

LibSafeMath.sol

pragma solidity ^0.4.25;

library LibSafeMath {
    
       
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    
    
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;

        return c;
    }
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
    
    
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }
}

(1)功能说明

本合约作为安全运算库合约,进行加,减操作

(2)接口说明

  • sub(uint256 a, uint256 b) internal pure returns (uint256):传入uint256类型的两个参数,进行想减,返回减后结果
  • add(uint256 a, uint256 b) internal pure returns (uint256):传入uint256类型的两个参数,进行想加,返回加后结果

(3)使用说明

作为库合约,不能直接部署,必须由其他合约引入使用

四、业务封装合约介绍(无对外接口)

1.角色库合约应用

IssuerRole.sol

pragma solidity ^0.4.25;

import "./LibRoles.sol";

contract IssuerRole {
    
    
    using LibRoles for LibRoles.Role;

    event IssuerAdded(address indexed account);
    event IssuerRemoved(address indexed account);

    LibRoles.Role private _issuers;

    constructor () internal {
    
    
        _issuers.add(msg.sender);
    }

    modifier onlyIssuer() {
    
    
        require(isIssuer(msg.sender), "IssuerRole: caller does not have the Issuer role");
        _;
    }

    function isIssuer(address account) public view returns (bool) {
    
    
        return _issuers.has(account);
    }

    function addIssuer(address account) public {
    
    
        _issuers.add(account);
        emit IssuerAdded(account);
    }

    function renounceIssuer(address account) public onlyIssuer {
    
    
        _issuers.remove(account);
        emit IssuerRemoved(account);
    }
}

(1)功能说明

引入库合约LibRoles.sol,具体化角色合约,提供角色判定是否存在,增加,销毁操作

(2)接口说明

  • isIssuer(address account) public view returns (bool):传入地址,判定当前地址是否存在,返回bool类型
  • addIssuer(address account):传入地址,添加此地址角色
  • renounceIssuer(address account):调用者必须是此合约的创建者,传入地址,删除此地址角色

(3)使用说明

作为封装合约,此处不单独使用

2.合约管理员

BasicAuth.sol

pragma solidity ^0.4.25;

contract BasicAuth {
    
    
    address public _owner;

    constructor() public {
    
    
        _owner = msg.sender;
    }

    modifier onlyOwner() {
    
     
        require(auth(msg.sender), "Only owner!");
        _; 
    }

    function setOwner(address owner)
        public
        onlyOwner
    {
    
    
        _owner = owner;
    }
    
    function auth(address src) public view returns (bool) {
    
    
        if (src == address(this)) {
    
    
            return true;
        } else if (src == _owner) {
    
    
            return true;
        } else {
    
    
            return false;
        }
    }
}

(1)功能说明

此合约提供修改部署此合约的初始管理员地址,以及判定地址是否为合约初始管理员地址或修改后的管理员地址

(2)接口说明

  • setOwner(address owner):传入一个地址,设置当前地址为合约管理员
  • auth(address src) public view returns (bool):判定传入的src地址是否为合约管理员地址

(3)使用说明

作为封装合约,此处不单独使用

3.底层数据存储

RewardPointData.sol

pragma solidity ^0.4.25;

import "./BasicAuth.sol";
import "./IssuerRole.sol";

contract RewardPointData is BasicAuth, IssuerRole {
    
    

    mapping(address => uint256) private _balances;
    mapping(address => bool) private _accounts;
    uint256 public _totalAmount;
    string public _description;
    address _latestVersion; 

    constructor(string memory description) public {
    
    
        _description = description;
    }

    modifier onlyLatestVersion() {
    
    
       require(msg.sender == _latestVersion);
        _;
    }

    function upgradeVersion(address newVersion) public {
    
    
        require(msg.sender == _owner);
        _latestVersion = newVersion;
    }
    

    function setBalance(address a, uint256 value) onlyLatestVersion public returns (bool) {
    
    
        _balances[a] = value;
        return true;
    }

    function setAccount(address a, bool b) onlyLatestVersion public returns (bool) {
    
    
        _accounts[a] = b;
        return true;
    }

    function setTotalAmount(uint256 amount) onlyLatestVersion public returns (bool) {
    
    
        _totalAmount = amount;
        return true;
    }

    function getAccountInfo(address account) public view returns (bool, uint256) {
    
    
        return (_accounts[account], _balances[account]);
    }

    function hasAccount(address account) public view returns(bool) {
    
    
         return _accounts[account];
    }

    function getBalance(address account) public view returns (uint256) {
    
    
        return _balances[account];
    }
}

(1)功能说明

此合约提供版本更替,设置积分余额,设置账户是否存在,设置积分总量,获取地址是否存在以及地址余额,判定账户是否存在,获取账户余额

(2)接口说明

  • upgradeVersion(address newVersion):更换版本地址,仅管理员可操作
  • setBalance(address a, uint256 value) onlyLatestVersion public returns (bool):传入地址和uint256类型的参数,给此地址设置余额
  • setAccount(address a, bool b) onlyLatestVersion public returns (bool):传入地址和bool类型的参数,给此地址设置是否存在
  • getAccountInfo(address account) public view returns (bool, uint256):传入地址,获取此地址的相关信息(是否存在,余额多少)
  • hasAccount(address account) public view returns(bool):传入地址,判定此地址是否存在
  • getBalance(address account) public view returns (uint256):传入地址,获取此地址的余额

(3)使用说明

作为封装合约,此处不单独使用

五、业务接口合约介绍

1.管理员部署合约

Admin.sol

pragma solidity ^0.4.25;

import "./BasicAuth.sol";
import "./RewardPointController.sol";
import "./RewardPointData.sol";

contract Admin is BasicAuth {
    
    
    address public _dataAddress; 
    address public _controllerAddress;

    constructor() public {
    
    
        RewardPointData data = new RewardPointData("Point of V1");
        _dataAddress = address(data);
        RewardPointController controller = new RewardPointController(_dataAddress);
        _controllerAddress = address(controller);
        data.upgradeVersion(_controllerAddress);
        data.addIssuer(msg.sender);
        data.addIssuer(_controllerAddress);
    }

    function upgradeVersion(address newVersion) public {
    
    
        RewardPointData data = RewardPointData(_dataAddress);
        data.upgradeVersion(newVersion);
    }
    
}

(1)功能说明

对外接口合约,用于初始化数据存储合约;以数据存储合约为地址,初始化创建积分控制台合约;更新版本地址信息

(2)接口说明

  • constructor():构造函数中,初始化各个合约以及对相关信息进行添加
  • upgradeVersion(address newVersion):传入新版本地址,修改地址

(3)使用说明

合约部署进行初始化操作,主要接口调用_controllerAddress(),用于获取RewardPointController合约的地址

  • 合约部署

在这里插入图片描述

  • 调用_controllerAddress,用于获取RewardPointController合约的地址
    在这里插入图片描述
    在这里插入图片描述

2.积分控制台合约

RewardPointController.sol

pragma solidity ^0.4.25;

import "./RewardPointData.sol";
import "./BasicAuth.sol";
import "./LibSafeMath.sol";

contract RewardPointController is BasicAuth {
    
    
    using LibSafeMath for uint256;

    RewardPointData _rewardPointData;

    event LogRegister(address account);
    event LogUnregister(address account);
    event LogSend( address indexed from, address indexed to, uint256 value);

    constructor(address dataAddress) public {
    
    
        _rewardPointData = RewardPointData(dataAddress);
    }

    modifier accountExist(address addr) {
    
     
        require(_rewardPointData.hasAccount(addr)==true && addr != address(0), "Only existed account!");
        _; 
    } 

    modifier accountNotExist(address account) {
    
     
        require(_rewardPointData.hasAccount(account)==false, "Account already existed!");
        _; 
    } 

    modifier canUnregister(address account) {
    
     
        require(_rewardPointData.hasAccount(account)==true && _rewardPointData.getBalance(account) == 0 , "Cann't unregister!");
        _; 
    } 

    modifier checkAccount(address sender) {
    
     
        require(msg.sender != sender && sender != address(0), "Can't transfer to illegal address!");
        _; 
    } 

    modifier onlyIssuer() {
    
    
        require(_rewardPointData.isIssuer(msg.sender), "IssuerRole: caller does not have the Issuer role");
        _;
    }

    function register() accountNotExist(msg.sender) public returns (address) {
    
    
        _rewardPointData.setAccount(msg.sender, true);
        // init balances
        _rewardPointData.setBalance(msg.sender, 0);
        emit LogRegister(msg.sender);
    }

    function unregister() canUnregister(msg.sender) public returns (address) {
    
    
        _rewardPointData.setAccount(msg.sender, false);
        emit LogUnregister(msg.sender);
    }

    function isRegistered(address addr) public view returns (bool) {
    
    
        return _rewardPointData.hasAccount(addr);
    }

    function balance(address addr) public view returns (uint256) {
    
    
        return _rewardPointData.getBalance(addr);
    }

    function transfer(address toAddress, uint256 value) accountExist(msg.sender) accountExist(toAddress)
        public returns(bool b, uint256 balanceOfFrom, uint256 balanceOfTo) {
    
    
            uint256 balance1 = _rewardPointData.getBalance(msg.sender);
            balanceOfFrom = balance1.sub(value);
            _rewardPointData.setBalance(msg.sender, balanceOfFrom);
            uint256 balance2 = _rewardPointData.getBalance(toAddress);
            balanceOfTo = balance2.add(value);
            _rewardPointData.setBalance(toAddress, balanceOfTo);
            emit LogSend(msg.sender, toAddress, value);
            b = true;
    }

    function destroy(uint256 value) accountExist(msg.sender) public returns (bool) {
    
    
        uint256 totalAmount = _rewardPointData._totalAmount();
        totalAmount = totalAmount.sub(value);
        _rewardPointData.setTotalAmount(totalAmount);
        uint256 balance1 = _rewardPointData.getBalance(msg.sender);
        balance1 = balance1.sub(value);
        _rewardPointData.setBalance(msg.sender, balance1);
        emit LogSend( msg.sender, address(0), value);
        return true;
    }


    function issue(address account, uint256 value) public accountExist(account) returns (bool) {
    
    
        uint256 totalAmount = _rewardPointData._totalAmount();
        totalAmount = totalAmount.add(value);
        _rewardPointData.setTotalAmount(totalAmount);
        uint256 balance1 = _rewardPointData.getBalance(account);
        balance1 = balance1.add(value);
        _rewardPointData.setBalance(account, balance1);
        emit LogSend( address(0), account, value);
        return true;
    }

    function isIssuer(address account) public view returns (bool) {
    
    
        return _rewardPointData.isIssuer(account);
    }

    function addIssuer(address account) public returns (bool) {
    
    
        _rewardPointData.addIssuer(account);
        return true;
    }

    function renounceIssuer() public returns (bool) {
    
    
        _rewardPointData.renounceIssuer(msg.sender);
        return true;
    }
}

(1)功能说明

此合约作为积分控制控制,用于注册账户,销毁账户,判定账户是否已经创建,查看账户余额,给某地址转发余额,注销某数量的账户余额等

(2)接口说明

  • addIssuer(address account) :添加资产发行者。只有资产发行者可以添加新的资产发行者。
  • issue(address account, uint256 value) :发行积分
  • isIssuer(address account) :判断是否是资产发行者。
  • addIssuer(address account) :添加发行者,只能由发行者添加。
  • renounceIssuer() :撤销发行者,只能撤销自己。
  • register() :普通用户账户注册。
  • unregister() :普通用户账户注销。
  • isRegistered(address addr) :判断普通用户是否注册。
  • balance(address addr) :查询普通用户的积分。
  • transfer(address toAddress, uint256 value) :往目标账户转积分
  • destroy(uint256 value) :销毁自己账户中的指定数量的积分

(3)使用说明

  • 不需要重新部署,以Admin.sol合约中调用_controllerAddress函数返回的RewardPointController合约地址来调用RewardPointController合约

  • 资产发行者操作说明:
    (1)管理员admin通过addIssuer添加其他发行者KT1
    在这里插入图片描述

    (2)通过isIssuer接口查询某个用户是否为资产发行者
    在这里插入图片描述
    在这里插入图片描述

    (3)发行者通过renounceIssuer接口撤销自己发行者的权限,并再次查询KT1是否为发行者
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 普通用户账户注册
    (1)通过register()接口注册普通用户账户user,积分默认为0
    在这里插入图片描述

    (2)通过isRegistered判断用户user是否已注册
    在这里插入图片描述
    在这里插入图片描述

  • 积分发行(只有发行者才能发行积分,普通用户无法发行)
    (1)发行者通过issue接口定向发行积分(只有注册后的用户才能接收)
    在这里插入图片描述

  • 积分操作(普通用户)
    (1)用户通过balance接口查询积分余额
    在这里插入图片描述
    在这里插入图片描述

(2)用户通过transfer接口实现积分转账(先注册user1,user再给user1转300积分,再查看user user1的积分)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(3)用户user通过destroy接口实现指定数额200的账户积分注销
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(3) 用户user1通过unregister接口销户,只有积分余额为0的账户可以直接销户
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43402353/article/details/122159926
今日推荐