리소스 로딩... 로딩...

FMZ를 사용하여 이더리움 기반의 웹3 개발을 쉽게 시작하십시오.

저자:FMZ~리디아, 생성: 2023-06-25 09:17:53, 업데이트: 2024-11-11 22:34:49

0000000000164f2434262e1cc", 트랜잭션 해시: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048 주소: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9 }, { 주소: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 blockHash: 0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9 블록 번호: 0x109b1cd, logIndex: 0xde, 삭제: 거짓, topics: [0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65, 0x00000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b], 데이터: 0x0000000000000000000000000000000000000000000000000000000164f2434262e1cc 트랜잭션 해시: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048 거래지수: 0x91 {cH00ffff}


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

그 가치는topics필드는 이벤트를 설명하는 데 사용되는 배열 구조입니다. 그것의 (배열) 길이가 4를 초과 할 수 없으며 첫 번째 요소는 이벤트의 서명 해시입니다. FMZ 양자 거래 플랫폼에서 우리는 이 서명 해시를 계산할 수 있습니다.Encode함수, 다음 코드를 사용하여:

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

계산keccak256해시 값 (hex 코딩)Transfer(address,address,uint256)0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.

그 가치는topics필드는 배열 구조입니다. 두 번째 요소와 세 번째 요소는 각각:

  • 발송 주소from

  • 수신 주소to

  • data

    이 자료는data필드는 다음과 같습니다.

    "data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
    

    특정 매개 변수 (스마트 계약의 솔리디티 코드에서 인덱스 선언이없는 매개 변수) 는data section.

    데이터를 분석0x0000000000000000000000000000000000000000000000000164f2434262e1cc

    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
    }
    

    이 데이터는 0.10047146239950075로 얻으며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() {
    // 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++
        }
    }
}

확인해https://etherscan.io/:

img

FMZ 디버깅 도구에서 실행된 테스트 코드 결과:

img

데이터from, to필드 또한 검색 시의 필요에 따라 분석 될 수 있습니다. 예를 들어:

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

실행 결과:

주소: 0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c

계약 사건 들 을 듣는 것

그 이후디버깅 도구이 섹션에서는 FMZ 양자 거래 플랫폼을 사용하여 라이브 거래를 생성하여 테스트합니다.

여기 우리는 이더리움 메인넷을 사용하고Transfer(address,address,uint256)사건의USDT지난 강의에서 배운 것을 바탕으로 특정 스마트 계약의 이벤트를 지속적으로 듣는 예를 설계하고 작성했습니다.

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

라이브 트레이딩으로 실행:

img

실행 결과에 대한 검증 섹션 (TODO: test) 도 코드에 기록됩니다. 간단한 검증 후TransferUSDT계약의 이벤트가 지속적으로 모니터링되고 데이터가 기록되며, 이러한 데이터와 즉시 얻은 이벤트 데이터의 비교를 통해 데이터가 다음과 일치하는지 관찰할 수 있습니다.

img

이벤트 필터링

이전 수업을 바탕으로Listening to contract events, 우리는 특정 주소로 전송을 듣기 위해 듣기 프로세스에 필터를 추가하여 확장합니다. 스마트 계약이 로그를 만들 때 (즉, 이벤트를 출시), 로그 데이터는topics최대 4개의 정보를 포함합니다. 그래서 우리는 필터 규칙을 디자인[[A1, A2, ...An], null, [C1], D]예를 들어

  1. [A1, A2, ...An]위치 데이터와 일치합니다.topics[0].
  2. Null위치 데이터와 일치합니다.topics[1].
  3. [C1]위치 데이터와 일치합니다.topics[2].
  4. D위치 데이터와 일치합니다.topics[3].
  • 조건 구조의 요소가 설정된 경우null필터링이 안되는 것을 의미합니다.null해당됩니다topics[1]그리고 모든 값이 일치합니다.
  • 조건 구조의 요소가 위치가 일치해야 한다는 것을 나타내는 하나의 값을 설정하면, 예를 들어[C1]해당됩니다topics[2]또는D해당됩니다topics[3], 맞지 않는 로그가 필터링됩니다.
  • 조건 구조의 요소가 배열이라면, 배열의 요소 중 적어도 하나가 일치해야 한다는 것을 의미합니다.[A1, A2, ...An]해당됩니다topics[0], [A1, A2, ...An]그 중 어느 하나와도 일치하는topics[0], 로그는 필터링되지 않습니다.

거래소에서 USDT 송금을 듣기

의 모니터링USDT바이낸스 거래소에서 송금된 거래:

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

이 코드는 실시간 거래에서 실행됩니다.

img

이 강의에서 우리는 이벤트 필터를 설계하는 방법을 소개했습니다.USDT이 예제 프로그램을 수정하고 확장하여 관심있는 이벤트를 듣고 새로운 거래가 무엇인지 확인할 수 있습니다.smart money어떤 새로운 항목을 만들었는지NFT부자들도 몰려들었습니다.

단위 전환

이더리움과 관련된 많은 계산은 최대 안전한 정수를 초과하는 값을 가지고 있습니다.JavaScript따라서 FMZ 양자 거래 플랫폼에서 큰 값을 처리하는 데 몇 가지 방법이 필요합니다. 우리는 이전 코스에서 특별히 사용했으며 세부적으로 다루지 않았습니다. 이 섹션에서는이 측면을 자세히 논의 할 것입니다.

최대 안전 정수를 인쇄JavaScript언어:

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

실행 결과:

번호: MAX_SAFE_INTEGER: 9007199254740991

빅인트

이더리움에서 정의된 가장 작은 단위는1wei, 그리고 정의1Gwei이 값은1000000000 wei. 1Gwei이더리움과 관련된 계산에서 큰 숫자가 아닙니다. 그리고 어떤 데이터는 이더리움보다 훨씬 크죠.Number.MAX_SAFE_INTEGER: 9007199254740991.

FMZ 양자 거래 플랫폼에서 우리는 플랫폼을 사용합니다BigInt이 매우 큰 정수 데이터를 표현하기 위해 객체를 사용 합니다.BigInt()이 문제를 해결하기 위해BigInt객체를 만들 수 있습니다.BigInt숫자를 사용하는 객체, 헥사데시말 숫자 문자열을 매개 변수로.toString()방법BigInt객체에서 문자열로 표현되는 데이터를 출력하기 위해서입니다.

이 사업은BigInt그 목적은:

  • 추가:+
  • 빼기:-
  • 곱셈:*
  • 부문:/
  • 모두로 작업:%
  • 전력 작업:*

다음 코드 예제를 참조하십시오.

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

디버깅 도구 테스트:

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대상은BigInt더 큰 값으로 부동 소수점을 나타낼 수 있고 덧셈, 셈, 곱셈, 나?? 을 지원합니다. 의BigFloat물체는toFixed() method.

다음 코드 예제를 참조하십시오.

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

디버깅 도구 테스트:

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

BigDecimal객체는 정수 값과 부동 소수점 값과 호환되며BigInt물체와BigFloat그리고 덧셈, 셈, 곱셈, 나누기를 지원합니다.

다음 코드 예제를 참조하십시오.

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

디버깅 도구에서 실행:

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

단위 전환

다음 두 가지 기능:toAmount(), toInnerAmount()우리는 이전 강의에서 여러 번 사용했습니다. 이 두 기능은 주로 데이터 정밀 변환을 위해 사용됩니다.

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

toAmount()함수는 변수를 변환 (감소) 합니다s정밀 매개 변수에 따라decimals. web3의 실제 개발에서, 그것은 종종 몇 개의 연쇄 된 열여섯 소수 데이터를 처리 할 필요가 있습니다. 우리는 종종 우리의 이전 과정에서 이것을 만났어요, 예를 들어,data현장 데이터Transfer(address,address,uint256)스마트 계약의 경우:

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

데이터를 처리할 때"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000", 우리는toAmount()이 프로세싱은 데이터 필드 데이터를 읽을 수 있는 값으로 변환하는 데 좋은 작업을 수행하도록 설계되었습니다.

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개의 ETH 토큰은1e18 wei, 우리가 데이터를 얻을 경우126564027559051260안쪽wei, ETH 토큰으로 변환하는 방법? 사용toAmount(, 18)함수는 매우 간단한 변환 방법입니다.toInnerAmount()함수는toAmount()(정밀도에 따라 확대), 이 두 가지 기능을 사용하여 데이터를 변환하는 것이 쉽습니다.

자바스크립트 언어의 정수 값 안전 범위는 주목할 필요가 있습니다.Number.MAX_SAFE_INTEGER, 그리고 다음 예제는 데이터를 변환할 때 숨겨진 문제를 보여줍니다:

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

디버깅 도구에서 실행할 수 있습니다:

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

관찰을 통해 우리는 다음과 같은 사실을 발견했습니다.

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

이 코드 줄은 로그 출력 값과 일치합니다.Converting 10000000000000000 to hex: 10000000000000000그 이유는 당연히 100000000000000000000이Number.MAX_SAFE_INTEGER.

하지만 소수점 값이 안전 범위 내에 있는 경우, 즉,Number.MAX_SAFE_INTEGER, 그toString(16)함수는 다시 제대로 변환합니다. 예를 들어:

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

심지어 블록체인에서도0.01ETH가10000000000000000안쪽wei이산화탄소Number.MAX_SAFE_INTEGER``, so a safer conversion for such cases is:빅인트 (100000000000000000000).string (16) ``.

시뮬레이션 호출

거래를 실행하고Write이더리움에 있는 스마트 계약의 방법은 일정량의 가스가 소요되며 때로는 실패하기도 합니다. 어떤 트랜잭션이 실패할 가능성이 있는지 전송하고 호출하기 전에 아는 것이 중요합니다. 테스트를 위해 이더리움에 시뮬레이션 호출이 있습니다.

eth_call

이더리움의 RPC 방법eth_call: 트랜잭션을 시뮬레이션하고 가능한 트랜잭션의 결과를 반환할 수 있지만 실제로 블록체인에서 트랜잭션을 실행하지는 않습니다.

eth_call이 방법은 2개의 매개 변수를 가지고 있습니다. 첫번째는 사전 구조입니다.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
}

두 번째 매개 변수는blockNumber: 표지판을 넘길 수 있습니다latest/pending/earliest, 등등:

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

다음으로, 우리는 스마트 계약 방법을approve그리고transfer토큰의 호출DAI시뮬레이션 호출의 예로, 다음 테스트 환경은 주요 이더리움 네트워크입니다.

시뮬레이션 호출 승인

우리는 모두approveERC20 계약에 대한 방법, 그리고 우리는 이전 과정에서 그것을 연습했습니다. ERC20 계약은 이미 FMZ 플랫폼 ABI에 내장되어 있기 때문에, 시뮬레이션에 의해 호출되는 스마트 계약의 ABI를 등록할 필요가 없습니다.

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

예제 코드는 먼저approve(address,uint256)방법 및 매개 변수 및 매개 변수 값0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffapprove이 방법은 권한의 최대 수를 나타냅니다. 권한은 주소에서 스마트 계약에 제공됩니다.0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45즉 라우터 계약Uniswap V3마지막으로 이더리움 RPC 메소드eth_call시뮬레이션이 필요합니다.gasPrice그리고gas농장에서transactionObject매개 변수들은 생략할 수 있습니다.

디버깅 도구가 실행되고 시뮬레이션은 승인 메소드를 성공적으로 승인하도록 호출합니다 (실제로 승인하지 않습니다):

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

또한 몇 가지 실패 시나리오를 시뮬레이션 할 수 있습니다.gasPrice그리고gas매개 변수, 지갑에 있는 ETH가 가스 요금을 지불하기에 충분하지 않으면 오류가 보고됩니다:

자금 부족

가스 비용이 너무 낮게 설정되면 오류가 보고됩니다.

내재 가스 너무 낮습니다: 21000가 있습니다. 21944가 필요합니다.

시뮬레이션 호출 전송

우리는 ERC20을 잘 알고 있습니다.transfer이 방법은 ERC20 토큰을 특정 지갑 주소로 전송할 수 있게 해줍니다.

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 
}

이 테스트 지갑에 DAI 토큰이 없기 때문에 디버그 툴에서 실행하면 다음 오류가 예상치 못한 상태로 나타났습니다.

실행 역행: Dai/불충분 재무

비탈릭 부테린의 지갑 주소를 확인해0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045이 지갑에는 DAI 토큰이 있는 것이 분명합니다. 그래서 시뮬레이션 호출의 전송 방향을 조정하고

코드를 수정합니다. 제가 언급한 변경 사항은:

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

