EVM
EVM运行在以太坊节点上。 对标JVM,在EVM上运行的是智能合约的字节码形式
搭建测试网络
Ganache geth?忽然好奇怎么搭建区块链的。。源代码是啥样的。。 记得买本计算机网络
1ETH=1e9GWei=1e18Wei
smart contract 法律条文,只有有人触发交易才会自动执行
learnblockchain.cn/2018/01/04/… 区块链实际上不记录余额,账本记录的是每个交易记录,要确定余额,需要计算网络上的所有交易信息
状态变量,局部变量
contract Person{
int public _age;
string public _name;
function Person(int age,string name){
_age = age;
_name = name;
}
fuction changePerson(string name){
var age1=age;
}
}
//其中_age,_name为状态变量,为storage存储
//函数中的形参age,name为局部变量,memory存储
- 写的还挺好的 blog.51cto.com/zero01/2351…
- 区块链学习路线图 blog.csdn.net/xilibi2003/…
- doc docs.soliditylang.org/en/v0.8.13/…
- solidity语法规范
blog.csdn.net/xilibi2003/…
呃但是在掘金里代码有空行代码就不特殊显示了,算了这样也很难看,有空换成独立代码块
- [ ] 回退函数(fallback fucntion是什么?) 一个合约里(只?)能有一个匿名函数,。。。then?合约的构造函数意义是啥,感觉solidity好像也是面向对象的coding? - 参数解构、元组tuple
- 记得重新学一下分布式
- 设计模式全忘了捏
view constant pure区别,【再找点别的文章读一下,比如在实际coding的时候什么情况更适合加这些view和pure。】
三个关键词都是不消耗gas,不读取/不改变状态变量 view和constant都是只可读不可改 pure是不可读也不可改
- [ ] external是什么关键字?
以太坊有三个可以存储项目的区域(花的gas是不同的)
1.存储storage:所有合约状态变量所在的位置。 2.内存memory:保存临时值并且在(外部)函数之间调用,便宜。 3.堆栈stack:保存小的临时局部变量,几乎免费,但只能保存有限数量的值 zhuanlan.zhihu.com/p/54167802 4.调用数据calldata:(?有的是123,有的是124,因为版本嘛?) 5.内存数据存储结构,插槽slot blog.csdn.net/rfrder/arti… 6.映射mapping只能存在storage中,数组和结构体可以声明storage或memeory
stroage和memeory的转换问题(?但是我好像没分清楚拷贝和存储和引用的区别,底层概念不清,囫囵吞枣)
1.storage转换为storage,更改的是内存,内存随之改变。 2.storage转换为memory,将数据从storage拷贝到memory中,不会改变原storage变量 3.memory转换为storage,前提是必须吧storage转换为memory类型的,否则会出错 (噢这么说好像很难理解,应该是这个样子a.memory赋值给默认storage的状态变量,可以,实际是将内存变量拷贝到存储中。 !!(高亮) b.memeory赋值给设置为storage的局部变量(默认memeory),不可以。 4.memory转换为memory,引用传递。
solidity的值类型(以下类型在传递时采用值传递)
1.boolean 2.integer:int/uint(uint8-uint256)默认为unit256 uint8=255 3.strinng(Solidity并不支持原生的字符串比较,需要使用keccak256哈希值来进行比较
require(keccak256(_valueName1) == keccak256(_valueName2));
//如果上述语句为true,则运行下述语句返回hi。否则停止并抛异常
return "hi";
4.address a.以太坊长度的地址为20个字节 20*8=160位(//40位的16进制)。可以用uint160位的编码。 b.分为外部地址和合约地址(?不太清除具体是啥还,o调用它的地址和合约本身地址?) c.地址之间支持大小比较 d.地址是所有合约的基础,所有合约都会继承地址对象,通过合约的地址串,调用合约的内部函数。//balance是属性,transefer/send等是方法)
每种区块链地址的长度是不同的
a.btc:34 b.eth:42(包含了前缀的0x) c.solana:44 ...
四种类型运算符
1.算术运算 2.逻辑运算 3.增量运算 4.按位运算
msg.sender/msg.value
solidity中最常见的api。还有很多常见的,可见blog.51cto.com/zero01/2351… 1.在solidity中,有些全局变量可以被所有函数调用,msg.sender就是其中之一,它是指当前合约或调用者的address。 2.在solidity中,功能的执行者始终需要从外部执行者来开始。一个合约在公链上不做事情(?是我理解的就是别人只做读操作?没啥用吗),除非有人调用其中的函数,所以msg.sender总是存在。
//更新合约余额
contract MappingExample{
mapping(address=>uint) public balances;
//一个更新合约余额的函数,将合约余额更新为newBalance。其中因为uint,所以前面需要mapping函数来从地址映射到uint
function update(uint newBalance){
balances[msg.sender] = newBalance;
}
}
contract MappingUser{//创建用户实例,返回合约更新后的余额
//通过函数来实现
fuction getUpdate() returns(uint){
MappingExample m = new MappingExample();
m.update(100);
return m.balance(this);
}
}
solidity的数据结构
1.struct a.自定义类型,最多16个成员
- b.*若函数以结构体作为参数,则函数修饰符必有private/internal(??为什么啊)
c.结构体内部不可以包含自己本身,但可以有结构体和mapping(?虽然不知道放mapping可以干啥,懒得找例子。。好困。。。再也不熬夜了4/27),并且可以不初始化他们。
2.array
a.编译时可以动态,可以固定长度
b.可以是struct类型
c.将array设为public 时自动提供getter方法
【
一些关于数组的细碎,没找到地儿放
1.注意固定长度array不能直接转换为动态数组(不能直接returns(bytes(name),先设置动态数组,把数据复制进动态数组,返回该动态数组) 2.动态数组可以直接转(returns(string)),固定长度array不能直接转string,先转动态array再转String 3.solidity支持二维数组,但是懒得仔细看,二维不支持put方法加数据。有空再细看。 】 3.mapping a.mapping(键类型=>值类型),建立键值对的对应关系。 b.例如在msg.sender中的代码里balances[msg.sender]=newBalance;是指将newBalance这个参数的值和msg.sender这个地址对应起来。 c.key类型有限制,struct不能作为key,实际上key的种类是有限的,比如struct和动态数组,枚举,contract等等都不能作为key d.value类型没有限制,甚至mapping类型也可以 e.solidity中mapping的key存储并不在其中,而是用的这个key的keccak256的值,所以solidity的mapping并没有length的概念,也没有set方法来添加或者设置映射(?其实不太理解为啥没有set,是因为之是自身直接写代码去映射嘛?不需要set来添加新的key-value对应?)。 f.mapping被申明为storage而非memeory(?不懂//嗷好像懂了因为mapping是也是一个合约里声明的数据结构,本质上和integer之类的没区别,所以sturct也是storage类型?) - [ ] stroage/memeory的值传递/指传递并没搞明白(24/4),等下再看,默认局部变量如的函数参数(包括返回值)是memeory,默认状态变量是storage没错吧?晕了怎么写的都不一样,stroage和memory的转换还没搞明白,先搞明白这个吧,//搞明白了已经,写在了上边(25/4)。
//map例子:合约把地址,id和名字联系到一起
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract MappingDemo{
mapping(address=>uint) mapATU;
mapping(uint=>string) mapUTS;
uint public num=0;
//当用户注册时。
function register(string memory name) public {
//用户的地址是已知的
address account = msg.sender;
num++;
mapATU[account] = num;
mapUTS[num] = name;
}
//获取地址所绑定的id
function getId(address a) returns(unit){
return mapATU[a];
//!!和Java其实一样的,不是唯一映射,不能反推,只是键值对,从key推value,这里因为代码的id是直接++,设计的时候逻辑重复,就是不能从id反推address。(所以咋设计id捏。。。忽然愣住)(如下设计)
}
}
//oooo噢噢噢噢4/26日,利用后面的modifier函数可以解决这个地址重复注册id的问题
contract MappingModifierDemo{
mapping(address=>unit) mapATU;
mapping(unit=>string) mapUTS;
unit num=0;
//如果是新用户,从来没有注册过id,那么利用他的addres的映射value应该为空
modifier isnew{
//mapATU(msg.sender)==null; 。。。呃,这样写对不对,是modifier必须用require嘛??//傻了?我上午在想什么呢??只写一个语句当然不对,这不就直接赋值为null嘛。或许。。我猜if(){}break;可以?但是require只需要一行。
require(manATU(msg.sender))==0;
}
//首先把当前地址和unit结合。
function combine() isnew{
num++;
mapATU(msg.sender)=num;
}
}
- 变量的作用域
solidity的控制结构
if,else,while,for(;;),do,break,continue...(和java/c之类的没什么区别)
构造函数
5.x以上的版本可以constructor(参数列表){} 4.x的版本只能function 合约名(参数列表){}
miodifier函数
我觉得把本质上可能就是为了能够简单的重用代码而已,也支持有(多个)参数,在调用时传参。但是当其他函数在调用多!个!modifier函数的时候应该注意一下他们语句之间的编译顺序,并不是简单的按顺序执行,会按每个函数第一个语句执行然后再倒叙,说不清楚。。代码能看的很简单。下次遇到再仔细看把。
constract demo01{
addrress public owner;
function demo01() public{//?不知道构造是不是一定要publci
owner=msg.sender;
}
//modifier函数!可以重复使用代码。
modifier confitAddress{
reqiire(msg.sender=owner);
}
fuction a() public confirAddress{
//会先执行require语句判断是否是当前合约调用者,如果不是则回滚
//如果是就执行下列语句
unit a;
...
}
}
继承
1.关键字:is
2.继承权限:默认不写、public、internal、external(属性没有这个关键字,方法有)的属性和方法都可以被子类继承和调用。但private修饰的不能
(回忆一下java里其实各个关键字也是,但本质上子类也继承了父类的private的属性方法,只是这些被private修饰的属性和方法都不允许被使用被更改被调用,不知道solidity是不是也是这样)
3.*注意一下public,external和internal的继承范围,
a.external可以被合约外部继承,在合约内部不行。
b.internal可以被合约内部继承,在合约外部无效。
(这个内外部??。。emmm不是很理解。所谓外部和内部,看到一句话是这样说的“以remix举例,在内部就是指合约内部可以调用这个函数,在外部就是指合约部署之后可以在旁侧看到这个函数的按钮。”)
4.支持多重继承
contract child is father,mather{}
5.继承external的使用方法
使用this
//创建带external方法的父类
contract AnimalDemo{
function eat() external return(string){
return "eat sth";
}
//external关键字不允许直接在合约内部调用,想要调用要用到this关键字
function eatExternal return(string){
return this.eat();
}
}
//创建调用external方法的子类
contract CatDemo is AnimalDemo{
//使用父类对象调用external方法
function eatExternal() return(string){
AnimalDemo a = new AnimalDemo();
a.eat();//..java里私有成员被继承后,不能通过对象访问父类私有属性和私有方法来着对吧……这样solidity是简单了一点,但底层逻辑是什么捏,对象为啥能调用??。。忽然想到,external他不是本来就可以在合约外部调用嘛,。。可以不用这么绕的。。这有什么必要吗。。。可以但没必要的感觉?也可能是我太naive。。如下,可以直接this
//使用this关键字在合约外部调用eaxternal方法
function eatExternal return(string){
return this.eat();
}
}
}
payable关键字
涉及币的转移时要用到,表明调用该函数时可以附带以太币
function pay() public payable{}
构造函数
*别忘了构造里不能用this就行,当前并没有完成创建
fallback function回调函数
1.一个合约里有且只有一个fallback function
2.没名字,没参数,external修饰
3.自己调用不了,子合约调用不了,只有当异常的时候
3.涉及转账的带payable的合约中必须有fallback function,没有paybable修饰的函数,则该合约无法接受别人转账
function() public payable{}
看一下详解www.cnblogs.com/wanghui-gar…
transfer、send异同
1.account.trasfer(msg.sender)/send(1)/call(1);都是向account账户转账,就这点比较反人类。。 2.看烦了。。0.5.x以后的版本好像有大改动。。算了别烦了好像这个很重要。。。。明天看。。。(4/26)
合约间的调用call delegateCall、callCode
gas
1.参考了一下zhuanlan.zhihu.com/p/380038608
2.eth的gas limited的默认金额是21000gas
3.fee=gas used*gas price
4.使用external的函数所消耗的gas少于使用public的,所以当一个函数只能被外部调用时最好使用external关键字
5.有时候觉得modifier和if语句都只有一句话,那么哪个更费钱啊??
比如说
modifier control(){
require(msg.sender == owner);
}
function kill() conrtol{
destruct(msg.sender);
}
和
function kill){
if(msg.sender==owner){
destruct(msg.sender);
}
}
。。原来这么看还是能少代码的?这样算少钱了嘛?还是说其实是一样的?比如说pure之类的不是不花gas?如果省钱了的话,那以后要注意不能直接主观臆断modifier更简单省钱。没省钱也注意一下代码的简洁性。
6.不太清楚各个代码都要花费多少,比如新建一个简单函数贵还是新建一个数据贵呢?我猜storage数据更贵?明天再看。。累了。。(2022/4/26) (4/27日view,constant,pure函数都是不消耗gas的,还有external函数消耗gas少)
abi编码
调用合约函数是在以太网中提交了一个行为,这个行为会附带一个数据,即abi的编码数据。solidity提供了api来获取这个编码数据。常用abi.encodeWithSignature【等会可以看一下官方abi
异常处理
比较特殊,没有try/catch。有assert,require,revert。solidity是通过回退来处理异常的(类似数据库事务) 1.发生异常时,会回退当前所有调用及其子调用所改变的状态,并给调用者一个标识,但是所花费的gas是不会退回的。 2.assert:判断内部错误 require:判断输入或外部错误 (呃。。看了半天还是觉得assert和require区别不大。。。主要就是内外部。?但是之前看到得代码里大家内部也用的require?) 3.assert类型异常时,会消耗掉所有gas(?是啥意思,把gas limited全用了??例如死循环啥的?),solidity会执行一个无效操作(指令0xfe) 4.require类型异常时,不会消耗?(为啥不..不是说不退回嘛。。饿了。。等会再看),solidity执行一个回退操作(指令0xfd) 5.在子调用中发生异常时自动上抛,但也有例外,如send和底层的函数调用call,delegatecall,callcode,发生异常时,这些函数返回false。 6.*注意,在一个不存在的地址上调用底层函数call,delegatecall,callcode时也会返回成功,所以在调用时,一定要先进行函数存在性检查
合约销毁/selfdestruct函数
1slefdestruct(address reciepient)将合约销毁并把余额发送到指定地址
function kill() { selfdestruct(this); }
2.允许合约在某个时间时自动停止,然后其他代码放到modifier声明的函数利用require来设置一个expired date。。(有别的关键字嘛,感觉这个嵌套不太合理还好费钱。。。)
- csdn回我的那个bug评论,关于uint与address 的转换,uint允许2,10,16进制,address是uint160标识,那不是可以直接转换?其他的unit类型通过先uint160来转换成address,那如果小于unit160,会不会丢数据啊。。。?可以强转嘛,明天再看。
ERC20/ERC721/ERC1155
ERC20 zhuanlan.zhihu.com/p/394288720 ERC1155 zhuanlan.zhihu.com/p/389331603 erc接口实现实例blog.csdn.net/BBinChina/a…
SOLIDITY的代理proxy
accessControl
enum枚举
事件
当智能合约被调用,会触发参数存储到日志中。每个交易包含0-n个日志记录,日志代表智能合约所触发的事件。 在Dapp中,如果监听了某事件,当某事件发生时,会进行回调。 我的理解里事件其实就是用在和web3.js的交互。类似java里的监听与前端页面。比如说如果没有事件,当我们在前端登录页面提交了用户名和密码,页面也没有刷新。但是有事件和监听的话,设置触发后,页面会随之刷新。
contract eventDemo{
string name;
uint age;
//定义一个事件
event setInfo(string name,uint age);
function setInfo(string _name,uint _age){
name=_name;
age=_age;
//触发这个事件
emit setInfo(_name,_age);
}
function getInfo returns(string,uint){
return name,age;
}
}
receive函数
using Counters for Counters.Counter;
function random() private view returns (uint256) {
return uint256(
keccak256(
abi.encodePacked(
block.timestamp
+ block.difficulty
+ uint256(keccak256(abi.encodePacked(block.coinbase))) / block.timestamp
+ block.gaslimit
+ uint256(keccak256(abi.encodePacked(msg.sender))) / block.timestamp
+ block.number
)
)
);
}
for (uint256 i = 0; i < payees.length; i++) {
(bool sent,) = payees[i].call{value : balance}("");
require(sent, "withdraw: Failed to send Ether");
}
openzeppling的library都提供了啥。。readMe里面好像有,再看一下
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
oprnzeppling库里的onlyRole函数源码,提供的OwnerShip blog.csdn.net/super_lixia…
solidity的override也是重写嘛?
编译到部署上链的数据流程
实例blog.csdn.net/m0_46262108… www.cnblogs.com/hushuning/p…
solidity的接口区别不大