写点什么

解锁智能合约的力量:区块链入门教程

  • 2023-03-02
    北京
  • 本文字数:6280 字

    阅读完需:约 21 分钟

解锁智能合约的力量:区块链入门教程

Smart Contract

什么是智能合约?

概述

和纸质合约一样,智能合约(smart contract)同样设立了一些交易的条款,比如谁欠谁多少钱,什么时候还,利息怎么计算等。不同的是,智能合约的条款是运行在区块链(例如以太坊)的一个程序 — 包含一些代码(函数)和数据(状态)。


contract Counter {    uint counter;
function Counter() public { counter = 0; } function count() public { counter = counter + 1; }}
复制代码


假设使用上面的代码创建一个智能合约。代码有一个类型为 uint(无符号整数)名为counter的变量。 counter变量的值就是该合约的状态。每当我们调用count()函数时,此智能合约的区块链状态将增加 1,这个状态是对任何人都可见的。


智能合约也是一个以太坊账户,一般称为合约账户。与用户账户类似,也可以:


  • 接收、持有和发送 ETH 和代币。

  • 与已部署的其它合约进行交互。


由于智能合约运行在区块链上,所以通过智能合约构建的应用程序或交易行为天生可以具有一些优势:


  • 准确、快速、高效 - 交易前会严格检查智能合约中预设的条件。

  • 安全 - 交易过程中没有银行等其它第三方中介参与,不用担心个人信息被窃取;交易确认后,篡改交易几乎不可能。

  • 透明 - 交易日志可以被所有网络参与者查看。

  • 防篡改 - 应用一旦部署在区块链上,就不能在不被人发现的情况下更改或删除。

使用智能合约构建的一些去中心化应用

  • Uniswap - 一个去中心化交易所,允许用户通过智能合约交易某些类型的加密货币,而无需任何中央机构设置汇率。

  • Compound - 一个使用智能合约让投资者赚取利息和借款人立即获得贷款的平台,而无需中间的银行。

  • USDC - 这种加密货币与美元挂钩,通过智能合约实现,每个 USDC 等于一美元。

智能合约编程实践

编程语言

目前可以用来开发智能合约的编程语言有如下几种:


  • Solidity

  • Vyper

  • Yul / Yul+


由于 Solidity 支持的语法更丰富,生态更完整,社区更活跃,所以比较推荐从 Solidity 入门智能合约开发。


Solidity Programming Language


// Specifies the version of Solidity, using semantic versioning.// 了解更多:https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragmapragma solidity ^0.8.0;
// 定义合约名称 `HelloWorld`。// 一个合约是函数和数据(其状态)的集合。// 一旦部署,合约就会留在以太坊区块链的一个特定地址上。// 了解更多: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.htmlcontract HelloWorld {
// 定义`string`类型变量 `message` // 状态变量是其值永久存储在合约存储中的变量。 // 关键字 `public` 使得可以从合约外部访问。 // 并创建了一个其它合约或客户端可以调用访问该值的函数。 string public message;
// 类似于很多基于类的面向对象语言, // 构造函数是仅在合约创建时执行的特殊函数。 // 构造器用于初始化合约的数据。 // 了解更多:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) public { // 接受一个字符变量 `initMessage` // 并为合约的存储变量`message` 赋值 message = initMessage; }
// 一个 public 函数接受字符参数并更新存储变量 `message` function update(string memory newMessage) public { message = newMessage; }}
复制代码

开发框架

使用一些成熟的开发框架可以帮助你更快的构建一个基于智能合约的去中心化应用。这些开发框架的一些基础功能包括格式化、编译、调试、部署智能合约,并提供一个可以测试的本地区块链网络。本文档使用hardhat演示,其它框架具体可以查看官方的文档。


Dapp Development Frameworks | ethereum.org

实践

初始化 Node.js 环境

## Install node version managercurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash## Hardhat支持的node最低版本是16.0nvm install v18.12.1nvm use v18.12.1node -v
复制代码

安装 Hardhat 组件

mkdir demo & cd demo## Install the Hardhat dependenciesnpm install --save-dev hardhat## initialize the projectsnpx hardhat
复制代码


