nome:
Var contractV3FactoryAddress =
Função toAmount (s, decimais) { Retorno Número (BigDecimal) (BigInt) (s) / (BigDecimal) (Math.pow) (10, decimais) (toString)) - Não.
Função para InnerAmount ((n, decimais) { retorno (BigDecimal(n) * BigDecimal(Math.pow(10, decimais))).toFixed(0) - Não.
função main (() { // ABI registrado no contrato da fábrica da Uniswapexchange.IO("abi", contratoV3FactoryEndereço, 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
}
Os principais dados de informações sobre preços são registrados em:sqrtPriceX96
O campo, que precisa de ser calculado com base em dados de precisão de tokens da combinação de trocas em conjunto com o preço atual do pool de trocas, com base emUniswap
A descrição na documentação é que nós implementamos uma função 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)
);
}
Com esta função, podemos calcular a combinação de tokens como1INCH/WETH
A taxa é de 10.000, o preço atual do poço.
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 variáveisprice
Mostrar:price: 0.000203
(Um 1 INCH para 0.000203 WETH)
Os inventores da plataforma de negociação quantitativa divulgaram um pacote completoUniswap
ModeloO modelo de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código de código.
A biblioteca de modelos tem muitos detalhes de design que vale a pena aprender:
Obtenção automática de informações de tokens
Para obter uma descrição do código, quando o parâmetro do modelo éAutoFetchTokens
Quando definido como verdadeiro, o programa de modelo acessa automaticamentehttps://tokens.coingecko.com/uniswap/all.json
O link, obtém e processa automaticamente a informação de todos os tokens. Isso permite que você não tenha que adicionar manualmente um token no código da política (ou seja, você precisa usar o token).addToken(name, address)
Adicionar um token) ‖
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ção de diferentes endereços de contratos com base nas configurações da cadeia
O modelo foi configuradoChainType
Parâmetros que permitem a troca de várias cadeias:
'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',
Código de seleção:
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étodo RPC para chamar Ethereumeth_chainId
Consultas atuaischainId
De acordo comchainId
A partir de agora, o site está disponível para download gratuito e download gratuito.Uniswap
A lista de endereços dos contratos foi divulgada.USDT
O endereço de um contrato pode variar de acordo com a cadeia.
Código de seleção:
// 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")
}
Usando o Uniswap V3
Neste modelo$.testUniswap()
A função é uma função de teste de funções de modelo, cujo código fornece exemplos de como usar o modelo:
$.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))
// ...
}
Quando uma política faz referência ao "Uniswap V3 transaction library" (como fazer referência ao template library pode ser consultado na documentação da plataforma FMZ), é possível invocar funções que estão embutidas nesse template library.
Criar um site chamadoex
Uma variável chamada "Uniswap V3 Transaction Library" é uma função de interface embalada.$.NewUniswapV3()
Criar um objeto para atribuir valor aex
。
let ex = $.NewUniswapV3()
Utilizaçãoex
Funções membros de objetosaddToken()
Adicionar informações sobre os tokens.
let tokenAddressMap = {
"USDT": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"1INCH": "0x111111111117dC0aa78b770fA6A738034120C302",
"USDC": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"DAI": "0x6b175474e89094c44da98b954eedeac495271d0f",
}
for (let name in tokenAddressMap) {
ex.addToken(name, tokenAddressMap[name])
}
Se você quiser obter e imprimir o preço do pool de câmbio de um par de transações, você pode usarex
Funções membros de objetosgetPrice()
O que você pode escrever é:
Log(ex.getPrice('ETH_USDT'))
Log(ex.getPrice('1INCH_USDT'))
Se você quiser executar uma operação de conversão, você pode usarex
Funções membros de objetosswapToken()
, executar a troca:
// 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'))
Neste capítulo, aprenderemos a ler os eventos de lançamento de contratos inteligentes usando a plataforma de negociação quantitativa dos inventores, que são armazenados nos registros do Ethereum Virtual Machine.
Os eventos de consulta de contratos inteligentes são liberados usando o método RPC do Ethereumeth_getLogs
O que é mais importante é que você tenha acesso aos dados de logs da cadeia, como foi explicado em nossos cursos anteriores sobre como chamar os nós do Ethereum RPC.
Por exemplo, nós conseguimosWETH
Os eventos contratados podem ser codificados usando o FMZ.Ferramentas de depuraçãoTestes, os objetos de troca são configurados para que os nós RPC sejam os nodes da rede Ethereum que estão sendo chamadoseth_getLogs
Quando o método é definido, temos três parâmetros.fromBlock
、toBlock
、address
Usamos os parâmetros fromBlock e toBlock para consultar dados limitados dentro de um bloco:
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 acessar os dados dos logs, por causa do grande volume de dados, nós omitimos alguns deles:
[{
"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"
}]
Podemos ver que há uma grande variedade de eventos nos dados de logs, se apenas nos interessarmosTransfer
O evento precisa de usar esses dados.Transfer
O incidente foi filtrado.
Os logs do Ethereum são divididos em duas partes, uma e uma temática.topics
; 2, dadosdata
。
Temáticatopics
Com oeth_getLogs
O resultado da execução do código do teste do capítulo é um exemplo.topics
Os dados dos campos são:
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c", "0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],
Esta étopics
O valor do campo (tema) é uma estrutura de matriz usada para descrever eventos. Especifica que seu tamanho não pode exceder 4 e que o primeiro elemento é o hash de assinatura do evento.
No inventor quantidade de plataforma de negociação, usamosEncode
A função pode calcular esse hash de assinatura usando o seguinte código:
function main() {
var eventFunction = "Transfer(address,address,uint256)"
var eventHash = Encode("keccak256", "string", "hex", eventFunction)
Log("eventHash:", "0x" + eventHash)
// eventHash: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
}
CalcularTransfer(address,address,uint256)
Não.keccak256
O hash (hex code) é:0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
。
topics
O valor do campo é uma estrutura de matrizes, com o segundo e o terceiro elementos:
from
to
Dadosdata
data
Os dados dos campos são:
"data": "0x0000000000000000000000000000000000000000000000000164f2434262e1cc",
Alguns parâmetros do evento (parâmetros no código Solidity do smart contract sem declarações indexadas) são armazenados emdata
O que é que ele está a fazer?
Analisar os dados0x0000000000000000000000000000000000000000000000000164f2434262e1cc
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
}
O resultado é: 0.10047146239950075, o que significa que o número de pessoas que estão usando o aplicativo é de 0.10047146239950075.data
Os dados correspondem ao valor da transferência.
A partir de agora, podemos começar a pesquisar os diários:
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++
}
}
}
Emhttps://etherscan.io/
A pergunta é:
Resultados do teste de código em FMZ Debug Tool:
A partir daí, você pode fazer uma análise de acordo com o que você precisa quando você está procurando.from
,to
Os dados dos campos, por exemplo:
function main() {
var from = "0x00000000000000000000000012b791bb27b3a4ee958b5a435fea7d49ec076e9c"
var address = "0x" + exchange.IO("encodePacked", "address", from)
Log("address:", address)
}
Os resultados:
Endereço: 0x12b791bb27b3a4ee958b5a435fea7d49ec076e9c
Por causa doFerramentas de depuraçãoO código é testado por um curto período de tempo e o conteúdo é exportado após a execução do código, não pode ser exibido em tempo real. O conteúdo desta seção é testado usando a plataforma de negociação quantitativa do inventor.
Aqui nós usamos o Ethereum para escutar.USDT
O contrato de moedaTransfer(address,address,uint256)
A partir do que aprendemos na aula anterior, nós projetamos um exemplo de um evento de contrato inteligente que é monitorado 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 = {