Pre:
朋友有一批raca potion的nft要出售,尝试用程序去挂单
raca的nft market的合约没开源,自然无法用abi、合约地址去实例化合约对象,然后快速调用合约对应函数。需要自己构造data,与合约交互。
InputData例子解析:
先了解一下InputData是怎么构成的。以下面最简单的合约为例,我们看看用参数 1 调用set(uint x),这个交易附带的数据是什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pragma solidity ^0.4.0;
contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; }
function get() public constant returns (uint) { return storedData; } }
|
当然第一步需要先把合约部署到以太坊网络上,然后用 “1” 作为参数调用set,如下图:
然后打开etherscan查看交易详情数据, 可以看到其附加数据如下图:
这个数据就是ABI的编码数据:
0x60fe47b10000000000000000000000000000000000000000000000000000000000000001
把上面交易的附加数据拷贝出来分析一下,这个数据可以分成两个子部分:
函数选择器(4字节)
第一个参数(32字节)
函数选择器值 实际是对函数签名字符串进行sha3(keccak256
哈希运算之后,取前4个字节,用代码表示就是:
1
| bytes4(sha3(“set(uint256)”)) == 0x60fe47b1
|
参数部分则是使用对应的16进制数。现在就好理解 附加数据怎么转化为对应的函数调用。
实验:
刚好自己有在练手solidity,有部署到ropsten测试网上。可以尝试能不能构造出已知交易的input_data
调用的合约函数:
1 2 3
| function createZoombie(string name) public { ... }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def mint_zoombie(self): func_abi = { "inputs": [ {"name": "abc", "type": "string"}, ], "name": "createZoombie", }
fn_selector = '0x' + encode_hex(function_abi_to_4byte_selector(func_abi))
args_data = encode_hex(encode_single('string', 'haha'))
print(fn_selector) print(args_data) total_data = fn_selector + args_data print(total_data)
|
实际的input_data
:
1 2
| 0x3dca7430 6861686100000000000000000000000000000000000000000000000000000000
|
构造出的input_data
:
1 2 3 4
| 0x3dca7430 0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000004 6861686100000000000000000000000000000000000000000000000000000000
|
没有构造成功,经过多个tx,观察发现中间多出来的数据是固定。但一时不知道是如何generate出来的,只能暂时先放弃了。
data的组成规则:
关于data,前八位为方法名以及参数类型的hash,只要方法名,参数个数,参数顺序以及参数类型确定,方法名hash就确定
hash往后64位进行分割,每一段就是方法的参数,不足64前面补零凑64位
参数中对于数组类型要注意,解析会有点特殊,一般会先以位数序号进行占位,然后到指定位序才是真正数组数据的起点,数组数据起点会先表明下面数据有多少位,然后才是数组数据的依次排列
raca nft挂单卖出:
授权:
授权这个data比较简单,找到多几个tx比较一下。
可知他的data是固定的,直接用就行了。
挂单卖出:
依然是找到多几个tx比较一下,按照data的组成规则分开观察。
1 2 3 4 5 6 7 8
| 0x467f963d 00000000000000000000000051353799f8550c9010a8b0cbfe6c02ca96e026e2 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000001 00000000000000000000000012bb890508c125661e03b09ec06e404bc9289040 0000000000000000000000000000000000000000000002f6d546136addf80000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
|
可知,主要是要修改数量和价格的数据,其他数据不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def create_potion_sell_order_input_data(self, price): logger.debug("[*] set price: {}".format(price)) price = self.bsc_ins.w3.toWei(price, 'ether')
price_hex_str = self.bsc_ins.w3.toHex(price) price_hex_str = price_hex_str.replace("0x", "") full_price_hex_str = add_pre_zero(price_hex_str) last_input_data = "0x467f963d00000000000000000000000051353799f8550c9010a8b0cbfe6c02ca96e026e20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000012bb890508c125661e03b09ec06e404bc9289040{}00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".format( full_price_hex_str)
return last_input_data
def get_tx_params(self, to_address, input_data, gas): now_nonce = self.bsc_ins.w3.eth.get_transaction_count(self.wallet_addr) to_address = Web3.toChecksumAddress(to_address) tx_params = { 'nonce': now_nonce, 'to': to_address, 'value': self.bsc_ins.w3.toWei(0, 'ether'), 'gas': gas, 'gasPrice': self.bsc_ins.w3.toWei('5', 'gwei'), 'data': input_data, } return tx_params
|
将tx用私钥签名,广播交易就可以了。然后检测对应账户里的raca数量就可以知道有没卖出成功。
Summary:
-
data的组成规则:前八位为方法名以及参数类型的hash,hash往后64位进行分割,每一段就是方法的参数,不足64前面补零凑64位
-
多找相关的tx比较,找规律
Refs: