DCA usando Uniswap V3
Este es unCrypto Trading, Multimodal AIflujo de automatización del dominio deautomatización que contiene 14 nodos.Utiliza principalmente nodos como Code, OneShot, Telegram, OneShotSynch, ScheduleTrigger. Automatizar la compra de tokens con DCA en Uniswap V3 y 1Shot API
- •Bot Token de Telegram
Nodos utilizados (14)
{
"id": "maYy0i7nYbJwepaQ",
"meta": {
"instanceId": "62f017ec8f130d172e2e5f39bbf09515036bfd403dfa60fe06f5ab14b78705d0",
"templateCredsSetupCompleted": true
},
"name": "DCA w/ Uniswap V3",
"tags": [],
"nodes": [
{
"id": "f43b592f-1203-47b8-9dac-61e8a9d3c9d3",
"name": "Calcular TWAP",
"type": "n8n-nodes-base.code",
"position": [
432,
160
],
"parameters": {
"jsCode": "// Constants\nconst MaxUint256 = (1n << 256n) - 1n;\nconst Q32 = 1n << 32n;\nconst ZERO = 0n;\nconst ONE = 1n;\n\nfunction mulShift(val, mulBy) {\n return (val * BigInt(mulBy)) >> 128n;\n}\n\n/**\n * Returns the sqrt ratio as a Q64.96 for the given tick.\n * The sqrt ratio is computed as sqrt(1.0001)^tick\n * @param {number} tick the tick for which to compute the sqrt ratio\n */\nfunction getSqrtRatioAtTick(tick) {\n if (!Number.isInteger(tick)) throw new Error(\"Tick must be integer\");\n const MIN_TICK = -887272;\n const MAX_TICK = 887272;\n\n if (tick < MIN_TICK || tick > MAX_TICK) throw new Error(\"Tick out of bounds\");\n\n const absTick = tick < 0 ? -tick : tick;\n\n let ratio =\n (absTick & 0x1) != 0\n ? 0xfffcb933bd6fad37aa2d162d1a594001n\n : 0x100000000000000000000000000000000n;\n if ((absTick & 0x2) != 0) ratio = mulShift(ratio, 0xfff97272373d413259a46990580e213an);\n if ((absTick & 0x4) != 0) ratio = mulShift(ratio, 0xfff2e50f5f656932ef12357cf3c7fdccn);\n if ((absTick & 0x8) != 0) ratio = mulShift(ratio, 0xffe5caca7e10e4e61c3624eaa0941cd0n);\n if ((absTick & 0x10) != 0) ratio = mulShift(ratio, 0xffcb9843d60f6159c9db58835c926644n);\n if ((absTick & 0x20) != 0) ratio = mulShift(ratio, 0xff973b41fa98c081472e6896dfb254c0n);\n if ((absTick & 0x40) != 0) ratio = mulShift(ratio, 0xff2ea16466c96a3843ec78b326b52861n);\n if ((absTick & 0x80) != 0) ratio = mulShift(ratio, 0xfe5dee046a99a2a811c461f1969c3053n);\n if ((absTick & 0x100) != 0) ratio = mulShift(ratio, 0xfcbe86c7900a88aedcffc83b479aa3a4n);\n if ((absTick & 0x200) != 0) ratio = mulShift(ratio, 0xf987a7253ac413176f2b074cf7815e54n);\n if ((absTick & 0x400) != 0) ratio = mulShift(ratio, 0xf3392b0822b70005940c7a398e4b70f3n);\n if ((absTick & 0x800) != 0) ratio = mulShift(ratio, 0xe7159475a2c29b7443b29c7fa6e889d9n);\n if ((absTick & 0x1000) != 0) ratio = mulShift(ratio, 0xd097f3bdfd2022b8845ad8f792aa5825n);\n if ((absTick & 0x2000) != 0) ratio = mulShift(ratio, 0xa9f746462d870fdf8a65dc1f90e061e5n);\n if ((absTick & 0x4000) != 0) ratio = mulShift(ratio, 0x70d869a156d2a1b890bb3df62baf32f7n);\n if ((absTick & 0x8000) != 0) ratio = mulShift(ratio, 0x31be135f97d08fd981231505542fcfa6n);\n if ((absTick & 0x10000) != 0) ratio = mulShift(ratio, 0x9aa508b5b7a84e1c677de54f3e99bc9n);\n if ((absTick & 0x20000) != 0) ratio = mulShift(ratio, 0x5d6af8dedb81196699c329225ee604n);\n if ((absTick & 0x40000) != 0) ratio = mulShift(ratio, 0x2216e584f5fa1ea926041bedfe98n);\n if ((absTick & 0x80000) != 0) ratio = mulShift(ratio, 0x48a170391f7dc42444e8fa2n);\n\n if (tick > 0) {\n ratio = MaxUint256 / ratio;\n }\n\n // back to Q96\n return ratio % Q32 > 0n ? ratio / Q32 + ONE : ratio / Q32;\n}\n\nfunction getPriceFromSqrtPriceX96(sqrtPriceX96, decimals0, decimals1) {\n // Ensure input is BigInt\n const sqrtPrice = BigInt(sqrtPriceX96.toString());\n\n // (sqrtPriceX96 ^ 2)\n const numerator = sqrtPrice * sqrtPrice;\n\n // Denominator = 2^192\n const denominator = 1n << 192n;\n\n // Raw price ratio (token1 per token0, no decimals adjusted)\n let ratio = Number(numerator * 10n**18n / denominator) / 1e18; \n // (we scale by 1e18 to stay precise when converting to Number)\n\n // Adjust for token decimals\n const decimalFactor = 10 ** (decimals0 - decimals1);\n const price = ratio * decimalFactor;\n\n return price;\n}\n\nconst diffTickCumulative = parseInt($('Fetch Pool TWA Observations').first().json.response[0][0]) - parseInt($('Fetch Pool TWA Observations').first().json.response[0][1]);\nconst diffSecondsPerLIquidityX128 = parseInt($('Fetch Pool TWA Observations').first().json.response[1][0]) - parseInt($('Fetch Pool TWA Observations').first().json.response[1][1]);\nconst secondsBetween = parseInt($('Swap Configs').first().json.secondsAgo);\nconst secondsBetweenX128 = BigInt(secondsBetween) << BigInt(128);\nconst averageTick = parseInt(diffTickCumulative/secondsBetween);\n\nconst sqrtPriceX96After = $input.first().json.result.decodedData[1];\nconst priceAfter = getPriceFromSqrtPriceX96(sqrtPriceX96After, 6, 8);\n\nconst sqrtTWAPricex96 = getSqrtRatioAtTick(averageTick);\nconst TWAP = getPriceFromSqrtPriceX96(sqrtTWAPricex96, 6,8);\nconst TWAL = secondsBetweenX128 / BigInt(diffSecondsPerLIquidityX128);\n\nconsole.log(\"TWAP\", 1/TWAP);\nconsole.log(\"Price After: \", 1/priceAfter);\nconsole.log(\"TWAL\", TWAL);\n\n$input.first().json.twap = TWAP; \n$input.first().json.sqrtTWAPriceX96 = sqrtTWAPricex96; \n$input.first().json.twal = TWAL; \n$input.first().json.quotePriceAfter = priceAfter; \n\nreturn $input.all()\n"
},
"typeVersion": 2
},
{
"id": "bd180960-416b-48f8-b524-9841428a34e9",
"name": "Obtener Observaciones TWA del Pool",
"type": "n8n-nodes-1shot.oneShot",
"position": [
-16,
160
],
"parameters": {
"params": "={\n \"secondsAgos\": [\"0\",\"{{ $json.secondsAgo }}\"]\n} ",
"operation": "read",
"contractMethodId": "19aa2a88-2121-4727-b1af-e4922259b645"
},
"credentials": {
"oneShotOAuth2Api": {
"id": "nkfF9AitCKUCrErK",
"name": "1Shot account"
}
},
"typeVersion": 1
},
{
"id": "46184b6b-263e-4e14-9518-2515e2d311a3",
"name": "Obtener Cotización de Intercambio",
"type": "n8n-nodes-1shot.oneShot",
"position": [
208,
160
],
"parameters": {
"params": "={\n \"params\": \n {\n \"tokenIn\": \"{{ $('Swap Configs').item.json.token0 }}\",\n \"tokenOut\": \"{{ $('Swap Configs').item.json.token1 }}\",\n \"amountIn\": \"{{ $('Swap Configs').item.json.secondsAgo }}\",\n \"fee\": \"500\",\n \"sqrtPriceLimitX96\": \"0\"\n }\n} ",
"operation": "simulate",
"contractMethodId": "8de7d518-a62a-4a05-8f79-68808b6fd609"
},
"credentials": {
"oneShotOAuth2Api": {
"id": "nkfF9AitCKUCrErK",
"name": "1Shot account"
}
},
"typeVersion": 1
},
{
"id": "6b4d7900-7fae-4c1d-80cf-2900dfb4e299",
"name": "Intercambiar Tokens",
"type": "n8n-nodes-1shot.oneShotSynch",
"onError": "continueRegularOutput",
"position": [
880,
80
],
"parameters": {
"params": "={\n\"params\": {\n\"tokenIn\": \"{{ $('Swap Configs').item.json.token0 }}\",\n\"tokenOut\": \"{{ $('Swap Configs').item.json.token1 }}\",\n\"fee\": \"{{ $('Swap Configs').item.json.fee }}\",\n\"recipient\": \"{{ $('Swap Configs').item.json.delegator }}\",\n\"amountIn\": \"{{ $('Swap Configs').item.json.amountDCA }}\",\n\"amountOutMinimum\": \"0\",\n\"sqrtPriceLimitX96\": \"{{ $('Calculate TWAP').item.json.sqrtTWAPriceX96 }}\"\n}\n}",
"operation": "executeAsDelegator",
"additionalFields": {
"memo": "=DCA Swap for {{ $('Swap Configs').item.json.amountDCA }}, TWAP: {{ $('Calculate TWAP').item.json.twap }}"
},
"contractMethodId": "86616048-5330-452f-8637-58859dc872c6",
"delegatorWalletAddress": "={{ $('Swap Configs').item.json.delegator }}"
},
"credentials": {
"oneShotOAuth2Api": {
"id": "nkfF9AitCKUCrErK",
"name": "1Shot account"
}
},
"typeVersion": 1
},
{
"id": "a11c7481-97bf-4cee-8a3f-e604cd09236d",
"name": "Programar Activador",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-464,
160
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 10
}
]
}
},
"typeVersion": 1.2
},
{
"id": "8c435f3f-72dc-4102-99bd-13cda1ea27c8",
"name": "Detalles de Falla",
"type": "n8n-nodes-base.telegram",
"position": [
1104,
256
],
"webhookId": "4bc3c459-fb40-44e5-a4b6-8565f2e92b62",
"parameters": {
"text": "=❌ Swap Failed",
"chatId": "5034284669",
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "uN6xtW1sUnA0WiMc",
"name": "@1shotdemobot"
}
},
"typeVersion": 1.2
},
{
"id": "d1512fe2-112f-48c0-8a98-cbbb08ab8f65",
"name": "Nota Adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-864,
224
],
"parameters": {
"width": 320,
"height": 224,
"content": "## Set Your DCA Schedule\n\nn8n makes it really easy to set a recurring schedule for your DCA purchases. Click on the Schedule Trigger node and set your frequency. "
},
"typeVersion": 1
},
{
"id": "4f6661d0-978c-4554-97eb-f91519dd8c85",
"name": "Nota Adhesiva1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-528,
-160
],
"parameters": {
"width": 720,
"height": 256,
"content": "## DCA configs\n\nSince we are using the Uniswap protocol directly, you'll need to set a few parameters in the Swap Configs node. \n\n1. Decide on the amount you want to spend on each DCA buy. \n2. Set the correct address for the Uniswap [SwapRouter](https://docs.uniswap.org/contracts/v3/reference/deployments/) contract\n3. Depending on the pool your are trading against, set the correct addresses for `token0` and `token1`. \n4. Also be sure to set the correct fee for the pool you are trading against (for most pools its just `500`). "
},
"typeVersion": 1
},
{
"id": "4b46e786-07d6-4184-8a90-ff264686c075",
"name": "Nota Adhesiva2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-128,
352
],
"parameters": {
"width": 672,
"height": 288,
"content": "## Connect to Your 1Shot API Account\n\nCreate an API key and secret in your 1Shot API account and connect your n8n instance by creating a credential. \n\n1. The `Fetch Pool TWA Observactions` should point to the `observe` method on your target trading pool (like this [one](https://app.uniswap.org/explore/pools/base/0xfBB6Eed8e7aa03B138556eeDaF5D271A5E1e43ef)). \n2. The `Get Swap Quote` should point to the `quoteExactInputSingle` on the QuoterV2 contract.\n3. The `Give Approval to Router` should call `approve` on the token you are DCA'ing out of. \n4. The `Swap Tokens` node should point at the `exactInputSingle` function on the Uniswap SwapRouterV2 contract. "
},
"typeVersion": 1
},
{
"id": "bb585dd2-901b-4bb3-b822-5ddf3ad80be7",
"name": "Dar Aprobación al Router",
"type": "n8n-nodes-1shot.oneShotSynch",
"onError": "continueRegularOutput",
"position": [
656,
160
],
"parameters": {
"params": "={\n \"spender\": \"{{ $('Swap Configs').item.json.router }}\", \n \"value\": \"{{ $('Swap Configs').item.json.amountDCA }}\"\n} ",
"operation": "executeAsDelegator",
"additionalFields": {
"memo": "=DCA Approve for {{ $('Swap Configs').item.json.amountDCA }}"
},
"contractMethodId": "b2c3382b-37d0-4d49-b5d2-d1c6e8770658",
"delegatorWalletAddress": "={{ $('Swap Configs').item.json.delegator }}"
},
"credentials": {
"oneShotOAuth2Api": {
"id": "nkfF9AitCKUCrErK",
"name": "1Shot account"
}
},
"typeVersion": 1
},
{
"id": "792928fb-59e5-44ba-a6b9-9f388c74f02a",
"name": "Nota Adhesiva3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1344,
224
],
"parameters": {
"width": 544,
"height": 176,
"content": "## Telegram Notifications\n\nIf you want to get notified on each DCA purchase, connect a Telegram bot. "
},
"typeVersion": 1
},
{
"id": "2edc9432-92f2-4feb-a44a-f6d6f5608db1",
"name": "Detalles de Éxito",
"type": "n8n-nodes-base.telegram",
"position": [
1328,
32
],
"webhookId": "4bc3c459-fb40-44e5-a4b6-8565f2e92b62",
"parameters": {
"text": "=✅ Swapped `{{ $('Swap Configs').item.json.amountDCA }}` `{{ $('Swap Configs').item.json.token0 }}` for {{ $('Get Swap Qoute').item.json.result.decodedData[0] }} {{ $('Swap Configs').item.json.token1 }}. \n\nThe `{{ $('Swap Configs').item.json.secondsAgo }}` second TWAP was `{{ $('Calculate TWAP').item.json.twap }}`. Your tx hash is `{{ $('Swap Tokens').item.json.transactionHash }}`.\n\nYou have {{ $json.response }} of token `{{ $('Swap Configs').item.json.token0 }}` left. ",
"chatId": "5034284669",
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"id": "uN6xtW1sUnA0WiMc",
"name": "@1shotdemobot"
}
},
"typeVersion": 1.2
},
{
"id": "3147ded9-fc81-4d00-b230-c313c0c3501a",
"name": "Obtener Saldo Restante de Fondos DCA",
"type": "n8n-nodes-1shot.oneShot",
"position": [
1104,
32
],
"parameters": {
"params": "={\n \"account\": \"{{ $('Swap Configs').item.json.delegator }}\"\n}",
"operation": "read",
"contractMethodId": "5fa806d3-e891-43de-b834-38f82dae653e"
},
"credentials": {
"oneShotOAuth2Api": {
"id": "nkfF9AitCKUCrErK",
"name": "1Shot account"
}
},
"typeVersion": 1
},
{
"id": "4d7da856-78f7-4553-a9f7-2a372688712b",
"name": "Configuraciones de Intercambio",
"type": "n8n-nodes-base.code",
"position": [
-240,
160
],
"parameters": {
"jsCode": "const secondsAgo = 120; // the size of your TWAP window\nconst amountDCA = 100000; // amount to swap each time\nconst delegator = \"0x9fead8b19c044c2f404dac38b925ea16adaa2954\"; // your delegated wallet address.\nconst router = \"0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E\"; // the uniswap SwapRouterV2\nconst token0 = \"0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238\"; // token0 of you r target pool\nconst token1 = \"0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14\"; // token1 of your target pool\nconst fee = 500; // the fee of your target pool\n\n$input.first().json.secondsAgo = secondsAgo;\n$input.first().json.amountDCA = amountDCA;\n$input.first().json.delegator = delegator;\n$input.first().json.router = router;\n$input.first().json.token0 = token0;\n$input.first().json.token1 = token1;\n$input.first().json.fee = fee;\n\nreturn $input.all();\n"
},
"typeVersion": 2
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "8c4f8d71-a91f-4481-a83b-17404b73e600",
"connections": {
"6b4d7900-7fae-4c1d-80cf-2900dfb4e299": {
"main": [
[
{
"node": "3147ded9-fc81-4d00-b230-c313c0c3501a",
"type": "main",
"index": 0
}
],
[
{
"node": "8c435f3f-72dc-4102-99bd-13cda1ea27c8",
"type": "main",
"index": 0
}
]
]
},
"4d7da856-78f7-4553-a9f7-2a372688712b": {
"main": [
[
{
"node": "bd180960-416b-48f8-b524-9841428a34e9",
"type": "main",
"index": 0
}
]
]
},
"f43b592f-1203-47b8-9dac-61e8a9d3c9d3": {
"main": [
[
{
"node": "bb585dd2-901b-4bb3-b822-5ddf3ad80be7",
"type": "main",
"index": 0
}
]
]
},
"46184b6b-263e-4e14-9518-2515e2d311a3": {
"main": [
[
{
"node": "f43b592f-1203-47b8-9dac-61e8a9d3c9d3",
"type": "main",
"index": 0
}
]
]
},
"a11c7481-97bf-4cee-8a3f-e604cd09236d": {
"main": [
[
{
"node": "4d7da856-78f7-4553-a9f7-2a372688712b",
"type": "main",
"index": 0
}
]
]
},
"bb585dd2-901b-4bb3-b822-5ddf3ad80be7": {
"main": [
[
{
"node": "6b4d7900-7fae-4c1d-80cf-2900dfb4e299",
"type": "main",
"index": 0
}
],
[
{
"node": "8c435f3f-72dc-4102-99bd-13cda1ea27c8",
"type": "main",
"index": 0
}
]
]
},
"bd180960-416b-48f8-b524-9841428a34e9": {
"main": [
[
{
"node": "46184b6b-263e-4e14-9518-2515e2d311a3",
"type": "main",
"index": 0
}
]
]
},
"3147ded9-fc81-4d00-b230-c313c0c3501a": {
"main": [
[
{
"node": "2edc9432-92f2-4feb-a44a-f6d6f5608db1",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Intermedio - Comercio de criptomonedas, IA Multimodal
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Compartir este flujo de trabajo