Les ressources ont été chargées... Je charge...

Commencez avec le développement web3 facilement basé sur Ethereum en utilisant FMZ

Auteur:FMZ~Lydia, Créé: 2023-06-25 09:17:53, Mis à jour: 2024-11-11 22:34:49

Le numéro d'enregistrement est le numéro d'enregistrement suivant: TransactionHash: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048 adresse : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9 Je ne sais pas. adresse : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2, blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9, numéro de bloc: 0x109b1cd, logIndex: 0xde, supprimé: faux, topics: [0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65, 0x00000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b], données: 0x0000000000000000000000000000000000000000000000000164f2434262e1cc, TransactionHash: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048 indice de transaction: 0x91 Je ne sais pas.


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"],  

La valeur de latopicsLe champ est une structure de tableau utilisée pour décrire l'événement. Il est spécifié que sa longueur (d'un tableau) ne peut pas dépasser 4 et que le premier élément est le hachage de signature de l'événement. Dans la plateforme de trading FMZ Quant, nous pouvons calculer ce hachage de signature en utilisantEncodefonction, en utilisant le code suivant:

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

Calculer lekeccak256valeur de hachage (codage hexagonale) deTransfer(address,address,uint256)est0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.

La valeur de latopicsle champ est une structure de tableau, avec le deuxième élément et le troisième élément, respectivement:

  • Adresse d'envoifrom

  • Adresse de réceptionto

  • data

    Les donnéesdatales champs sont:

    "data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
    

    Certains paramètres (paramètres sans déclarations indexées dans le code de solidité du contrat intelligent) sont stockés dans ledata section.

    Analyser les données0x0000000000000000000000000000000000000000000000000164f2434262e1cc

    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
    }
    

    Ces données sont obtenues sous la forme de 0,10047146239950075 et ledataest le montant de transfert correspondant.


Ce qui précède a été expliqué, pratiqué et prêt à l'emploi.

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

Vérifiez.https://etherscan.io/:

img

Résultats du code de test exécuté dans l'outil de débogage FMZ:

img

Les donnéesfrom, toLes champs peuvent également être analysés en fonction des besoins au moment de la récupération, par exemple:

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

Résultats de l'exécution:

Adresse: 0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c

Écouter les événements contractuels

Depuis leoutil de débogageDans cette section, nous utilisons la plateforme de trading FMZ Quant pour créer des transactions en direct pour tester.

Ici, nous utilisons le mainnet Ethereum, et nous écoutonsTransfer(address,address,uint256)événement de laUSDTBasé sur ce que nous avons appris dans la dernière leçon, nous avons conçu et écrit un exemple d'écoute continue des événements d'un certain contrat intelligent:

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])
    }
}
*/

En cours de négociation:

img

Pour les résultats d'exécution, une section de validation (TODO: test) est également écrite dans le code.Transferl'événement du contrat USDT est constamment surveillé et les données enregistrées et une comparaison entre ces données et les données d'événement obtenues à la fois permet d'observer que les données sont compatibles avec:

img

Filtrage des événements

Basé sur la leçon précédenteListening to contract events, nous l'élargissons en ajoutant des filtres au processus d'écoute pour écouter les transferts vers et depuis des adresses spécifiées.topicsNous avons donc conçu une règle de filtre avec[[A1, A2, ...An], null, [C1], D]comme exemple.

  1. [A1, A2, ...An]correspond aux données à la positiontopics[0].
  2. Nullcorrespond aux données à la positiontopics[1].
  3. [C1]correspond aux données à la positiontopics[2].
  4. Dcorrespond aux données à la positiontopics[3].
  • Si un élément de la structure de condition est défininullsignifie qu'il n'est pas filtré, par exemplenullcorrespond àtopics[1]et toute correspondance de valeur.
  • Si l'élément de la structure des conditions définit une valeur unique indiquant que la position doit correspondre, par exemple[C1]correspond àtopics[2]ouDcorrespond àtopics[3], et les journaux non correspondants sont filtrés.
  • Si l'élément de la structure de condition est un tableau, cela signifie qu'au moins un des éléments du tableau doit correspondre, par exemple[A1, A2, ...An]correspond àtopics[0], [A1, A2, ...An]avec n'importe lequel d'entre eux correspondanttopics[0], alors les journaux ne seront pas filtrés.

Écouter les virements en USDT des bourses

Surveillance de laUSDTles transactions transférées depuis et vers la Bourse Binance:

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

Le code ci-dessus en cours de négociation en direct:

img

Dans cette leçon, nous avons présenté comment concevoir un filtre d'événement.USDTVous pouvez modifier et étendre ce programme d'exemple pour écouter tout événement qui vous intéresse, pour voir quelles nouvelles transactionssmart moneyIl y a eu des changements.NFTLes magnats se sont précipités, etc.

Conversions d'unités

Beaucoup de calculs liés à Ethereum ont des valeurs qui dépassent l'entier sûr maximum de laJavaScriptPar conséquent, certaines méthodes sont nécessaires sur la plateforme de trading FMZ Quant pour gérer les grandes valeurs, que nous avons utilisées spécifiquement dans les cours précédents et que nous n'avons pas couvertes en détail.

Imprimez l'entier le plus sûr défini dans leJavaScriptlangue:

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

Résultats de l'exécution:

Numéro.MAX_SAFE_INTEGER: 9007199254740991

Je veux dire, BigInt.

La plus petite unité définie dans Ethereum est1wei, et la définition1Gweiest égal à1000000000 wei. 1Gwein'est pas vraiment un très grand nombre dans les calculs liés à Ethereum, et certaines données sont beaucoup plus grandes que cela.Number.MAX_SAFE_INTEGER: 9007199254740991.

Chez FMZ Quant Trading Platform, nous utilisons les plateformesBigIntl'objet pour représenter ces données entières très grandes. Utiliser le constructeurBigInt()pour construire leBigIntVous pouvez construireBigIntles objets utilisant des chaînes numériques hexadécimales comme paramètres.toString()méthode deBigIntObjet pour produire les données représentées par l'objet sous forme de chaîne.

Les opérations soutenues par leBigIntl'objet sont:

  • Ajout:+
  • Soustraction:-
  • Multiplication:*
  • Division:/
  • Opérations par modulo:%
  • Opérations de puissance:*

Voir les exemples de codes suivants:

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

Test de l' outil de débogage:

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

LeBigFloatl'objet est utilisé de façon similaire à laBigIntobjet pour représenter des nombres à virgule flottante avec des valeurs plus grandes, et il prend également en charge l'addition, la soustraction, la multiplication et la division. LeBigFloatl'objet soutient letoFixed() method.

Voir l'exemple de code suivant:

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

Test de l' outil de débogage:

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

Le BigDecimal

LeBigDecimalObjet est compatible avec les valeurs entières et les valeurs en virgule flottante, et prend en charge l'initialisation avec leBigIntl'objet et leBigFloatobjet, et il prend également en charge l'addition, la soustraction, la multiplication et la division.

Voir l'exemple de code suivant:

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

Exécuter dans l'outil de débogage:

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

Conversions d'unités

Les deux fonctions suivantes:toAmount(), toInnerAmount()Nous avons utilisé plusieurs fois dans les cours précédents, ces deux fonctions sont principalement utilisées pour la conversion de données de précision.

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

LetoAmount()fonction convertit (réduit) une variablesselon le paramètre de précisiondecimalsDans le développement pratique de web3, il est souvent nécessaire de traiter avec des données hexadécimales enchaînées. Nous avons souvent rencontré cela dans nos cours précédents, par exemple, ledatadonnées de terrain dans leTransfer(address,address,uint256)événement de contrat intelligent:

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

Lors du traitement des données"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000", nous utilisons letoAmount()Ce traitement est conçu pour faire un bon travail de conversion des données de champ de données en valeurs lisibles.

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 jeton ETH, comme nous le savons, est1e18 wei, si nous obtenons des données126564027559051260danswei, comment le convertir en jetons ETH? En utilisant letoAmount(, 18)La fonction est une méthode de conversion trèstoInnerAmount()fonction est l'opération inverse de latoAmount()Les données sont faciles à convertir à l'aide de ces deux fonctions.

