name
var contractV3FactoryAddress =
функция toAmount ((s, десятичные знаки) { возвращение числа (BigDecimal) (BigInt) (s) / (BigDecimal) (Math.pow), десятичных знаков (toString)) Я не знаю.
функция toInnerAmount ((n, десятичные знаки) { return (BigDecimal(n) * BigDecimal(Math.pow(10, десятичные цифры))).toFixed(0) Я не знаю.
function main (() {
// Зарегистрированный АБИ завода Uniswap
exchange.IO ((
// 注册Uniswap路由合约的ABI
exchange.IO("abi", contractV3SwapRouterV2Address, abiRoute)
// 获取交易对的池地址
var tokenIn = {name : "1INCH", address: "0x111111111117dC0aa78b770fA6A738034120C302", decimals: exchange.IO("api", "0x111111111117dC0aa78b770fA6A738034120C302", "decimals")}
var tokenOut = {name : "WETH", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: exchange.IO("api", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals")}
var poolAddress = exchange.IO("api", contractV3FactoryAddress, "getPool", tokenIn.address, tokenOut.address, 10000)
// 注册池合约ABI
exchange.IO("abi", poolAddress, abiPool)
var slot0 = exchange.IO("api", poolAddress, "slot0")
Log("slot0:", slot0)
}
获取到兑换池的价格信息,打印出代码中```slot0```变量:
```javascript
{
"feeProtocol":0,
"unlocked":true,
"sqrtPriceX96":"1128983883551457130720648561",
"tick":"-85025",
"observationIndex":5,
"observationCardinality":6,
"observationCardinalityNext":6
}
Основные данные о ценовой информации хранятся вsqrtPriceX96
Поле, требующее вычислить текущую цену обменного пула на основе данных точности токенов обменной комбинации, на основеUniswap
Описание в документации показывает, что мы реализовали функцию для вычисления:
function computePoolPrice(decimals0, decimals1, sqrtPriceX96) {
// sqrtPriceX96 = sqrt(price) * 2^96
[decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt);
const TWO = BigInt(2);
const TEN = BigInt(10);
const SIX_TENTH = BigInt(1000000);
const Q192 = (TWO ** BigInt(96)) ** TWO;
return (
Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) /
Number(SIX_TENTH)
);
}
Используйте эту функцию, чтобы вычислить комбинацию токенов как1INCH/WETH
В этом случае, если вы не хотите, чтобы ваши деньги были потрачены в банке, вы можете использовать их для покупки в банке.
function computePoolPrice(decimals0, decimals1, sqrtPriceX96) {
// sqrtPriceX96 = sqrt(price) * 2^96
[decimals0, decimals1, sqrtPriceX96] = [decimals0, decimals1, sqrtPriceX96].map(BigInt);
const TWO = BigInt(2);
const TEN = BigInt(10);
const SIX_TENTH = BigInt(1000000);
const Q192 = (TWO ** BigInt(96)) ** TWO;
return (
Number((sqrtPriceX96 ** TWO * TEN ** decimals0 * SIX_TENTH) / (Q192 * TEN ** decimals1)) /
Number(SIX_TENTH)
);
}
function main() {
var tokenIn = {name : "1INCH", address: "0x111111111117dC0aa78b770fA6A738034120C302", decimals: exchange.IO("api", "0x111111111117dC0aa78b770fA6A738034120C302", "decimals")}
var tokenOut = {name : "WETH", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: exchange.IO("api", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals")}
// 获取的slot0变量中"sqrtPriceX96":"1128983883551457130720648561",
var price = computePoolPrice(tokenIn.decimals, tokenOut.decimals, "1128983883551457130720648561")
Log("price:", price)
}
Печать переменныхprice
Показывает:price: 0.000203
1 INCH обменяется на 0.000203 WETH).
Изобретатели квантовой торговой платформы обнародовали упакованныйUniswap
ШаблоныВ настоящее время существует множество функций, таких как обмен, получение цены, запрос баланса кошелька, без необходимости повторного написания кода на основе содержания вышеприведенного, можно читать этот шаблон, чтобы узнать более глубокое изучение исходного кода и разработать приложения в направлении Web3.
В библиотеке шаблонов есть много деталей дизайна, которые стоит изучить:
AutoFetchTokens
При установке на true шаблон автоматически переходит к программеhttps://tokens.coingecko.com/uniswap/all.json
Ссылка, получение и автоматическая обработка информации обо всех токенах. Это позволяет без необходимости вручную добавлять какой-либо токен в стратегический код (если он не нужен).addToken(name, address)
Добавить токен) ‒ if (AutoFetchTokens) {
let res = JSON.parse(HttpQuery("https://tokens.coingecko.com/uniswap/all.json"))
Log("fetch", res.tokens.length, "tokens from", res.name)
res.tokens.forEach(function(token) {
if (token.chainId == chainId && token.symbol != "WETH") {
self.tokenInfo[token.symbol] = {
name: token.symbol,
decimals: token.decimals,
address: token.address
}
}
})
}
ChainType
Параметры, поддерживающие переключение нескольких цепей: 'https://rpc.ankr.com/eth',
'https://arb1.arbitrum.io/rpc',
'https://mainnet.optimism.io/',
'https://rpc.ankr.com/avalanche',
'https://polygon-rpc.com',
'https://rpc.ankr.com/celo',
Код выбора:
if (typeof(ChainType) === 'number') {
let chainRpc = [
'',
'https://rpc.ankr.com/eth',
'https://arb1.arbitrum.io/rpc',
'https://mainnet.optimism.io/',
'https://rpc.ankr.com/avalanche',
'https://polygon-rpc.com',
'https://rpc.ankr.com/celo',
//'https://mainnet.aurora.dev',
//'https://bsc-dataseed.binance.org',
//'https://exchainrpc.okex.org'
][ChainType]
if (chainRpc && chainRpc.length > 0) {
e.IO("base", chainRpc)
Log("change base rpc to", chainRpc)
}
}
Метод RPC для вызова Ethereumeth_chainId
Запрос текущийchainId
В соответствии сchainId
Посмотрите, как это делается.Uniswap
Например, в 2010 году в Нью-Йорке было подписано соглашение о сотрудничестве.USDT
Смарт-контракты могут иметь разные адреса в разных цепочках.
Код выбора:
// https://docs.uniswap.org/contracts/v3/reference/deployments
let WETHAddress = {
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // Ethereum
3: "0xc778417E063141139Fce010982780140Aa0cD5Ab", // Ropsten
4: "0xc778417E063141139Fce010982780140Aa0cD5Ab", // Rinkeby
5: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", // Goerli
42: "0xd0A1E359811322d97991E03f863a0C30C2cF029C", // Kovan
10: "0x4200000000000000000000000000000000000006", // Optimism
69: "0x4200000000000000000000000000000000000006", // Optimistic Kovan
42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Arbitrum One
421611: "0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681", // Arbitrum Rinkeby
137: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", // Polygon
80001: "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889", // Polygon Mumbai
}
let chainId = e.IO("api", "ETH", "eth_chainId")
if (chainId) {
chainId = Number(chainId)
Log("chainId: ", chainId)
let addr = WETHAddress[chainId]
if (addr) {
Log("Register WETH address", addr)
self.addToken("ETH", addr)
}
if (chainId == 42220) {
// Celo Address
ContractV3Factory = '0xAfE208a311B21f13EF87E33A90049fC17A7acDEc'
ContractV3SwapRouterV2 = '0x5615CDAb10dc425a742d643d949a7F474C01abc4'
self.addToken('CELO', '0x471ece3750da237f93b8e339c536989b8978a438')
} else if (chainId == 42161) {
self.addToken('USDT', '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9')
}
} else {
panic("get chain Id error")
}
В этом шаблоне$.testUniswap()
Функция - это функция для тестирования функций шаблона, код которой дает примеры вызовов использования шаблона:
$.testUniswap = function() {
let ex = $.NewUniswapV3()
Log("walletAddress: ", ex.walletAddress)
let tokenAddressMap = {
"USDT": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"1INCH": "0x111111111117dC0aa78b770fA6A738034120C302",
"USDC": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"DAI": "0x6b175474e89094c44da98b954eedeac495271d0f",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
// swap 0.01 ETH to USDT
Log(ex.swapToken('ETH', 0.01, 'USDT'))
let usdtBalance = ex.balanceOf('USDT')
Log("balance of USDT", usdtBalance)
// swap USDT to DAI then DAI to ETH
Log(ex.swapToken('USDT', usdtBalance, 'DAI,ETH'))
Log("balance of ETH", ex.getETHBalance())
// Log(ex.sendETH('0x11111', 0.02))
// ...
}
Когда политика ссылается на "Uniswap V3 Transaction Library" ("Как ссылаться на шаблонную библиотеку можно посмотреть в документации FMZ"), можно вызвать функцию, завернутую в эту шаблонную библиотеку.
Создать сайт под названиемex
Переменные, вызывающие шаблоны "Uniswap V3 Transaction Library" в упакованном интерфейсе$.NewUniswapV3()
Создание объекта, придающего значениеex
。
let ex = $.NewUniswapV3()
Использованиеex
Функции-члены объектаaddToken()
Добавить токенную информацию.
let tokenAddressMap = {
"USDT": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"1INCH": "0x111111111117dC0aa78b770fA6A738034120C302",
"USDC": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"DAI": "0x6b175474e89094c44da98b954eedeac495271d0f",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
Если вы хотите получить и распечатать цены обменного пула для определенной пары, вы можете использоватьex
Функции-члены объектаgetPrice()
В этом случае мы можем написать так:
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
Если вы хотите выполнить обменную операцию, вы можете использоватьex
Функции-члены объектаswapToken()
, выполните обмен:
// swap 0.01 ETH to USDT
Log(ex.swapToken('ETH', 0.01, 'USDT'))
let usdtBalance = ex.balanceOf('USDT')
Log("balance of USDT", usdtBalance)
// swap USDT to DAI then DAI to ETH
Log(ex.swapToken('USDT', usdtBalance, 'DAI,ETH'))
В этой главе мы узнаем, как читать события, выпущенные смарт-контрактами с помощью квантовой торговой платформы изобретателей, которые хранятся в журналах виртуальной машины Ethereum.
Запрос событий, выпущенных умными контрактами, требует использования методов RPC на Эфириумеeth_getLogs
Например, в этом случае вы можете получить данные о цепочках, которые мы рассказывали в предыдущих уроках о том, как вызвать RPC-ноты на Ethereum.
Например, мы получаемWETH
Контрактные события могут быть написаны в коде, используя FMZ.Инструменты для дебютированияТест, обменный объект, настроенный на RPC-ноты для сети Эфириума, вызываетeth_getLogs
Мы задали три параметра.fromBlock
、toBlock
、address
Мы использовали параметры fromBlock и toBlock для ограничения запросов данных в блоке:
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" // WETH合约的地址
}
var logs = exchange.IO("api", "eth", "eth_getLogs", params)
// 由于数据量比较大,如果使用Log函数打印,数据会被截断。使用return将完整数据返回在页面「函数结果」编辑框中
return logs
}
Поскольку данные доступны в больших объемах, мы исключили некоторые из них:
[{
"data": "0x00000000000000000000000000000000000000000000000001c1a55000000000",
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000006b75d8af000000e20b7a7ddf000ba900b4009a80", "0x000000000000000000000000bcb095c1f9c3dc02e834976706c87dee5d0f1fb6"],
"transactionHash": "0x27f9bf5abe3148169b4b85a83e1de32bd50eb81ecc52e5af006157d93353e4c4",
"transactionIndex": "0x0",
"removed": false,
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
"blockNumber": "0x109b1cc",
"logIndex": "0x0"
}, {
"data": "0x00000000000000000000000000000000000000000000000008ea20cdea027c00",
"logIndex": "0x5",
"topics": ["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c", "0x0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d"],
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"blockHash": "0x847be24a7b159c292bda030a011dfec89487b70e71eed486969b032d6ef04bad",
"blockNumber": "0x109b1cc",
"removed": false,
"transactionHash": "0xace3afa02e8af5d1ef6fc1635fbdf7bee37624547937ea5272c23968dd034c09",
"transactionIndex": "0x1"
},
...
{
"blockNumber": "0x109b1cd",
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"data": "0x00000000000000000000000000000000000000000000000002c053531ab8a000",
"logIndex": "0xd3",
"removed": false,
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582", "0x000000000000000000000000252ba9b5916171dbdadd2cec7f91875a006955d0"],
"transactionHash": "0x3012b82891f85b077cfe1c12cb9722b93c696ef2c37d67981ccddcc9c3396aca",
"transactionIndex": "0x8d",
"blockHash": "0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9"
}, {
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],
"transactionIndex": "0x91",
"logIndex": "0xdb",
"removed": false,
"blockNumber": "0x109b1cd",
"data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
"transactionHash": "0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048",
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"blockHash": "0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9"
}, {
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"blockHash": "0xcd3d567c9bd02a4549b1de0dc638ab5523e847c3c156b096424f56c633000fd9",
"blockNumber": "0x109b1cd",
"logIndex": "0xde",
"removed": false,
"topics": ["0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],
"data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
"transactionHash": "0x6aa8d80daf42f442591e7530e31323d05e1d6dd9f9f9b9c102e157d89810c048",
"transactionIndex": "0x91"
}]
Вы можете увидеть, что в данных журналов есть всевозможные события, если мы просто обратим внимание на их значение.Transfer
Иногда, когда мы говорим о событиях, мы говорим о том, что они происходят.Transfer
В результате произошедшее было отфильтровано.
Логотип Ethereum разделен на две части:topics
; 2, данныеdata
。
topics
С помощьюeth_getLogs
Например, результаты выполнения тестов по разделам.topics
Данные в поле: "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],
Этоtopics
Значение поля (тема) является матричной структурой, используемой для описания событий. Установлено, что длина его ((арей) не может превышать 4. Первый элемент является хеш-значением подписи события.
Мы использовали платформу для количественного обмена, разработанную изобретателямиEncode
Функция может вычислить этот хэш-значение подписи, используя следующий код:
function main() {
var eventFunction = "Transfer(address,address,uint256)"
var eventHash = Encode("keccak256", "string", "hex", eventFunction)
Log("eventHash:", "0x" + eventHash)
// eventHash: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
}
ВычислитьTransfer(address,address,uint256)
О чем вы?keccak256
Хеш-значение (хексокодирование)0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
。
- 发出地址```from```
- 接收地址```to```
- 数据```data```
```data```字段的数据为:
```desc
"data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
Некоторые параметры в событиях (параметры в коде Solidity для умных контрактов без индексированных заявлений) хранятся вdata
В частности.
Проанализировать данные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, 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)
// 遍历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 Debug Tool:
Вы также можете проанализировать запросы в зависимости от их потребности.from
,to
Данные в поле, например:
function main() {
var from = "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c"
var address = "0x" + exchange.IO("encodePacked", "address", from)
Log("address:", address)
}
Результаты:
0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c
Из-заИнструменты для дебютированияВ этом разделе мы создаем реальные диски для тестирования с помощью квантовой торговой платформы изобретателей.
Здесь мы используем эфириум, чтобы прослушивать.USDT
Это соглашение.Transfer(address,address,uint256)
На основе того, что мы выучили в прошлом классе, мы разработали пример того, как постоянно прослушивать события, связанные со смарт-контрактами:
функция toInnerAmount ((n, десятичные знаки) { return (BigDecimal(n) * BigDecimal(Math.pow(10, десятичные цифры))).toFixed(0) Я не знаю.
Функция addEventListener ((контрактАдрес, событие, callBack) {
var self = {}
self.eventHash =
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 = {