디버깅 도구 테스트:

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

FMZ 양자 거래 플랫폼을 사용하여 거래 결과를 시뮬레이션하고 잠재적으로 실패한 거래를 전송하여 불필요한 가스 수수료를 피하는 것이 쉽습니다. 우리는 Vitalik Buterin의 지갑에 돈을 송금하고 Vitalik Buterin의 지갑에 돈을 송금하는 호출을 시뮬레이션하기 위해 코스의 이 장의 예제 코드를 사용했습니다. 물론이기에 더 많은 사용이 있습니다.eth_call상상력을 사용하세요.eth_call어떤 방법일까요?

ERC721 계약의 식별

우리는 ETH와 BTC와 같은 토큰이 균일화된 토큰이고, 당신의 지갑의 토큰은 내 지갑의 토큰과 다르지 않다는 것을 알고 있습니다. 그러나 부동산, 유물, 가상 예술품 등과 같이 균일하지 않은 많은 것들이 있습니다. 이것들은 추상적으로 균일한 토큰으로 표현될 수 없습니다. 따라서 비균형적인 객체를 추상화하기 위해 ERC721 표준이 있고, NFT와 관련 개념이 있습니다. 이더리움에 배포된 많은 스마트 계약들 중에서 어떤 스마트 계약들이 ERC721 표준 스마트 계약인지 어떻게 알 수 있을까요?

ERC721을 식별하려면 먼저 ERC165 표준을 아는 것이 중요합니다.

ERC165

ERC165 표준을 통해 스마트 계약은 다른 계약이 검사할 수 있도록 지원하는 인터페이스를 선언할 수 있습니다. ERC165 인터페이스 계약은 오직 하나의 기능을 가지고 있습니다.supportsInterface(bytes4 interfaceId), 매개 변수interfaceId쿼리해야 하는 인터페이스 ID입니다. 만약 계약이 인터페이스 ID를 구현한다면 룰어 참값을 반환합니다. 그렇지 않으면 잘못된 값을 반환합니다.

여기 우리는 어떻게 이interfaceId계산되고 코딩된 것입니다.

ERC165 표준예를 들면:

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

인터페이스의 함수 서명 (함수 이름과 매개 변수 유형 목록으로 구성된) 에 대해, ERC165 인터페이스 계약에서 계약이 하나의 기능만을 가지고 있는 경우, 불균형 연산을 수행하기 위해:

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

이 인터페이스의 인터페이스 식별자는 0x01ffc9a7입니다. 당신은 바이트를 실행하여 이것을 계산할 수 있습니다.

함수 서명을 직접 계산하고 첫 번째 4 바이트를interfaceId.

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

테스트는 디버그 툴에서 실행할 수 있습니다.

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

계산된 결과들이ERC165 표준 document.

ERC721

다음은 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);
}

만약 우리가 스마트 계약이 ERC721 계약인지 판단하려면 먼저interfaceIdERC721 계약의 우리는supportsInterface(bytes4 interfaceId)이전 강의에서 우리는 ERC165 표준의 몇 가지 개념과interfaceId, 그리고 우리는 직접 계산하기 위해 코드를 작성합니다:

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

코드는Encode()함수 서그라마를 계산하기 위한 함수keccak256알고리즘), 그리고 위의 코드 예제에서 계산을 위해,Encode()함수"raw", 함수는ArrayBuffer종류JavaScript언어. 수행하기 위해^(이소 또는) 두 개의 작업ArrayBuffer객체, 당신은TypedArray에 기초 한 견해ArrayBuffer객체, 그리고 그 안에 있는 데이터를 반복해서 iso-or 동작을 하나씩 수행합니다.

디버깅 도구에서 실행:

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

계산된 결과는eip-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);
...

ERC721 인터페이스 ID를 사용하면 계약이 ERC721 표준 계약인지 아닌지를 결정할 수 있습니다.BAYC먼저 ABI를 등록해야 합니다. 그리고 다음 세 가지 방법을 호출하기 때문에 다음 세 가지 방법을 등록할 수 있습니다.

  • 지원 인터페이스 (인터페이스Id)
  • 기호 (()
  • 이름 (()

특정 코드는 다음과 같습니다.

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

테스트는 디버깅 도구에서 실행할 수 있습니다:

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

관련

더 많은