IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Solidity 开发手册

    haofly发表于 2022-11-03 13:00:00
    love 0

    安装配置

    • 开发IDE: 我一般就直接用idea了,有solidity插件,但是以太坊有一个官方的IDE: remix

    • 如果是使用hardhat,那么就不用单独安装了,它会安装指定的版本的solc的

    1
    2
    3
    4
    brew update
    brew upgrade
    brew tap ethereum/ethereum
    brew install solidity

    语法

    • 可见性修饰符(只能其中一种)

      • public: 任何用户或者合约都能调用和访问
      • private: 只能在本合约内部调用和访问
      • external: 和public类似,不过只能在合约外调用,不能被合约内的其他函数调用
      • Internal: 和private类似,不过可以在继承的子合约中调用副合约的函数
    • 函数修饰符

      • view: 可是使用合约中的变量,只是在本地执行,不会消耗gas,不会修改合约状态(例如修改变量、触发事件等)
      • pure: 只能使用局部的变量,入参或者方法内部的变量,既不读取状态,也不改变状态,同样是本地执行,不会消耗gas
      • payable: 表示一个函数能够附加以太币调用,例如一些需要转账的函数
    • 函数入参修饰符

      • memory: 表示这里是值传递
      • storage: 表示是指针传递
    • 变量分类,注意每个变量在声明时都会有一个对应其类型的默认值(address -> address(0), boolean -> false, enum -> 第一个元素),没有空值null的概念

      • 状态变量:变量值会一直保存在合约的存储空间中
      • 局部变量:仅在函数执行过程中有效,函数退出后就无效了
      • 全局变量:保存在全局命名空间中的变量,用于获取区块链相关信息
    • 内置全局变量

      • block.number(uint): 当前区块号
      • block.timestamp(uint): 当前区块的时间戳,等同于now
      • block.gaslimit(uint): 当前区块的gaslimit
      • msg.sender(address): 消息发送者
      • msg.value(uint): 当前消息的wei值
      • now: 当前区块的时间长
      • tx.gasprice(uint): 当前transaction的gas价格
      • tx.origin(address payable): 当前交易的发送者地址
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    pragma solidity ^0.8.4;

    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

    contract ERC20Token is ERC20 {// 支持继承
    IERC20 token;

    uint256 override amount;

    mapping (address => bool) public wallets;// 如果要存储一个list并且要判断其是否存在,没有直接的array判断方法,但是可以用这种mapping来实现,wallets[address]=true, requier(wallet[address], '')

    struct Transaction {// 创建一个结构体类型
    address user;
    uint timestamp;
    }
    Transaction[] transactions;// 数组对象

    mapping (address => uint256) public investors; // mapping对象,可以用于记录key value的数据

    address public constant MY_ADDRESS = 0x.....;// 产量可以消耗更低的gas

    // 继承的时候可以写新的构造函数,并且可以将新的构造函数中的参数传递给父类进行初始化
    constructor(uint256 totalSupply, string memory name, string memory symbol, address _anotherToken) ERC20(name, symbol) {
    _mint(msg.sender, totalSupply);
    token IERC20(anotherToken);// 将另外一个合约作为参数传递进来


    transactions.push(
    Transaction('xx', 'bbb')// 结构体的初始化
    );// 数组默认有一个push方法
    }

    // 获取当前的sender
    function getMsgSender() public view returns(address) {
    return msg.sender;
    }

    function func1(unit amount) {
    require(isAllowed[msg.sender], 'Caller not allowed to mint');// 类似于断言,只有满足前面的条件才行,否则会报错
    assert(amount > 123);// 也是断言,但是没有报错信息
    }
    }

    常用智能合约概念

    ERC20/BTC

    ERC721/NFT

    • 非同质化代币(NFT)

    • 每个NFT在链上其实就是一个uint256的token id,而metadata信息则是存储中心化的外部的,比如自己建的服务器或者S3这种图片服务存储中心,通过配置合约的_baseURI可以设置其url前缀,然后后面加上token id就是tokenURI了

    • 标准方法:

      • balanceof(address _owner): 只是返回账户拥有的NFT的数量
      • ownerOf(uint256 _tokenId): 获取指定NFT token所属的账户地址
      • safeTransferFrom: 将NFT从一个地址转移到另一个地址,from必须是自己的账户地址
      • transferFrom
      • approve:更改或者确认NFT的授权地址,授权将某个NFT转移到另一个账户
      • setApprovalForAllgetApproved
      • isApprovedForAll
    • 标准事件

      • Transfer: 当NFT的所有权改变时触发该事件

      • Approval:当更改或确认NFT的授权地址时触发

    智能合约代码库

    OpenZeppelin

    • 相当于智能合约的标准仓库了,包含了经过社区审查的很多标准的智能合约源代码,不用重复造轮子了
    • 测试也可以直接用仓库里面的tests下的测试文件,但是需要注意的是它是用truffle的语法来写测试的,如果用的是hardtest来运行测试用例,需要安装hardhat-truffle5插件,详情见使用hardhat部署智能合约
    • 比较常用的有:
      • ERC20:@openzeppelin/contracts/token/ERC20/ERC20.sol
    • 安装完成后npm install @openzeppelin/contracts后可以直接在solidity中进行引入
    • 提供了很多的帮助功能
      • Ownable:增加管理员的管理功能,可以直接给方法添加onlyOwner即可实现只有管理员能够执行的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //SPDX-License-Identifier: Unlicense
    pragma solidity ^0.8.4;

    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";

    contract ERC20Token is ERC20, Ownable {
    AnotherContract anotherContract;// 可以声明另外一个contract,这样可以在方法里面直接调用它的方法

    constructor(string memory name, string memory symbol, address initialHolder, uint256 initialSupply) ERC20(name, symbol) {
    _mint(initialHolder, initialSupply);
    }

    function mint(address account, uint256 amount) public onlyOwner {
    _mint(account, amount);
    }
    }

    Solidity by Example

    • 也比较多的,但和上面那个比较来就比较逊色了

    TroubleShooting

    • Type literal_string “WALLET_ADDRESS” is not implicity convertiable to expected type address: 我这边是将balances["0x..."]改为了balances[0x...]就可以了
    • Please pass numbers as strings or BN objects to avoid precision errors: 在solidity中,一般的数字都会要求使用字符串或者大数对象BN来表示,防止精度问题web3.utils.toWei(String(123), 'ether')
    • Function has override specified but does not override anything: override的时候参数多一个少一个居然报的是这个错误


沪ICP备19023445号-2号
友情链接