nombre:
el contratoV3FactoryAdres =
Función de cantidad (s, decimales) { Retorno de número (BigDecimal) (BigInt) (s) / (BigDecimal) (Math.pow) (10, decimales) (toString)) ¿ Por qué?
Función para InnerAmount ((n, decimales) { Retorno (BigDecimal ((n) * BigDecimal ((Math.pow ((10, decimales))).aFixed ((0) ¿ Por qué?
La función principal (()) { // ABI registrado en el contrato de fábrica de Uniswapexchange.IO("Abi", contratoV3Factory Dirección, abiFactory)
// 注册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
}
Los datos de información sobre los precios principales se registran en:sqrtPriceX96
Los campos que se requieren para calcular el precio actual de la bolsa de cambio, según los datos de precisión de los tokens de la combinación de intercambios.Uniswap
La descripción en la documentación es que implementamos una función para calcular:
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)
);
}
Usamos esta función para calcular la combinación de tokens como1INCH/WETH
El precio de cambio de un grupo de 10 000 es el precio actual.
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)
}
Imprimir las variablesprice
Muestra:price: 0.000203
En el caso de las monedas, el valor de las monedas es de 1 INCH por 0.000203 WETH.
Los inventores de la plataforma de intercambio cuantitativo han revelado un nuevo paquete.Uniswap
Las plantillasLa versión de la página web, que incluye el código de código de la página web, ha implementado funciones como canjear, obtener precios, consultar el saldo de la billetera, y no hay necesidad de reescribir el código de acuerdo con lo descrito anteriormente.
En la biblioteca de modelos hay muchos detalles de diseño que vale la pena aprender:
Obtención automática de la información de los tokens
Extraer un fragmento de código explicativo, cuando los parámetros de la plantillaAutoFetchTokens
Cuando se establece como verdadero, el programa de plantillas accede automáticamente.https://tokens.coingecko.com/uniswap/all.json
Enlace, obtención y procesamiento automático de la información de todos los tokens. Esto permite que no se necesite añadir manualmente un token en el código de la política.addToken(name, address)
Y ahora, el nuevo token está disponible.
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
}
}
})
}
Adapta diferentes direcciones de contrato según la cadena de configuración
La plantilla está configuradaChainType
Los parámetros que permiten cambiar varias cadenas:
'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',
El código de selección es:
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)
}
}
Métodos de RPC para llamar a Ethereumeth_chainId
Buscar ahorachainId
¿Qué es lo que está pasando?chainId
En la página web de la organización, se puede leer:Uniswap
El sitio web de la organización es el siguiente:USDT
Los contratos inteligentes pueden tener diferentes direcciones en diferentes cadenas.
El código de selección es:
// 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")
}
Utilizando la librería de clases de transacciones Uniswap V3
En esta plantilla$.testUniswap()
La función es una función de prueba de la función de la plantilla, cuyo código da ejemplos de cómo llamar a la plantilla:
$.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))
// ...
}
Cuando una política hace referencia a la librería de clases de transacciones Uniswap V3 (como hacer referencia a la librería de clases de plantillas puede referirse a la documentación de la plataforma FMZ), se puede llamar a la función que está envuelta en esta librería de clases de plantillas.
Crear una página llamadaex
Las variables de las funciones de interfaz que se llaman "Uniswap V3 Transaction Library" están envueltas en la plantilla$.NewUniswapV3()
Crear un objeto para asignarle un valorex
。
let ex = $.NewUniswapV3()
Usoex
Función miembro de un objetoaddToken()
Añadir información sobre los tokens para registrarse.
let tokenAddressMap = {
"USDT": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"1INCH": "0x111111111117dC0aa78b770fA6A738034120C302",
"USDC": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"DAI": "0x6b175474e89094c44da98b954eedeac495271d0f",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
Si desea obtener e imprimir el precio de un grupo de cambio de un par de transacciones, puede utilizarex
Función miembro de un objetogetPrice()
En el caso de los bloggers, el nombre de la red social puede ser:
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
Se puede usar si se quiere realizar una operación de conversión.ex
Función miembro de un objetoswapToken()
En el caso de las mujeres, el cambio se realiza:
// 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'))
En este capítulo, aprenderemos a leer los eventos de la liberación de contratos inteligentes usando la plataforma de intercambio cuantitativa de inventores, los cuales se almacenan en los registros de la máquina virtual Ethereum.
Los eventos de consulta para la liberación de contratos inteligentes requieren el método RPC de Ethereumeth_getLogs
Para obtener los datos de los registros en la cadena, se explicó en el curso anterior cómo llamar a los nodos RPC de Ethereum.
Por ejemplo, obtenemosWETH
Los eventos contratados pueden ser codificados usando FMZ.Herramientas de depuraciónEn la prueba, los nodos de RPC configurados para objetos de intercambio son nodos de la red Ethereum que están siendo llamadoseth_getLogs
En el método, hemos especificado tres parámetros.fromBlock
、toBlock
、address
En el caso de los bloques de bloques de datos, los parámetros fromBlock y toBlock se utilizan para consultar los datos dentro de un bloque:
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
}
Para acceder a los datos de los registros, hemos omitido algunos de ellos debido a su gran contenido:
[{
"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"
}]
Se puede ver que hay una gran variedad de eventos en los datos de los registros, y si solo nos preocupamos porTransfer
Los eventos necesitan que los datos se transfieran a otros lugares.Transfer
El incidente fue filtrado.
Los registros de Ethereum están divididos en dos partes, uno y tema:topics
2 Datosdata
。
El tematopics
¿Qué es esto?eth_getLogs
Los resultados de ejecución de código de las pruebas de capítulos son ejemplos.topics
Los datos de los campos son:
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],
Esto estopics
El valor de un campo (tema) es una estructura de arquivos que se utiliza para describir eventos. Se especifica que su longitud no puede ser superior a 4. El primer elemento es el hash de la firma del evento.
En el inventor de la plataforma de intercambio cuantitativo, usamosEncode
La función puede calcular el hash de la firma usando el siguiente código:
function main() {
var eventFunction = "Transfer(address,address,uint256)"
var eventHash = Encode("keccak256", "string", "hex", eventFunction)
Log("eventHash:", "0x" + eventHash)
// eventHash: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
}
CalculadoTransfer(address,address,uint256)
¿Qué es esto?keccak256
El valor de hash (hex) es:0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
。
topics
El valor del campo es una estructura de matriz, el segundo elemento y el tercer elemento son:
from
to
Datosdata
data
Los datos de los campos son:
"data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
Algunos de los parámetros de los eventos (los parámetros en el código de Solidity de los contratos inteligentes que no tienen declaraciones indexadas) se almacenan endata
En la parte de abajo.
Para analizar los datos0x0000000000000000000000000000000000000000000000000164f2434262e1cc
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
}
El resultado es: 0.10047146239950075, que es el número de personas que se encuentran en el país.data
Los datos corresponden al importe de las transferencias.
En la página web del blog, se puede leer: "El blog de los bloggers está en línea con el blog de los bloggers, y el blog de los bloggers está en línea con el blog de los bloggers".
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++
}
}
}
En elhttps://etherscan.io/
Encuestas:
Los resultados de la prueba de código en FMZ:
También se puede analizar según las necesidades de la búsqueda.from
,to
Los datos de los campos, por ejemplo:
function main() {
var from = "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c"
var address = "0x" + exchange.IO("encodePacked", "address", from)
Log("address:", address)
}
Los resultados:
Dirección: 0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c
Debido aHerramientas de depuraciónSólo se puede probar el código durante un corto período de tiempo, y el contenido solo se exporta después de la ejecución del código, no se puede mostrar en tiempo real, exportar registros.
Aquí usamos el Ethereum para escuchar.USDT
Este contrato de divisasTransfer(address,address,uint256)
Los eventos, basados en lo que aprendimos en la clase anterior, diseñamos un ejemplo de un evento de contratación inteligente que se escucha continuamente:
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 = {