solidity汇编语言汇总
注:solidity内联汇编中都是以字节为单位的,[0x00,0x20)表示的范围是2^32-1,但在计算机中占了256位,256 /8=32=0x20
和 solidity
一样,Assembly 也会解析注释、文字和标识符,所以你可以使用通常的 //
和 /* */
来进行注释。 内联汇编程序由 assembly { ... }
来标记,在这些大括号内可以使用以下内容。
0x123
、42
或 "abc"
(不超过 32 个字符的字符串)mload sload dup1 sstore
,操作码列表请看后面add(1,mlod(0))
name:
let x := 7
、let x := add(y, 3)
或者 let x
(初始值将被置为 empty(0))jump(name)
、3 x add
3 =: x
x := add(y,3)
{let x := 3 { let y := add(x,1) }}
注:在下表中,mem[a...b)
表示从位置 a
开始至(不包括)位置 b
的内存字节数,storage[p]
表示位置 p
处的存储内容。
Instruction | Explanation | ||
---|---|---|---|
stop | - | F | 停止执行,与 return(0,0) 等价 |
add(x, y) | F | x + y | |
sub(x, y) | F | x - y | |
mul(x, y) | F | x * y | |
div(x, y) | F | x / y | |
sdiv(x, y) | F | x / y,以二进制补码作为符号 | |
mod(x, y) | F | x % y | |
smod(x, y) | F | x % y,以二进制补码作为符号 | |
exp(x, y) | F | x 的 y 次幂 | |
not(x) | F | ~x,对 x 按位取反 | |
lt(x, y) | F | 如果 x < y 为 1,否则为 0 | |
gt(x, y) | F | 如果 x > y 为 1,否则为 0 | |
slt(x, y) | F | 如果 x < y 为 1,否则为 0,以二进制补码作为符号 | |
sgt(x, y) | F | 如果 x > y 为 1,否则为 0,以二进制补码作为符号 | |
eq(x, y) | F | 如果 x == y 为 1,否则为 0 | |
iszero(x) | F | 如果 x == 0 为 1,否则为 0 | |
and(x, y) | F | x 和 y 的按位与 | |
or(x, y) | F | x 和 y 的按位或 | |
xor(x, y) | F | x 和 y 的按位异或 | |
byte(n, x) | F | x 的第 n 个字节,这个索引是从 0 开始的 | |
shl(x, y) | C | 将 y 逻辑左移 x 位 | |
shr(x, y) | C | 将 y 逻辑右移 x 位 | |
sar(x, y) | C | 将 y 算术右移 x 位 | |
addmod(x, y, m) | F | 任意精度的 (x + y) % m | |
mulmod(x, y, m) | F | 任意精度的 (x * y) % m | |
signextend(i, x) | F | 对 x 的最低位到第 (i * 8 + 7) 进行符号扩展 | |
keccak256(p, n) | F | keccak(mem[p...(p + n))) | |
jump(label) | - | F | 跳转到标签 / 代码位置 |
jumpi(label, cond) | - | F | 如果条件为非零,跳转到标签 |
pc | F | 当前代码位置 | |
pop(x) | - | F | 删除(弹出)栈顶的 x 个元素 |
dup1 ... dup16 | F | 将栈内第 i 个元素(从栈顶算起)复制到栈顶 | |
swap1 ... swap16 | * | F | 将栈顶元素和其下第 i 个元素互换 |
mload(p) | F | mem[p...(p + 32)) | |
mstore(p, v) | - | F | mem[p...(p + 32)) := v |
mstore8(p, v) | - | F | mem[p] := v & 0xff (仅修改一个字节) |
sload(p) | F | storage[p] | |
sstore(p, v) | - | F | storage[p] := v |
msize | F | 内存大小,即最大可访问内存索引 | |
gas | F | 执行可用的 gas | |
address | F | 当前合约 / 执行上下文的地址 | |
balance(a) | F | 地址 a 的余额,以 wei 为单位 | |
caller | F | 调用发起者(不包括 delegatecall ) |
|
callvalue | F | 随调用发送的 Wei 的数量 | |
calldataload(p) | F | 位置 p 的调用数据(32 字节) | |
calldatasize | F | 调用数据的字节数大小 | |
calldatacopy(t, f, s) | - | F | 从调用数据的位置 f 的拷贝 s 个字节到内存的位置 t |
codesize | F | 当前合约 / 执行上下文地址的代码大小 | |
codecopy(t, f, s) | - | F | 从代码的位置 f 开始拷贝 s 个字节到内存的位置 t |
extcodesize(a) | F | 地址 a 的代码大小 | |
extcodecopy(a, t, f, s) | - | F | 和 codecopy(t, f, s) 类似,但从地址 a 获取代码 |
returndatasize | B | 最后一个 returndata 的大小 | |
returndatacopy(t, f, s) | - | B | 从 returndata 的位置 f 拷贝 s 个字节到内存的位置 t |
create(v, p, s) | F | 用 mem[p...(p + s)) 中的代码创建一个新合约、发送 v wei 并返回 新地址 | |
create2(v, n, p, s) | C | 用 mem[p...(p + s)) 中的代码,在地址 keccak256(<address> . n . keccak256(mem[p...(p + s))) 上 创建新合约、发送 v wei 并返回新地址 | |
call(g, a, v, in, insize, out, outsize) | F | 使用 mem[in...(in + insize)) 作为输入数据, 提供 g gas 和 v wei 对地址 a 发起消息调用, 输出结果数据保存在 mem[out...(out + outsize)), 发生错误(比如 gas 不足)时返回 0,正确结束返回 1 | |
callcode(g, a, v, in, insize, out, outsize) | F | 与 call 等价,但仅使用地址 a 中的代码 且保持当前合约的执行上下文 |
|
delegatecall(g, a, in, insize, out, outsize) | F | 与 callcode 等价且保留 caller 和 callvalue |
|
staticcall(g, a, in, insize, out, outsize) | F | 与 call(g, a, 0, in, insize, out, outsize) 等价 但不允许状态修改 |
|
return(p, s) | - | F | 终止运行,返回 mem[p...(p + s)) 的数据 |
revert(p, s) | - | B | 终止运行,撤销状态变化,返回 mem[p...(p + s)) 的数据 |
selfdestruct(a) | - | F | 终止运行,销毁当前合约并且把资金发送到地址 a |
invalid | - | F | 以无效指令终止运行 |
log0(p, s) | - | F | 以 mem[p...(p + s)) 的数据产生不带 topic 的日志 |
log1(p, s, t1) | - | F | 以 mem[p...(p + s)) 的数据和 topic t1 产生日志 |
log2(p, s, t1, t2) | - | F | 以 mem[p...(p + s)) 的数据和 topic t1、t2 产生日志 |
log3(p, s, t1, t2, t3) | - | F | 以 mem[p...(p + s)) 的数据和 topic t1、t2、t3 产生日志 |
log4(p, s, t1, t2, t3, t4) | - | F | 以 mem[p...(p + s)) 的数据和 topic t1、t2、t3 和 t4 产生日志 |
origin | F | 交易发起者地址 | |
gasprice | F | 交易所指定的 gas 价格 | |
blockhash(b) | F | 区块号 b 的哈希 - 目前仅适用于不包括当前区块的最后 256 个区块 | |
coinbase | F | 当前的挖矿收益者地址 | |
timestamp | F | 从当前 epoch 开始的当前区块时间戳(以秒为单位) | |
number | F | 当前区块号 | |
difficulty | F | 当前区块难度 | |
gaslimit | F | 当前区块的 gas 上限 |
mstore
的用法语法:
mstore(position, value)
其中,position
是要写入的内存位置,以字节为单位;value
是要写入的数据。注意,value
的大小必须是 32 字节。
演示:
function mstore() external pure returns(uint) { // 123
uint num = 123;
assembly {
mstore(0x00, num) // 或 mstore(0x00, 123)
return(0x00, 0x20)
}
}
返回的结果:
return
的用法语法:
return(start, end)
其中start
是要读取的起始内存位置,以字节为单位;end
是要读取的结尾内存位置。注意,end
的大小必须是 32 字节的倍数。
示例:
一个返回值:
function _return1() external pure returns(uint) {
uint num = 999;
assembly {
mstore(0x00, num)
mstore(0x20, 33)
return(0x20, 0x40)
}
}
一个以上的返回值:
如果函数有多个返回值,则必须将它们打包成一个元组
function _return2() external pure returns(uint,uint) {
uint num = 666;
assembly {
mstore(0x00, num)
mstore(0x20, 999)
return(0x00, 0x40)
}
}
两个示例的运行结果:
add,sub,mul,div,mod,exp
的用法语法:
add(value1, value2)
sub(value1, value2)
mul(value1, value2)
div(value1, value2)
mod(value1, value2)
exp(value1, value2)
其中,value1
和 value2
是要相加,减,乘,除,取模,幂运算
的两个值。
示例:
function add(uint x, uint y) external pure returns(uint) {
assembly {
let result := add(x, y)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function sub(uint x, uint y) external pure returns(uint) {
assembly {
let result := sub(x, y)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function mul(uint x, uint y) external pure returns(uint) {
assembly {
let result := mul(x, y)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function div(uint x, uint y) external pure returns(uint) {
assembly {
let result := div(x, y)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function mod(uint x, uint y) external pure returns(uint) {
assembly {
let result := mod(x, y)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function exp(uint x, uint y) external pure returns(uint) {
assembly {
let result := exp(x, y)
mstore(0x00, result)
return(0x00, 0x20)
}
}
运行结果:
not,and,or,xor,shl,shr,sar
not(x) // ~x,对 x 按位取反
and(x,y) // x 和 y 的按位与
or(x,y) // x 和 y 的按位或
xor(x,y) // x 和 y 的按位异或
shl(x,y) // 将 y 逻辑左移 x 位
shr(x,y) // 将 y 逻辑右移 x 位
sar(x,y) // 将 y 算术右移 x 位
示例:
uint的取值的范围是:[0,115792089237316195423570985008687907853269984665640564039457584007913129639935]
/*
0:uint256: 1
1:uint256:115792089237316195423570985008687907853269984665640564039457584007913129639934
*/
function not(uint num) external pure returns(uint, uint) {
assembly {
let result := num
mstore(0x00, result)
mstore(0x20, not(result))
return(0x00,64)
}
}
// 1010
// 1011
// 1010 = 10
function and(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := and(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
// 1010
// 1011
// 1011 = 11
function or(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := or(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
// 1010
// 1011
// 1011 = 1
function xor(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := xor(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
// 逻辑左移 将 n2 左移 n1 位
// 0001
// << 2
// 0100 = 4
function shl(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := shl(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
// 逻辑右移 将 n2 右移 n1 位
// 0100
// >> 2
// 0001 = 1
function shr(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := shr(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
// 算术右移 将 n2 算术右移 n1 位
// 1011 = 11
// >> 2
// 0010 = 14
function sar(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := sar(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
运行结果:
其中,shr
和sar
的结果是相同的,我认为这和solidtiy中无符号整数有关
lt,gt,eq,iszero
语法:
lt(x, y) // 如果 x < y 为 1,否则为 0
gt(x, y) // 如果 x > y 为 1,否则为 0
eq(x, y) // 如果 x == y 为 1,否则为 0
iszero(x) // 如果 x == 0 为 1,否则为 0
示例:
function lt(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := lt(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function gt(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := gt(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function eq(uint n1, uint n2) external pure returns(uint) {
assembly {
let result := eq(n1, n2)
mstore(0x00, result)
return(0x00, 0x20)
}
}
function iszero(uint n1) external pure returns(uint) {
assembly {
let result := iszero(n1)
mstore(0x00, result)
return(0x00, 0x20)
}
}
运行结果:
心得:我觉得这个操作码简单理解起来就是:操作是一回事,存是一回事,取又是另一回事。先操作,再存,最后取,例如:
let sum := add(x,y) mstore(0x00, sum) return(0x00, 0x20)
这就是典型的先执行
add
操作,再将算出来的sum
结果保存到EVM
中,最后再从EVM
中取出来
addmod,mulmod
的用法语法:
addmod(x, y, m) //任意精度的 (x + y) % m
mulmod(x, y, m) //任意精度的 (x * y) % m
"addmod" 操作符的语法为:addmod(x, y, m)
。它将两个参数 x
和 y
相加,然后将结果对参数 m
取模,最后返回结果。"mulmod"同理。
示例:
function _addmod(uint256 x, uint256 y, uint256 m) public pure returns (uint256) {
uint256 result;
assembly {
result := addmod(x, y, m)
}
return result;
}
function _mulmod(uint256 x, uint256 y, uint256 m) public pure returns (uint256) {
uint256 result;
assembly {
result := mulmod(x, y, m)
}
return result;
}
运行结果:
keccak256
的用法语法:
keccak256(p, n) //keccak(mem[p...(p + n)))
示例:
function _keccak256(string memory message) public pure returns (bytes32) {
bytes32 result;
assembly {
result := keccak256(add(message, 32), mload(message))
}
return result;
}
function _keccak256_1(string memory message) public pure returns (bytes32) {
bytes32 result = keccak256(abi.encodePacked(message));
return result;
}
function _keccak256_2(string memory message) public pure returns (bytes32) {
bytes32 result = keccak256(abi.encode(message));
return result;
}
运行结果:
从结果中可以看到,汇编中的keccak256
使用的打包方式是紧打包
即abi.encodePacked()
拓展:
解读:
assembly {result := keccak256(add(message, 32), mload(message))}
首先知道,
string
类型的变量在EVM
中的存储方式类似于动态数组,在智能合约的书写位置即插槽slot
存储的是,string
变量值存储的位置,而在存储具体位置上,string变量
前32位存储的是string值的长度,32位之后才是具体的string变量的值
。我们可以通过代码验证一下:
function _stringToBytes(string memory message) public pure returns (bytes memory) { bytes memory result = (abi.encode(message)); return result; }
运行结果:
由运行结果的三个部分我们看出:
2:
是参数biyou
在内存中的存储位置(应该是slot2
);
5:
是参数biyou
的长度;
6269796f75:
参数的内容:"biyou"
的 UTF-8 编码(在这里等同于 ASCII 编码),并在右侧(低位)用 0 值字节补充到 32 字节。其次,
(add(message, 32)
,是为了找到message
在内存中的地址,可以理解为,经过(add(message, 32)
这一步骤之后,(经过实际验证之后这是错误的)由如下代码以及结果可知,message
的指针已经指向了包括message
的长度以及具体数据这个整体的第一个索引(add(message, 32)
指向的是跳过了字符串长度,字符串具体值的第一个索引。代码:
function add(string memory message) public pure returns (bytes32) { bytes32 result; assembly { result := add(message, 32) return(result,32) } }
运行结果:
说明:如果直接填入
message
好像会自动找到message
值的具体存储位置然后,
mload(message)
,则是找到message
的长度,通过代码验证代码:
function mload(string memory message) public pure returns (bytes32) { bytes32 result; assembly { result := mload(message) } return result; }
运行结果:
总结:
add(message, 32)
先将指针指向message
具体的值,mload(message)
获取message
的长度,keccak256(add(message, 32), mload(message))
则是对message
具体的值进行hash,即keccak256(0x6269796f75000000000000000000000000000000000000000000000000000000)
,其结果和keccak256(abi.encodePacked(message))
结果相同。
mload
的用法语法:
mload(p) //mem[p...(p + 32))
读取指定位置p
后的 32
位内存地址中的值
示例:
function mload(uint index) public pure returns(uint) {
uint result;
assembly {
let n1 := 666
let n2 := 999
mstore(0x00,n1)
mstore(0x20,n2)
result := mload(index)
}
return result;
}
运行结果:
number的用法
语法:
number() // 当前区块
示例:
function getCurrentInstructionNumber() public view returns (uint) {
assembly {
let instructionNumber := number()
mstore(0x00,instructionNumber)
return(0x00,32)
}
}
运行结果:
sload 和 sstore的用法
语法:
sload(p) // storage[p]
sstore(p, v) // storage[p] := v
① 其中,p
是一个uint
类型的值,表示要读取字的存储器地址。sload
指令的作用是从以p
为地址的存储器位置中读取一个字,并将其作为uint
类型的值返回。类似于使用web3 或 etherjs
的 gerAtStorage(p)
的操作
② 其中,p
是一个uint
类型的值,表示要存储字的存储器地址,v
是一个uint
类型的值,表示要存储的字的值。sstore
指令的作用是将v
存储到以p
为地址的存储器位置中。具体来说,sstore
指令将字节序列v
的前32个字节存储到以p
为地址的存储器位置中。
示例:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0;
contract assembly_day1 {
address owner;
constructor() {
owner = msg.sender;
}
// 查询当前合约的拥有者
function getOwner() external view returns(address) {
return owner;
}
function sload(uint position) public view returns (uint) {
uint value;
assembly {
value := sload(position)
}
return value;
}
function sstore(uint position,uint _addr) public returns (uint) {
assembly {
sstore(position, _addr)
let result := sload(position)
return(mul(position, 32), 32)
}
}
}
运行结果:
调用sstore
之前:
调用sstore
之后:
msize的用法
语法:
msize() //内存大小,即最大可访问内存索引
msize
指令的作用是返回当前合约的存储器大小,以字节为单位。存储器大小是当前合约的存储器使用量,用于存储合约的状态变量和其他数据。
示例:
function msize() public view returns (uint) {
uint size;
assembly {
size := msize()
}
return size;
}
运行结果:
gas的用法
语法:
gas() // 执行可用的 gas
gas
标识符返回一个uint
类型的值,表示当前合约中可用的燃料数量。燃料是一种代币,用于支付交易费用和合约执行费用。在Solidity中,可以使用gas
标识符获取当前合约中还剩余的燃料数量,以便在执行合约代码时进行相应的控制。
示例:
function gas() public view returns (uint) {
uint remaining;
assembly {
remaining := gas()
}
return remaining;
}
运行结果:
address的用法
语法:
address() //当前合约 / 执行上下文的地址
address
标识符可以与其他指令结合使用,用于获取合约地址或其他地址的信息。
示例:
function getContractAddress() public view returns (address) {
address addr;
assembly {
addr := address()
}
return addr;
}
function getCallerAddress() public view returns (address) {
address addr;
assembly {
addr := caller()
}
return addr;
}
运行结果:
balance的用法
语法:
balance(a) // 地址 a 的余额,以 wei 为单位
其中,a
是一个地址类型的值,表示要查询余额的地址。balance
标识符的作用是返回指定地址的余额,以wei为单位。
示例:
function getBalance(address account) public view returns (uint) {
uint _balance;
assembly {
_balance := balance(account)
}
return _balance;
}
运行结果:
caller的用法
语法:
caller() // 调用发起者
caller
标识符返回一个address
类型的值,表示当前函数的调用者地址
。调用者是指调用当前函数的合约
或外部账户
。
示例:
function getCaller() public view returns (address) {
address callerAddr;
assembly {
callerAddr := caller()
}
return callerAddr;
}
运行结果:
extcodesize的用法
语法:
extcodesize(address) //地址 address 的代码大小
其中,address
表示要获取代码长度的合约地址。
示例:
function extcodesize(address _test) external view returns(uint) {
assembly {
let size := extcodesize(_test)
mstore(0x00, size)
return(0x00, 0x20)
}
}
运行结果:
create的用法
语法:
create(v, p, s) // 用 mem[p...(p + s)) 中的代码创建一个新合约、发送 v wei 并返回 新地址
其中,v
表示要向新合约发送的以太币数量,p
表示要复制到新合约地址内存中的代码的偏移量,s
表示要复制到新合约地址内存中的代码的长度。
示例:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0;
contract test {
string public name;
constructor() {
name = "biyou";
}
}
contract assembly_day2 {
function createNewContract(uint value, bytes memory code) public returns (address) {
address newContract;
assembly {
newContract := create(
value, // 表示要向新合约发送的以太币数量
add(code, 0x20), // 表示跳过数组长度
mload(code)) // 读取code的长度
}
return newContract;
}
}
运行结果:
create2的用法
语法:
create2(v, n, p, s) // 用 mem[p...(p + s)) 中的代码,在地址 keccak256(<address> . n . keccak256(mem[p...(p + s))) 上 创建新合约、发送 v wei 并返回新地址
其中,v
表示要向新合约发送的以太币数量,n
表示要复制到新合约地址内存中的代码的偏移量,p
表示要复制到新合约地址内存中的代码的长度,s
是一个任意的32字节值,用于生成新合约地址。
示例:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0;
contract test {
string public name;
constructor() {
name = "biyou";
}
}
contract assembly_day2 {
// /* 使用代码中 create2() */
function create2NewContractByCode(string memory salt) public view returns (address) {
bytes memory code = type(test).creationCode;
address result = address(uint160(uint(
keccak256(
abi.encodePacked(
uint8(0xff),
address(this),
keccak256(abi.encodePacked(salt)),
keccak256(code)
)
)
)));
return result;
}
// /* 使用内联汇编自带的 create2() */
function create2NewContractByInline(string memory salt) public returns (address) {
bytes memory code = type(test).creationCode;
bytes32 _salt = keccak256(abi.encodePacked(salt));
address result;
assembly {
result := create2(
0, // value 表示要向新合约发送的以太币数量
add(code, 32), // 表示跳过数组长度
mload(code), // 读取code的长度
_salt // 盐
)
}
return result;
}
}
运行结果:
由结果可以看出,采用两种方法创建出的新合约地址是相同的;
需要注意的是,两种方法中各参数所处的位置是很不相同的。
selfdestruct的用法
语法:
selfdestruct(_addr) // _addr表示合约注销之后钱转移的地址
address
表示要将剩余的以太币发送到的目标地址。
示例:
function _selfdestruct(address _addr) external payable {
assembly {
selfdestruct(_addr)
}
}
运行结果:
未执行前:
contract test的余额:
执行之后:
contract test的余额:
origin的用法
语法:
origin()
origin
指令用于获取当前交易的发送者地址。与msg.sender
不同的是,origin
指令返回的是交易的最初发送者地址,即交易链的起点。
示例:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0;
contract test {
string public name;
constructor() {
name = "biyou";
}
function testOrigin(address _addr) public view returns(address) {
address result = assembly_day2(_addr).origin();
return result;
}
}
contract assembly_day2 {
constructor() payable {
}
function origin() public view returns (address) {
address _origin;
assembly {
_origin := origin()
}
return _origin;
}
}
运行结果:
blockhash的用法
语法:
blockhash(blockNumber) //区块号 blockNumber 的哈希 - 目前仅适用于不包括当前区块的最后 256 个区块
blockNumber
表示要获取哈希值的块号。请注意,由于Ethereum区块链的设计,只能获取最近的256个块的哈希值。因此,blockNumber
的值必须在当前块与最近的256个块之间。
示例:
// 如下写法hash的值是 0x0000000000000000000000000000000000000000000000000000000000000000
// 因为 当前区块还没有出生,语法写的很清楚了,是我没有理解到位
function getBlockHash() public view returns (uint, bytes32) {
bytes32 hash;
uint num;
assembly {
num := number()
hash := blockhash(num) // number() 获取当前区块号
}
return (num, hash);
}
运行结果:
timestamp的用法
语法:
timestamp()
timestamp
指令用于获取当前块的时间戳。时间戳表示当前块生成的时间,以秒为单位。
示例:
function getCurrentTimestamp() public view returns (uint) {
uint _timestamp;
assembly {
_timestamp := timestamp()
}
return _timestamp;
}
运行结果:
call的用法
语法:
call(g, a, v, in, insize, out, outsize)
其中,g
是可用的gas数量,a
是要调用的合约地址,v
是要发送的以太币数量,in
是要发送的调用数据,insize
是调用数据的长度,out
是一个指向输出缓冲区的指针,outsize
是输出缓冲区的大小。
示例:
function fun(uint256 num1, uint256 num2) external returns (uint256) {
return sum = num1 + num2;
}
function inline_call() public returns(bool success){
bytes memory data = abi.encodeWithSignature("fun(uint256,uint256)", 260, 261);
address to = address(this);
assembly {
success := call(gas(), to, 0, add(data, 0x20), mload(data), 0, 0)
}
}
运行结果:
**
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!