Solidity一篇就够了

这个是中文官网 Solidity 中文文档

这个是在线开发工具 Remix 官网

数据类型

solidity的数据类型可以分为三种,分别是值类型引用类型映射

值类型

以下类型也称为值类型,因为这些类型的变量将始终按值来传递。 也就是说,当这些变量被用作函数参数或者用在赋值语句中时,总会进行值拷贝。


  • bool类型 :ture false
  • 整形 :无符号整型 uint 和整形 int
  • address 类型 :以太坊地址 address 和 可支付地址 address payable
  • 字节 byte
  • unicode 字面常量
  • enum 类型
// SPDX-License-Identifier: MIT

contract Demo{
	bool a = true;
	uint b = 100;
	int c = -100;
	address d = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
	byte e = 'a';
	string f = unicode"中国";
	enum g {
		one,
		two,
		three
	}
}
引用类型

引用类型可以通过多个不同的名称修改它的值,而值类型的变量,每次都有独立的副本。因此,必须比值类型更谨慎地处理引用类型。 目前,引用类型包括结构,数组和映射,如果使用引用类型,则必须明确指明数据存储哪种类型的位置(空间)里:

  • 内存memory 即数据在内存中,因此数据仅在其生命周期内(函数调用期间)有效。不能用于外部调用。
  • 存储storage 状态变量保存的位置,只要合约存在就一直存储。
  • 调用数据calldata 用来保存函数参数的特殊数据位置,是一个只读位置

更改数据位置或类型转换将始终产生自动进行一份拷贝,而在同一数据位置内(对于 存储storage 来说)的复制仅在某些情况下进行拷贝。

  • 数组 : bytesstring
  • 结构体 struct
// SPDX-License-Identifier: MIT

contract Demo{
	string a = "abc";
	bytes b = new bytes[];
	struct c {
		string name;
		uint age;
	}
}

数据位置

所有的引用类型,如 数组结构体 类型,都有一个额外注解 数据位置 ,来说明数据存储位置。 有三种位置:

  • 存储 storage : 永久存储。
  • 内存 memory : 只能在函数中访问。
  • 调用数据 calldata : 只能在函数接收参数处使用。
  • stack

映射

映射类型在声明时的形式为 mapping(KeyType => ValueType)。 其中 KeyType 可以是任何基本类型,即可以是任何的内建类型, bytesstring 或合约类型、枚举类型。 而其他用户定义的类型或复杂的类型如:映射、结构体、即除 bytesstring 之外的数组类型是不可以作为 KeyType 的类型的。

ValueType 可以是包括映射类型在内的任何类型。

// SPDX-License-Identifier: MIT

contract Demo{
	mapping(address=>uint) public balances;
	
	function setBalances(uint value) public {
		balances[msg.sender] = value;
	}
}

修饰符

  • public 所有合约与账号都可以调用。
  • private 只有在定义该函数的合约可以调用。
  • internal 当前合约或继承该合约的,类似protected关键字。
  • external 只有其他合约或者账号可以调用,定义该函数的合约不能调用,除非使用this关键字。

错误Error

  • assert(bool condition) 如果不满足条件,此方法调用将导致一个无效的操作码,对状态所做的任何更改将还原。这个方法是用来处理内部错误的。
  • require(boo condition) 如果不满足条件,此方法调用将恢复到原始状态。此方法用于检查输入或者外部组件的错误。
  • require(bool condition,string memore message) 如果不满足此条件,此方法调用将恢复到原始状态。此方法用于检查或外部组件错误。它提供了一个提供自定义消息的选项。
  • revert() 此方法将中止执行并将所作的更改还原为执行前状态。
  • revert(string memore reason) 此方法将中止执行并将所作的更改还原为执行前状态。它提供了一个提供自定义消息的选项。

单位

以太币单位

以太币Ether 单位之间的换算就是在数字后边加上 weigweiether 来实现的,如果后面没有单位,缺省为 wei。

