0000000000164f2434262e1cc",
트랜잭션 해시: 0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048
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/
:
FMZ 디버깅 도구에서 실행된 테스트 코드 결과:
데이터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])
}
}
*/
라이브 트레이딩으로 실행:
실행 결과에 대한 검증 섹션 (TODO: test) 도 코드에 기록됩니다. 간단한 검증 후Transfer
USDT계약의 이벤트가 지속적으로 모니터링되고 데이터가 기록되며, 이러한 데이터와 즉시 얻은 이벤트 데이터의 비교를 통해 데이터가 다음과 일치하는지 관찰할 수 있습니다.
이전 수업을 바탕으로Listening to contract events
, 우리는 특정 주소로 전송을 듣기 위해 듣기 프로세스에 필터를 추가하여 확장합니다. 스마트 계약이 로그를 만들 때 (즉, 이벤트를 출시), 로그 데이터는topics
최대 4개의 정보를 포함합니다. 그래서 우리는 필터 규칙을 디자인[[A1, A2, ...An], null, [C1], D]
예를 들어
[A1, A2, ...An]
위치 데이터와 일치합니다.topics[0]
.Null
위치 데이터와 일치합니다.topics[1]
.[C1]
위치 데이터와 일치합니다.topics[2]
.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)
}
}
이 코드는 실시간 거래에서 실행됩니다.
이 강의에서 우리는 이벤트 필터를 설계하는 방법을 소개했습니다.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
객체는 정수 값과 부동 소수점 값과 호환되며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.01
ETH가10000000000000000
안쪽wei
이산화탄소Number.MAX_SAFE_INTEGER``, so a safer conversion for such cases is:
빅인트 (100000000000000000000).string (16) ``.
거래를 실행하고Write
이더리움에 있는 스마트 계약의 방법은 일정량의 가스가 소요되며 때로는 실패하기도 합니다. 어떤 트랜잭션이 실패할 가능성이 있는지 전송하고 호출하기 전에 아는 것이 중요합니다. 테스트를 위해 이더리움에 시뮬레이션 호출이 있습니다.
이더리움의 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
시뮬레이션 호출의 예로, 다음 테스트 환경은 주요 이더리움 네트워크입니다.
우리는 모두approve
ERC20 계약에 대한 방법, 그리고 우리는 이전 과정에서 그것을 연습했습니다. 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)
방법 및 매개 변수 및 매개 변수 값0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
의approve
이 방법은 권한의 최대 수를 나타냅니다. 권한은 주소에서 스마트 계약에 제공됩니다.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가 필요합니다.
우리는 ERC20transfer
이 방법은 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
어떤 방법일까요?
우리는 ETH와 BTC와 같은 토큰이 균일화된 토큰이고, 당신의 지갑의 토큰은 내 지갑의 토큰과 다르지 않다는 것을 알고 있습니다. 그러나 부동산, 유물, 가상 예술품 등과 같이 균일하지 않은 많은 것들이 있습니다. 이것들은 추상적으로 균일한 토큰으로 표현될 수 없습니다. 따라서 비균형적인 객체를 추상화하기 위해 ERC721 표준이 있고, NFT와 관련 개념이 있습니다. 이더리움에 배포된 많은 스마트 계약들 중에서 어떤 스마트 계약들이 ERC721 표준 스마트 계약인지 어떻게 알 수 있을까요?
ERC721을 식별하려면 먼저 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 계약 표준의 인터페이스 정의를 살펴보자.
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 계약인지 판단하려면 먼저interfaceId
ERC721 계약의 우리는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를 등록해야 합니다. 그리고 다음 세 가지 방법을 호출하기 때문에 다음 세 가지 방법을 등록할 수 있습니다.
특정 코드는 다음과 같습니다.
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