Il est important de noter la plage de sécurité des valeurs entières dans le langage JavaScript,Number.MAX_SAFE_INTEGER, et l'exemple suivant illustre un problème caché lors de la conversion des données:

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

Il est possible d'exécuter dans l'outil de débogage:

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

Par l'observation, nous avons constaté que:

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

Cette ligne de code correspond à la sortie log:Converting 10000000000000000 to hex: 10000000000000000La raison est naturellement que 10000000000000000 est au-delàNumber.MAX_SAFE_INTEGER.

Mais lorsque la valeur décimale est dans la plage de sécurité, c'est-à-dire inférieure àNumber.MAX_SAFE_INTEGER, letoString(16)fonction le convertit correctement à nouveau, par exemple:

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

Dans la blockchain, même0.01ETH converti en une valeur de10000000000000000dansweidépasseraNumber.MAX_SAFE_INTEGER``, so a safer conversion for such cases is:Je suis désolée pour vous.

Appels de simulation

L'exécution des opérations et l'appel desWriteIl est important de savoir quelles transactions sont susceptibles d'échouer avant de les envoyer et de les appeler.

et_call

Méthode RPC d'Ethereumeth_call: il peut simuler une transaction et retourner le résultat d'une transaction possible, mais il n'exécute pas réellement la transaction sur la blockchain.

Leeth_callLa méthode comporte 2 paramètres, le premier étant une structure de dictionnaire,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
}

Le deuxième paramètre estblockNumber: vous pouvez passer l'étiquettelatest/pending/earliest, etc.:

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

Ensuite, nous prenons la méthode du contrat intelligentapproveettransferles appels du jetonDAIcomme exemple pour les appels de simulation, et l'environnement de test suivant est le réseau Ethereum principal.

Appel de simulation approuvé

Nous sommes tous familiers avec leapproveLa simulation de l'interface utilisateur est basée sur une méthode de détection des contrats ERC20, et nous l'avons pratiquée dans les cours précédents.

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

Le code de l'exemple encode d'abord leapprove(address,uint256)méthode et paramètres, ainsi que la valeur du paramètre0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffde l'annéeapproveLa méthode indique le nombre maximal d'autorisations.0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45C'est à dire le contrat de routeur pourUniswap V3Enfin la méthode Ethereum RPCeth_callVous pouvez voir que legasPriceetgaschamps dans letransactionObjectles paramètres peuvent être omis.

L'outil de débogage est exécuté et la simulation appelle la méthode approuver pour autoriser avec succès (elle n'autorise pas réellement):

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

Il est également possible de simuler certains scénarios d'échec, lorsque nous ajustons legasPriceetgasparamètres, si l'ETH dans le portefeuille n'est pas suffisant pour payer les frais d'essence, une erreur sera signalée:

insuffisance des fonds

Lorsque le coût de l'essence est trop bas, une erreur est signalée:

gaz intrinsèque trop faible: avoir 21000, vouloir 21944 (gaz fourni 21000)

Transfert d'appel de simulation

Nous connaissons les ERC20transferCette méthode vous permet de transférer des jetons ERC20 vers une certaine adresse de portefeuille, alors essayons de simuler un transfert de 1000 DAI vers 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 
}

Comme je n'ai pas de jetons DAI dans ce portefeuille de test, en l'exécutant dans l'outil de débogage, l'erreur suivante a été signalée de façon inattendue:

l'exécution est inversée: Dai/solde insuffisant

Vérifiez l'adresse du portefeuille de Vitalik Buterin:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045Il est clair que ce portefeuille a des jetons DAI.

Modifiez le code, où les modifications que j'ai fait commentaires:

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

Testez l' outil de débogage:

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

En utilisant la plateforme de trading FMZ Quant, il est facile de simuler les résultats des transactions et d'éviter une perte inutile de frais de gaz liés à l'envoi de transactions potentiellement échouées.eth_callUtilisez votre imagination.eth_callQuelle est la méthode?