assert(1 wei == 1);
assert(1 gwei == 1e9);
assert(1 ether == 1e18);
时间单位

秒是缺省时间单位,在时间单位之间,数字后面带有 secondsminuteshoursdaysweeks 的可以进行换算,基本换算关系如下:

  • 1 == 1 seconds
  • 1 minutes == 60 seconds
  • 1 hours == 60 minutes
  • 1 days == 24 hours
  • 1 weeks == 7 days

表达式和结构控制

JavaScript 中的大部分控制结构在 Solidity 中都是可用的,除了 switchgoto。 因此 Solidity 中有 ifelsewhiledoforbreakcontinuereturn? : 这些与在 C 或者 JavaScript 中表达相同语义的关键词。

与 C 和 JavaScript 不同, Solidity 中非布尔类型数值不能转换为布尔类型,因此 if (1) { ... } 的写法在 Solidity 中 无效

ERC

简介

ERC 全称是 “Ethereum Request for Comment”,表示以太坊的意见征求稿,ERC中包含技术和组织等注意事项及标准。这套标准其实不光由以太坊官方提出,还有一些以太坊爱好者提出。是以太坊生态系统中被广泛使用的关键标准。

什么叫做代币?

代币可以在以太坊中表示任何东西:

  • 彩票卷
  • 像美元一样的法定货币
  • 在线平台中的信誉积分
  • 游戏中一个角色的技能
  • 一盎司黄金
  • 及更多…

ERC-20 提出了一个同质化代币的标准,换句话说,它们具有一种属性,使得每个代币都与另一个代币(在类型和价值上)完全相同。 例如,一个 ERC-20 代币就像以太币一样,意味着一个代币会并永远会与其他代币一样。

下面举例一些比较知名的基于 ERC-20 协议代币:

Tether (USDT)

Chainlink (LINK)

Binance coin (BNB)

USD coin (USDC)

Wrapped bitcoin (WBTC)

Dai (DAI)

代币信息

一个标准的代币信息需要包括一个下面这些信息:

  • 代币名称 name
  • 代币标识 symbol
  • 代币小数位数 decimals
  • 代币的总发行量 totalSupply
  • 某地址的代币数量 balance
  • 授权代币数量 allowance

如果智能合约实施了下列方法和事件,它可以被称为 ERC-20 代币合约,一旦部署,将负责跟踪以太坊上创建的代币。

详情链接

方法

function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)

事件

event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)

合约案例

智能合约代码

# Kun.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

import "./Context.sol";

contract Kun is Context {
    /*
    0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
    0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
    */

    // 1.代币信息
    // 代币名称 name
    // 代币标识 symbol
    // 代币小数位数 decimals
    // 代币的总发行量 totalSupply
    // 某地址代币数量 balance
    // 授权代币数量 allowance
    string private _name;
    string private _symbol;
    uint8 private _decimals;
    uint256 private _totalSupply;
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowance;

    // 2.初始化
    constructor() {
        _name = "Kun";
        _symbol = "KUN";
        _decimals = 18;
        _mint(_msgSender(), 10000 * 10 * _decimals);
    }

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // 合约内部函数

    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero addres");
        _totalSupply += amount;
        unchecked {
            _balances[account] += amount;
        }
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, unicode"ERC20: 余额不足");
        unchecked {
            _balances[from] = fromBalance - amount;
            _balances[to] += amount;
        }
        emit Transfer(from, to, amount);
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve from the zero address");
        _allowance[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, unicode"余额不足");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    // 取值器
    function name() public view returns (string memory) {
        return _name;
    }

    function symbol() public view returns (string memory) {
        return _symbol;
    }

    function decimals() public view returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    // 返回某个账户下有多少钱
    function balanceOf(address _owner) public view returns (uint256 balance) {
        return _balances[_owner];
    }

    // 返回授权代币数量
    function allowance(address _owner, address _spender)
        public
        view
        returns (uint256 remaining)
    {
        return _allowance[_owner][_spender];
    }

    // 代币转发
    function transfer(address _to, uint256 _value)
        public
        returns (bool success)
    {
        address owner = _msgSender();
        _transfer(owner, _to, _value);
        return true;
    }

    /*
        主体:借款人,贷款人,中介公司,房屋出售者 account
        授权:贷款人(银行) 借钱给我 approve 100w
        提款:从银行贷款账户里提钱给自己 tranferFrom(from, to, amount) 1w
        支付房款:借款人转账给房屋出售者 tranferFrom 90w
        支付佣金:借款人转账给中介公司 tranferFrom 9w
    */
    // 代币授权
    function approve(address _spender, uint256 _value) public returns (bool) {
        // 银行授权给我(银行贷款给我)owner是授权人 _spender被授权人
        address owner = _msgSender();
        _approve(owner, _spender, _value);
        return true;
    }

    // 授权代币转发
    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) public returns (bool) {
        address owner = _msgSender();
        _spendAllowance(_from, owner, _value); // 修改授权信息
        _transfer(_from, _to, _value); // 执行转账
        return true;
    }
}
# Context.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract Context {
    function _msgSender() view internal returns (address){
        return msg.sender;
    }
}

