Sumber daya yang dimuat... Pemuatan...

Mulailah dengan Pengembangan web3 Mudah Berdasarkan Ethereum Menggunakan FMZ

Penulis:FMZ~Lydia, Dibuat: 2023-06-25 09:17:53, Diperbarui: 2024-11-11 22:34:49

0000000000164f2434262e1cc", transaksiHash: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048, alamat: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9 {\cH00FFFF}, {\cH00FFFF} alamat: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9, Blok Nomor: 0x109b1cd, logIndex: 0xde, dihapus: false, topics: [0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65, 0x00000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b], data: 0x00000000000000000000000000000000000000000000000000000164f2434262e1cc, transaksiHash: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048, Index transaksi: 0x91 Aku tidak tahu.


We can see that there are various events in the logs data, if we only care about ```Transfer``` events, we need to filter out the ```Transfer``` events in these data.

### Retrieving Logs

The Ethereum log is divided into two parts: 1. ```topics```; 2. ```data```.

- ```topics```
  Taking the results of the code run for the ```eth_getLogs``` section test as an example, the data in the ```topics``` field is:

  ```desc
  "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],  

Nilai daritopicsfield adalah struktur array yang digunakan untuk menggambarkan peristiwa. Hal ini ditentukan bahwa panjangnya (array) tidak dapat melebihi 4 dan elemen pertama adalah hash tanda tangan dari peristiwa. Dalam FMZ Quant Trading Platform, kita dapat menghitung hash tanda tangan ini menggunakanEncodefungsi, menggunakan kode berikut:

function main() {
    var eventFunction = "Transfer(address,address,uint256)"
    var eventHash = Encode("keccak256", "string", "hex", eventFunction)
    Log("eventHash:", "0x" + eventHash)
    // eventHash: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
}

Menghitungkeccak256nilai hash (kode hex) dariTransfer(address,address,uint256)adalah0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.

Nilai daritopicsbidang adalah struktur array, dengan elemen kedua, dan elemen ketiga, masing-masing:

  • Alamat pengirimanfrom

  • Alamat penerimato

  • data

    Data dalamdatabidang adalah:

    "data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
    

    Parameter tertentu dalam hal (parameter tanpa deklarasi terindeks dalam kode Soliditas kontrak pintar) disimpan dalamdata section.

    Menganalisis data0x0000000000000000000000000000000000000000000000000164f2434262e1cc

    function toAmount(s, decimals) {
        return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
    }
    
    function main() {
        var value = "0x0000000000000000000000000000000000000000000000000164f2434262e1cc"
        Log(toAmount(value, 0) / 1e18)  // 0.10047146239950075
    }
    

    Data ini diperoleh sebagai 0,10047146239950075 dandataadalah jumlah transfer yang sesuai.


Hal di atas telah dijelaskan, dilatih dan siap untuk pergi.

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() {
    // getBlockNumber
    var blockNumber = exchange.IO("api", "eth", "eth_blockNumber")
    Log("blockNumber:", blockNumber)

    // get logs
    var fromBlock = "0x" + (toAmount(blockNumber, 0) - 1).toString(16)
    var toBlock = "0x" + toAmount(blockNumber, 0).toString(16)
    var params = {
        "fromBlock" : fromBlock,
        "toBlock" : toBlock,
        "address" : "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
    }
    var logs = exchange.IO("api", "eth", "eth_getLogs", params)

    // Traverse logs
    var eventFunction = "Transfer(address,address,uint256)"
    var eventHash = "0x" + Encode("keccak256", "string", "hex", eventFunction)
    Log("eventHash:", eventHash)

    var counter = 0
    for (var i = logs.length - 1; i >= 0 && counter < 10; i--) {
        if (logs[i].topics[0] == eventHash) {
            Log("Event Transfer, data:", toAmount(logs[i].data, 0) / 1e18, ", blockNumber:", toAmount(logs[i].blockNumber, 0), ", transactionHash:", logs[i].transactionHash,
              ", log:", logs[i])
            counter++
        }
    }
}

Periksa.https://etherscan.io/:

img

Hasil dari kode uji yang dijalankan dalam alat debugging FMZ:

img

Data difrom, tobidang juga dapat dianalisis tergantung pada kebutuhan pada saat pengambilan, misalnya:

function main() {
    var from = "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c"
    var address = "0x" + exchange.IO("encodePacked", "address", from)
    Log("address:", address)
}

Hasil pelaksanaan:

Alamat: 0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c

Mendengarkan Peristiwa Kontrak

Sejakalat debuggingDalam bagian ini, kami menggunakan FMZ Quant Trading Platform untuk membuat perdagangan langsung untuk menguji.

Di sini kita menggunakan Ethereum mainnet, dan kita mendengarkanTransfer(address,address,uint256)peristiwaUSDTBerdasarkan apa yang kita pelajari dalam pelajaran terakhir, kami merancang dan menulis contoh dari terus mendengarkan peristiwa kontrak pintar tertentu:

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
    /* TODO: test
    self.isFirst = true 
    */ 

    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]
        }
        // Log("fromBlockNumber:", self.fromBlockNumber, ", currBlockNumber:", currBlockNumber, "#FF0000")
        
        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 = null 
function main() {
    var event = "Transfer(address,address,uint256)"
    var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"
    var decimals = exchange.IO("api", contractAddress, "decimals")
    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])
        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: test
var arrLog = []
function onexit() {
    Log("End the run and verify the record")
    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("Length varies!")
        return 
    }
    
    for (var i = 0; i < arrLog.length; i++) {
        Log("Determine the blockNumber:", logs[i].blockNumber == arrLog[i].blockNumber, ", Determine from:", logs[i].topics[1] == arrLog[i].topics[1], 
            "Determine to:", logs[i].topics[2] == arrLog[i].topics[2])
    }
}
*/

Berjalan pada perdagangan langsung:

img

Untuk hasil pelaksanaan, bagian validasi (TODO: test) juga ditulis dalam kode.Transferperistiwa kontrak USDT terus dipantau dan data dicatat dan perbandingan antara data ini dan data peristiwa yang diperoleh sekaligus dapat diamati bahwa data tersebut konsisten dengan:

img

Penyaringan Acara

Berdasarkan pelajaran sebelumnyaListening to contract events, kita memperluasnya dengan menambahkan filter untuk proses mendengarkan untuk mendengarkan transfer ke dan dari alamat tertentu.topicsJadi kita merancang aturan filter dengan[[A1, A2, ...An], null, [C1], D]sebagai contoh.

  1. [A1, A2, ...An]sesuai dengan data pada posisitopics[0].
  2. Nullsesuai dengan data pada posisitopics[1].
  3. [C1]sesuai dengan data pada posisitopics[2].
  4. Dsesuai dengan data pada posisitopics[3].
  • Jika elemen dalam struktur kondisi ditetapkannullberarti tidak disaring, misalnyanullsesuai dengantopics[1]dan setiap nilai yang cocok.
  • Jika elemen dalam struktur kondisi menetapkan nilai tunggal yang menunjukkan bahwa posisi harus sesuai, misalnya[C1]sesuai dengantopics[2]atauDsesuai dengantopics[3], dan log yang tidak cocok disaring.
  • Jika elemen dalam struktur kondisi adalah array, itu berarti bahwa setidaknya salah satu elemen dalam array harus cocok, misalnya[A1, A2, ...An]sesuai dengantopics[0], [A1, A2, ...An]dengan salah satu dari mereka cocoktopics[0], maka log tidak akan disaring.

Mendengarkan transfer USDT dari bursa

PemantauanUSDTtransaksi yang ditransfer dari dan ke Binance Exchange:

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("Set filter conditions:", 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) {
                // Check the filter condition, and execute the judgment if the filter condition is set
                if (self.filters.length != 0) {
                    // Initial filter marker
                    var isFilter = true 
                    // Traverse filter condition setting
                    for (var j = 0; j < self.filters.length; j++) {
                        // Take a filter setting, e.g: [[A1, A2, ...An], null, [C1], D]
                        var cond = self.filters[j]

                        // Traverse the filter setting
                        var final = true
                        for (var topicsIndex = 0; topicsIndex < cond.length; topicsIndex++) {
                            // Take one of the conditions in the filter setting, if it is the first condition: i.e. the data to be compared with topics[0]
                            var condValue = cond[topicsIndex]

                            // Data in the logs
                            if (topicsIndex > logs[i].topics.length - 1) {
                                continue 
                            }
                            var topicsEleValue = logs[i].topics[topicsIndex]
                            // If it's a Transfer event, you need to handle the from and to
                            if (logs[i].topics[0] == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") {
                                if (topicsIndex == 1 || topicsIndex == 2) {
                                    topicsEleValue = "0x" + exchange.IO("encodePacked", "address", topicsEleValue)
                                }
                            }

                            // If the condValue type is an array, it means that there are multiple comparison conditions in this position, and the multiple condition comparison is a logical or relationship
                            if (Array.isArray(condValue) && condValue.length > 1) {
                                // Determine 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() {
    // Initial clean-up log
    LogReset(1)
    LogProfitReset()

    var event = "Transfer(address,address,uint256)"                          // Listening to events
    var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"       // USDT contract address
    var decimals = exchange.IO("api", contractAddress, "decimals")           // Get the precision information of USDT token
    var accountBinanceAddress = "0x28C6c06298d514Db089934071355E5743bf21d60" // Binance hot wallet address
    accountBinanceAddress = accountBinanceAddress.toLowerCase()              // Addresses are handled in lowercase
    Log(exchange.IO("api", contractAddress, "name"), " decimals:", decimals)

    // Creating a listener object
    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("Binance transfer out - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#CD32CD")
        } else if (toAddress == accountBinanceAddress) {
            Log("Binance transfer in - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#FF0000")
        }        
    })

    // Set up event filtering
    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("Binance wallet address:", accountBinanceAddress, " balance:", balance, direction)
            LogProfit(balance, "&")   // Drawing only, no log printing
            preBalance = balance
        }
        LogStatus(_D(), "Binance wallet address:", accountBinanceAddress, ", balance:", balance)
        Sleep(5000 * 3)
    }
}

Kode di atas yang berjalan dalam perdagangan langsung:

img

Dalam pelajaran ini, kami memperkenalkan cara merancang filter acara.USDTAnda dapat memodifikasi dan memperluas contoh program ini untuk mendengarkan setiap acara yang Anda minati, untuk melihat apa transaksi barusmart moneytelah dibuat, apa item baru yangNFTOrang-orang besar telah bergegas, dll.

Konversi satuan

Banyak perhitungan yang terkait dengan Ethereum memiliki nilai yang melebihi bilangan bulat aman maksimum dariJavaScriptOleh karena itu, beberapa metode diperlukan pada FMZ Quant Trading Platform untuk menangani nilai besar, yang telah kita gunakan secara khusus dalam kursus sebelumnya dan tidak dibahas secara rinci.

Mencetak bilangan bulat aman maksimum yang didefinisikan dalamJavaScriptBahasa:

function main() {
    Log("Number.MAX_SAFE_INTEGER:", Number.MAX_SAFE_INTEGER)
}

Hasil pelaksanaan:

Nomor.MAX_SAFE_INTEGER: 9007199254740991

BigInt

Satuan terkecil yang didefinisikan dalam Ethereum adalah1wei, dan definisi1Gweisama dengan1000000000 wei. 1GweiJadi data ini dengan nilai yang sangat besar dapat dengan mudah melebihiNumber.MAX_SAFE_INTEGER: 9007199254740991.

Di FMZ Quant Trading Platform, kami menggunakan platformBigIntobjek untuk mewakili data bilangan bulat yang sangat besar ini.BigInt()untuk membangunBigIntAnda dapat membangunBigIntobjek menggunakan angka, hexadecimal string angka sebagai parameter.toString()metodeBigIntobjek untuk output data yang diwakili oleh objek sebagai string.

Operasi yang didukung olehBigIntobjeknya adalah:

  • Tambahan:+
  • Pengurangan:-
  • Perkalian*
  • Divisi:/
  • Operasi modulo:%
  • Operasi daya:*

Lihat contoh kode berikut:

function main() {
    // Decimal representation of 1Gwei
    var oneGwei = 1000000000

    // Decimal to hexadecimal conversion of 1Gwei
    var oneGweiForHex = "0x" + oneGwei.toString(16)

    Log("oneGwei : ", oneGwei)
    Log("oneGweiForHex : ", oneGweiForHex)

    // Constructing BigInt objects
    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("The square root of 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))
}

Pengujian alat debugging:

2023-06-08 11:39:50		Info	Number.MAX_SAFE_INTEGER * 2 : 18014398509481982
2023-06-08 11:39:50		Info	Number.MAX_SAFE_INTEGER : 9007199254740991
2023-06-08 11:39:50		Info	The square root of 100 : 10
2023-06-08 11:39:50		Info	1Gwei ** 2 : 1000000000000000000
2023-06-08 11:39:50		Info	(1Gwei + 1) % 1Gwei : 1
2023-06-08 11:39:50		Info	1Gwei + 1Gwei : 2000000000
2023-06-08 11:39:50		Info	1Gwei - 1Gwei : 0
2023-06-08 11:39:50		Info	1Gwei * 1Gwei : 1000000000000000000
2023-06-08 11:39:50		Info	1Gwei / 1Gwei : 1
2023-06-08 11:39:50		Info	oneGweiForHex : 0x3b9aca00
2023-06-08 11:39:50		Info	oneGwei : 1000000000

BigFloat

PeraturanBigFloatobjek digunakan dengan cara yang sama denganBigIntobjek untuk mewakili bilangan koma mengambang dengan nilai yang lebih besar, dan juga mendukung penjumlahan, pengurangan, perkalian dan pembagian. PeraturanBigFloatobjek mendukungtoFixed() method.

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

Pengujian alat debugging:

2023-06-08 13:56:44		Info	pi / 2.0 : 1.57
2023-06-08 13:56:44		Info	pi * 2.0 : 6.28
2023-06-08 13:56:44		Info	pi - oneGweiForHex : -999999996.86
2023-06-08 13:56:44		Info	pi + oneGwei : 1000000003.14

BigDecimal

PeraturanBigDecimalobject kompatibel dengan nilai integer dan nilai floating point, dan mendukung inisialisasi denganBigIntobjek danBigFloatobjek, dan juga mendukung penjumlahan, pengurangan, perkalian dan pembagian.

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

Menjalankan alat debugging:

2023-06-08 14:52:53		Info	pi / pi : 1
2023-06-08 14:52:53		Info	2.0 * pi : 6.283
2023-06-08 14:52:53		Info	oneGwei - pi : 999999996.8585
2023-06-08 14:52:53		Info	oneGwei + pi : 1000000003.1415
2023-06-08 14:52:53		Info	BigFloat(pi) : 3.1415
2023-06-08 14:52:53		Info	BigInt(oneGwei) : 1e+9
2023-06-08 14:52:53		Info	oneGweiForHex : 1e+9
2023-06-08 14:52:53		Info	oneGwei : 1e+9
2023-06-08 14:52:53		Info	pi : 3.14

Konversi satuan

Dua fungsi berikut:toAmount(), toInnerAmount()kita telah digunakan berkali-kali dalam kursus sebelumnya, kedua fungsi ini 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)
}

PeraturantoAmount()fungsi mengkonversi (mengurangi) variabelsMenurut parameter presisidecimalsDalam pengembangan praktis web3, seringkali perlu untuk berurusan dengan beberapa data hexadecimal berantai. Kami sering menemukan ini dalam kursus kami sebelumnya, misalnya,datadata lapangan diTransfer(address,address,uint256)peristiwa kontrak pintar:

{
	"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000",
	"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000006b75d8af000000e20b7a7ddf000ba900b4009a80", "0x000000000000000000000000bcb095c1f9c3dc02e834976706c87dee5d0f1fb6"],
	"transactionHash": "0x27f9bf5abe3148169b4b85a83e1de32bd50eb81ecc52e5af006157d93353e4c4",
	"transactionIndex": "0x0",
	"removed": false,
	"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
	"blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
	"blockNumber": "0x109b1cc",
	"logIndex": "0x0"
}

Saat memproses data"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000", kita menggunakantoAmount()Pemrosesan ini dirancang untuk melakukan pekerjaan yang baik dari konversi data data lapangan ke 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))  // Print out 0.12656402755905127
}

1 token ETH, seperti yang kita tahu, adalah1e18 wei, jika kita mendapatkan data126564027559051260dalamwei, bagaimana mengubahnya menjadi token ETH? MenggunakantoAmount(, 18)fungsi adalah metode konversi yang sangat sederhana.toInnerAmount()fungsi adalah operasi terbalik daritoAmount()fungsi (tergantung pada presisi, memperbesar), dan mudah untuk mengkonversi data menggunakan kedua fungsi ini.

Penting untuk dicatat rentang keamanan nilai bilangan bulat dalam bahasa JavaScript,Number.MAX_SAFE_INTEGER, dan contoh berikut menggambarkan masalah tersembunyi ketika mengubah 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)
    
    // Decimal value 10000000000000000 -> Hexadecimal value 0x2386f26fc10000
    Log("Convert", innerAmount, "to hexadecimal:", innerAmount.toString(16))
    Log("Convert", BigInt(10000000000000000).toString(10), "to hexadecimal:", BigInt(10000000000000000).toString(16))
    
    Log("0x" + BigInt(10000000000000000).toString(16), "Convert to decimal:", toAmount("0x" + BigInt(10000000000000000).toString(16), 0))
}

Hal ini mungkin untuk menjalankan di alat debugging:

2023-06-15 16:21:40		Info	Convert 0x2386f26fc10000 to decimal: 10000000000000000
2023-06-15 16:21:40		Info	Convert 10000000000000000 to hexadecimal: 2386f26fc10000
2023-06-15 16:21:40		Info	Convert 10000000000000000 to hexadecimal: 10000000000000000
2023-06-15 16:21:40		Info	typeof(innerAmount): number , innerAmount: 10000000000000000
2023-06-15 16:21:40		Info	innerAmount: 10000000000000000
2023-06-15 16:21:40		Info	Number.MAX_SAFE_INTEGER: 9007199254740991

Melalui pengamatan kami menemukan bahwa:

Log("Convert", innerAmount, "to hexadecimal:", innerAmount.toString(16))

Baris kode ini sesuai dengan log output:Converting 10000000000000000 to hex: 10000000000000000, yang tidak dikonversi dengan benar. alasannya tentu saja bahwa 10000000000000000 adalah di luarNumber.MAX_SAFE_INTEGER.

Tapi ketika nilai desimal berada dalam kisaran aman, yaitu, kurang dariNumber.MAX_SAFE_INTEGER, yangtoString(16)fungsi mengubahnya dengan benar lagi, misalnya:

function main() {
    var value = 1000
    Log("Convert value to hexadecimal:", "0x" + value.toString(16))   // 0x3e8
    Log("Convert 0x3e8 to decimal:", Number("0x3e8"))               // 1000
}

Di blockchain, bahkan0.01ETH dikonversi menjadi nilai10000000000000000dalamweiakan melebihiNumber.MAX_SAFE_INTEGER``, so a safer conversion for such cases is:BigInt ((10000000000000000).toString ((16) ``.

Simulasi Panggilan

Melakukan transaksi dan memanggilWritemetode kontrak pintar pada Ethereum biaya sejumlah gas dan kadang-kadang gagal. Penting untuk mengetahui transaksi mana yang mungkin gagal sebelum mengirim dan memanggil mereka. Ada panggilan simulasi pada Ethereum untuk pengujian.

Eth_call

Metode RPC Ethereumeth_call: dapat mensimulasikan transaksi dan mengembalikan hasil dari kemungkinan transaksi, tetapi tidak benar-benar mengeksekusi transaksi di blockchain.

Peraturaneth_callmetode memiliki 2 parameter, yang pertama adalah struktur kamus,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: Anda bisa melewati labellatest/pending/earliest, dll:

/* 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
*/

Selanjutnya, kita mengambil metode kontrak pintarapprovedantransferpanggilan tokenDAIsebagai contoh untuk panggilan simulasi, dan lingkungan pengujian berikut adalah jaringan Ethereum utama.

Simulasi panggilan disetujui

Kita semua akrab denganapprovemetode untuk kontrak ERC20, dan kami telah mempraktikkannya dalam kursus sebelumnya. karena kontrak ERC20 sudah dibangun ke dalam platform FMZ ABI, tidak perlu mendaftarkan ABI dari kontrak pintar yang akan dipanggil oleh simulasi.

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

Kode dalam contoh pertama mengkodeapprove(address,uint256)metode dan parameter, dan nilai parameter0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdariapprovemetode menunjukkan jumlah maksimum otorisasi. otorisasi diberikan kepada kontrak pintar di alamat0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45Artinya kontrak router untukUniswap V3Akhirnya metode Ethereum RPCeth_callAnda dapat melihat bahwagasPricedangasbidang ditransactionObjectparameter dapat dihilangkan.

Alat debugging dijalankan dan simulasi memanggil metode approve untuk mengotorisasi dengan sukses (itu tidak benar-benar mengotorisasi):

2023-06-09 11:58:39		Info	ret: 0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 11:58:39		Info	ERC20 token DAI approve encode, data: 095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Hal ini juga mungkin untuk mensimulasikan beberapa skenario kegagalan, ketika kita menyesuaikangasPricedangasparameter, jika ETH di dompet tidak cukup untuk membayar biaya gas, kesalahan akan dilaporkan::

dana yang tidak cukup

Ketika biaya gas diatur terlalu rendah, kesalahan akan dilaporkan:

gas intrinsik terlalu rendah: memiliki 21000, ingin 21944 (disuplai gas 21000)

Simulasi pemindahan panggilan

Kita sudah terbiasa dengan ERC20transfermetode, yang memungkinkan Anda untuk mentransfer token ERC20 ke alamat dompet tertentu, jadi mari kita coba mensimulasikan transfer 1000 DAI ke Vitalik Buterin.

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

    // transfer to Vitalik Buterin
    var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
    var transferAmount = toInnerAmount(1000, decimals_DAI)
    Log("Transfer amount:", 1000, "DAI, use toInnerAmount convert to:", 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 saya tidak memiliki token DAI di dompet uji ini, menjalankan di alat debug melaporkan kesalahan berikut secara tak terduga:

Eksekusi terbalik: Dai/saldos tidak cukup

Periksa alamat dompet Vitalik Buterin:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045, jelas bahwa dompet ini memiliki token DAI. jadi mari menyesuaikan arah transfer panggilan simulasi dan mensimulasikan transfer 1000 DAI dari Vitalik Buterin kepada kita.

Mengubah kode, di mana perubahan yang saya buat komentar:

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("Transfer amount:", 1000, "DAI, use toInnerAmount convert to:", transferAmount)

    // encode transfer
    var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
        wallet, transferAmount)     // Use the wallet variable as a parameter and change the transfer recipient's address to my own

    var transactionObject = {
        "from" : walletVitalik,     // Use the walletVitalik variable as the value of the from field to simulate that the call was made from the Vitalik Buterin's wallet address
        "to" : contractAddress_DAI,
        "data" : "0x" + data,
    }
    var blockNumber = "latest"
    
    var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
    Log(ret)
}

Tes alat debugging:

2023-06-09 13:34:31		Info	0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 13:34:31		Info	Transfer amount: 1000 DAI, use toInnerAmount convert to: 1000000000000000000000

Dengan menggunakan FMZ Quant Trading Platform, mudah untuk mensimulasikan hasil transaksi dan menghindari kehilangan biaya gas yang tidak perlu dari mengirim transaksi yang berpotensi gagal. Kami menggunakan contoh kode dari bab ini dari kursus untuk mensimulasikan panggilan untuk mentransfer uang ke dompet Vitalik Buterin dan dompet Vitalik Buterin untuk mentransfer uang kepada kita.eth_callGunakan imajinasi Anda, apa yang akan Anda gunakaneth_callMetode untuk?

Mengidentifikasi Kontrak ERC721

Kami tahu bahwa token seperti ETH dan BTC adalah token homogenisasi, dan token di dompet Anda tidak berbeda dari token di dompet saya. Tapi ada banyak hal di dunia yang tidak homogen, seperti real estat, barang antik, karya seni virtual, dll. Ini tidak dapat diwakili oleh token homogen dalam abstraksi. Oleh karena itu, ada standar ERC721 untuk abstrak objek yang tidak homogen, dan ada NFT dan konsep terkait. Jadi di antara banyak kontrak pintar yang digunakan di Ethereum, bagaimana kita mengidentifikasi kontrak pintar mana yang merupakan kontrak pintar standar ERC721?

Untuk mengidentifikasi ERC721, penting untuk mengetahui standar ERC165 terlebih dahulu.

ERC165

Dengan standar ERC165, kontrak pintar dapat menyatakan antarmuka yang didukungnya untuk kontrak lain untuk diperiksa.supportsInterface(bytes4 interfaceId), parameterinterfaceIdadalah ID antarmuka yang akan ditanyakan. Jika kontrak mengimplementasikan ID antarmuka mengembalikan nilai benar boolean, jika tidak mengembalikan nilai palsu.

Di sini kita akan berbicara tentang bagaimana iniinterfaceIdDihitung dan dikodekan secara khusus.

Standar ERC165menunjukkan 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 antarmuka (terdiri dari nama fungsi dan daftar jenis parameter) untuk melakukan operasi ketidaksamaan, untuk kontrak antarmuka ERC165 di mana kontrak 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.

Menghitung tanda tangan fungsi secara langsung dan mengambil pertama 4 byte untuk mencapaiinterfaceId.

function main() {
    var ret = Encode("keccak256", "string", "hex", "supportsInterface(bytes4)")
    Log("supportsInterface(bytes4) interfaceId:", "0x" + ret.slice(0, 8))
}

Tes dapat dijalankan di alat debug di:

2023-06-13 14:53:35		Info	supportsInterface(bytes4) interfaceId: 0x01ffc9a7

Hal ini dapat dilihat bahwa hasil yang dihitung konsisten dengan deskripsi dalamStandar ERC165 document.

ERC721

Selanjutnya mari kita lihat 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 menentukan apakah kontrak pintar adalah kontrak ERC721, pertama-tama kita perlu tahuinterfaceIddari kontrak ERC721 sebelum kita bisa mencoba untuk menggunakansupportsInterface(bytes4 interfaceId)Dalam kursus sebelumnya kita telah membiasakan diri dengan beberapa konsep dari standar ERC165 dan algoritma untuk menghitunginterfaceId, dan kita menulis kode untuk menghitung langsung:

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 "Error: the number of elements in the array is 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 "Error: TypeArray view length is different"
                    }

                    for (var index = 0; index < ret.length; index++) {
                        ret[index] ^= viewData[index]
                    }
                }
            }
            ret = Encode("raw", "raw", "hex", ret.buffer)
        }
    } else {
        throw "Error: The parameter requires an array type."
    }

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

Kode ini menggunakanEncode()fungsi untuk perhitungan tanda tangan fungsi (thekeccak256algoritma), dan untuk perhitungan dalam contoh kode di atas, menentukan output parameter dariEncode()fungsi sebagai"raw", fungsi mengembalikanArrayBufferjenisJavaScriptbahasa. Untuk melakukan^(iso-atau) operasi pada duaArrayBufferobjek, Anda perlu untuk membuatTypedArraypandangan berdasarkanArrayBufferobjek, kemudian mengulangi melalui data di dalamnya dan melakukan iso-atau operasi satu per satu.

Jalankan di alat debugging:

2023-06-13 15:04:09		Info	0x80ac58cd

Hal ini dapat dilihat bahwa hasil yang dihitung konsisten dengan yang dijelaskan dalameip-721.

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 menentukan apakah kontrak adalah kontrak standar ERC721 atau tidak.BAYCUntuk melakukan tes, yang merupakan kontrak yang mengikuti ERC721. pertama kita perlu mendaftarkan ABI, dan karena kita hanya memanggil tiga metode berikut, kita dapat mendaftarkan tiga metode ini:

  • mendukung Interface ((interfaceId)
  • lambang ((()
  • nama (()

Kode spesifiknya adalah sebagai berikut:

function main() {
    // Contract address for ERC721, BAYC is used here
    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 Interface Id, calculated in the previous course
    var interfaceId = "0x80ac58cd"

    // Register ABI
    exchange.IO("abi", testContractAddress, testABI)

    // Call the supportsInterface method
    var isErc721 = exchange.IO("api", testContractAddress, "supportsInterface", interfaceId)

    // Output Information
    Log("Contract address:", testContractAddress)
    Log("Contract name:", exchange.IO("api", testContractAddress, "name"))
    Log("Contract code:", exchange.IO("api", testContractAddress, "symbol"))
    Log("Whether the contract is ERC721 standard:", isErc721)
}

Tes dapat dijalankan di alat debugging:

2023-06-13 16:32:57		Info	Whether the contract is ERC721 standard: true
2023-06-13 16:32:57		Info	Contract code: BAYC
2023-06-13 16:32:57		Info

Berkaitan

Lebih banyak