Identifier les contrats ERC721

Nous savons que les jetons comme ETH et BTC sont des jetons homogénéisés, et le jeton de votre portefeuille n'est pas différent du jeton de mon portefeuille. Mais il y a beaucoup de choses dans le monde qui ne sont pas homogènes, comme l'immobilier, les antiquités, les œuvres d'art virtuelles, etc. Ceux-ci ne peuvent pas être représentés par des jetons homogènes en abstraction. Par conséquent, il existe la norme ERC721 pour abstraire les objets non homogènes, et il existe NFT et des concepts connexes. Alors, parmi les nombreux contrats intelligents déployés sur Ethereum, comment identifier quels contrats intelligents sont des contrats intelligents standard ERC721?

Pour identifier ERC721, il est important de connaître d'abord la norme ERC165.

Résultats de l'enquête

Avec la norme ERC165, un contrat intelligent peut déclarer les interfaces qu'il prend en charge pour que d'autres contrats puissent être vérifiés.supportsInterface(bytes4 interfaceId), le paramètreinterfaceIdest l'ID d'interface à interroger. Si le contrat implémente l'interfaceId renvoie une valeur vraie booléenne, sinon il renvoie une valeur fausse.

Ici, nous allons parler de la façon dont ceinterfaceIdest calculé et codé spécifiquement.

Règlement ERC165Voici un exemple:

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

Pour la signature de fonction de l'interface (composée d'un nom de fonction et d'une liste de types de paramètres) pour effectuer une opération de dissemblance, pour un contrat d'interface ERC165 dont le contrat n'a qu'une seule fonction:

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

L'identifiant d'interface pour cette interface est 0x01ffc9a7. Vous pouvez le calculer en exécutant bytes4 ((keccak256 ((supportsInterface ((bytes4) )); ou en utilisant le contrat Sélecteur ci-dessus.

Calculer la signature de la fonction directement et prendre ses 4 premiers octets pour arriver àinterfaceId.

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

Les tests peuvent être exécutés dans l'outil de débogage à:

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

On constate que les résultats calculés sont conformes à la description dans leRèglement ERC165 document.

Résultats de l'enquête

Considérons ensuite la définition de l'interface de la norme 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);
}

Si nous voulons déterminer si un contrat intelligent est un contrat ERC721, nous devons d'abord connaîtreinterfaceIdNous ne pouvons pas nous contenter d'un accord avec ERC721 avant d'essayer desupportsInterface(bytes4 interfaceId)Dans les cours précédents, nous nous sommes familiarisés avec certains concepts de la norme ERC165 et avec l'algorithme de calcul de la valeur de l'ERC165.interfaceId, et nous écrivons un code pour calculer directement:

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

Le code utilise leEncode()fonction pour le calcul de la signature de la fonction (lekeccak256Pour le calcul dans l'exemple de code ci-dessus, en spécifiant le paramètre de sortie duEncode()fonction comme"raw", la fonction renvoie leArrayBuffertype deJavaScriptLe langage. Pour effectuer une^(iso-ou) opération sur deuxArrayBufferLes objets, vous devez créer unTypedArrayLe rapport de l'AgenceArrayBufferObjet, puis itérer à travers les données et effectuer l'iso-ou l'opération un par un.

Exécutez dans l'outil de débogage:

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

On constate que les résultats calculés sont cohérents avec ceux décritsEIP-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);
...

Avec l'ID d'interface ERC721, nous pouvons déterminer si un contrat est un contrat standard ERC721 ou non.BAYCPour faire le test, qui est un contrat qui suit ERC721. d'abord, nous devons enregistrer l'ABI, et puisque nous appelons seulement les trois méthodes suivantes, nous pouvons enregistrer ces trois méthodes:

  • Prise en charge de l'interface (ID de l'interface)
  • le symbole
  • Nom (s)

Les codes spécifiques sont les suivants:

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

Les tests peuvent être exécutés dans l'outil de débogage:

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

Relationnée

Plus de