前端代码

前提:智能合约部署到线上,并且在项目中的 artifacts 中导出合约的 json 放在前端项目里面。

首先创建 Vue3 项目,并且安装 web3包。

判断小狐狸钱包是否连接

if (typeof window.ethereum != 'undefined') {
  console.log("小狐狸钱包插件已安装")
}

创建合约对象 :

import Web3 from 'web3';
import kunJSON from './assets/Kun.json'

const geerliWS = "wss://sepolia.infura.io/ws/v3/your apis";
const web3 = new Web3(geerliWS)
const myContract = web3.eth.Contract(kunJSON.abi, "部署的合约地址");

const name = ref("")
const symbol = ref("")
const totalSupply = ref("")
const balanceOf = ref("");

// 获取代币信息
(async () => {
  const account = await web3.eth.requestAccounts()// 获取当前连接的地址
  name.value = await myContract.methods.name().call();
  symbol.value = await myContract.methods.symbol().call();
  totalSupply.value = await myContract.methods.totalSupply().call();
  balanceOf.value = await myContract.methods.balanceOf(account[0]).call();
})();

// 转账
const send = () => {
  const account = web3.utils.toWei("1", "ether")
  myContract.methods.transfer("转账地址", account).send({
    from: "被转账地址"
  }).on("receipt", (receipt) => {
    console.log(receipt)
  })
}

注意:需要消耗 gas 费的时候,末尾需要携带 .send({...}) 方法,而不需要消耗 gas 费的时,则使用 call()

openzeppelin 智能合约库

openzeppelin 官网

OpenZeppelin 是一个使用以太坊智能合约语言 Solidity 进行构建的开发框架,可以简化 智能合约Dapp 的开发。

OpenZeppelin 合约和库已成为行业标准,其开源代码模板历经了以太坊及其他区块链的实战考验,帮助开发者最大限度降低风险。Openzeppelin 代码包括使用度最高的ERC标准及拓展部署,已被社区在各类指南以及操作教程中大量使用。

Contract Wizard

OpenZeppelin 开发了一种基于网络的线上智能合约交互式工具,它可能是使用 OpenZepplin 代码编写智能合约最简单快捷的方式。这一工具被成为 Contracts Wizard

基本使用

首先进入官网,进入到这个页面:

在这里插入图片描述

设置完 代币名称单位数量 后,暗色区域会自动生成代码,复制代码到自己的项目中即可。

然后在 Remix 调试和部署界面修改虚拟机的环境为你使用的钱包,钱包授权之后,把合约部署支付一定的 以太币 后,在钱包面板中添加代币后,代币则添加成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值