整型uint 和 int
uint 是默认指uint256,只能是非负的整数,以太坊严格规范,金额类型的数据都使用uint
int 默认是int256,可正可负。
uint和int之间不能相互转化
对于智能合约一定要合理安排整数类型声明,防止整数溢出的问题。
固定长度bytes类型
和uint是一样的,bytes默认是bytes1,byte1相当于uint8,bytes可以从1写到bytes32,bytes32其实就是uint256,它的长度其实就是对应的后缀数字,1-32,以一增加,但是其长度属性是不可以修改的。
动态长度bytes类型
定义方式需要bytes对象
bytes name = new bytes(2) //定义一个长度为2的bytes类型
可以修改每个值,也可以修改长度,或通过数组的push方法也可以改变长度
name[0] = 0x7a
name[1] = 0x88
name.length=5
name.push(0x99)
bytes类型一般用于十六进制的数据存储,固定长度bytes数据可以进行长度的转换,通过bytes1,截取长度为1,bytes2截取长度为2,如果截取长度大于该bytes字节的长度,那么补0。
bytes name = 0x7a68
bytes1(name) //0x7a
bytes2(name) //0x7a68
bytes3(name) //0x7a6800
固定长度bytes数据可以转动态长度字节数组,通过new bytes(bytes)的方式,然后创建一个新的动态长度bytes对象,通过for循环的形式将每个字节添加到新的bytes对象即可。
bytes2 name = 0x7a68
bytes newName = new bytes(name.length)
for(uint i;i<name.length;i++){
newName[i] = name[i]
}
string类型
string类型不能直接获取长度,必须通过转为bytes类型后才可以,通过bytes的[index]获取内容,获取到的是十六进制的数据,修改单个字节也是通过bytes类型。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Demo{
string name = "name";
function getLength() view public returns (uint){
return bytes(name).length;
}
function getName() view public returns (bytes memory){
return bytes(name);
}
function changeName() public returns (string memory){
bytes(name)[0] = 'L';
return name;
}
}
如果name为汉字,那么所一个汉字占三个,特殊字符占一个字节。
动态长度bytes转string:
bytes name = new Bytes(2);
name[0] = 0x7a;
name[1] = 0x68;
function transfer() public view returns(string){
return string(name);
}
固定长度bytes转string:分两步,固定长度bytes转为动态长度bytes,将动态长度bytes转为string
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Transfer{
bytes2 name = 0x7a68;
function changeToDynamic() public returns (string memory){
bytes memory newName = new bytes(name.length);
//转为动态数组
for(uint i=0;i<name.length;i++){
newName[i] = name[i];
}
return this.transferTostring(newName);
}
function transferTostring(bytes memory _name) public returns (string memory){
return string(_name); //动态长度字节转为字符串
}
}
固定数组
可以获取长度,但是长度length是不可以改变的
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Array{
uint [5] arr = [1,2,3,4,5];
function Init () public{
arr[0] = 1;
arr[1] = 100;
}
}
动态数组:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Array{
uint [] arr = [1,2,3,4,5];
function Init () public{
arr[0] = 1;
arr[1] = 100;
}
function getArray() view public returns (uint [] memory){
return arr;
}
function changeLength() public {
arr.push(); //添加长度
}
function changeLength2() public {
arr.pop(); //减少数组长度
}
}
固定长度二维数组
定义语法法与其他语言有区别,一维数组的长度取决于后一个长度参数,二维长度取决于第一个参数,但是获取时和其他的一致,下面固定长度的也不能修改长度:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract TwoArray {
uint256[2][3] arr = [[1, 2], [3, 4], [5, 6]];
function add() view public returns (uint256) {
uint256 sum=0;
for (uint256 i = 0; i < arr.length; i++) {
for (uint256 j = 0; j < arr[0].length; j++) {
sum += arr[i][j];
}
}
return sum;
}
function change() public returns (uint256 [2][3] memory) {
for (uint256 i = 0; i < arr.length; i++) {
for (uint256 j = 0; j < arr[0].length; j++) {
arr[i][j]=4;
}
}
return arr;
}
}
动态长度二维数组
可修改长度。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract TwoArray {
uint256[][] arr = [[1, 2], [3, 4], [5, 6]];
function add() public view returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
for (uint256 j = 0; j < arr[0].length; j++) {
sum += arr[i][j];//求和
}
}
return sum;
}
function change() public returns (uint256[][] memory) {
for (uint256 i = 0; i < arr.length; i++) {
for (uint256 j = 0; j < arr[0].length; j++) {
arr[i][j] = 4; //改值
}
}
return arr;
}
function getData() public view returns (uint256[][] memory) {
return arr;
}
//修改长度
function changeLength() public returns (uint256[][] memory) {
arr[0].push(3);
arr[1].pop();
return arr;
}
}
数组字面量:
不可以修改数组的内容以及长度 。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Array{
function getArray() pure public returns ( uint8[3] memory){
return [1,2,3];
}
function getArray2() pure public returns ( uint[3] memory){
return [uint(1),2,3];
}
}
上图使用字面量的形式返回数组,在定义数组时,uint会默认最小匹配原则,从uint8开始,如果存储数据超过255,则使用uint256,所以在上述function的返回值中,使用uint8数据类型。第二种方式可以指定存储数据的类型,给数组第一个元素添加即可,剩下的元素则默认为uint数据,此时默认是uint256。
address类型
在比特币的网络中是没有账户地址的概念,但在以太坊中是有的,包括合约账户地址和外部账户地址,是一串16进制的数据字符串。address是通过uint160进行存储的,两种类型可以相互强制转化,地址之间是可以进行比较大小的。
address account = 0x4D26100f60525C5Ef35DA5B4dFc7B80d55050574
uint160 num = uint160(account)
address addr = address(num)
payable,支付功能(remix调试)
函数中需要使用payable关键字,我们可以通过这个函数给合约地址转账,这里的金额为wei的单位。可以看到当前的this其实就是当前合约的地址,既然我们可以通过this.balance获取余额,那么我们也可以使用地址来获取不同账户的余额。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract PayTest{
function pay() payable public {
}
function getBalance() public view returns(uint){
return address(this).balance;
}
function geThis() public view returns(address){
return address(this);
}
function getOtherBalance(address account) public view returns(uint){
return account.balance;
}
}
给不同的账户进行转账
function transfer() public payable {
address account = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB;//其他的账户
payable (account).transfer(msg.value);
}
如果此transfer函数函数内部函数体为空,那么就会默认转到当前的合约账户,同时也可以修改为有参数的函数,不使用msg.value。如果出现msg.value的以太币数量大于代码里转账的以太币数量,那么多余的数量会直接转至当前合约账户。
常用全局变量
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Global{
function getDlobal() public view returns (address){
return msg.sender;
}
}
msg.sender(address):合约调用者账户地址。
msg.data(bytes):完整的调用数据。
msg.sender(address):当前调用发起人的地址。
msg.value(address):这个消息所携带的以太币,单位是wei。
block.coinbase(address):当前矿工地址。
block.difficulty(uint):当前矿的难度。
block.number(uint):当前区块号。
block.timestamp(uint):时间戳。
now(uint):当前块的时间戳。
tx.gasprice(uint):交易的gas价格。
tx.origin(address):交易的发送者。
send函数
function send() public payable returns(bool){
address account = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB;
return payable (account).send(msg.value);
}
一般不用,底层函数会有点问题,有时候出现问题也不会报错,与transfer的用法一样。
调用递归深度不能超过1024,如果gas不够会执行失败,使用这个方法要检查结果是否成功,transfer相对比较安全。
mapping 映射
一对一的映射关系,一个mappig对象可以有多个数据,但是必须遵循相应的映射关系。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Mapping{
mapping(address => uint) id;
mapping( uint => string) name;
uint sum = 0;
function register(string memory _name) public {
sum++;
id[msg.sender] = sum;
name[sum] = _name;
}
function getId(address _address) public view returns (uint){
return id[_address];
}
function getName(uint _id) public view returns (string memory){
return name[_id];
}
}
函数
遵循以下模版条件
function funName(paramType paramName) { public | internal | external | public} [pure | constant | view | payable] [ returns (type) ]
重载:函数名相同,传入参数不同会有不同的功能,不考虑函数返回值是否相同。但是要注意如果参数类型为uint以及uint8,如果传参255以内的数据,会出现报错的情况,因为两者都包含对应的数据,如果传参256就不会报错了,uint8的最大值就是255。
还有个特殊的类型,uint160和address类型本质是一样的,所以这两个无论何时报错的。不可以作为判断的条件
function test(address _address) public{}
function test(uint160 _id) public{}
function test2(uint id) public{}
function test2(uint8 id) public{}
函数传参
在进行传参时,可以正常传参,也可以以对象的形式传参(可以不考虑传参的顺序),在直接调用时可以只传一个参数(函数设置两个参数),但是如果在其他函数中调用时,必须严格遵守传入所有的参数。
函数返回值
返回值要有数据类型,同时可以有参数名称,也可以没有,有个小技巧,修改函数参数不用return。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract Return{
function mul() public pure returns(uint num){
num = 100;
}
function mul2() public pure returns(uint num){
num = 100;
return 10
}
function getResult(uint a,uint b) public pure returns(uint add,uint x){
add = a+b;
x = a*b;
}
}
如果Return和参数修改都存在的话,以Return的值为准,同时可以返回多个参数,可以进行简化:
function getResult(uint a,uint b) public pure returns(uint add,uint x){
return (a+b,a*b)
}
可以用于值的交换。
作用域
同一作用域中不能重复定义相同变量,遵循作用域和作用域规则。
权限修饰
public,internal,external函数可以被继承,private只能合约内部使用,不能被继承,外部不能被调用。
public在合约的内部外部,子合约都可以调用。
internal不能被外部调用,但是可以在合约内部以及子合约的内部使用。
external:只能在合约外部调用,继承的合约内部也不可以调用。但也可以通过this来调用,在这里是通过地址来调用,原理也是通过外部的形式调用。
在new关键字下创建的合约对象也可以使用external方法。
继承:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
contract father{
function test() public pure returns(string memory){
return "Father";
}
}
contract son is father{
function oops() public pure returns(string memory){
return test();
}
}
contract Test{
father f =new father();
function opt() public view returns(string memory){
return f.test();
}
}