第 1 行 // SPDX-License-Identifier: MIT 就是合约的版权声明。其中 SPDX-License-Identifier(SPDX 许可标示) 是标注当前的智能合约采用什么样的对外开放标准,该标准规定了别人是否拥有商业开发,学习使用等权利。代码中使用的 MIT 规定了其他人随便用该代码,但出问题不负责。MIT 详细解释;SPDX 许可标示的注释在文件的任何位置都可以被编译器识别到的,按照规范建议把它放在文件的顶部第一行。
如果一个项目开源了智能合约的源代码,可以更好地建立社区信任。但是由于提供源代码就不可避免的涉及到版权或者法律问题。所以 solidity 鼓励开源,但是开源并不等于放弃版权。如果你不想指定任何许可证,或者代码就是不想开源,Solidity 推荐使用 UNLICENSED ;UNLICENSED 不存在于 SPDX 许可证列表中,与 UNLICENSE (授予所有人所有权利)不同,它比 UNLICENSE 多一个 D 字母。
需要注意: 源代码这里,编译器不会验证 SPDX 许可标示是否符合规范,比如我可以写为
// SPDX-License-Identifier: ANBANG,并不会影响代码的运行。但是这里的标示会被打包在bytecode metadata里。
当我们使用 remix 编译合约的时候,会在根目录创建 artifacts 文件夹,其中包含 build-info 记录构建信息的文件夹,以及每个合约名字作为文件名的文件夹,比如 contract Hello 将生成
Hello.json 文件
Hello_metadata.json 文件
Hello.json 文件结构
{
deploy: {},
data: {
bytecode: {},
deployedBytecode: {},
gasEstimates: {},
methodIdentifiers: {},
},
abi: [],
};
Hello_metadata.json 文件结构
{
compiler: {
version: "0.8.17+commit.xxx",
},
language: "Solidity",
output: {
abi: [],
devdoc: {},
userdoc: {},
},
settings: {},
sources: {
"aaa.sol": {
keccak256:
"0x637c141739144cd991b9350336a1f8c3b948811d7ed743fefb4aad99d7bb362f",
license: "kyp",
urls: [
"bzz-raw://9eea517225b90242d6e3761046f5f5a8f0a2393747c89f3af01f34ad00764dc4",
"dweb:/ipfs/QmXp5wap9ZNC9fihdA7aLMe7bKWBjeAuv7khEuvKrgp9Bx",
],
},
},
version: 1,
};
// SPDX-License-Identifier: kyp 中的 kyp 就是在 sources -> filename.sol -> license 中
第 2 行 pragma solidity ^0.8.17; 指令,它是告诉编译器,我当前的合约代码采用的是 Solidity 0.8.17 这个版本为基础编写的,解析部署时需要在匹配的版本下进行,在区块链浏览器上进行合约验证时,也需要选择匹配的版本。
⓵ 使用 ^ 的意义和优点
而 ^0.8.17 中的 ^ 表示小版本兼容,大版本不兼容,相当于 pragma solidity >= 0.8.17 < 0.9.0;。他既不允许低于0.8.17的编译器编译,也不允许大于等于 0.9.0 版本的编译器进行编译。之所以这么写,不写死 pragma solidity 0.8.17; 是为了可以享受到编译器的补丁版,比如以后出来了 0.8.40 版本,那么当前合约是可以运行在未来的 0.8.40 这个新版本的编译器。但是如果是大版本升级到了 0.9.0,那么编译器不会用新版的0.9.x解析,会使用 0.8 的最后一个稳定版本来进行解析编译。这里如果不加 ^,直接写pragma solidity 0.8.17;,就是告诉编译器,当前合约只选择在 0.8.17 版本来编译和部署;这样做的缺点就是享受不到以后出的补丁版的编译器。
⓶ 跨大版本的合约
如果你打算跨大版本的合约,可以使用> />=/</<=来操作,比如 pragma solidity >=0.7.0 <0.9.0;。
注意:
pragma指令只对当前的源文件起作用,如果把文件B导入到文件A,文件 B 的 pragma 将不会自动应用于文件 A。
总结:
pragma solidity ^0.8.17;是用来告诉编译器应该选择什么版本来解析编译当前代码。
pragma指令只对当前的源文件起作用。注:一份源文件可以包含多个版本声明、多个导入声明和多个合约声明。
第 3 行的 contract Hello {} 是合约的基本结构;其中 contract 声明了当前代码块内是一个完整的合约。而 Hello 是当前合约的名字,合约名称是必须的,首字母一般采用大写字母开头。
contract 代表特殊的意义,这种有特殊意义的词,在编程界里一般被称为 保留关键字;保留关键字是现在或者将来被用到的特殊代指,都有固定意义,所以保留关键字不能作为变量名和函数名字。
总结:
contract 基本结构是 contract ContractName {}
Solidity 合约中,合约的名字是必须的。
合约的名称,一般约定为 大驼峰命名方式
contract 是保留关键字
保留关键字不能作为变量名和函数名
扩展:
注:一份源文件可以包含多个版本声明、多个导入声明和多个合约声明。
⓵ 变量
合约内的 message 叫做状态变量,状态变量是永久地存储在合约存储中的值。关于变量的更多信息,会在后续 [变量] 那一章详细介绍
⓶ 函数
函数是代码的可执行单元,是一组逻辑的集合。关于变量的更多信息,会在后续 函数 那一章详细介绍
⓷ this 关键字
Solidity 中 this 代表合约对象本身;
可以通过 address(this) 获取合约地址。
可以通过 this.fnName 获取 external 函数
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
function contractAds() external view returns (address) {
return address(this);
}
function testExternal() external view returns (address) {
return this.contractAds();
}
}
⓸ 合约地址/合约创建者地/合约调用者地址
这三个地址概念一定要完全理解。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// 这三个地址的概念一定要理解清楚
contract Demo {
address public owner;
constructor() {
// 可以用在 constructor 内获取当前合约地址
owner = address(this);
// 不可以在构造函数内调用函数,因为此时合约还没有完成构建好。
// this.caller(); 相当于从外部调用 caller 方法
// owner = this.caller();
}
function caller() external view returns (address) {
return this.contractAds(); // 内部调用 external 可见性的函数
}
function contractAds() external view returns (address) {
return address(this);
}
}
⓹ 合约属性:type 关键字
type(C).name:获得合约名
type(C).creationCode:获得包含创建合约字节码的内存字节数组。它可以在内联汇编中构建自定义创建例程,尤其是使用 create2 操作码。 不能在合约本身或派生的合约访问此属性。 因为会引起循环引用。
type(C).runtimeCode:获得合约的运行时字节码的内存字节数组。这是通常由 C 的构造函数部署的代码。 如果 C 有一个使用内联汇编的构造函数,那么可能与实际部署的字节码不同。 还要注意库在部署时修改其运行时字节码以防范定期调用(guard against regular calls)。 与 .creationCode 有相同的限制,不能在合约本身或派生的合约访问此属性。 因为会引起循环引用。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Hello {
string public message = "Hello World!";
}
contract Demo {
function name() external pure returns (string memory) {
return type(Hello).name;
}
function creationCode() external pure returns (bytes memory) {
return type(Hello).creationCode;
}
function runtimeCode() external pure returns (bytes memory) {
return type(Hello).runtimeCode;
}
}
除了上面介绍的版权声明,版本限制,contract 外,合约文件还包括 import, interface,library,一起展开介绍下
功能:从其他文件内倒入需要的变量或者函数。
⓵ 导入方式
既可以导入本地文件,也可以导入 url(网络上的 ipfs,http 或者 git 文件)
导入所有的全局标志
import "filename";
到当前全局范围
导入本地文件:import "./ERC20.sol";,其中./表示当前目录,查找路径参考
导入网络文件:import "https://github.com/aaa/.../tools.sol";
导入本地 NPM 库:
$ npm install @openzeppelin/contracts
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
导入所有的全局标志,并创建新的全局符号
方式一:
import * as symbolName from "filename";方式二:
import "filename" as symbolName;
按需导入,按需修改名称
import {symbol1 as aliasName, symbol2} from "filename";
不推荐导入变量标示名到当前全局范围的方式,因为不可控,容易污染当前的命名空间。如果全局导入,推荐使用 import "filename" as symbolName;
注:一份源文件可以包含多个版本声明、多个导入声明和多个合约声明。
⓶ 导入时候的本地路径
上文中的 filename 总是会按路径来处理,以 / 作为目录分割符、以 . 标示当前目录、以 .. 表示父目录。 当 . 或 .. 后面跟随的字符是 / 时,它们才能被当做当前目录或父目录。 只有路径以当前目录 . 或父目录 .. 开头时,才能被视为相对路径。
用 import "./x.sol" as x; 语句导入当前源文件同目录下的文件 x.sol 。 如果用import "x.sol" as x; 代替,可能会引入不同的文件(在全局 include directory 中)。
最终导入哪个文件取决于编译器(见下文)到底是怎样解析路径的。 通常,目录层次不必严格映射到本地文件系统, 它也可以映射到能通过诸如 ipfs,http 或者 git 发现的资源。
⓵ 接口使用案例
在下面的例子中,定义了 cat 合约以及 dog 合约。他们都有 eat 方法.以此他们都可以被上面的 animalEat 接口所接收。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Cat {
uint256 public age;
function eat() public returns (string memory) {
age++;
return "cat eat fish";
}
function sleep1() public pure returns (string memory) {
return "sleep1";
}
}
contract Dog {
uint256 public age;
function eat() public returns (string memory) {
age += 2;
return "dog miss you";
}
function sleep2() public pure returns (string memory) {
return "sleep2";
}
}
interface AnimalEat {
function eat() external returns (string memory);
}
contract Animal {
function test(address _addr) external returns (string memory) {
AnimalEat general = AnimalEat(_addr);
return general.eat();
}
}
⓶ type(I).interfaceId
返回接口I 的 bytes4 类型的接口 ID,接口 ID 参考: EIP-165 定义的, 接口 ID 被定义为 XOR (异或) 接口内所有的函数的函数选择器(除继承的函数。
上面的代码种,可以增加如下的函数来查看 interfaceId;
contract Animal {
// ...
function interfaceId() external pure returns (bytes4) {
return type(AnimalEat).interfaceId;
}
}
更多内容在interface:接口那一章详细介绍。
库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过 EVM 的特性来复用代码。
library Set {
struct Data { mapping(uint => bool) flags; }
function test(){
}
}
其他合约调用库文件的内容直接通过库文件名.方法名例如:Set.test()。
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
对于我正在编写的Rails3应用程序,我正在考虑从本地文件系统上的XML、YAML或JSON文件中读取一些配置数据。重点是:我应该把这些文件放在哪里?Rails应用程序中是否有用于存储此类内容的默认位置?附带说明一下,我的应用程序部署在Heroku上。 最佳答案 我经常做的是:如果文件是通用配置文件:我在目录/config中创建一个YAML文件,每个环境有一个上层key如果我为每个环境(大项目)创建一个文件:我为每个环境创建一个YAML并将它们存储在/config/environments/然后我在加载YAML的地方创建了一个初始化
在我的mac上安装几个东西时遇到这个问题,我认为这个问题来自将我的豹子升级到雪豹。我认为这个问题也与macports有关。/usr/local/lib/libz.1.dylib,filewasbuiltfori386whichisnotthearchitecturebeinglinked(x86_64)有什么想法吗?更新更具体地说,这发生在安装nokogirigem时日志看起来像:xslt_stylesheet.c:127:warning:passingargument1of‘Nokogiri_wrap_xml_document’withdifferentwidthduetoproto
前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型
目录H2数据库入门以及实际开发时的使用1.H2数据库的初识1.1H2数据库介绍1.2为什么要使用嵌入式数据库?1.3嵌入式数据库对比1.3.1性能对比1.4技术选型思考2.H2数据库实战2.1H2数据库下载搭建以及部署2.1.1H2数据库的下载2.1.2数据库启动2.1.2.1windows系统可以在bin目录下执行h2.bat2.1.2.2同理可以通过cmd直接使用命令进行启动:2.1.2.3启动后控制台页面:2.1.3spring整合H2数据库2.1.3.1引入依赖文件2.1.4数据库通过file模式实际保存数据的位置2.2H2数据库操作2.2.1Mysql兼容模式2.2.2Mysql模式
2022年底,OpenAI的预训练模型ChatGPT给人工智能领域的爱好者和研究人员留下了深刻的印象和启发,他展现的惊人能力将人工智能的研究和应用热度推向高潮,网上也充斥着和ChatGPT的各种聊天,他可以作诗、写小说、写代码、讨论疫情问题等。下面就是一些他的神回复:人命关天的坑: 写歌,留给词作者的机会不多了。。。 回答人类怎么样面对人工智能: 什么是ChatGPT?借用网上的一段介绍,ChatGPT是由人工智能研究实验室OpenAI在2022年11月30日发布的全新聊天机器人模型,一款人工智能技术驱动的自然语言处理工具。它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动