solidity 内联汇编

  • BY_DLIFE
  • 更新于 2024-04-30 11:48
  • 阅读 1409

solidity汇编语言汇总

官方文档✈

注:solidity内联汇编中都是以字节为单位的,[0x00,0x20)表示的范围是2^32-1,但在计算机中占了256位,256 /8=32=0x20

1. 语法

solidity一样,Assembly 也会解析注释、文字和标识符,所以你可以使用通常的 ///* */ 来进行注释。 内联汇编程序由 assembly { ... } 来标记,在这些大括号内可以使用以下内容。

  • 字面常数,也就是 0x12342"abc" (不超过 32 个字符的字符串)
  • 操作码(在“instruction style”内),比如 mload sload dup1 sstore,操作码列表请看后面
  • 函数风格操作码,比如 add(1,mlod(0))
  • 标签,比如 name:
  • 变量声明,比如 let x := 7let x := add(y, 3) 或者 let x (初始值将被置为 empty(0))
  • 标识符(标签或者汇编局部变量以及用作内联汇编时的外部变量),比如 jump(name)3 x add
  • 赋值(在“instruction style”内),比如 3 =: x
  • 函数风格赋值,比如 x := add(y,3)
  • 一些控制局部变量作用域的语句块,比如 {let x := 3 { let y := add(x,1) }}

2. 常用操作码

注:在下表中,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 等价且保留 callercallvalue
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 上限

2.1 基础操作码的演示

2.1.1 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) 
    }
}

返回的结果:

image.png

2.1.2 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)
    }
}

两个示例的运行结果:

image.png

2.1.3 算术运算add,sub,mul,div,mod,exp的用法

语法:

add(value1, value2)
sub(value1, value2)
mul(value1, value2)
div(value1, value2)
mod(value1, value2)
exp(value1, value2)

其中,value1value2 是要相加,减,乘,除,取模,幂运算的两个值。

示例:

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)
    }
}

运行结果:

image.png

2.1.4 位运算: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
// &lt;&lt; 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)
    }
}

运行结果:

image.png

其中,shrsar的结果是相同的,我认为这和solidtiy中无符号整数有关

2.1.5 比较运算符:lt,gt,eq,iszero

语法:

lt(x, y)  // 如果 x &lt; 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)
        }
    }

运行结果:

image.png

心得:我觉得这个操作码简单理解起来就是:操作是一回事,存是一回事,取又是另一回事。先操作,再存,最后取,例如:

let sum := add(x,y)
mstore(0x00, sum)
return(0x00, 0x20)

这就是典型的先执行 add操作,再将算出来的 sum 结果保存到 EVM中,最后再从EVM中取出来

2.2 进阶操作码的演示

2.2.1 addmod,mulmod的用法

语法:

addmod(x, y, m) //任意精度的 (x + y) % m
mulmod(x, y, m) //任意精度的 (x * y) % m

"addmod" 操作符的语法为:addmod(x, y, m)。它将两个参数 xy 相加,然后将结果对参数 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;
    }

运行结果:

image.png

2.2.2 keccak256的用法

关于string类型的存储方式

语法:

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;      
    }

运行结果:

image.png

从结果中可以看到,汇编中的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;
    }

运行结果:

image.png

由运行结果的三个部分我们看出:

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)               
        }         
    }

运行结果:

image.png

说明:如果直接填入message好像会自动找到message值的具体存储位置

然后,mload(message),则是找到message的长度,通过代码验证

代码:

    function mload(string memory message) public pure returns (bytes32) {
            bytes32 result;
            assembly {
                result := mload(message)              
        }
            return result;
    }

运行结果:

image.png

总结:add(message, 32)先将指针指向message具体的值, mload(message)获取message的长度,keccak256(add(message, 32), mload(message))则是对message具体的值进行hash,即keccak256(0x6269796f75000000000000000000000000000000000000000000000000000000),其结果和keccak256(abi.encodePacked(message))结果相同。

2.2.3 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;
    }

运行结果:

image.png

2.2.4 number的用法

语法:

number() // 当前区块

示例:

    function getCurrentInstructionNumber() public view returns (uint) {
        assembly {
            let instructionNumber := number()
            mstore(0x00,instructionNumber)
            return(0x00,32)
        }
    }

