var logs = exchange.IO("api", "eth", "eth_getLogs", params)
if (!logs) {
return
}
for (var i = 0; i < logs.length; i++) {
if (toAmount(logs[i].blockNumber, 0) > self.latestBlockNumber) {
/* TODO: test
if (self.isFirst) {
self.firstBlockNumber = toAmount(logs[i].blockNumber, 0)
Log("firstBlockNumber:", self.firstBlockNumber)
self.isFirst = false
}
*/
callBack(logs[i])
}
}
self.latestBlockNumber = currBlockNumber
self.fromBlockNumber = self.latestBlockNumber - 1
}
self.latestBlockNumber = self.getBlockNumber()
self.fromBlockNumber = self.latestBlockNumber - 1
return self
}
var listener = nol
Fungsi utama
var event =
listener = addEventListener(contractAddress, event, function(log) {
var fromAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[1])
var toAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[2])
Log("Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0))
/* TODO: test
arrLog.push(log)
*/
})
while (true) {
listener.run()
Sleep(5000)
}
}
/* TODO: tes Var arrLog = [] fungsi onexit ((() { Log (mengacu pada akhir pelaksanaan, verifikasi log) var firstBlockNumber = listener.firstBlockNumber var endBlockNumber = listener.latestBlockNumber
Log("getLogs, from:", firstBlockNumber, " -> to:", endBlockNumber)
var fromBlock = "0x" + (firstBlockNumber).toString(16)
var toBlock = "0x" + (endBlockNumber).toString(16)
var params = {
"fromBlock" : fromBlock,
"toBlock" : toBlock,
"topics" : ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
"address" : "0xdac17f958d2ee523a2206206994597c13d831ec7"
}
var logs = exchange.IO("api", "eth", "eth_getLogs", params)
Log("arrLog:", arrLog.length)
Log("logs:", logs.length)
if (arrLog.length != logs.length) {
Log("长度不等!")
return
}
for (var i = 0; i < arrLog.length; i++) {
Log("判断blockNumber:", logs[i].blockNumber == arrLog[i].blockNumber, ",判断from:", logs[i].topics[1] == arrLog[i].topics[1],
"判断to:", logs[i].topics[2] == arrLog[i].topics[2])
}
} */
实盘运行:
![img](/upload/asset/16ffd65adc050d33056d.png)
对于执行结果,代码中也编写了验证部分(TODO: test)。经过简单验证可以看到持续监控USDT合约的```Transfer```事件并且记录数据,用这个数据和一次性获取的事件数据对比可以观察出数据一致:
![img](/upload/asset/16e07390a11a606276b1.png)
### 事件过滤
在上一节课程「监听合约事件」的基础上,我们拓展一下,在监听的过程中增加过滤器,监听指定地址的转入转出。当智能合约创建日志时(即释放事件),日志数据中```topics```最多包含4条信息。所以我们设计一个过滤规则,以```[[A1, A2, ...An], null, [C1], D]```为例子。
1、```[A1, A2, ...An]```对应```topics[0]```位置的数据。
2、```null```对应```topics[1]```位置的数据。
3、```[C1]```对应```topics[2]```位置的数据。
4、```D```对应```topics[3]```位置的数据。
- 如果条件结构中的元素设置```null```表示不被过滤,例如```null```对应```topics[1]```,任何值都匹配。
- 如果条件结构中的元素设置单个值表示该位置必须匹配,例如```[C1]```对应```topics[2]```或者```D```对应```topics[3]```,不匹配的日志被过滤。
- 如果条件结构中的元素是一个数组,表示数组中的元素至少有一个要匹配,例如```[A1, A2, ...An]```对应```topics[0]```,```[A1, A2, ...An]```中有任意一个和```topics[0]```匹配,则日志不会被过滤。
**监听交易所的USDT转账**
监控从币安交易所转出、转入币安交易所```USDT```的交易:
```javascript
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(n, decimals) {
return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function addEventListener(contractAddress, event, callBack) {
var self = {}
self.eventHash = "0x" + Encode("keccak256", "string", "hex", event)
self.contractAddress = contractAddress
self.latestBlockNumber = 0
self.fromBlockNumber = 0
self.firstBlockNumber = 0
self.filters = []
self.setFilter = function(filterCondition) {
if (filterCondition.length > 4) {
throw "filterCondition error"
}
self.filters.push(filterCondition)
Log("设置过滤条件:", filterCondition)
}
self.getTokenBalanceOfWallet = function(walletAddress, tokenAddress, tokenDecimals) {
var balance = exchange.IO("api", tokenAddress, "balanceOf", walletAddress)
if (balance) {
return toAmount(balance, tokenDecimals)
}
return null
}
self.getBlockNumber = function() {
var maxTry = 10
for (var i = 0; i < maxTry; i++) {
var ret = exchange.IO("api", "eth", "eth_blockNumber")
if (ret) {
return toAmount(ret, 0)
}
Sleep(5000)
}
throw "getBlockNumber failed"
}
self.run = function() {
var currBlockNumber = self.getBlockNumber()
var fromBlock = "0x" + self.fromBlockNumber.toString(16)
var toBlock = "0x" + currBlockNumber.toString(16)
var params = {
"fromBlock" : fromBlock,
"toBlock" : toBlock,
"address" : self.contractAddress,
"topics" : [self.eventHash]
}
var logs = exchange.IO("api", "eth", "eth_getLogs", params)
if (!logs) {
return
}
for (var i = 0; i < logs.length; i++) {
if (toAmount(logs[i].blockNumber, 0) > self.latestBlockNumber) {
// 检查过滤条件,设置了过滤条件则执行判断
if (self.filters.length != 0) {
// 初始过滤标记
var isFilter = true
// 遍历过滤条件设置
for (var j = 0; j < self.filters.length; j++) {
// 取一个过滤设置,例如:[[A1, A2, ...An], null, [C1], D]
var cond = self.filters[j]
// 遍历这个过滤设置
var final = true
for (var topicsIndex = 0; topicsIndex < cond.length; topicsIndex++) {
// 拿到这个过滤设置中的某一个条件,如果是第一个条件:即要和topics[0]对比的数据
var condValue = cond[topicsIndex]
// 日志中的数据
if (topicsIndex > logs[i].topics.length - 1) {
continue
}
var topicsEleValue = logs[i].topics[topicsIndex]
// 如果是Transfer事件,需要处理from和to
if (logs[i].topics[0] == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") {
if (topicsIndex == 1 || topicsIndex == 2) {
topicsEleValue = "0x" + exchange.IO("encodePacked", "address", topicsEleValue)
}
}
// 如果condValue类型是数组,表示该位置的对比条件有多个,多个条件对比是逻辑或关系
if (Array.isArray(condValue) && condValue.length > 1) {
// 判断 condValue[0] == topicsEleValue || condValue[1] == topicsEleValue
final = final && condValue.some(element => element === topicsEleValue)
}else if (condValue === null) {
final = final && true
} else {
final = final && (condValue === topicsEleValue)
}
}
if (final) {
isFilter = false
}
}
if (isFilter) {
continue
}
}
callBack(logs[i])
}
}
self.latestBlockNumber = currBlockNumber
self.fromBlockNumber = self.latestBlockNumber - 1
}
self.latestBlockNumber = self.getBlockNumber()
self.fromBlockNumber = self.latestBlockNumber - 1
return self
}
var listener = null
function main() {
// 初始清理日志
LogReset(1)
LogProfitReset()
var event = "Transfer(address,address,uint256)" // 监听事件
var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7" // USDT合约地址
var decimals = exchange.IO("api", contractAddress, "decimals") // 获取USDT token的精度信息
var accountBinanceAddress = "0x28C6c06298d514Db089934071355E5743bf21d60" // Binance 热钱包地址
accountBinanceAddress = accountBinanceAddress.toLowerCase() // 地址处理为小写
Log(exchange.IO("api", contractAddress, "name"), " decimals:", decimals)
// 创建监听对象
listener = addEventListener(contractAddress, event, function(log) {
var fromAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[1])
var toAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[2])
if (fromAddress == accountBinanceAddress) {
Log("币安转出 - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#CD32CD")
} else if (toAddress == accountBinanceAddress) {
Log("转入币安 - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#FF0000")
}
})
// 设置事件过滤
listener.setFilter([null, accountBinanceAddress, null]) // Binance -> USDT
listener.setFilter([null, null, accountBinanceAddress]) // USDT -> Binance
var preBalance = 0
while (true) {
listener.run()
var balance = listener.getTokenBalanceOfWallet(accountBinanceAddress, contractAddress, decimals)
if (balance) {
var direction = ""
if (preBalance != 0 && preBalance > balance) {
direction = " ↓ " + (preBalance - balance) + "#CD32CD"
} else if (preBalance != 0 && preBalance < balance) {
direction = " ↑ " + (balance - preBalance) + "#FF0000"
}
Log("币安钱包地址:", accountBinanceAddress, " 余额:", balance, direction)
LogProfit(balance, "&") // 只画图,不打印日志
preBalance = balance
}
LogStatus(_D(), "币安钱包地址:", accountBinanceAddress, ", 余额:", balance)
Sleep(5000 * 3)
}
}
Kode di atas berjalan di hard disk:
Pada bagian ini, kami menjelaskan bagaimana merancang penyaring acara. Dan kami menggunakan ini untuk memata-matai yang terkait dengan dompet panas di Bursa Binance.USDT
Anda dapat memodifikasi, memperluas, dan memperluas program ini untuk memantau setiap peristiwa yang Anda minati.smart money
Saya tidak tahu apa yang telah dilakukan, tapi saya tidak tahu apa yang telah dilakukan.NFT
"Saya tidak tahu apa yang akan terjadi dengan proyek-proyek baru yang dibangun di Gedung Putih", katanya.
Dalam banyak perhitungan terkait Ethereum, angka-angka di luarJavaScript
Jadi, di inventor quantized trading platform, ada beberapa metode yang dibutuhkan untuk menangani angka besar, metode yang telah kita gunakan secara khusus dalam pelajaran sebelumnya, dan tidak dijelaskan secara rinci.
MencetakJavaScript
Jumlah integer keamanan maksimum yang didefinisikan dalam bahasa:
function main() {
Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER)
}
Hasilnya:
Nomor.MAX_SAFE_INTEGER: 9007199254740991
Satuan terkecil yang didefinisikan di Ethereum adalah1wei
Definisi1Gwei
sama dengan1000000000 wei
。1Gwei
Dalam perhitungan yang berkaitan dengan Ethereum sebenarnya bukan angka yang besar, beberapa data jauh lebih besar dari itu.Number.MAX_SAFE_INTEGER: 9007199254740991
。
Di inventor platform perdagangan kuantitatif, kami menggunakan platform yang memiliki nilai yang sangat besar.BigInt
Objek untuk menunjukkan data integer yang sangat besar ini.BigInt()
Untuk membangunBigInt
Objek. Bisa menggunakan angka, string angka 16 digit sebagai constructor parameterBigInt
Objek. Penggunaan.BigInt
objektoString()
Metode ini mengekspor data yang diwakili oleh objek dalam bentuk string.
BigInt
Operasi yang didukung oleh objek:
+
-
*
/
%
**
Perhatikan contoh kode berikut:
function main() {
// 1Gwei的十进制表示
var oneGwei = 1000000000
// 1Gwei的十进制转换为十六进制表示
var oneGweiForHex = "0x" + oneGwei.toString(16)
Log("oneGwei : ", oneGwei)
Log("oneGweiForHex : ", oneGweiForHex)
// 构造BigInt对象
Log("1Gwei / 1Gwei : ", (BigInt(oneGwei) / BigInt(oneGweiForHex)).toString(10))
Log("1Gwei * 1Gwei : ", (BigInt(oneGwei) * BigInt(oneGweiForHex)).toString(10))
Log("1Gwei - 1Gwei : ", (BigInt(oneGwei) - BigInt(oneGweiForHex)).toString(10))
Log("1Gwei + 1Gwei : ", (BigInt(oneGwei) + BigInt(oneGweiForHex)).toString(10))
Log("(1Gwei + 1) % 1Gwei : ", (BigInt(oneGwei + 1) % BigInt(oneGweiForHex)).toString(10))
Log("1Gwei ** 2 : ", (BigInt(oneGwei) ** BigInt(2)).toString(10))
Log("100 的平方根 : ", (BigInt(100) ** BigFloat(0.5)).toString(10))
Log("Number.MAX_SAFE_INTEGER : ", BigInt(Number.MAX_SAFE_INTEGER).toString(10))
Log("Number.MAX_SAFE_INTEGER * 2 : ", (BigInt(Number.MAX_SAFE_INTEGER) * BigInt("2")).toString(10))
}
Percobaan alat debugging:
2023-06-08 11:39:50 信息 Number.MAX_SAFE_INTEGER * 2 : 18014398509481982
2023-06-08 11:39:50 信息 Number.MAX_SAFE_INTEGER : 9007199254740991
2023-06-08 11:39:50 信息 100 的平方根 : 10
2023-06-08 11:39:50 信息 1Gwei ** 2 : 1000000000000000000
2023-06-08 11:39:50 信息 (1Gwei + 1) % 1Gwei : 1
2023-06-08 11:39:50 信息 1Gwei + 1Gwei : 2000000000
2023-06-08 11:39:50 信息 1Gwei - 1Gwei : 0
2023-06-08 11:39:50 信息 1Gwei * 1Gwei : 1000000000000000000
2023-06-08 11:39:50 信息 1Gwei / 1Gwei : 1
2023-06-08 11:39:50 信息 oneGweiForHex : 0x3b9aca00
2023-06-08 11:39:50 信息 oneGwei : 1000000000
BigFloat
objek danBigInt
Penggunaan objek serupa, digunakan untuk menunjukkan angka titik lebur dengan nilai yang lebih besar, juga mendukung operasi perkalian ditambah dikurangi.BigFloat
Dukungan objektoFixed()
Bagaimana caranya?
Perhatikan contoh kode berikut:
function main() {
var pi = 3.14
var oneGwei = "1000000000"
var oneGweiForHex = "0x3b9aca00"
Log("pi + oneGwei : ", (BigFloat(pi) + BigFloat(oneGwei)).toFixed(2))
Log("pi - oneGweiForHex : ", (BigFloat(pi) - BigFloat(oneGweiForHex)).toFixed(2))
Log("pi * 2.0 : ", (BigFloat(pi) * BigFloat(2.0)).toFixed(2))
Log("pi / 2.0 : ", (BigFloat(pi) / BigFloat(2.0)).toFixed(2))
}
Percobaan alat debugging:
2023-06-08 13:56:44 信息 pi / 2.0 : 1.57
2023-06-08 13:56:44 信息 pi * 2.0 : 6.28
2023-06-08 13:56:44 信息 pi - oneGweiForHex : -999999996.86
2023-06-08 13:56:44 信息 pi + oneGwei : 1000000003.14
BigDecimal
Objek yang kompatibel dengan integer, floating point, mendukung penggunaanBigInt
"Sudah tidak ada yang bisa dilakukan.BigFloat
Object initialization, juga mendukung operasi penjumlahan dan pengurangan.
Perhatikan contoh kode berikut:
function main() {
var pi = 3.1415
var oneGwei = 1000000000
var oneGweiForHex = "0x3b9aca00"
Log("pi : ", BigDecimal(pi).toFixed(2))
Log("oneGwei : ", BigDecimal(oneGwei).toString())
Log("oneGweiForHex : ", BigDecimal(BigInt(oneGweiForHex)).toString())
Log("BigInt(oneGwei) : ", BigDecimal(BigInt(oneGwei)).toString())
Log("BigFloat(pi) : ", BigDecimal(BigFloat(pi)).toFixed(4))
Log("oneGwei + pi : ", (BigDecimal(oneGwei) + BigDecimal(pi)).toString())
Log("oneGwei - pi : ", (BigDecimal(oneGwei) - BigDecimal(pi)).toString())
Log("2.0 * pi : ", (BigDecimal(2.0) * BigDecimal(pi)).toString())
Log("pi / pi : ", (BigDecimal(pi) / BigDecimal(pi)).toString())
}
Perangkat debugging berjalan di:
2023-06-08 14:52:53 信息 pi / pi : 1
2023-06-08 14:52:53 信息 2.0 * pi : 6.283
2023-06-08 14:52:53 信息 oneGwei - pi : 999999996.8585
2023-06-08 14:52:53 信息 oneGwei + pi : 1000000003.1415
2023-06-08 14:52:53 信息 BigFloat(pi) : 3.1415
2023-06-08 14:52:53 信息 BigInt(oneGwei) : 1e+9
2023-06-08 14:52:53 信息 oneGweiForHex : 1e+9
2023-06-08 14:52:53 信息 oneGwei : 1e+9
2023-06-08 14:52:53 信息 pi : 3.14
Fungsi kedua berikut:toAmount()
、toInnerAmount()
Kita telah menggunakan dua fungsi ini beberapa kali dalam pelajaran sebelumnya, yang terutama digunakan untuk konversi presisi data.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(n, decimals) {
return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
toAmount()
Fungsi ini akan mengubah variabel.s
Perhatikan parameter presisi.decimals
Untuk melakukan konversi. Dalam pengembangan web3 yang sebenarnya, seringkali diperlukan untuk memproses data 16 digit pada beberapa rantai.
Dan saya ingin menunjukkan kepada Anda beberapa hal yang sering kita temui dalam kursus sebelumnya, seperti kontrak pintar.Transfer(address,address,uint256)
Peristiwadata
Data bidang:
{
"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000",
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000006b75d8af000000e20b7a7ddf000ba900b4009a80", "0x000000000000000000000000bcb095c1f9c3dc02e834976706c87dee5d0f1fb6"],
"transactionHash": "0x27f9bf5abe3148169b4b85a83e1de32bd50eb81ecc52e5af006157d93353e4c4",
"transactionIndex": "0x0",
"removed": false,
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
"blockNumber": "0x109b1cc",
"logIndex": "0x0"
}
Pengolahan data"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000"
Saya tidak tahu apa yang terjadi.toAmount()
Fungsi. Desain pengolahan seperti ini dapat digunakan dengan baik.data
Data bidang dikonversi menjadi nilai yang dapat dibaca.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
var data = "0x00000000000000000000000000000000000000000000000001c1a55000000000"
Log(toAmount(data, 18)) // 打印出 0.12656402755905127
}
Satu token ETH yang kita tahu adalah1e18 wei
Jika kita mendapatkan satu,wei
Data dalam satuan126564027559051260
Bagaimana cara mengubahnya menjadi jumlah token ETH?
PenggunaantoAmount(, 18)
Fungsi dapat diubah dengan sangat mudah.toInnerAmount()
Jadi fungsi ini adalahtoAmount()
Operasi kebalikan dari fungsi (menurut presisi, pembesaran), menggunakan kedua fungsi ini sangat mudah untuk mengubah data.
Yang perlu diperhatikan adalah rentang keamanan integer dalam bahasa JavaScript, yaituNumber.MAX_SAFE_INTEGER
Contoh berikut menunjukkan masalah yang lebih tersembunyi dalam konversi data:
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(n, decimals) {
return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
var amount = 0.01
var innerAmount = Number(toInnerAmount(amount, 18))
Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER) // 9007199254740991
Log("innerAmount:", innerAmount) // 10000000000000000
Log("typeof(innerAmount):", typeof(innerAmount), ", innerAmount:", innerAmount)
// 十进制数值 10000000000000000 -> 十六进制数值 0x2386f26fc10000
Log("转换", innerAmount, "为十六进制:", innerAmount.toString(16))
Log("转换", BigInt(10000000000000000).toString(10), "为十六进制:", BigInt(10000000000000000).toString(16))
Log("0x" + BigInt(10000000000000000).toString(16), "转换为10进制:", toAmount("0x" + BigInt(10000000000000000).toString(16), 0))
}
Anda dapat menjalankan alat debugging di:
2023-06-15 16:21:40 信息 0x2386f26fc10000 转换为10进制: 10000000000000000
2023-06-15 16:21:40 信息 转换 10000000000000000 为十六进制: 2386f26fc10000
2023-06-15 16:21:40 信息 转换 10000000000000000 为十六进制: 10000000000000000
2023-06-15 16:21:40 信息 typeof(innerAmount): number , innerAmount: 10000000000000000
2023-06-15 16:21:40 信息 innerAmount: 10000000000000000
2023-06-15 16:21:40 信息 Number.MAX_SAFE_INTEGER: 9007199254740991
Dengan mengamati, kita menemukan:
Log("转换", innerAmount, "为十六进制:", innerAmount.toString(16))
Di bawah ini adalah hasil log yang sesuai dengan baris kode:转换 10000000000000000 为十六进制: 10000000000000000
Jika kita tidak melakukan konversi yang benar, tentu saja itu karena kita telah melampaui 1.000.000.000.000.000.000.Number.MAX_SAFE_INTEGER
。
Tapi jika angka desimal berada dalam batas aman, yaitu kurang dariNumber.MAX_SAFE_INTEGER
Saya tidak tahu apa yang terjadi.toString(16)
Konversi fungsi adalah normal, misalnya:
function main() {
var value = 1000
Log("把value转换为十六进制:", "0x" + value.toString(16)) // 0x3e8
Log("把0x3e8转换为十进制:", Number("0x3e8")) // 1000
}
Bahkan di bidang blockchain,0.01
Satu ETH ditukarkan denganwei
Nilai dalam satuan10000000000000000
dan lebih dari itu.Number.MAX_SAFE_INTEGER
Jadi, untuk situasi seperti ini, cara yang lebih aman adalah:BigInt(10000000000000000).toString(16)
。
Ini adalah salah satu cara yang paling efektif untuk melakukan transaksi dan memanggil kontrak pintar di Ethereum.Write
Metode membutuhkan biaya gas tertentu dan kadang-kadang berisiko gagal. Mengetahui transaksi mana yang mungkin gagal sebelum mengirim transaksi atau memanggil sangat penting. Metode panggilan analog digunakan untuk pengujian di Ethereum.
Metode RPC untuk Ethereumeth_call
: dapat mensimulasikan transaksi dan mengembalikan hasil transaksi yang mungkin, tetapi tidak benar-benar mengeksekusi transaksi di blockchain.
eth_call
Ada dua parameter untuk metode, parameter pertama adalah struktur kata, dan parameter kedua adalah struktur kata.transactionObject
:
// transactionObject
{
"from" : ..., // The address from which the transaction is sent
"to" : ..., // The address to which the transaction is addressed
"gas" : ..., // The integer of gas provided for the transaction execution
"gasPrice" : ..., // The integer of gasPrice used for each paid gas encoded as hexadecimal
"value" : ..., // The integer of value sent with this transaction encoded as hexadecimal
"data" : ..., // The hash of the method signature and encoded parameters. For more information, see the Contract ABI description in the Solidity documentation
}
Parameter kedua adalahblockNumber
Tag: dapat dikirimlatest
/pending
/earliest
Dan sebagainya:
/* blockNumber
The block number in hexadecimal format or the string latest, earliest, pending, safe or
finalized (safe and finalized tags are only supported on Ethereum, Gnosis, Arbitrum,
Arbitrum Nova and Avalanche C-chain), see the default block parameter description in
the official Ethereum documentation
*/
Dan kemudian kita menggunakan token.DAI
Cara Kontrak Pintarapprove
、transfer
Contoh panggilan analog adalah lingkungan pengujian berikut untuk Ethereum.
Untuk kontrak ERC20approve
Metode ini sudah kita ketahui dan sudah kita praktikkan di kursus sebelumnya. Karena kontrak ERC20 sudah terintegrasi ke dalam FMZ platform ABI, maka tidak perlu mendaftarkan kontrak pintar ABI yang akan kita simulasikan.
function main() {
var contractAddressUniswapV3SwapRouterV2 = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
var contractAddress_DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"
var wallet = exchange.IO("address")
// encode approve
var data = exchange.IO("encode", contractAddress_DAI, "approve(address,uint256)",
contractAddressUniswapV3SwapRouterV2, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
Log("ERC20 token DAI approve encode, data:", data)
var transactionObject = {
"from" : wallet,
"to" : contractAddress_DAI,
// "gasPrice" : "0x" + parseInt("21270894680").toString(16),
// "gas" : "0x" + parseInt("21000").toString(16),
"data" : "0x" + data,
}
var blockNumber = "latest"
var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
Log("ret:", ret)
}
Ini adalah contoh dari kode yang pertama kali akanapprove(address,uint256)
Metode, parameter, kode, dan lain sebagainya.approve
Nilai parameter metode0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Menunjukkan jumlah maksimal otorisasi.0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
Ini adalahUniswap V3
Kontrak perutean. Menggunakan metode Ethereum RPC terakhireth_call
Simulasi. Anda dapat melihat.transactionObject
Dalam parametergasPrice
、gas
Bidang ini dapat dihilangkan.
Alat debugging berjalan, panggilan simulasi untuk menyetujui metode otorisasi berhasil (tidak benar-benar otorisasi):
2023-06-09 11:58:39 信息 ret: 0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 11:58:39 信息 ERC20 token DAI approve encode, data: 095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Kita juga dapat meniru beberapa skenario kegagalan ketika kita menyesuaikan.gasPrice
dangas
Pada saat parameter, jika ETH dalam dompet tidak cukup untuk membayar biaya gas, maka akan muncul kesalahan:
dana yang tidak cukup
Jika Anda mengatur harga gas yang terlalu rendah, Anda akan mendapatkan kesalahan:
gas intrinsik terlalu rendah: memiliki 21000, ingin 21944 (disuplai gas 21000)
Untuk ERC20transfer
Metode yang tidak kita kenal juga, yaitu cara mentransfer token ERC20 ke alamat dompet, kita coba simulasi mentransfer 1000 DAI ke V.
function toInnerAmount(n, decimals) {
return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
var walletVitalik = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
var contractAddress_DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"
var wallet = exchange.IO("address")
// 转账给V神
var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
var transferAmount = toInnerAmount(1000, decimals_DAI)
Log("转账金额:", 1000, "DAI, 使用 toInnerAmount 转换为:", transferAmount)
// encode transfer
var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
walletVitalik, transferAmount)
var transactionObject = {
"from" : wallet,
"to" : contractAddress_DAI,
"data" : "0x" + data,
}
var blockNumber = "latest"
var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
return ret
}
Karena dompet uji coba saya tidak memiliki token DAI, hasil yang diharapkan dalam alat debugging adalah kesalahan:
Eksekusi terbalik: Dai/saldos tidak cukup
Lihat alamat dompet V:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
Jadi kita akan menyesuaikan arah transfer yang digunakan untuk panggilan analog, dan kita akan melakukan simulasi bahwa V-God memberikan kita transfer 1000 DAI.
Di sini, saya menulis komentar di mana saya mengubah kode:
function toInnerAmount(n, decimals) {
return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
var walletVitalik = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
var contractAddress_DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"
var wallet = exchange.IO("address")
var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
var transferAmount = toInnerAmount(1000, decimals_DAI)
Log("转账金额:", 1000, "DAI, 使用 toInnerAmount 转换为:", transferAmount)
// encode transfer
var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
wallet, transferAmount) // 使用wallet变量作为参数,转账接收方地址改为我自己
var transactionObject = {
"from" : walletVitalik, // 使用walletVitalik变量作为from字段的值,模拟这个调用是由V神钱包地址发出
"to" : contractAddress_DAI,
"data" : "0x" + data,
}
var blockNumber = "latest"
var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
Log(ret)
}
Percobaan alat debugging:
2023-06-09 13:34:31 信息 0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 13:34:31 信息 转账金额: 1000 DAI, 使用 toInnerAmount 转换为: 1000000000000000000000
Dengan menggunakan inventor platform perdagangan kuantitatif, hasil transaksi dapat dengan mudah disimulasikan dan menghindari kehilangan biaya gas yang tidak perlu untuk mengirim transaksi yang mungkin gagal. Kami menggunakan kode contoh dalam kursus bab ini untuk mensimulasikan transfer ke dompet V, panggilan dari dompet V untuk transfer ke kami.eth_call
Cara yang lebih berguna. Gunakan imajinasi Anda dan Anda akan mendapatkan lebih banyak.eth_call
Di mana metode ini digunakan?
Kami tahu bahwa token seperti ETH, BTC, dan lain-lain adalah token homogen, dan token yang Anda pegang tidak berbeda dari yang saya pegang. Tetapi ada banyak hal di dunia yang berbeda kualitasnya, misalnya: real estat, barang antik, karya seni virtual, dan lain-lain, yang tidak dapat diwakili secara abstrak dengan token homogen. Jadi, di antara banyak kontrak pintar yang digunakan di Ethereum, bagaimana kita mengenali kontrak pintar yang sesuai dengan standar ERC721?
Untuk mengidentifikasi ERC721, Anda harus terlebih dahulu memahami standar ERC165.
Dengan standar ERC165, kontrak cerdas dapat menyatakan antarmuka yang didukungnya untuk pemeriksaan kontrak lain. Kontrak antarmuka ERC165 hanya memiliki satu fungsi:supportsInterface(bytes4 interfaceId)
, parameterinterfaceId
ID antarmuka yang akan ditanyakan. Jika kontrak mengimplementasikan ID antarmuka tersebut, nilai Bull kembali benar, jika tidak, kembali palsu.
Di bawah ini kita akan membahasnya.interfaceId
Di sini, Anda dapat menemukan beberapa cara untuk menghitung, mengkode, dan mengkoneksi data.
Standar ERC165Di sini ada beberapa contoh:
pragma solidity ^0.4.20;
interface Solidity101 {
function hello() external pure;
function world(int) external pure;
}
contract Selector {
function calculateSelector() public pure returns (bytes4) {
Solidity101 i;
return i.hello.selector ^ i.world.selector;
}
}
Untuk tanda tangan fungsi pada antarmuka (yang terdiri dari daftar nama fungsi dan jenis parameter) yang berbeda atau dihitung, untuk kontrak antarmuka ERC165 yang hanya memiliki satu fungsi:
pragma solidity ^0.4.20;
interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
ID antarmuka untuk antarmuka ini adalah 0x01ffc9a7. Anda dapat menghitung ini dengan menjalankan bytes4 ((keccak256 ((
supportsInterface ((bytes4) )); atau menggunakan kontrak Selector di atas.
Jadi jika kita menghitung tanda tangan secara langsung, dan kita mengambil 4 byte pertama dari tanda tangan, kita akan mendapatkaninterfaceId
。
function main() {
var ret = Encode("keccak256", "string", "hex", "supportsInterface(bytes4)")
Log("supportsInterface(bytes4) interfaceId:", "0x" + ret.slice(0, 8))
}
Perangkat lunak ini memungkinkan pengguna untuk menjalankan tes di perangkat debugging:
2023-06-13 14:53:35 信息 supportsInterface(bytes4) interfaceId: 0x01ffc9a7
Anda dapat melihat hasil perhitungan danStandar ERC165"Selain itu, saya juga ingin berbagi pengalaman saya dengan teman-teman saya.
Berikut ini adalah definisi antarmuka dari standar kontrak ERC721:
interface ERC721 /* is ERC165 */ {
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
Jika kita ingin menilai apakah sebuah kontrak pintar adalah kontrak ERC721, kita harus terlebih dahulu mengetahui apa yang terjadi pada kontrak ERC721.interfaceId
Dan kemudian kita bisa mencoba menggunakansupportsInterface(bytes4 interfaceId)
Metodologi, di kelas sebelumnya kita sudah terbiasa dengan beberapa konsep dan perhitungan standar ERC165interfaceId
Kita menulis kode langsung untuk menghitung algoritma:
function calcSelector(arrSelector) {
var ret = null
if (Array.isArray(arrSelector)) {
if (arrSelector.length == 1) {
ret = Encode("keccak256", "string", "hex", arrSelector[0])
} else if (arrSelector.length == 0) {
throw "错误:数组中元素个数为0"
} else {
var viewEncodeData = null
for (var i = 0; i < arrSelector.length; i++) {
if (i == 0) {
ret = new Uint8Array(Encode("keccak256", "string", "raw", arrSelector[i]))
} else {
viewData = new Uint8Array(Encode("keccak256", "string", "raw", arrSelector[i]))
if (viewData.length != ret.length) {
throw "错误:TypeArray view长度不同"
}
for (var index = 0; index < ret.length; index++) {
ret[index] ^= viewData[index]
}
}
}
ret = Encode("raw", "raw", "hex", ret.buffer)
}
} else {
throw "错误:参数需要数组类型。"
}
return "0x" + ret.slice(0, 8)
}
function main() {
// supportsInterface(bytes4): 0x01ffc9a7
// var ret = calcSelector(["supportsInterface(bytes4)"])
// ERC721Metadata: 0x5b5e139f
/*
var arrSelector = [
"name()",
"symbol()",
"tokenURI(uint256)"
]
var ret = calcSelector(arrSelector)
*/
// ERC721: 0x80ac58cd
// /*
var arrSelector = [
"balanceOf(address)",
"ownerOf(uint256)",
"safeTransferFrom(address,address,uint256,bytes)",
"safeTransferFrom(address,address,uint256)",
"transferFrom(address,address,uint256)",
"approve(address,uint256)",
"setApprovalForAll(address,bool)",
"getApproved(uint256)",
"isApprovedForAll(address,address)",
]
var ret = calcSelector(arrSelector)
// */
Log(ret)
}
digunakan dalam kodeEncode()
Fungsi untuk menghitung tanda tangan fungsikeccak256
Algorithm), untuk perhitungan dalam contoh kode di atas,Encode()
Fungsi ini memiliki parameter output:"raw"
Fungsi ini akan mengembalikanJavaScript
BahasaArrayBuffer
Jenisnya.
Jika Anda ingin menghitung duaArrayBuffer
Obyek dilakukan^
Operasi (diferensial) harus didasarkan padaArrayBuffer
Pembuatan objekTypedArray
Anda dapat melihat gambar, lalu menjelajahi data di dalamnya, dan melakukan penghitungan atau penghitungan secara individual.
Perangkat debugging berjalan di:
2023-06-13 15:04:09 信息 0x80ac58cd
Anda dapat melihat hasil perhitungan daneip-721Perbedaan yang dijelaskan di bawah ini.
pragma solidity ^0.4.20;
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface ERC721 /* is ERC165 */ {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
...
Dengan ID antarmuka ERC721, kita dapat menilai apakah suatu kontrak adalah kontrak standar ERC721.BAYC
Untuk melakukan pengujian, ini adalah kontrak yang mengikuti ERC721, pertama kita perlu mendaftarkan ABI, karena kita hanya dapat mendaftarkan tiga metode ini karena hanya menggunakan tiga metode berikut:
Kode spesifiknya adalah sebagai berikut:
function main() {
// ERC721的合约地址,这里用的BAYC
var testContractAddress = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
var testABI = `[{
"inputs": [{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}],
"name": "supportsInterface",
"outputs": [{
"internalType": "bool",
"name": "",
"type": "bool"
}],
"stateMutability": "view",
"type": "function"
}, {
"inputs": [],
"name": "symbol",
"outputs": [{
"internalType": "string",
"name": "",
"type": "string"
}],
"stateMutability": "view",
"type": "function"
}, {
"inputs": [],
"name": "name",
"outputs": [{
"internalType": "string",
"name": "",
"type": "string"
}],
"stateMutability": "view",
"type": "function"
}]`
// ERC721接口Id,在之前的课程中计算得出
var interfaceId = "0x80ac58cd"
// 注册ABI
exchange.IO("abi", testContractAddress, testABI)
// 调用supportsInterface方法
var isErc721 = exchange.IO("api", testContractAddress, "supportsInterface", interfaceId)
// 输出信息
Log("合约地址:", testContractAddress)
Log("合约名称:", exchange.IO("api", testContractAddress, "name"))
Log("合约代号:", exchange.IO("api", testContractAddress, "symbol"))
Log("合约是否为ERC721标准:", isErc721)
}
Perangkat lunak ini memungkinkan pengguna untuk menjalankan tes di perangkat debugging:
2023-06-13 16:32:57 信息 合约是否为ERC721标准: true
2023-06-13 16:32:57 信息 合约代号: BAYC
2023-06-13 16:32:57 信息 合约名称: BoredApeYachtClub
2023-06-13 16:32:57 信息 合约地址: 0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
Mengidentifikasi alamat0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
Dengan demikian, perusahaan akan dapat mengekspor produk mereka ke negara lain.
Dalam artikel ini, kami akan menjelaskan bagaimana untuk menilai kontrak ERC721, dan kemudian kontrak ERC20 yang tidak mendukung standar ERC165 harus diidentifikasi dengan cara lain.
Apa itu?calldata
Menurut pengarangnya, deskripsi sederhana yang umum digunakan adalah:
"calldata" adalah kode untuk panggilan atau parameter pada suatu fungsi di Ethereum, "calldata" dikodekan sesuai dengan spesifikasi ABI ("Application Binary Interface") kontrak.
Jadi kita bisa menggunakan kontrak ERC20 yang kita pelajari di kelas sebelumnya.balanceOf
、transfer
Metode panggilan, bersama dengan parameter pada saat panggilan, dikodekan sebagaicalldata
◦ Di beberapa skenario aplikasi, misalnya:Interaksi antar kontrakSaya tidak tahu apa yang akan terjadi.calldata
Di sini kita akan membahas beberapa skenario aplikasi lainnya.
Bagaimana cara mengkode panggilan kontrak cerdas untuk mendapatkancalldata
?
Di inventor kuantitatif platform perdagangan dapat digunakanexchange.IO("encode", ...)
Untuk mengkodekan panggilan fungsi kontrak pintar, sangat mudah digunakan.exchange.IO("encode", ...)
Parameter pertama dari fungsi adalah string tetap."encode"
; parameter kedua adalah alamat kontrak pintar; parameter ketiga adalah nama metode kontrak pintar yang akan dienkode; parameter lainnya ditransmisikan ke nilai parameter tertentu dari metode kontrak pintar yang akan dienkode.
Ketika kita mengkode panggilan untuk metode kontrak pintar dan menghasilkancalldata
Dalam data, jika metode kontrak pintar ini adalah metode Write (yaitu: menulis operasi), kita perlu untuk mengubah hasil yang dihasilkan.calldata
Data sebagai bidang data transaksi dan kemudian menggunakan metode RPC Ethereumeth_sendRawTransaction
Mengirim permintaan data mentah yang berisi transaksi ke jaringan Ethereum.
eth_sendRawTransaction
Hanya ada satu parameter untuk metode.data
:
data: Transaksi yang ditandatangani (biasanya ditandatangani dengan perpustakaan, menggunakan kunci pribadi Anda)
Yang inidata
Parameter adalah data transaksi setelah perhitungan tanda tangan. Struktur data transaksi Ethereum terdiri dari bidang-bidang berikut:
{
"nonce": "0x1", // 交易发送方的账户交易次数
"gasPrice": "0x12a05f200", // 交易的Gas价格
"gasLimit": "0x5208", // 交易的Gas限制
"to": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", // 目标合约地址或接收方地址
"value": "0x4563918244F40000", // 转账的以太币数量
"data": "0x0123456789ABCDEF", // 要发送给合约的数据
}
Bagaimana cara mendaftar untuk transaksi Ethereum?
Di inventor kami menggunakan platform perdagangan kuantitatif.Encode()
Fungsi untuk melakukan perhitungan tanda tangan, contohnya kita tulis dalam pelajaran berikutnya "Mengeksekusi metode Write calldata".
Untuk metode Readcalldata
Perangkat lunak ini dapat digunakan untuk mengeksekusi, menggunakan metode RPC yang telah kita pelajari sebelumnya:eth_call
Untuk melakukan hal ini, kita akan menjelaskan sebelumnya.eth_call
Ini adalah metode RPC Ethereum yang hanya melakukan kontrak pintar.Write
Demonstrasi metode yang digunakan dalam bab inicalldata
Cara untuk melakukan panggilan metode smart contract Read. Mari kita gunakan kontrak WETH untuk melakukan panggilan metode WETH.balanceOf
Cara membaca saldo token WETH di dompet saat ini.
Kami menggunakan alat debugging untuk menguji di Ethereum:
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
// WETH合约的ABI
var abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]`
// WETH合约地址
var wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
// 注册WETH合约的ABI
exchange.IO("abi", wethAddress, abiWETH)
// 当前配置的交易所对象的钱包地址
var walletAddress = exchange.IO("address")
// 编码WETH合约的deposit方法调用
var calldataForDeposit = exchange.IO("encode", wethAddress, "balanceOf(address)", walletAddress)
Log("calldataForDeposit:", "0x" + calldataForDeposit)
// 构造transaction,作为eth_call的第一个参数
var transaction = {
"from" : walletAddress,
"to" : wethAddress,
"data" : "0x" + calldataForDeposit,
}
// eth_call的第二个参数
var blockNumber = "latest"
// 使用eth_call调用
var ret = exchange.IO("api", "eth", "eth_call", transaction, blockNumber)
var wethBalance = exchange.IO("decode", "uint256", ret) // 可以使用exchange.IO("decode", ...) 函数解码
Log("wethBalance:", toAmount(wethBalance, 18)) // 从以wei为单位,换算成WETH个数为单位
}
Perangkat debugging berjalan di:
2023-06-15 11:51:31 信息 wethBalance: 0.015
2023-06-15 11:51:31 信息 calldataForDeposit: 0x70a082310000000000000000000000006b3f11d807809b0b1e5e3243df04a280d9f94bf4
Jika metode kontrak pintar memiliki nilai pengembalian, itu bisa digunakan.exchange.IO("decode", ...)
Fungsi decoding.calldata
Dengan cara ini, Anda dapat mengkoneksikan semua data yang Anda miliki dengan data yang Anda miliki.balanceOf
Dengan metode yang sama, saya mendapatkan saldo WETH dari dompet uji saya sebesar 0.015 WETH.
Untuk melakukan calldata pada metode Write, Anda harus menggunakan metode RPC:eth_sendRawTransaction
Saya tidak tahu.
Kami menggunakan alat debugging untuk menguji di Ethereum:
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(s, decimals) {
return (BigDecimal(s)*BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
// WETH合约的ABI
var abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed