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
함수 main() {
var event =
listener = addEventListener(contractAddress, event, function(log) {
var fromAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[1])
var toAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[2])
Log("Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0))
/* TODO: test
arrLog.push(log)
*/
})
while (true) {
listener.run()
Sleep(5000)
}
}
/* TODO: 테스트
var arrLog = []
function onexit ((() {
로그 (
Log("getLogs, from:", firstBlockNumber, " -> to:", endBlockNumber)
var fromBlock = "0x" + (firstBlockNumber).toString(16)
var toBlock = "0x" + (endBlockNumber).toString(16)
var params = {
"fromBlock" : fromBlock,
"toBlock" : toBlock,
"topics" : ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
"address" : "0xdac17f958d2ee523a2206206994597c13d831ec7"
}
var logs = exchange.IO("api", "eth", "eth_getLogs", params)
Log("arrLog:", arrLog.length)
Log("logs:", logs.length)
if (arrLog.length != logs.length) {
Log("长度不等!")
return
}
for (var i = 0; i < arrLog.length; i++) {
Log("判断blockNumber:", logs[i].blockNumber == arrLog[i].blockNumber, ",判断from:", logs[i].topics[1] == arrLog[i].topics[1],
"判断to:", logs[i].topics[2] == arrLog[i].topics[2])
}
} */
实盘运行:
![img](/upload/asset/16ffd65adc050d33056d.png)
对于执行结果,代码中也编写了验证部分(TODO: test)。经过简单验证可以看到持续监控USDT合约的```Transfer```事件并且记录数据,用这个数据和一次性获取的事件数据对比可以观察出数据一致:
![img](/upload/asset/16e07390a11a606276b1.png)
### 事件过滤
在上一节课程「监听合约事件」的基础上,我们拓展一下,在监听的过程中增加过滤器,监听指定地址的转入转出。当智能合约创建日志时(即释放事件),日志数据中```topics```最多包含4条信息。所以我们设计一个过滤规则,以```[[A1, A2, ...An], null, [C1], D]```为例子。
1、```[A1, A2, ...An]```对应```topics[0]```位置的数据。
2、```null```对应```topics[1]```位置的数据。
3、```[C1]```对应```topics[2]```位置的数据。
4、```D```对应```topics[3]```位置的数据。
- 如果条件结构中的元素设置```null```表示不被过滤,例如```null```对应```topics[1]```,任何值都匹配。
- 如果条件结构中的元素设置单个值表示该位置必须匹配,例如```[C1]```对应```topics[2]```或者```D```对应```topics[3]```,不匹配的日志被过滤。
- 如果条件结构中的元素是一个数组,表示数组中的元素至少有一个要匹配,例如```[A1, A2, ...An]```对应```topics[0]```,```[A1, A2, ...An]```中有任意一个和```topics[0]```匹配,则日志不会被过滤。
**监听交易所的USDT转账**
监控从币安交易所转出、转入币安交易所```USDT```的交易:
```javascript
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(n, decimals) {
return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function addEventListener(contractAddress, event, callBack) {
var self = {}
self.eventHash = "0x" + Encode("keccak256", "string", "hex", event)
self.contractAddress = contractAddress
self.latestBlockNumber = 0
self.fromBlockNumber = 0
self.firstBlockNumber = 0
self.filters = []
self.setFilter = function(filterCondition) {
if (filterCondition.length > 4) {
throw "filterCondition error"
}
self.filters.push(filterCondition)
Log("设置过滤条件:", filterCondition)
}
self.getTokenBalanceOfWallet = function(walletAddress, tokenAddress, tokenDecimals) {
var balance = exchange.IO("api", tokenAddress, "balanceOf", walletAddress)
if (balance) {
return toAmount(balance, tokenDecimals)
}
return null
}
self.getBlockNumber = function() {
var maxTry = 10
for (var i = 0; i < maxTry; i++) {
var ret = exchange.IO("api", "eth", "eth_blockNumber")
if (ret) {
return toAmount(ret, 0)
}
Sleep(5000)
}
throw "getBlockNumber failed"
}
self.run = function() {
var currBlockNumber = self.getBlockNumber()
var fromBlock = "0x" + self.fromBlockNumber.toString(16)
var toBlock = "0x" + currBlockNumber.toString(16)
var params = {
"fromBlock" : fromBlock,
"toBlock" : toBlock,
"address" : self.contractAddress,
"topics" : [self.eventHash]
}
var logs = exchange.IO("api", "eth", "eth_getLogs", params)
if (!logs) {
return
}
for (var i = 0; i < logs.length; i++) {
if (toAmount(logs[i].blockNumber, 0) > self.latestBlockNumber) {
// 检查过滤条件,设置了过滤条件则执行判断
if (self.filters.length != 0) {
// 初始过滤标记
var isFilter = true
// 遍历过滤条件设置
for (var j = 0; j < self.filters.length; j++) {
// 取一个过滤设置,例如:[[A1, A2, ...An], null, [C1], D]
var cond = self.filters[j]
// 遍历这个过滤设置
var final = true
for (var topicsIndex = 0; topicsIndex < cond.length; topicsIndex++) {
// 拿到这个过滤设置中的某一个条件,如果是第一个条件:即要和topics[0]对比的数据
var condValue = cond[topicsIndex]
// 日志中的数据
if (topicsIndex > logs[i].topics.length - 1) {
continue
}
var topicsEleValue = logs[i].topics[topicsIndex]
// 如果是Transfer事件,需要处理from和to
if (logs[i].topics[0] == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") {
if (topicsIndex == 1 || topicsIndex == 2) {
topicsEleValue = "0x" + exchange.IO("encodePacked", "address", topicsEleValue)
}
}
// 如果condValue类型是数组,表示该位置的对比条件有多个,多个条件对比是逻辑或关系
if (Array.isArray(condValue) && condValue.length > 1) {
// 判断 condValue[0] == topicsEleValue || condValue[1] == topicsEleValue
final = final && condValue.some(element => element === topicsEleValue)
}else if (condValue === null) {
final = final && true
} else {
final = final && (condValue === topicsEleValue)
}
}
if (final) {
isFilter = false
}
}
if (isFilter) {
continue
}
}
callBack(logs[i])
}
}
self.latestBlockNumber = currBlockNumber
self.fromBlockNumber = self.latestBlockNumber - 1
}
self.latestBlockNumber = self.getBlockNumber()
self.fromBlockNumber = self.latestBlockNumber - 1
return self
}
var listener = null
function main() {
// 初始清理日志
LogReset(1)
LogProfitReset()
var event = "Transfer(address,address,uint256)" // 监听事件
var contractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7" // USDT合约地址
var decimals = exchange.IO("api", contractAddress, "decimals") // 获取USDT token的精度信息
var accountBinanceAddress = "0x28C6c06298d514Db089934071355E5743bf21d60" // Binance 热钱包地址
accountBinanceAddress = accountBinanceAddress.toLowerCase() // 地址处理为小写
Log(exchange.IO("api", contractAddress, "name"), " decimals:", decimals)
// 创建监听对象
listener = addEventListener(contractAddress, event, function(log) {
var fromAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[1])
var toAddress = "0x" + exchange.IO("encodePacked", "address", log.topics[2])
if (fromAddress == accountBinanceAddress) {
Log("币安转出 - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#CD32CD")
} else if (toAddress == accountBinanceAddress) {
Log("转入币安 - ", " Transfer:", fromAddress, "->", toAddress, ", value:", toAmount(log.data, decimals), ", blockNumber:", toAmount(log.blockNumber, 0), "#FF0000")
}
})
// 设置事件过滤
listener.setFilter([null, accountBinanceAddress, null]) // Binance -> USDT
listener.setFilter([null, null, accountBinanceAddress]) // USDT -> Binance
var preBalance = 0
while (true) {
listener.run()
var balance = listener.getTokenBalanceOfWallet(accountBinanceAddress, contractAddress, decimals)
if (balance) {
var direction = ""
if (preBalance != 0 && preBalance > balance) {
direction = " ↓ " + (preBalance - balance) + "#CD32CD"
} else if (preBalance != 0 && preBalance < balance) {
direction = " ↑ " + (balance - preBalance) + "#FF0000"
}
Log("币安钱包地址:", accountBinanceAddress, " 余额:", balance, direction)
LogProfit(balance, "&") // 只画图,不打印日志
preBalance = balance
}
LogStatus(_D(), "币安钱包地址:", accountBinanceAddress, ", 余额:", balance)
Sleep(5000 * 3)
}
}
이 코드는 실제 디스크에서 실행됩니다:
이 강의에서는 이벤트 필터를 설계하는 방법을 소개합니다. 그리고 이를 통해 비트코인 거래소 핫 월트와 관련된 정보를 감청합니다.USDT
트랜잭션. 당신은 이 패러다임을 수정하고 확장하여 당신이 관심있는 모든 이벤트를 감청할 수 있습니다.smart money
어떤 새로운 거래가 이루어졌는지,NFT
이 모든 것은 새로운 프로젝트의 시작으로 이어지고 있습니다.
에테리움과 관련된 많은 계산에서 숫자는JavaScript
언어의 최대 안전 정수이다. 따라서 발명자 계량 거래 플랫폼에서 큰 숫자를 처리하기 위해 몇 가지 방법이 필요합니다. 이러한 방법은 이전 강의에서도 구체적으로 사용되었습니다.
인쇄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
。
우리는 이 플랫폼을 사용해서BigInt
객체들은 이 초대한 정수 데이터를 나타냅니다.BigInt()
창조하기 위해BigInt
객체. 숫자, 16진수 문자열을 함수로 구성할 수 있다.BigInt
객체.BigInt
물체의toString()
메소드는 객체가 나타내는 데이터를 문자열 형태로 출력합니다.
BigInt
객체 지원 동작:
+
-
*
/
%
**
다음의 코드 예제를 참조하십시오:
function main() {
// 1Gwei的十进制表示
var oneGwei = 1000000000
// 1Gwei的十进制转换为十六进制表示
var oneGweiForHex = "0x" + oneGwei.toString(16)
Log("oneGwei : ", oneGwei)
Log("oneGweiForHex : ", oneGweiForHex)
// 构造BigInt对象
Log("1Gwei / 1Gwei : ", (BigInt(oneGwei) / BigInt(oneGweiForHex)).toString(10))
Log("1Gwei * 1Gwei : ", (BigInt(oneGwei) * BigInt(oneGweiForHex)).toString(10))
Log("1Gwei - 1Gwei : ", (BigInt(oneGwei) - BigInt(oneGweiForHex)).toString(10))
Log("1Gwei + 1Gwei : ", (BigInt(oneGwei) + BigInt(oneGweiForHex)).toString(10))
Log("(1Gwei + 1) % 1Gwei : ", (BigInt(oneGwei + 1) % BigInt(oneGweiForHex)).toString(10))
Log("1Gwei ** 2 : ", (BigInt(oneGwei) ** BigInt(2)).toString(10))
Log("100 的平方根 : ", (BigInt(100) ** BigFloat(0.5)).toString(10))
Log("Number.MAX_SAFE_INTEGER : ", BigInt(Number.MAX_SAFE_INTEGER).toString(10))
Log("Number.MAX_SAFE_INTEGER * 2 : ", (BigInt(Number.MAX_SAFE_INTEGER) * BigInt("2")).toString(10))
}
디뷰팅 도구 테스트:
2023-06-08 11:39:50 信息 Number.MAX_SAFE_INTEGER * 2 : 18014398509481982
2023-06-08 11:39:50 信息 Number.MAX_SAFE_INTEGER : 9007199254740991
2023-06-08 11:39:50 信息 100 的平方根 : 10
2023-06-08 11:39:50 信息 1Gwei ** 2 : 1000000000000000000
2023-06-08 11:39:50 信息 (1Gwei + 1) % 1Gwei : 1
2023-06-08 11:39:50 信息 1Gwei + 1Gwei : 2000000000
2023-06-08 11:39:50 信息 1Gwei - 1Gwei : 0
2023-06-08 11:39:50 信息 1Gwei * 1Gwei : 1000000000000000000
2023-06-08 11:39:50 信息 1Gwei / 1Gwei : 1
2023-06-08 11:39:50 信息 oneGweiForHex : 0x3b9aca00
2023-06-08 11:39:50 信息 oneGwei : 1000000000
BigFloat
객체와BigInt
객체 사용은 비슷하며, 더 큰 숫자를 나타내는 부동 소수점을 위해 사용되며, 덧셈 곱셈도 지원한다.BigFloat
객체 지원toFixed()
어떻게 해야 할까요?
다음의 코드 예제를 참조하십시오:
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 信息 pi / 2.0 : 1.57
2023-06-08 13:56:44 信息 pi * 2.0 : 6.28
2023-06-08 13:56:44 信息 pi - oneGweiForHex : -999999996.86
2023-06-08 13:56:44 信息 pi + oneGwei : 1000000003.14
BigDecimal
객체는 전체값, 부동소수점값을 호환하고, 사용 지원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 信息 pi / pi : 1
2023-06-08 14:52:53 信息 2.0 * pi : 6.283
2023-06-08 14:52:53 信息 oneGwei - pi : 999999996.8585
2023-06-08 14:52:53 信息 oneGwei + pi : 1000000003.1415
2023-06-08 14:52:53 信息 BigFloat(pi) : 3.1415
2023-06-08 14:52:53 信息 BigInt(oneGwei) : 1e+9
2023-06-08 14:52:53 信息 oneGweiForHex : 1e+9
2023-06-08 14:52:53 信息 oneGwei : 1e+9
2023-06-08 14:52:53 信息 pi : 3.14
이 두 함수는 다음과 같습니다.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
<소축> 변환을 수행한다. 실제 웹3 개발에서는 종종 일련의 사십육진 데이터 처리가 필요합니다.
우리는 지난 강의에서 자주 접했던 것처럼,Transfer(address,address,uint256)
이 사건의data
필드 데이터:
{
"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000",
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000006b75d8af000000e20b7a7ddf000ba900b4009a80", "0x000000000000000000000000bcb095c1f9c3dc02e834976706c87dee5d0f1fb6"],
"transactionHash": "0x27f9bf5abe3148169b4b85a83e1de32bd50eb81ecc52e5af006157d93353e4c4",
"transactionIndex": "0x0",
"removed": false,
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
"blockNumber": "0x109b1cc",
"logIndex": "0x0"
}
데이터 처리"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000"
이 시간에는 사용toAmount()
함수. 이런 처리 디자인은data
필드 데이터는 읽을 수 있는 값으로 변환됩니다.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
var data = "0x00000000000000000000000000000000000000000000000001c1a55000000000"
Log(toAmount(data, 18)) // 打印出 0.12656402755905127
}
1개의 ETH 토큰은1e18 wei
그리고 만약 우리가wei
단위 데이터126564027559051260
어떻게 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)
// 十进制数值 10000000000000000 -> 十六进制数值 0x2386f26fc10000
Log("转换", innerAmount, "为十六进制:", innerAmount.toString(16))
Log("转换", BigInt(10000000000000000).toString(10), "为十六进制:", BigInt(10000000000000000).toString(16))
Log("0x" + BigInt(10000000000000000).toString(16), "转换为10进制:", toAmount("0x" + BigInt(10000000000000000).toString(16), 0))
}
디뷰팅 도구에서 실행할 수 있습니다:
2023-06-15 16:21:40 信息 0x2386f26fc10000 转换为10进制: 10000000000000000
2023-06-15 16:21:40 信息 转换 10000000000000000 为十六进制: 2386f26fc10000
2023-06-15 16:21:40 信息 转换 10000000000000000 为十六进制: 10000000000000000
2023-06-15 16:21:40 信息 typeof(innerAmount): number , innerAmount: 10000000000000000
2023-06-15 16:21:40 信息 innerAmount: 10000000000000000
2023-06-15 16:21:40 信息 Number.MAX_SAFE_INTEGER: 9007199254740991
우리는 관찰을 통해 발견합니다.
Log("转换", innerAmount, "为十六进制:", innerAmount.toString(16))
이 코드 라인의 로그 출력은 다음과 같습니다.转换 10000000000000000 为十六进制: 10000000000000000
이 모든 것은 100000000000000000000을 넘어선 것이므로Number.MAX_SAFE_INTEGER
。
하지만 십진수값이 안전 범위 안에 있으면Number.MAX_SAFE_INTEGER
이 모든 것은toString(16)
함수 변환은 정상입니다. 예를 들어:
function main() {
var value = 1000
Log("把value转换为十六进制:", "0x" + value.toString(16)) // 0x3e8
Log("把0x3e8转换为十进制:", Number("0x3e8")) // 1000
}
하지만, 이 모든 것들은0.01
그리고 이 모든 것이wei
단위 값10000000000000000
그리고 더 나아가Number.MAX_SAFE_INTEGER
그래서 이런 상황에서 더 안전한 전환은 다음과 같습니다.BigInt(10000000000000000).toString(16)
。
에테리움에서 거래를 실행하고 스마트 계약을 호출하는Write
방법들은 특정 가스 비용을 소비해야 하며, 때로는 실패의 위험이 있다. 트랜잭션을 전송하거나 호출하기 전에 어떤 트랜잭션이 실패할 수 있는지 아는 것이 매우 중요하다. 에테리움에는 테스트를 위해 모방 호출 방법이 있다.
이더리움의 RPC 방법eth_call
: 트랜잭션을 시뮬레이션하고 가능한 트랜잭션 결과를 반환할 수 있지만 실제로 블록체인에서 트랜잭션을 실행하지 않습니다.
eth_call
방법에는 두 개의 매개 변수가 있습니다. 첫 번째 매개 변수는 사전 구조입니다.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
*/
다음으로 우리는 토큰을 사용합니다.DAI
스마트 계약의 방법approve
、transfer
이 모형 호출의 예로, 다음 테스트 환경은 이더리움 네트워크이다.
ERC20 계약에 대해approve
이 방법은 우리가 이미 잘 알고 있고 이전 강의에서 연습한 방법이다. 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)
이 문서는 다른 문장들을 포함하고 있습니다.approve
방법의 매개 변수 값0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
최대의 권한을 표시합니다. 스마트 계약에 권한을 부여, 주소0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
그리고 이것은Uniswap V3
에테리움의 라우팅 계약. 마지막으로 에테리움 RPC 방법을 호출eth_call
모형을 만들어 보세요.transactionObject
변수에서gasPrice
、gas
필드는 삭제할 수 있습니다.
디버깅 도구가 실행되고, 모의 호출 approve 방법 승인을 성공 (실제로 승인을 하지 않습니다):
2023-06-09 11:58:39 信息 ret: 0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 11:58:39 信息 ERC20 token DAI approve encode, data: 095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
그리고 우리는 실패한 시나리오를 모방할 수 있습니다.gasPrice
그리고gas
만약 지갑에 있는 ETH가 가스 비용을 지불하기에 충분하지 않다면,
자금 부족
이 글은 "가스 요금을 너무 낮게 설정하면 오류가 발생합니다".
내재 가스 너무 낮습니다: 21000가 있습니다. 21944가 필요합니다.
ERC20에 대한transfer
이 방법은 우리가 모르는 방법이 아닙니다. 이 방법은 ERC20 토큰을 지갑 주소로 전송할 수 있습니다. 우리는 V 신에게 1000 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")
// 转账给V神
var decimals_DAI = exchange.IO("api", contractAddress_DAI, "decimals")
var transferAmount = toInnerAmount(1000, decimals_DAI)
Log("转账金额:", 1000, "DAI, 使用 toInnerAmount 转换为:", transferAmount)
// encode transfer
var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
walletVitalik, transferAmount)
var transactionObject = {
"from" : wallet,
"to" : contractAddress_DAI,
"data" : "0x" + data,
}
var blockNumber = "latest"
var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
return ret
}
제 테스트 지갑에는 DAI 토큰이 없기 때문에 디뷰팅 도구에서 실행되는 예상 결과 중 하나가 잘못되었습니다.
실행 역행: Dai/불충분 재무
이 지갑의 주소를 보세요:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
이 지갑에는 DAI 토큰이 들어있다는 것을 알 수 있습니다. 그래서 우리는 모형 호출의 송금 방향을 조정합니다. V 신이 우리에게 1000 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("转账金额:", 1000, "DAI, 使用 toInnerAmount 转换为:", transferAmount)
// encode transfer
var data = exchange.IO("encode", contractAddress_DAI, "transfer(address,uint256)",
wallet, transferAmount) // 使用wallet变量作为参数,转账接收方地址改为我自己
var transactionObject = {
"from" : walletVitalik, // 使用walletVitalik变量作为from字段的值,模拟这个调用是由V神钱包地址发出
"to" : contractAddress_DAI,
"data" : "0x" + data,
}
var blockNumber = "latest"
var ret = exchange.IO("api", "eth", "eth_call", transactionObject, blockNumber)
Log(ret)
}
디뷰팅 도구 테스트:
2023-06-09 13:34:31 信息 0x0000000000000000000000000000000000000000000000000000000000000001
2023-06-09 13:34:31 信息 转账金额: 1000 DAI, 使用 toInnerAmount 转换为: 1000000000000000000000
발명자의 양적 거래 플랫폼을 사용하여 거래의 결과를 편리하게 시뮬레이션 할 수 있으며 실패할 수있는 거래를 전송하는 데 불필요한 가스 비용 손실을 방지 할 수 있습니다. 우리는 이 장 강의의 예시 코드를 사용하여 V의 지갑에 송금, V의 지갑이 우리에게 송금하는 호출을 시뮬레이션했습니다. 물론,이 방법은eth_call
이 방법은 더 유용합니다. 상상력을 발휘하면eth_call
이 방법은 어디서 사용되나요?
우리는 ETH, BTC와 같은 토큰이 동화 토큰이라고 알고 있습니다. 당신의 토큰과 나의 토큰은 다르지 않습니다. 그러나 세상에는 많은 것들이 있습니다. 예를 들어 부동산, 유물, 가상 예술품 등은 동화 토큰으로 추상화 될 수 없습니다. 그래서 ERC721 표준이 있습니다. 그렇다면 이더리움에 배포된 수많은 스마트 계약들 중에서 어떤 스마트 계약들이 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 信息 supportsInterface(bytes4) interfaceId: 0x01ffc9a7
그리고 그 결과,ERC165 표준문헌에 나오는 설명은 일치합니다.
다음으로 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 계약인지 판단하려면 먼저 ERC721 계약의 성격을 알아야 합니다.interfaceId
그리고 그 다음에는 사용하려고 시도합니다.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 "错误:数组中元素个数为0"
} else {
var viewEncodeData = null
for (var i = 0; i < arrSelector.length; i++) {
if (i == 0) {
ret = new Uint8Array(Encode("keccak256", "string", "raw", arrSelector[i]))
} else {
viewData = new Uint8Array(Encode("keccak256", "string", "raw", arrSelector[i]))
if (viewData.length != ret.length) {
throw "错误:TypeArray view长度不同"
}
for (var index = 0; index < ret.length; index++) {
ret[index] ^= viewData[index]
}
}
}
ret = Encode("raw", "raw", "hex", ret.buffer)
}
} else {
throw "错误:参数需要数组类型。"
}
return "0x" + ret.slice(0, 8)
}
function main() {
// supportsInterface(bytes4): 0x01ffc9a7
// var ret = calcSelector(["supportsInterface(bytes4)"])
// ERC721Metadata: 0x5b5e139f
/*
var arrSelector = [
"name()",
"symbol()",
"tokenURI(uint256)"
]
var ret = calcSelector(arrSelector)
*/
// ERC721: 0x80ac58cd
// /*
var arrSelector = [
"balanceOf(address)",
"ownerOf(uint256)",
"safeTransferFrom(address,address,uint256,bytes)",
"safeTransferFrom(address,address,uint256)",
"transferFrom(address,address,uint256)",
"approve(address,uint256)",
"setApprovalForAll(address,bool)",
"getApproved(uint256)",
"isApprovedForAll(address,address)",
]
var ret = calcSelector(arrSelector)
// */
Log(ret)
}
코드에 사용되었습니다.Encode()
함수 서명 계산을 수행하는 함수keccak256
이 알고리즘은, 위의 코드 예제에서 계산을 위해Encode()
이 함수의 출력 매개 변수는"raw"
이 함수는JavaScript
언어ArrayBuffer
이 글의 제목은
이 두 개의 값은ArrayBuffer
객체 수행^
(이름 또는) 연산은ArrayBuffer
객체 생성TypedArray
뷰를 열고, 그 데이터들을 탐색하고, 하나씩 오차 또는 계산을 한다.
디뷰팅 도구에서 실행:
2023-06-13 15:04:09 信息 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
테스트를 위해, 이것은 ERC721을 따르는 계약입니다. 먼저 우리는 ABI를 등록해야합니다. 우리는 다음 세 가지 방법을 호출하기 때문에 세 가지 방법을 등록 할 수 있습니다.
이 코드는 다음과 같습니다:
function main() {
// ERC721的合约地址,这里用的BAYC
var testContractAddress = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
var testABI = `[{
"inputs": [{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}],
"name": "supportsInterface",
"outputs": [{
"internalType": "bool",
"name": "",
"type": "bool"
}],
"stateMutability": "view",
"type": "function"
}, {
"inputs": [],
"name": "symbol",
"outputs": [{
"internalType": "string",
"name": "",
"type": "string"
}],
"stateMutability": "view",
"type": "function"
}, {
"inputs": [],
"name": "name",
"outputs": [{
"internalType": "string",
"name": "",
"type": "string"
}],
"stateMutability": "view",
"type": "function"
}]`
// ERC721接口Id,在之前的课程中计算得出
var interfaceId = "0x80ac58cd"
// 注册ABI
exchange.IO("abi", testContractAddress, testABI)
// 调用supportsInterface方法
var isErc721 = exchange.IO("api", testContractAddress, "supportsInterface", interfaceId)
// 输出信息
Log("合约地址:", testContractAddress)
Log("合约名称:", exchange.IO("api", testContractAddress, "name"))
Log("合约代号:", exchange.IO("api", testContractAddress, "symbol"))
Log("合约是否为ERC721标准:", isErc721)
}
디뷰팅 도구에서 테스트를 실행할 수 있습니다:
2023-06-13 16:32:57 信息 合约是否为ERC721标准: true
2023-06-13 16:32:57 信息 合约代号: BAYC
2023-06-13 16:32:57 信息 合约名称: BoredApeYachtClub
2023-06-13 16:32:57 信息 合约地址: 0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
주소 결정0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
이 계약은 ERC721 표준에 의해 이루어졌습니다.
이 강연에서는 ERC721 계약을 어떻게 판단할 수 있는지 소개했습니다. ERC20가 ERC165 표준을 지원하지 않는 계약을 다른 방법으로 식별해야 합니다. 당신은 계약이 ERC20 표준인지 확인하는 방법을 알고 있습니까?
무슨 일이죠?calldata
저자의 이해에 따르면, 간단한 일반적인 설명은 다음과 같습니다.
"calldata"는 이더리움에 있는 함수 호출, 변수에 대한 코딩이며, "calldata"는 계약의 ABI (Application Binary Interface) 스펙에 따라 코딩된다.
예를 들어, 이전 강의에서 배운 ERC20 계약의balanceOf
、transfer
메소드 호출은, 호출 당시의 파라미터와 함께,calldata
◎ 어떤 응용 시나리오에서 예를 들어:계약 간 상호 작용이 모든 것은 우리가 할 수 있는 일입니다.calldata
물론, 다른 많은 응용 시나리오가 있습니다.
어떻게 하면calldata
?
발명가 양적 거래 플랫폼을 사용할 수 있습니다exchange.IO("encode", ...)
스마트 계약 함수 호출에 대한 코딩은 매우 간단합니다.exchange.IO("encode", ...)
함수의 첫 번째 변수는 고정된 문자열입니다."encode"
; 두 번째 매개 변수는 스마트 계약의 주소; 세 번째 매개 변수는 암호화하려는 스마트 계약 방법의 이름; 나머지 매개 변수는 암호화하려는 스마트 계약 방법의 특정 매개 변수 값으로 전달됩니다.
우리는 이 방법을 사용할 수 있습니다calldata
데이터에서, 만약 이 스마트 계약 방식이 Write 방법 (즉: Write 동작) 이라면, 우리는 생성된calldata
거래의 데이터 필드로서의 데이터, 그리고 Ethereum의 RPC 방법을 사용합니다.eth_sendRawTransaction
이 트랜잭션을 포함하는 원본 데이터의 요청을 에테리움 네트워크에 전송합니다.
eth_sendRawTransaction
이 방법의 매개 변수는 하나만 있습니다.data
:
데이터: 서명된 트랜잭션 (일반적으로 개인 키를 사용하여 라이브러리로 서명)
이것은data
매개 변수 (parameter) 는 서명 계산 후 거래 데이터의 데이터입니다. 이더리움의 거래 데이터 구조는 주로 다음과 같은 필드를 가지고 있습니다.
{
"nonce": "0x1", // 交易发送方的账户交易次数
"gasPrice": "0x12a05f200", // 交易的Gas价格
"gasLimit": "0x5208", // 交易的Gas限制
"to": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", // 目标合约地址或接收方地址
"value": "0x4563918244F40000", // 转账的以太币数量
"data": "0x0123456789ABCDEF", // 要发送给合约的数据
}
에테리움 거래에 서명하는 방법은 무엇입니까?
우리는 발명가들의 양적 거래 플랫폼을 사용합니다.Encode()
이 함수는 서명 계산을 수행합니다. 예를 들어, 우리는 다음 강의에서 "Write Method execute calldata"를 작성했습니다.
이 방법은calldata
이 작업은 우리가 이전에 배운 RPC 방법을 사용하여 수행됩니다.eth_call
이 모든 것은 우리가 할 수 있는 일입니다.eth_call
이 에테리움의 RPC 방식은 단지 스마트 계약을 합니다.Write
이 강의에서 사용하는 방법의 예시calldata
이 방법은 SMART CONTRACT READ 방법의 호출을 수행하는 방법을 보여줍니다.balanceOf
현재 지갑의 WETH 토큰 잔액을 읽는 방법.
우리는 이더리움에서 테스트를 위해 디뷰팅 도구를 사용했습니다.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function main() {
// WETH合约的ABI
var abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}]`
// WETH合约地址
var wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
// 注册WETH合约的ABI
exchange.IO("abi", wethAddress, abiWETH)
// 当前配置的交易所对象的钱包地址
var walletAddress = exchange.IO("address")
// 编码WETH合约的deposit方法调用
var calldataForDeposit = exchange.IO("encode", wethAddress, "balanceOf(address)", walletAddress)
Log("calldataForDeposit:", "0x" + calldataForDeposit)
// 构造transaction,作为eth_call的第一个参数
var transaction = {
"from" : walletAddress,
"to" : wethAddress,
"data" : "0x" + calldataForDeposit,
}
// eth_call的第二个参数
var blockNumber = "latest"
// 使用eth_call调用
var ret = exchange.IO("api", "eth", "eth_call", transaction, blockNumber)
var wethBalance = exchange.IO("decode", "uint256", ret) // 可以使用exchange.IO("decode", ...) 函数解码
Log("wethBalance:", toAmount(wethBalance, 18)) // 从以wei为单位,换算成WETH个数为单位
}
디뷰팅 도구에서 실행:
2023-06-15 11:51:31 信息 wethBalance: 0.015
2023-06-15 11:51:31 信息 calldataForDeposit: 0x70a082310000000000000000000000006b3f11d807809b0b1e5e3243df04a280d9f94bf4
만약 스마트 계약의 방법이 반환값을 가지고 있다면,exchange.IO("decode", ...)
함수 해독.calldata
그리고 이 모든 것은 우리가 할 수 있는 것입니다.balanceOf
이 방법은 동일합니다. 내 테스트 지갑의 WETH 잔액은 0.015 WETH입니다.
Write 메소드의 calldata 실행을 위해서는 RPC 메소드를 사용해야 합니다:eth_sendRawTransaction
ᅳ
우리는 이더리움에서 테스트를 위해 디뷰팅 도구를 사용했습니다.
function toAmount(s, decimals) {
return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}
function toInnerAmount(s, decimals) {
return (BigDecimal(s)*BigDecimal(Math.pow(10, decimals))).toFixed(0)
}
function main() {
// WETH合约的ABI
var abiWETH = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed