diff --git a/Osmosis/osmosis-starter/docker-compose.yml b/Osmosis/osmosis-starter/docker-compose.yml index b873283d9..047ce9724 100644 --- a/Osmosis/osmosis-starter/docker-compose.yml +++ b/Osmosis/osmosis-starter/docker-compose.yml @@ -35,8 +35,8 @@ services: - ${SUB_COMMAND:-} # set SUB_COMMAND env variable to "test" to run tests - -f=/app - --db-schema=app - - --workers=4 - - --batch-size=30 + - --workers=1 + - --batch-size=2 - --unfinalized-blocks=true healthcheck: test: ["CMD", "curl", "-f", "http://subquery-node:3000/ready"] diff --git a/Osmosis/osmosis-starter/package.json b/Osmosis/osmosis-starter/package.json index cecafc126..2c4082c8e 100644 --- a/Osmosis/osmosis-starter/package.json +++ b/Osmosis/osmosis-starter/package.json @@ -1,5 +1,5 @@ { - "name": "Osmosis", + "name": "osmosis-starter", "version": "0.0.1", "description": "This project can be use as a starting point for developing your Cosmos (Osmosis) based SubQuery project. This Cosmos Example Project indexes all swaps on Osmosis' on chain DEX", "main": "dist/index.js", diff --git a/Osmosis/osmosis-starter/project.ts b/Osmosis/osmosis-starter/project.ts index 5090cdf9a..a5ba42a03 100644 --- a/Osmosis/osmosis-starter/project.ts +++ b/Osmosis/osmosis-starter/project.ts @@ -10,7 +10,7 @@ const project: CosmosProject = { version: "0.0.1", name: "osmosis-starter", description: - "This project can be use as a starting point for developing your Cosmos osmosis based SubQuery project", + "This project can be use as a starting point for developing your Cosmos Osmosis based SubQuery project. It indexes all swaps in the Osmosis DEX", runner: { node: { name: "@subql/node-cosmos", @@ -35,27 +35,29 @@ const project: CosmosProject = { * If you use a rate limited endpoint, adjust the --batch-size and --workers parameters * These settings can be found in your docker-compose.yaml, they will slow indexing but prevent your project being rate limited */ - endpoint: ["https://osmosis.api.onfinality.io/public"], + endpoint: ["https://rpc.osmosis.zone:443"], chaintypes: new Map([ [ "osmosis.gamm.v1beta1", { file: "./proto/osmosis/gamm/v1beta1/tx.proto", - messages: ["MsgSwapExactAmountIn"], + messages: [ + "MsgSwapExactAmountIn", + "MsgSwapExactAmountOut", + "MsgJoinSwapShareAmountOut", + ], }, ], [ " osmosis.poolmanager.v1beta1", { - // needed by MsgSwapExactAmountIn file: "./proto/osmosis/poolmanager/v1beta1/swap_route.proto", - messages: ["SwapAmountInRoute"], + messages: ["SwapAmountOutRoute", "SwapAmountInRoute"], }, ], [ "cosmos.base.v1beta1", { - // needed by MsgSwapExactAmountIn file: "./proto/cosmos/base/v1beta1/coin.proto", messages: ["Coin"], }, @@ -65,17 +67,31 @@ const project: CosmosProject = { dataSources: [ { kind: CosmosDatasourceKind.Runtime, - startBlock: 9798050, + startBlock: 12678493, mapping: { file: "./dist/index.js", handlers: [ { - handler: "handleMessage", + handler: "handleMsgSwapExactAmountIn", kind: CosmosHandlerKind.Message, filter: { type: "/osmosis.gamm.v1beta1.MsgSwapExactAmountIn", }, }, + { + handler: "handleMsgSwapExactAmountOut", + kind: CosmosHandlerKind.Message, + filter: { + type: "/osmosis.gamm.v1beta1.MsgSwapExactAmountOut", + }, + }, + { + handler: "handleMsgJoinSwapShareAmountOut", + kind: CosmosHandlerKind.Message, + filter: { + type: "/osmosis.gamm.v1beta1.MsgJoinSwapShareAmountOut", + }, + }, ], }, }, diff --git a/Osmosis/osmosis-starter/schema.graphql b/Osmosis/osmosis-starter/schema.graphql index 2fec17099..0c0b5d165 100644 --- a/Osmosis/osmosis-starter/schema.graphql +++ b/Osmosis/osmosis-starter/schema.graphql @@ -2,25 +2,31 @@ # Add the `@index` or `@index(unique: true)` annotation after any non-key field # https://academy.subquery.network/build/graphql.html#indexing-by-non-primary-key-field -# type Block @entity { -# id: ID! # The block hash -# height: BigInt! -# } +enum Direction { + IN + OUT +} -# type Transaction @entity { -# id: ID! -# blockHeight: BigInt! -# timestamp: String! -# } +enum Message { + MsgSwapExactAmountIn + MsgSwapExactAmountOut + MsgJoinSwapShareAmountOut +} type Swap @entity { id: ID! sender: String! + direction: Direction! txHash: String! blockHeight: BigInt! + date: Date! + message: Message! tokenInDenom: String tokenInAmount: BigInt - tokenOutMin: BigInt! + tokenOutMin: BigInt + tokenOutDenom: String + tokenOutAmount: BigInt + tokenInMax: BigInt swapRoutes: [SwapRoute] @derivedFrom(field: "swap") #This is virtual field } @@ -29,10 +35,12 @@ type SwapRoute @entity { pool: Pool! swap: Swap! tokenInDenom: String - tokenOutDenom: String! + tokenOutDenom: String } type Pool @entity { id: ID! + createdBlockHeight: BigInt! + created: Date! swapRoutes: [SwapRoute] @derivedFrom(field: "pool") #This is virtual field } diff --git a/Osmosis/osmosis-starter/src/mappings/mappingHandlers.ts b/Osmosis/osmosis-starter/src/mappings/mappingHandlers.ts index 8deaf96e8..303c3a70c 100644 --- a/Osmosis/osmosis-starter/src/mappings/mappingHandlers.ts +++ b/Osmosis/osmosis-starter/src/mappings/mappingHandlers.ts @@ -1,54 +1,142 @@ -import { MsgSwapExactAmountInMessage } from "../types/CosmosMessageTypes"; -import { Pool, Swap, SwapRoute } from "../types"; +import { + MsgSwapExactAmountInMessage, + MsgSwapExactAmountOutMessage, + MsgJoinSwapShareAmountOutMessage, +} from "../types/CosmosMessageTypes"; +import { Pool, Swap, SwapRoute, Direction, Message } from "../types"; +import { CosmosBlock } from "@subql/types-cosmos"; -async function checkGetPool(id: string): Promise { +async function checkGetPool(id: string, block: CosmosBlock): Promise { // Check that the pool exists and create new ones if now let pool = await Pool.get(id); if (!pool) { - pool = new Pool(id); + pool = Pool.create({ + id, + createdBlockHeight: BigInt(block.header.height), + created: new Date(block.header.time.toISOString()), + }); await pool.save(); } return pool; } -export async function handleMessage( - msg: MsgSwapExactAmountInMessage -): Promise { - // You can see an example record here https://www.mintscan.io/osmosis/txs/6A22C6C978A96D99FCB08826807C6EB1DCBDCEC6044C35105B624A81A1CB6E24?height=9798771 - logger.info(`New Swap Message received at block ${msg.block.header.height}`); - // logger.info(JSON.stringify(msg.tx.tx.events)); // You can use this to preview the data - - // We first create a new swap record - const swap = Swap.create({ +function createSwap( + msg: + | MsgSwapExactAmountInMessage + | MsgSwapExactAmountOutMessage + | MsgJoinSwapShareAmountOutMessage, + direction: Direction, + message: Message +): Swap { + return Swap.create({ id: `${msg.tx.hash}-${msg.idx}`, txHash: msg.tx.hash, - blockHeight: BigInt(msg.block.block.header.height), + blockHeight: BigInt(msg.block.header.height), sender: msg.msg.decodedMsg.sender, - tokenInDenom: msg.msg.decodedMsg.tokenIn?.denom, - tokenInAmount: msg.msg.decodedMsg.tokenIn - ? BigInt(msg.msg.decodedMsg.tokenIn.amount) - : undefined, - tokenOutMin: BigInt(msg.msg.decodedMsg.tokenOutMinAmount), + direction, + message, + date: new Date(msg.block.header.time.toISOString()), }); +} + +export async function handleMsgSwapExactAmountIn( + msg: MsgSwapExactAmountInMessage +): Promise { + logger.info( + `Processing MsgSwapExactAmountIn at block ${msg.block.header.height.toString()}` + ); + // We first create a new swap record + const swap = createSwap(msg, Direction.IN, Message.MsgSwapExactAmountIn); + swap.tokenInDenom = msg.msg.decodedMsg.tokenIn?.denom; + swap.tokenInAmount = msg.msg.decodedMsg.tokenIn + ? BigInt(msg.msg.decodedMsg.tokenIn.amount) + : undefined; + swap.tokenOutMin = BigInt(msg.msg.decodedMsg.tokenOutMinAmount); // Save this to the DB await swap.save(); // Create swap routes from the array on the message - let lastTokenOutDenom = swap.tokenInDenom; + let currentTokenInDenom = swap.tokenInDenom; for (const route of msg.msg.decodedMsg.routes) { const index = msg.msg.decodedMsg.routes.indexOf(route); // Check that the pool aready exists - const pool = await checkGetPool(route.poolId.toString()); + const pool = await checkGetPool(route.poolId.toString(), msg.block); const swapRoute = SwapRoute.create({ id: `${msg.tx.hash}-${msg.idx}-${index}`, poolId: pool.id, swapId: swap.id, - tokenInDenom: lastTokenOutDenom, + tokenInDenom: currentTokenInDenom, tokenOutDenom: route.tokenOutDenom, }); - lastTokenOutDenom = route.tokenOutDenom; + currentTokenInDenom = route.tokenOutDenom; + await swapRoute.save(); + } +} + +export async function handleMsgSwapExactAmountOut( + msg: MsgSwapExactAmountOutMessage +): Promise { + logger.info( + `Processing MsgSwapExactAmountOut at block ${msg.block.header.height.toString()}` + ); + // We first create a new swap record + const swap = createSwap(msg, Direction.OUT, Message.MsgSwapExactAmountOut); + swap.tokenOutDenom = msg.msg.decodedMsg.tokenOut.denom; + swap.tokenOutAmount = msg.msg.decodedMsg.tokenOut + ? BigInt(msg.msg.decodedMsg.tokenOut.amount) + : undefined; + swap.tokenInMax = BigInt(msg.msg.decodedMsg.tokenInMaxAmount); + + // Save this to the DB + await swap.save(); + + // Create swap routes from the array on the message + let currentTokenOutDenom = swap.tokenOutDenom; + for (const route of msg.msg.decodedMsg.routes) { + const index = msg.msg.decodedMsg.routes.indexOf(route); + // Check that the pool aready exists + const pool = await checkGetPool(route.poolId.toString(), msg.block); + + const swapRoute = SwapRoute.create({ + id: `${msg.tx.hash}-${msg.idx}-${index}`, + poolId: pool.id, + swapId: swap.id, + tokenInDenom: route.tokenInDenom, + tokenOutDenom: currentTokenOutDenom, + }); + currentTokenOutDenom = route.tokenInDenom; await swapRoute.save(); } } + +export async function handleMsgJoinSwapShareAmountOut( + msg: MsgJoinSwapShareAmountOutMessage +): Promise { + logger.info( + `Processing MsgJoinSwapShareAmountOut at block ${msg.block.header.height.toString()}` + ); + // We first create a new swap record + const swap = createSwap(msg, Direction.IN, Message.MsgJoinSwapShareAmountOut); + swap.tokenInDenom = msg.msg.decodedMsg.tokenInDenom; + swap.tokenInMax = BigInt(msg.msg.decodedMsg.tokenInMaxAmount); + swap.tokenOutAmount = BigInt(msg.msg.decodedMsg.shareOutAmount); + + // Save this to the DB + await swap.save(); + + // Create swap routes from the array on the message + const pool = await checkGetPool( + msg.msg.decodedMsg.poolId.toString(), + msg.block + ); + + const swapRoute = SwapRoute.create({ + id: `${msg.tx.hash}-${msg.idx}`, + poolId: pool.id, + swapId: swap.id, + tokenInDenom: msg.msg.decodedMsg.tokenInDenom, + }); + await swapRoute.save(); +}