$ npx hardhatYou are using a version of Node.js that is not supported by Hardhat, and it may work incorrectly, or not work at all.
Please, make sure you are using a supported version of Node.js.
To learn more about which versions of Node.js are supported go to https://hardhat.org/nodejs-versions888 888 888 888 888888 888 888 888 888888 888 888 888 8888888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888888 888 "88b 888P" d88" 888 888 "88b "88b 888888 888 .d888888 888 888 888 888 888 .d888888 888888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
👷 Welcome to Hardhat v2.12.7 👷‍
? What do you want to do? …❯ Create a JavaScript project Create a TypeScript project Create an empty hardhat.config.js Quit
复制代码


在交互式命令行中创建一个默认的Typescript应用,完成后目录结构大致为:


| - demo| -- contracts // 存放.sol合约代码| -- node_modules | -- scripts // 存放合约部署脚本| -- test // 存放单元测试代码| -- .gitignore| -- hardhat.config.ts // hardhat框架的配置文件| -- package.json| -- tsconfig.json
复制代码

编写合约代码

示例实现一个合约,核心功能是实现一个托管账户逻辑,包含基础的存储和提现功能:


// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.8.2 <0.9.0;
/** * @title Escrow * @dev Store & retrieve value in a variable */contract Escrow { /// @dev Emitted when `value` is deposited for `payee`. event Deposited(address indexed payee, uint256 weiAmount); /// @dev Emitted when `value` is withdrawn from `payee`. event Withdrawn(address indexed payee, uint256 weiAmount);
/// @dev The owner of the contract. address private _owner; /// @dev Mapping of address to deposits. mapping(address => uint256) private _deposits;
/** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _owner = msg.sender; }
/** * @dev Stores the sent amount as credit to be withdrawn. * * Emits a {Deposited} event. */ function deposit() public payable virtual { uint256 amount = msg.value; _deposits[msg.sender] += amount;
// emit the event emit Deposited(msg.sender, amount); }
/** * @dev Withdraw accumulated balance for a payee, forwarding all gas to the * recipient. * * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. * Make sure you trust the recipient, or are either following the * checks-effects-interactions pattern or using {ReentrancyGuard}. * * Emits a {Withdrawn} event. */ function withdraw() public virtual { address payee = msg.sender; uint256 amount = _deposits[payee];
// check if the balance is enough to send the payment require(amount > 0, "Address: insufficient balance");
// set the balance to 0 before sending the payment _deposits[payee] = 0;
(bool success, ) = payee.call{value: amount}(""); require(success, "Address: unable to send value, may have reverted");
// emit the event emit Withdrawn(payee, amount); }
/** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; }
/** * @dev Returns the amount of wei owned by an address. */ function balance() public view virtual returns (uint256) { return _deposits[msg.sender]; }}
复制代码


编译合约:


$ npx hardhat compileGenerating typings for: 1 artifacts in dir: typechain-types for target: ethers-v5Successfully generated 6 typings!Compiled 1 Solidity file successfully
复制代码

编写测试用例

import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";import { expect } from "chai";import { ethers } from "hardhat";
describe("Escrow", function () { // We define a fixture to reuse the same setup in every test. // We use loadFixture to run this setup once, snapshot that state, // and reset Hardhat Network to that snapshot in every test. async function deployFixture() { // Contracts are deployed using the first signer/account by default const [owner, payee] = await ethers.getSigners();
const Escrow = await ethers.getContractFactory("Escrow"); const escrow = await Escrow.deploy();
return { escrow, owner, payee }; }
describe("Deployment", function () { it("Should set the right owner", async function () { const { escrow, owner } = await loadFixture(deployFixture);
expect(await escrow.owner()).to.equal(owner.address); }); });
describe("Deposit", function () { it("Should deposit funds ok", async function () { const { escrow, payee } = await loadFixture(deployFixture);
await expect(escrow.connect(payee).deposit({ value: 100 })) .to.be.ok .to.emit(escrow, "Deposited") .withArgs(payee.address, 100) .to.changeEtherBalances([escrow, payee], [100, -100]); }); });
describe("Withdrawals", function () { it("Should withdraw funds ok", async function () { const { escrow, payee } = await loadFixture(deployFixture);
await expect(escrow.connect(payee).deposit({ value: 100 })) .to.be.ok .to.emit(escrow, "Deposited") .withArgs(payee.address, 100) .to.changeEtherBalances([escrow, payee], [100, -100]);
await expect(escrow.connect(payee).withdraw()) .to.be.ok .to.emit(escrow, "Withdrawn") .withArgs(payee.address, 100) .to.changeEtherBalances([escrow, payee], [-100, 100]); });
it("Should fail if insufficient balance", async function () { const { escrow, payee } = await loadFixture(deployFixture);
await expect(escrow.connect(payee).deposit({ value: 100 })) .to.be.ok .to.emit(escrow, "Deposited") .withArgs(payee.address, 100) .to.changeEtherBalances([escrow, payee], [100, -100]);
await expect(escrow.connect(payee).withdraw()) .to.be.ok
await expect(escrow.connect(payee).withdraw()) .to.be.revertedWith("Address: insufficient balance") }); });});
复制代码


执行测试用例:$ npx hardhat test



生成测试报告:$ npx hardhat coverage



生成 gas 消耗报告:$ REPORT_GAS=true npx hardhat test



部署到测试链

  1. 申请Alchemy账号,创建一个 Relay Network 节点。

  2. 创建一个测试钱包地址,并导出 private key。

  3. 编辑 Hardhat 配置文件:


import { HardhatUserConfig } from "hardhat/config";import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = { solidity: "0.8.17", networks: { goerli: { // Go to https://www.alchemyapi.io, sign up, create url: 'https://eth-goerli.g.alchemy.com/v2/{alchemy_api_key}', // Replace this private key with your Goerli account private key // To export your private key from Metamask, open Metamask and // go to Account Details > Export Private Key // Beware: NEVER put real Ether into testing accounts accounts: [ '{account_private_key}', ], gas: 5000000, gasPrice: 70000000000, }, }};
export default config;
复制代码


  1. 编辑部署脚本:


import { ethers } from "hardhat";
async function main() { const Escrow = await ethers.getContractFactory('Escrow') const escrow = await Escrow.deploy()
console.log(`Escrow deployed to Goerili. Address: ${escrow.address}`)}
// We recommend this pattern to be able to use async/await everywhere// and properly handle errors.main().catch((error) => { console.error(error); process.exitCode = 1;});
复制代码


  1. 执行部署脚本


$ npx hardhat run --network goerli scripts/deploy.tsEscrow deployed to Goerili. Address: 0x7B36f017429681ccE27f28D968E570eF7bE2Cc0f
复制代码


  1. 在 etherscan 上查看合约信息


总结

入门智能合约的开发后,仍有很多话题需要继续学习,例如:


  • 理解智能合约中变量的不同存储类型。

  • 理解智能合约运行的基础 - 以太坊虚拟机(EVM)。

  • 学习智能合约的优化方法和技巧,可以更好的缩减代码容量和执行的成本。

  • 学习并重视智能合约的安全问题,传统的软件应用可以通过升级修复 bug,由于智能合约一旦部署后续的更新会十分困难,所以做好安全审查对于代码安全十分重要。一旦智能合约出现安全风险可以是数以亿计的损失,甚至导致公司破产。

相关资源



关于领创集团

​(Advance Intelligence Group)

领创集团成立于 2016 年,致力于通过科技创新的本地化应用,改造和重塑金融和零售行业,以多元化的业务布局打造一个服务于消费者、企业和商户的生态圈。集团旗下包含企业业务和消费者业务两大板块,企业业务包含 ADVANCE.AI 和 Ginee,分别为银行、金融、金融科技、零售和电商行业客户提供基于 AI 技术的数字身份验证、风险管理产品和全渠道电商服务解决方案;消费者业务 Atome Financial 包括亚洲领先的先享后付平台 Atome 和数字金融服务。2021 年 9 月,领创集团宣布完成超 4 亿美元 D 轮融资,融资完成后领创集团估值已超 20 亿美元,成为新加坡最大的独立科技创业公司之一。

发布于: 刚刚阅读数: 3
用户头像

智慧领创美好生活 2021-08-12 加入

AI技术驱动的科技集团,致力于以技术赋能为核心,通过科技创新的本地化应用,改造和重塑金融和零售行业,以多元化的业务布局打造一个服务于消费者、企业和商户的生态圈,带来个性化、陪伴式的产品服务和优质体验。

评论

发布
暂无评论
解锁智能合约的力量:区块链入门教程_区块链_领创集团Advance Intelligence Group_InfoQ写作社区