diff --git a/.env.example b/.env.example deleted file mode 100644 index fdc35b7..0000000 --- a/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -TEST_COVALENT_API_KEY -PORT = "8080" diff --git a/services/decoder/protocols/uniswap-v2/abis/uniswap-v2.factory.abi.json b/services/decoder/protocols/uniswap-v2/abis/uniswap-v2.factory.abi.json new file mode 100644 index 0000000..68cd3f7 --- /dev/null +++ b/services/decoder/protocols/uniswap-v2/abis/uniswap-v2.factory.abi.json @@ -0,0 +1,193 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "allPairsLength", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPairs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "allPairsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + } + ], + "name": "createPair", + "outputs": [ + { + "internalType": "address", + "name": "pair", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeTo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeToSetter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getPair", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeTo", + "type": "address" + } + ], + "name": "setFeeTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "name": "setFeeToSetter", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/services/decoder/protocols/uniswap-v2/uniswap-v2.configs.ts b/services/decoder/protocols/uniswap-v2/uniswap-v2.configs.ts index 9ef28de..3fd7e78 100644 --- a/services/decoder/protocols/uniswap-v2/uniswap-v2.configs.ts +++ b/services/decoder/protocols/uniswap-v2/uniswap-v2.configs.ts @@ -3,11 +3,28 @@ import { type Configs } from "../../decoder.types"; const configs: Configs = [ { protocol_name: "uniswap-v2", - // ? only for testing address: "0xaf31fd9c3b0350424bf96e551d2d1264d8466205", is_factory: false, chain_name: "eth-mainnet", }, + { + address: "0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc", + is_factory: false, + protocol_name: "uniswap-v2", + chain_name: "eth-mainnet", + }, + { + address: "0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852", + is_factory: false, + protocol_name: "uniswap-v2", + chain_name: "eth-mainnet", + }, + { + address: "0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f", + is_factory: true, + protocol_name: "uniswap-v2", + chain_name: "eth-mainnet", + }, ]; export default configs; diff --git a/services/decoder/protocols/uniswap-v2/uniswap-v2.decoders.ts b/services/decoder/protocols/uniswap-v2/uniswap-v2.decoders.ts index 7fe38fc..256cd40 100644 --- a/services/decoder/protocols/uniswap-v2/uniswap-v2.decoders.ts +++ b/services/decoder/protocols/uniswap-v2/uniswap-v2.decoders.ts @@ -1,9 +1,9 @@ import { prettifyCurrency, type Token } from "@covalenthq/client-sdk"; import { type Abi, decodeEventLog } from "viem"; import { GoldRushDecoder } from "../../decoder"; -import { TimestampParser } from "../../../../utils/functions"; import { type EventType } from "../../decoder.types"; import PairABI from "./abis/uniswap-v2.pair.abi.json"; +import FactoryABI from "./abis/uniswap-v2.factory.abi.json"; import { DECODED_ACTION, DECODED_EVENT_CATEGORY, @@ -16,7 +16,6 @@ GoldRushDecoder.on( async (log_event, tx, chain_name, covalent_client): Promise => { const { sender_address: exchange_contract, - block_signed_at, raw_log_data, raw_log_topics, } = log_event; @@ -41,11 +40,7 @@ GoldRushDecoder.on( let inputToken: Token | null = null, outputToken: Token | null = null, inputValue: bigint = BigInt(0), - outputValue: bigint = BigInt(0), - inputDecimals: number = 0, - outputDecimals: number = 0; - - const prices: number[] = []; + outputValue: bigint = BigInt(0); const { data } = await covalent_client.XykService.getPoolByAddress( chain_name, @@ -70,33 +65,6 @@ GoldRushDecoder.on( decoded.amount0Out, ]; } - - inputDecimals = +inputToken.contract_decimals; - outputDecimals = +outputToken.contract_decimals; - - const date = TimestampParser(block_signed_at, "YYYY-MM-DD"); - const pricesData = await Promise.all( - [inputToken, outputToken].map( - ({ contract_address: token_address }) => - covalent_client.PricingService.getTokenPrices( - chain_name, - "USD", - token_address, - { - from: date, - to: date, - } - ) - ) - ); - pricesData.forEach(({ data }) => { - const price = data?.[0]?.items; - if (price?.[0]?.price) { - prices.push(price[0].price); - } else { - prices.push(1); - } - }); } return { @@ -124,27 +92,33 @@ GoldRushDecoder.on( ticker_logo: inputToken?.logo_url ?? null, ticker_symbol: inputToken?.contract_ticker_symbol ?? null, value: inputValue.toString(), - decimals: inputDecimals, + decimals: +(inputToken?.contract_decimals ?? 18), pretty_quote: prettifyCurrency( inputToken?.quote_rate ?? 0 * (Number(inputValue) / - Math.pow(10, inputDecimals)) + Math.pow( + 10, + +(inputToken?.contract_decimals ?? 18) + )) ), - heading: "Input", + heading: "Token In", }, { ticker_logo: outputToken?.logo_url ?? null, ticker_symbol: outputToken?.contract_ticker_symbol ?? null, value: outputValue.toString(), - decimals: outputDecimals, + decimals: +(outputToken?.contract_decimals ?? 18), pretty_quote: prettifyCurrency( outputToken?.quote_rate ?? 0 * (Number(outputValue) / - Math.pow(10, outputDecimals)) + Math.pow( + 10, + +(outputToken?.contract_decimals ?? 18) + )) ), - heading: "Output", + heading: "Token Out", }, ], }; @@ -176,25 +150,11 @@ GoldRushDecoder.on( }; }; - let token0: Token | null = null, - token1: Token | null = null, - value0: bigint = BigInt(0), - value1: bigint = BigInt(0); - const { data } = await covalent_client.XykService.getPoolByAddress( chain_name, "uniswap_v2", exchange_contract ); - const { token_0, token_1 } = data?.items?.[0]; - if (token_0 && token_1) { - [token0, token1, value0, value1] = [ - token_0, - token_1, - decoded.amount0, - decoded.amount1, - ]; - } return { action: DECODED_ACTION.SWAPPED, @@ -213,36 +173,338 @@ GoldRushDecoder.on( ], tokens: [ { - ticker_logo: token0?.logo_url ?? null, - ticker_symbol: token0?.contract_ticker_symbol ?? null, - value: value0.toString(), - decimals: +(token0?.contract_decimals ?? 18), + ticker_logo: data?.items?.[0]?.token_0?.logo_url ?? null, + ticker_symbol: + data?.items?.[0]?.token_0?.contract_ticker_symbol ?? + null, + value: decoded.amount0.toString(), + decimals: +( + data?.items?.[0]?.token_0?.contract_decimals ?? 18 + ), pretty_quote: prettifyCurrency( - token0?.quote_rate ?? + data?.items?.[0]?.token_0?.quote_rate ?? 0 * - (Number(value0) / + (Number(decoded.amount0) / Math.pow( 10, - +(token0?.contract_decimals ?? 18) + +( + data?.items?.[0]?.token_0 + ?.contract_decimals ?? 18 + ) )) ), - heading: "Input", + heading: "Token 0 Deposited", }, { - ticker_logo: token1?.logo_url ?? null, - ticker_symbol: token1?.contract_ticker_symbol ?? null, - value: value1.toString(), - decimals: +(token1?.contract_decimals ?? 18), + ticker_logo: data?.items?.[0]?.token_1?.logo_url ?? null, + ticker_symbol: + data?.items?.[0]?.token_1?.contract_ticker_symbol ?? + null, + value: decoded.amount1.toString(), + decimals: +( + data?.items?.[0]?.token_1?.contract_decimals ?? 18 + ), pretty_quote: prettifyCurrency( - token1?.quote_rate ?? + data?.items?.[0]?.token_1?.quote_rate ?? 0 * - (Number(value1) / + (Number(decoded.amount1) / Math.pow( 10, - +(token1?.contract_decimals ?? 18) + +( + data?.items?.[0]?.token_1 + ?.contract_decimals ?? 18 + ) )) ), - heading: "Output", + heading: "Token 1 Deposited", + }, + ], + }; + } +); + +GoldRushDecoder.on( + "uniswap-v2:Burn", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { + sender_address: exchange_contract, + raw_log_data, + raw_log_topics, + } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as "0x${string}", + eventName: "Burn", + }) as { + eventName: "Burn"; + args: { + sender: string; + amount0: bigint; + amount1: bigint; + to: string; + }; + }; + + const { data } = await covalent_client.XykService.getPoolByAddress( + chain_name, + "uniswap_v2", + exchange_contract + ); + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Burn", + protocol: { + logo: log_event.sender_logo_url as string, + name: log_event.sender_name as string, + }, + details: [ + { + heading: "Sender", + value: decoded.sender, + type: "address", + }, + { + heading: "To", + value: decoded.to, + type: "address", + }, + ], + tokens: [ + { + ticker_logo: data?.items?.[0]?.token_0?.logo_url ?? null, + ticker_symbol: + data?.items?.[0]?.token_0?.contract_ticker_symbol ?? + null, + value: decoded.amount0.toString(), + decimals: +( + data?.items?.[0]?.token_0?.contract_decimals ?? 18 + ), + pretty_quote: prettifyCurrency( + data?.items?.[0]?.token_0?.quote_rate ?? + 0 * + (Number(decoded.amount0) / + Math.pow( + 10, + +( + data?.items?.[0]?.token_0 + ?.contract_decimals ?? 18 + ) + )) + ), + heading: "Token 0 Redeemed", + }, + { + ticker_logo: data?.items?.[0]?.token_1?.logo_url ?? null, + ticker_symbol: + data?.items?.[0]?.token_1?.contract_ticker_symbol ?? + null, + value: decoded.amount1.toString(), + decimals: +( + data?.items?.[0]?.token_1?.contract_decimals ?? 18 + ), + pretty_quote: prettifyCurrency( + data?.items?.[0]?.token_1?.quote_rate ?? + 0 * + (Number(decoded.amount1) / + Math.pow( + 10, + +( + data?.items?.[0]?.token_1 + ?.contract_decimals ?? 18 + ) + )) + ), + heading: "Token 1 Redeemed", + }, + ], + }; + } +); + +GoldRushDecoder.on( + "uniswap-v2:Sync", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { + sender_address: exchange_contract, + raw_log_data, + raw_log_topics, + } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as "0x${string}", + eventName: "Sync", + }) as { + eventName: "Sync"; + args: { + reserve0: bigint; + reserve1: bigint; + }; + }; + + const { data } = await covalent_client.XykService.getPoolByAddress( + chain_name, + "uniswap_v2", + exchange_contract + ); + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Sync", + protocol: { + logo: log_event.sender_logo_url as string, + name: log_event.sender_name as string, + }, + tokens: [ + { + ticker_logo: data?.items?.[0]?.token_0?.logo_url ?? null, + ticker_symbol: + data?.items?.[0]?.token_0?.contract_ticker_symbol ?? + null, + value: decoded.reserve0.toString(), + decimals: +( + data?.items?.[0]?.token_0?.contract_decimals ?? 18 + ), + pretty_quote: prettifyCurrency( + data?.items?.[0]?.token_0?.quote_rate ?? + 0 * + (Number(decoded.reserve0) / + Math.pow( + 10, + +( + data?.items?.[0]?.token_0 + ?.contract_decimals ?? 18 + ) + )) + ), + heading: "Reserve 0", + }, + { + ticker_logo: data?.items?.[0]?.token_1?.logo_url ?? null, + ticker_symbol: + data?.items?.[0]?.token_1?.contract_ticker_symbol ?? + null, + value: decoded.reserve1.toString(), + decimals: +( + data?.items?.[0]?.token_1?.contract_decimals ?? 18 + ), + pretty_quote: prettifyCurrency( + data?.items?.[0]?.token_1?.quote_rate ?? + 0 * + (Number(decoded.reserve1) / + Math.pow( + 10, + +( + data?.items?.[0]?.token_1 + ?.contract_decimals ?? 18 + ) + )) + ), + heading: "Reserve 1", + }, + ], + }; + } +); + +GoldRushDecoder.on( + "uniswap-v2:PairCreated", + ["eth-mainnet"], + FactoryABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: FactoryABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "PairCreated", + strict: false, + }) as { + eventName: "PairCreated"; + args: { + token0: string; + token1: string; + pair: string; + allPairsLength: bigint; + }; + }; + + const { data } = await covalent_client.XykService.getPoolByAddress( + chain_name, + "uniswap_v2", + decoded.pair + ); + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Pair Created", + protocol: { + logo: log_event.sender_logo_url as string, + name: log_event.sender_name as string, + }, + details: [ + { + heading: "Token 0 Name", + value: data?.items?.[0]?.token_0?.contract_name || "", + type: "text", + }, + { + heading: "Token 0 Ticker Symbol", + value: + data?.items?.[0]?.token_0?.contract_ticker_symbol || "", + type: "text", + }, + { + heading: "Token 0 Decimals", + value: ( + data?.items?.[0]?.token_0?.contract_decimals ?? 18 + ).toString(), + type: "text", + }, + { + heading: "Token 0 Address", + value: data?.items?.[0]?.token_0?.contract_address || "", + type: "address", + }, + { + heading: "Token 1 Name", + value: data?.items?.[0]?.token_1?.contract_name || "", + type: "text", + }, + { + heading: "Token 1 Ticker Symbol", + value: + data?.items?.[0]?.token_1?.contract_ticker_symbol || "", + type: "text", + }, + { + heading: "Token 1 Decimals", + value: ( + data?.items?.[0]?.token_1?.contract_decimals ?? 18 + ).toString(), + type: "text", + }, + { + heading: "Token 1 Address", + value: data?.items?.[0]?.token_1?.contract_address || "", + type: "address", + }, + { + heading: "Pair Address", + value: decoded.pair, + type: "address", }, ], }; diff --git a/services/decoder/protocols/uniswap-v2/uniswap-v2.test.ts b/services/decoder/protocols/uniswap-v2/uniswap-v2.test.ts index ca76043..b636eaa 100644 --- a/services/decoder/protocols/uniswap-v2/uniswap-v2.test.ts +++ b/services/decoder/protocols/uniswap-v2/uniswap-v2.test.ts @@ -42,4 +42,61 @@ describe("uniswap-v2", () => { expect(event.tokens?.length).toEqual(2); expect(event.details?.length).toEqual(1); }); + + test("eth-mainnet:Burn", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ + "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY, + }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0xf419cd1a89b928cb93f38237e9b1e6743218fbb87aaac678cb1f950951b7476e", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Burn"); + if (!event) { + throw Error("Event not found"); + } + expect(event.tokens?.length).toEqual(2); + }); + + test("eth-mainnet:Sync", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ + "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY, + }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0xf419cd1a89b928cb93f38237e9b1e6743218fbb87aaac678cb1f950951b7476e", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Sync"); + if (!event) { + throw Error("Event not found"); + } + expect(event.tokens?.length).toEqual(2); + }); + + test("eth-mainnet:PairCreated", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ + "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY, + }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0x9584cdf7d99a22e18843cf26c484018bfb11ab4ce4f2d898ec69075ed8e3c8dc", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Pair Created"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(9); + }); }); diff --git a/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.NonfungiblePositionManager.abi.json b/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.NonfungiblePositionManager.abi.json new file mode 100644 index 0000000..ab6e387 --- /dev/null +++ b/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.NonfungiblePositionManager.abi.json @@ -0,0 +1,1221 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_WETH9", + "type": "address" + }, + { + "internalType": "address", + "name": "_tokenDescriptor_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "DecreaseLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "IncreaseLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WETH9", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Max", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Max", + "type": "uint128" + } + ], + "internalType": "struct INonfungiblePositionManager.CollectParams", + "name": "params", + "type": "tuple" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "createAndInitializePoolIfNecessary", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct INonfungiblePositionManager.DecreaseLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "decreaseLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct INonfungiblePositionManager.IncreaseLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "increaseLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint256", + "name": "amount0Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct INonfungiblePositionManager.MintParams", + "name": "params", + "type": "tuple" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "refundETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermitAllowed", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermitAllowedIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "selfPermitIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Owed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Owed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "uniswapV3MintCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "unwrapWETH9", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.factory.abi.json b/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.factory.abi.json new file mode 100644 index 0000000..6ea719a --- /dev/null +++ b/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.factory.abi.json @@ -0,0 +1,236 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "FeeAmountEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "createPool", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "enableFeeAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "feeAmountTickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "getPool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "parameters", + "outputs": [ + { + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.pair.abi.json b/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.pair.abi.json new file mode 100644 index 0000000..a9d0f5c --- /dev/null +++ b/services/decoder/protocols/uniswap-v3/abis/uniswap-v3.pair.abi.json @@ -0,0 +1,988 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "CollectProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid1", + "type": "uint256" + } + ], + "name": "Flash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextOld", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextNew", + "type": "uint16" + } + ], + "name": "IncreaseObservationCardinalityNext", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0New", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1New", + "type": "uint8" + } + ], + "name": "SetFeeProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount1", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collectProtocol", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal0X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal1X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "flash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + } + ], + "name": "increaseObservationCardinalityNext", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidityPerTick", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "observations", + "outputs": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32[]", + "name": "secondsAgos", + "type": "uint32[]" + } + ], + "name": "observe", + "outputs": [ + { + "internalType": "int56[]", + "name": "tickCumulatives", + "type": "int56[]" + }, + { + "internalType": "uint160[]", + "name": "secondsPerLiquidityCumulativeX128s", + "type": "uint160[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFees", + "outputs": [ + { + "internalType": "uint128", + "name": "token0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "token1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feeProtocol0", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "feeProtocol1", + "type": "uint8" + } + ], + "name": "setFeeProtocol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "snapshotCumulativesInside", + "outputs": [ + { + "internalType": "int56", + "name": "tickCumulativeInside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityInsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsInside", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "internalType": "int256", + "name": "amount1", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int16", + "name": "", + "type": "int16" + } + ], + "name": "tickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "name": "ticks", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/services/decoder/protocols/uniswap-v3/uniswap-v3.configs.ts b/services/decoder/protocols/uniswap-v3/uniswap-v3.configs.ts new file mode 100644 index 0000000..e4d2b1a --- /dev/null +++ b/services/decoder/protocols/uniswap-v3/uniswap-v3.configs.ts @@ -0,0 +1,24 @@ +import { type Configs } from "../../decoder.types"; + +const configs: Configs = [ + { + address: "0x1f98431c8ad98523631ae4a59f267346ea31f984", + is_factory: true, + protocol_name: "uniswap-v3", + chain_name: "eth-mainnet", + }, + { + address: "0xcbcdf9626bc03e24f779434178a73a0b4bad62ed", + is_factory: false, + protocol_name: "uniswap-v3", + chain_name: "eth-mainnet", + }, + { + address: "0xc36442b4a4522e871399cd717abdd847ab11fe88", + is_factory: false, + protocol_name: "uniswap-v3", + chain_name: "eth-mainnet", + }, +]; + +export default configs; diff --git a/services/decoder/protocols/uniswap-v3/uniswap-v3.decoders.ts b/services/decoder/protocols/uniswap-v3/uniswap-v3.decoders.ts new file mode 100644 index 0000000..8b19a3d --- /dev/null +++ b/services/decoder/protocols/uniswap-v3/uniswap-v3.decoders.ts @@ -0,0 +1,658 @@ +import { GoldRushDecoder } from "../../decoder"; +import type { EventDetails, EventTokens } from "../../decoder.types"; +import { type EventType } from "../../decoder.types"; +import { + DECODED_ACTION, + DECODED_EVENT_CATEGORY, +} from "../../decoder.constants"; +import { decodeEventLog, type Abi } from "viem"; +import FactoryABI from "./abis/uniswap-v3.factory.abi.json"; +import PairABI from "./abis/uniswap-v3.pair.abi.json"; +import PositionManagerABI from "./abis/uniswap-v3.NonfungiblePositionManager.abi.json"; +import { TimestampParser } from "../../../../utils/functions"; + +GoldRushDecoder.on( + "uniswap-v3:PoolCreated", + ["eth-mainnet"], + FactoryABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: FactoryABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "PoolCreated", + }) as { + eventName: "PoolCreated"; + args: { + token0: string; + token1: string; + fee: bigint; + tickSpacing: bigint; + pool: string; + }; + }; + + const details: EventDetails = [ + { + heading: "Token 0", + value: decoded.token0, + type: "address", + }, + { + heading: "Token 1", + value: decoded.token0, + type: "address", + }, + { + heading: "Fee", + value: + new Intl.NumberFormat().format(Number(decoded.fee) / 1e4) + + "%", + type: "text", + }, + { + heading: "Tick Spacing", + value: decoded.tickSpacing.toString(), + type: "text", + }, + { + heading: "Pool", + value: decoded.pool, + type: "address", + }, + ]; + + const date = TimestampParser(tx.block_signed_at, "YYYY-MM-DD"); + + const { data: Token0 } = + await covalent_client.PricingService.getTokenPrices( + chain_name, + "USD", + decoded.token0, + { + from: date, + to: date, + } + ); + + const { data: Token1 } = + await covalent_client.PricingService.getTokenPrices( + chain_name, + "USD", + decoded.token1, + { + from: date, + to: date, + } + ); + + const tokens: EventTokens = [ + { + heading: "Token 0 Information", + value: "0", + decimals: Token0?.[0]?.contract_decimals, + ticker_symbol: Token0?.[0]?.contract_ticker_symbol, + ticker_logo: Token0?.[0]?.logo_url, + pretty_quote: Token0?.[0]?.prices?.[0]?.pretty_price, + }, + { + heading: "Token 1 Information", + value: "0", + decimals: Token1?.[0]?.contract_decimals, + ticker_symbol: Token1?.[0]?.contract_ticker_symbol, + ticker_logo: Token1?.[0]?.logo_url, + pretty_quote: Token1?.[0]?.prices?.[0]?.pretty_price, + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Pool Created", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + tokens, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:Burn", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "Burn", + }) as { + eventName: "Burn"; + args: { + owner: string; + tickLower: bigint; + tickUpper: bigint; + amount: bigint; + amount0: bigint; + amount1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Owner", + value: decoded.owner, + type: "address", + }, + { + heading: "Tick Lower", + value: decoded.tickLower.toString(), + type: "text", + }, + { + heading: "Tick Upper", + value: decoded.tickUpper.toString(), + type: "text", + }, + { + heading: "Amount", + value: decoded.amount.toString(), + type: "text", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Burn", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:Mint", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "Mint", + }) as { + eventName: "Mint"; + args: { + sender: string; + owner: string; + tickLower: bigint; + tickUpper: bigint; + amount: bigint; + amount0: bigint; + amount1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Sender", + value: decoded.sender, + type: "address", + }, + { + heading: "Owner", + value: decoded.owner, + type: "address", + }, + { + heading: "Tick Lower", + value: decoded.tickLower.toString(), + type: "text", + }, + { + heading: "Tick Upper", + value: decoded.tickUpper.toString(), + type: "text", + }, + { + heading: "Amount", + value: decoded.amount.toString(), + type: "text", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Mint", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:Swap", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "Swap", + }) as { + eventName: "Swap"; + args: { + sender: string; + recipient: string; + amount0: bigint; + amount1: bigint; + sqrtPriceX96: bigint; + liquidity: bigint; + tick: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Sender", + value: decoded.sender, + type: "address", + }, + { + heading: "Recipient", + value: decoded.recipient, + type: "address", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + { + heading: "sqrtPriceX96", + value: decoded.sqrtPriceX96.toString(), + type: "text", + }, + { + heading: "Liquidity", + value: decoded.liquidity.toString(), + type: "text", + }, + { + heading: "Tick", + value: decoded.tick.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Swap", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:Collect", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "Collect", + }) as { + eventName: "Collect"; + args: { + owner: string; + recipient: string; + tickLower: bigint; + tickUpper: bigint; + amount0: bigint; + amount1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Owner", + value: decoded.owner, + type: "address", + }, + { + heading: "Recipient", + value: decoded.recipient, + type: "address", + }, + { + heading: "Tick Lower", + value: decoded.tickLower.toString(), + type: "text", + }, + { + heading: "Tick Upper", + value: decoded.tickUpper.toString(), + type: "text", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Collect Fees", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:Flash", + ["eth-mainnet"], + PairABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PairABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "Flash", + }) as { + eventName: "Flash"; + args: { + sender: string; + recipient: string; + amount0: bigint; + amount1: bigint; + paid0: bigint; + paid1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Sender", + value: decoded.sender, + type: "address", + }, + { + heading: "Recipient", + value: decoded.recipient, + type: "address", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + { + heading: "Paid 0", + value: decoded.paid0.toString(), + type: "text", + }, + { + heading: "Paid 1", + value: decoded.paid1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Flash Loan", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:DecreaseLiquidity", + ["eth-mainnet"], + PositionManagerABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PositionManagerABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "DecreaseLiquidity", + }) as { + eventName: "DecreaseLiquidity"; + args: { + tokenId: bigint; + liquidity: bigint; + amount0: bigint; + amount1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Token ID", + value: decoded.tokenId.toString(), + type: "text", + }, + { + heading: "Liquidity", + value: decoded.liquidity.toString(), + type: "text", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Decrease Liquidity", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:IncreaseLiquidity", + ["eth-mainnet"], + PositionManagerABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PositionManagerABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "IncreaseLiquidity", + }) as { + eventName: "IncreaseLiquidity"; + args: { + tokenId: bigint; + liquidity: bigint; + amount0: bigint; + amount1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Token ID", + value: decoded.tokenId.toString(), + type: "text", + }, + { + heading: "Liquidity", + value: decoded.liquidity.toString(), + type: "text", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Increase Liquidity", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); + +GoldRushDecoder.on( + "uniswap-v3:Collect", + ["eth-mainnet"], + PositionManagerABI as Abi, + async (log_event, tx, chain_name, covalent_client): Promise => { + const { raw_log_data, raw_log_topics } = log_event; + + const { args: decoded } = decodeEventLog({ + abi: PositionManagerABI, + topics: raw_log_topics as [], + data: raw_log_data as `0x${string}`, + eventName: "Collect", + }) as { + eventName: "Collect"; + args: { + tokenId: bigint; + recipient: string; + amount0: bigint; + amount1: bigint; + }; + }; + + const details: EventDetails = [ + { + heading: "Token ID", + value: decoded.tokenId.toString(), + type: "text", + }, + { + heading: "Recipient", + value: decoded.recipient, + type: "address", + }, + { + heading: "Amount 0", + value: decoded.amount0.toString(), + type: "text", + }, + { + heading: "Amount 1", + value: decoded.amount1.toString(), + type: "text", + }, + ]; + + return { + action: DECODED_ACTION.SWAPPED, + category: DECODED_EVENT_CATEGORY.DEX, + name: "Collect", + protocol: { + logo: log_event.sender_logo_url as string, + name: "Uniswap V3", + }, + details, + }; + } +); diff --git a/services/decoder/protocols/uniswap-v3/uniswap-v3.test.ts b/services/decoder/protocols/uniswap-v3/uniswap-v3.test.ts new file mode 100644 index 0000000..885bb87 --- /dev/null +++ b/services/decoder/protocols/uniswap-v3/uniswap-v3.test.ts @@ -0,0 +1,142 @@ +import request from "supertest"; +import app from "../../../../api"; +import { type EventType } from "../../decoder.types"; + +describe("uniswap-v3", () => { + test("eth-mainnet:PoolCreated", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0xf87d91f3d72a8e912c020c2e316151f3557b1217b44d4f6b6bec126448318530", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Pool Created"); + if (!event) { + throw Error("Event not found"); + } + expect(event.tokens?.length).toEqual(2); + expect(event.details?.length).toEqual(5); + }); + + test("eth-mainnet:Burn", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0x3d1748ea19a9c6c3b7690652fca03c54f6636f1403b9df25e4965ddfa765f06c", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Burn"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(6); + }); + + test("eth-mainnet:Mint", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0x509ffb3e2e1338991b27284d6365a93bdf36ac50a9a89e6260b5f791bf0e50e6", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Mint"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(7); + }); + + test("eth-mainnet:Swap", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0xf0c18bcdeb3b167a7323499307b6a18031450bf955cf9ec1153231f97898f391", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Swap"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(7); + }); + + test("eth-mainnet:Collect", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0x7c927bbab8a2f60f0a36ee9425c03db556a44c87dddf855d5641f5f1c2270ebd", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Collect Fees"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(6); + }); + + test("eth-mainnet:Flash", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0xe3fcabe33a5ebf9ed6450f11b907da4a5d72f2e58917e8b2ae20fb259be385d4", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Flash Loan"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(6); + }); + + test("eth-mainnet:DecreaseLiquidity", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0x3d1748ea19a9c6c3b7690652fca03c54f6636f1403b9df25e4965ddfa765f06c", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Decrease Liquidity"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(4); + }); + + test("eth-mainnet:IncreaseLiquidity", async () => { + const res = await request(app) + .post("/api/v1/tx/decode") + .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY }) + .send({ + chain_name: "eth-mainnet", + tx_hash: + "0x509ffb3e2e1338991b27284d6365a93bdf36ac50a9a89e6260b5f791bf0e50e6", + }); + const { events } = res.body as { events: EventType[] }; + const event = events.find(({ name }) => name === "Increase Liquidity"); + if (!event) { + throw Error("Event not found"); + } + expect(event.details?.length).toEqual(4); + }); +});