首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Solidity进阶学习

经过前面关于Solidity编程语言的学习,我们已经可以编写出我们自己基本的代码功能了,也掌握了怎么去发行我们的代币了。那么,在这一节中,我将会带着大家学习下,在我们编写合约的过程中,我们需要注意一些什么问题,以及感受一下它区别于我们常规的编程语言写接口又有什么区别呢。让我们继续往下学习吧。

首先,智能合约,作为合约那么一定在排版上应该是非常简单清晰明了的。我们按照这种以下代码的排版方式,我们的合约看起来就会非常顺眼。

我们在刚开始写合约的时候,一定是先声明版权。之后,我们一定要选择Solidity的最新版本,因为最新版本一定是经过优化过的,对代码安全提示或者代码底层优化方面肯定也是优化过了的。最后,我们才开始导入我们需要的外部文件,如果是想要开发遵循EIP协议的合约,那么我们一定是要引用官方Openzeppelin开源的安全代码。

其次,以上这些前提工作做好之后,我们开始编写我们的主要的合约逻辑。在编写合约代码的时候,如果我们有用到library库合约,我们就先使用using for 将我们需要的库引入进来。然后我们在其后都声明一个写入数据到链上的事件,这样,我们想要用其它支持的编程语言与链交互的时候,就可以调用这个方法获取到这个事件返回给我们的更多的具体数据。在声明变量的时候,如果我们能用结构体我们就用结构体将变量在结构体来声明。需要注意的是,在声明变量的时候,我们要将同一种类型放在一起,因为这里面涉及到EVM底层的堆栈分配设计。但是声明的变量不能超过堆栈的限制,否则合约将不会被编译。补充一点,在Solidity语言中,uint和uint256是一样的。当我们声明了uint,其实就是uint256。一般来说,我们建议用uint来书写就可以了,因为即使我们写了uint8或者uint64等,EVM底层同样也会帮我们转换成uint256来处理。

在结构体之后,我们再紧跟着声明数组和映射。就这样,保持我们的合约代码层次结构分明,同时更重要的是考虑到底层可以不用帮我们做数据类型的再次转换,这样我们可以使代码保持在比较好的堆栈分配,使我们部署合约的成本尽量减少,也使我们的合约更加安全。

最后,我们编写我们的初始化构造函数。然后我们将写入方法和读取方法,以及一些不重要的方法依次写在合约。整个代码现在看起来,就很清晰。希望大家以后在编写合约的时候,尽量养成良好的习惯。遵循安全,简单,清晰的原则。

代码示例如下:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17; // 最新版本 0.8及以上

import '@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol'; // 官方Openzeppelin库合约

contract Master is ERC721Enumerable,Ownable {

// 首先将需要使用的代码库写在合约首位

using Strings for uint256;

// 事件

event Write(string indexed data);

// 变量

address private _owner;

// 声明私有变量,私有的通常用下划线

address public manager;

uint private _count;

uint public score;

string public color;

string public weight;

bool public isTrue;

// 将变量放在结构体

struct Users {

address account;

uint64 beforeAmount;

uint126 afterAmount;

string info;

}

// 数组

uint[] arr1;

address[] arr2;

// 映射

mapping(uint => string) maps; mapping(uint => Users) userMapping;

// 构造函数

constructor() public {

...

...

}

// 写入方法1

function WriteLogic1() external {

_writeLogic();

}

// 写入方法1

function WriteLogic2() external {

_writeLogic();

}

// 读取方法1

function ReadLogic1() external {

...

}

// 读取方法2

function ReadLogic2() external {

...

}

// 写入方法具体逻辑

function _writeLogic() private {

...

}

// 其它不重要的方法

function otherNotImportantFunction() internal {

...

}

}

前面,我们强调了合约的版本,以及着重强调了合约的排版问题。接着,我们要考虑的是合约的可见性的问题。合约的可见性,在之前的文章中,我们也有讲过,分为private,internal,external和public。当我们在编写写入数据的方法时,我们建议使用external,因为这个相对于public来说安全一些,再一个也是节约部署合约成本的一个细节。在涉及到逻辑比较严谨的代码中,我们建议使用private函数,让我们的写入数据的方法直接去调用即可,例如上面代码中的_writeLogic方法。这四个的区别是:private只能在本合约调用,internal可以在本合约和继承的合约调用,external只能在外部合约调用而本合约调用不了,public则没有限制,public的使用也是要小心谨慎,否则方法容易被黑客利用,造成不必要的损失。

我们在编写合约的时候,一定要使代码量越少越好,这里的代码量包含了引入的代码库。代码量少,代码不至于太过复杂,安全性也会高。当我们编写合约逻辑的时候,发现我们的代码量特别多,这时候就要小心谨慎的审阅我们的代码,包括不限于检查代码中的算术溢出或者转账等逻辑代码。

以太坊部署合约,执行合约写入数据的方法都需要花费gas费,也就是需要花费ETH来作为交易的手续费。这就要求我们在确保合约代码安全性的前提下,尽量的使我们的代码简洁,将需要上链的数据上链,将不必要的数据进行链下处理。正确合理的引用官方的库合约代码,比如之前我们的NFT代码每次铸造都需要触发一次mint方法,每一次都需要消耗gas费,那么我们就用官方的ERC721Enumerable.sol这份合约,我们可以使用官方的ERC721A合约,这样,我们就可以实现批量铸造,一次性铸造多个NFT啦。

编写合约,就是要考虑代码的安全性,简洁性,节约部署成本。但是需要提醒一下的是,我们不能为了节省成本而节省成本,什么意思呢?当我们的代码逻辑中,首先考虑的是它的安全问题,而成本问题只能优化对代码主要逻辑和安全影响不大的部分,对于影响比较大的,我们还是要保留,即使它多花了我们些部署合约的费用。

合约的安全性一直是智能合约开发者的首要问题。由于一切的合约产品都是要开源源代码的,这就意味着任何人都可以查看到源代码的逻辑,此时合约的安全问题就显得非常重要。因为合约一旦部署,是不可控的。合约的安全是一个重要的话题,需要大家好好在学习合约的路途上,多去摸索,也多去看看别人的合约是如何被攻击的,从中学习经验。

经过上面的讲解,我们可以看到,合约的编写与我们传统的写开发接口是不一样的。区块链没有并发的概念,而我们在开发传统的接口的时候,我们经常要考虑到使用同步还是异步,使用什么样的数据库等相关考虑的问题,而且也还考虑到服务器的承载量,代码的负载均衡等诸多问题。而在区块链世界中,这些基本上都不用考虑到。

好了,这次关于以太坊Solidity语言的学习到此就结束了。在接下来的文章中,我们来讲讲什么是web3,以及怎么使用web3来与我们的合约进行交互。咱们下期再见!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230424A0637600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券