运行结果:

image.png

2.2.5 sload 和 sstore的用法

语法:

sload(p) // storage[p]
sstore(p, v) // storage[p] := v

① 其中,p是一个uint类型的值,表示要读取字的存储器地址。sload指令的作用是从以p为地址的存储器位置中读取一个字,并将其作为uint类型的值返回。类似于使用web3 或 etherjsgerAtStorage(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之前:

image.png

调用sstore之后:

image.png

2.2.6 msize的用法

语法:

msize() //内存大小,即最大可访问内存索引

msize指令的作用是返回当前合约的存储器大小,以字节为单位。存储器大小是当前合约的存储器使用量,用于存储合约的状态变量和其他数据。

示例:

function msize() public view returns (uint) {
    uint size;
    assembly {
        size := msize()
    }
    return size;
}

运行结果:

image.png

2.2.7 gas的用法

语法:

gas() // 执行可用的 gas

gas标识符返回一个uint类型的值,表示当前合约中可用的燃料数量。燃料是一种代币,用于支付交易费用和合约执行费用。在Solidity中,可以使用gas标识符获取当前合约中还剩余的燃料数量,以便在执行合约代码时进行相应的控制。

示例:

    function gas() public view returns (uint) {
        uint remaining;
        assembly {
            remaining := gas()
        }
        return remaining;
    }

运行结果:

image.png

2.2.8 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;
    }

运行结果:

image.png

2.2.9 balance的用法

语法:

balance(a) // 地址 a 的余额,以 wei 为单位

其中,a是一个地址类型的值,表示要查询余额的地址。balance标识符的作用是返回指定地址的余额,以wei为单位。

示例:

    function getBalance(address account) public view returns (uint) {
        uint _balance;
        assembly {
            _balance := balance(account)
        }
        return _balance;
    }

运行结果:

image.png

2.2.10 caller的用法

语法:

caller() // 调用发起者

caller标识符返回一个address类型的值,表示当前函数的调用者地址。调用者是指调用当前函数的合约外部账户

示例:

    function getCaller() public view returns (address) {
        address callerAddr;
        assembly {
            callerAddr := caller()
        }
        return callerAddr;
    } 

运行结果:

image.png

2.2.11 extcodesize的用法

语法:

extcodesize(address) //地址 address 的代码大小

其中,address表示要获取代码长度的合约地址。

示例:

    function extcodesize(address _test) external view returns(uint) {
        assembly {
            let size := extcodesize(_test)
            mstore(0x00, size)
            return(0x00, 0x20)
        }
    }

运行结果:

image.png

2.2.12 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;
    }

}

运行结果:

image.png

2.2.13 create2的用法

语法:

create2(v, n, p, s) // 用 mem[p...(p + s)) 中的代码,在地址 keccak256(&lt;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;
    }
}

运行结果:

image.png

由结果可以看出,采用两种方法创建出的新合约地址是相同的;

需要注意的是,两种方法中各参数所处的位置是很不相同的。

2.2.14 selfdestruct的用法

语法:

selfdestruct(_addr) // _addr表示合约注销之后钱转移的地址

address表示要将剩余的以太币发送到的目标地址。

示例:

    function _selfdestruct(address _addr) external  payable {
        assembly {
            selfdestruct(_addr)
        }
    }

运行结果:

未执行前:

contract test的余额:

image.png

执行之后:

contract test的余额:

image.png

2.2.15 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;
    }
}

运行结果:

image.png

2.2.16 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);
    }

运行结果:

image.png

2.2.17 timestamp的用法

语法:

timestamp()

timestamp指令用于获取当前块的时间戳。时间戳表示当前块生成的时间,以秒为单位。

示例:

    function getCurrentTimestamp() public view returns (uint) {
        uint _timestamp;

        assembly {
            _timestamp := timestamp()
        }
        return _timestamp;
    } 

运行结果:

image.png

2.2.18 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)
        }
    }

运行结果:

image.png

**

点赞 4
收藏 7
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
BY_DLIFE
BY_DLIFE
0x39CF...9999
立志成为一名优秀的智能合约审计师、智能合约开发工程师,文章内容为个人理解,如有错误,欢迎在评论区指出。