From 0a04d7e1a7d99e1f22c155248eac683a8a615143 Mon Sep 17 00:00:00 2001 From: 0xChqrles Date: Tue, 1 Oct 2024 16:30:01 +0200 Subject: [PATCH] implement liquidity addition indexing --- .gitignore | 1 - .vscode/settings.json | 3 - backend/drizzle/0002_fixed_whirlwind.sql | 7 + backend/drizzle/0003_silent_namor.sql | 8 + backend/drizzle/meta/0002_snapshot.json | 502 ++++++++++++++++ backend/drizzle/meta/0003_snapshot.json | 536 ++++++++++++++++++ backend/drizzle/meta/_journal.json | 14 + backend/package.json | 3 +- backend/src/db/int8range.ts | 49 ++ backend/src/db/schema.ts | 136 +---- backend/yarn.lock | 26 +- indexers/.env.example | 2 - indexers/.gitignore | 176 +----- indexers/Dockerfile | 14 + indexers/deno.json | 19 - indexers/docker-compose.yml | 66 --- indexers/src/escrow-locked.indexer.ts | 56 -- indexers/src/escrow-unlocked.indexer.ts | 57 -- indexers/src/liquidity-addition.ts | 95 ++++ .../src/revolut-liquidity-added.indexer.ts | 60 -- .../src/revolut-liquidity-locked.indexer.ts | 53 -- .../revolut-liquidity-retrieved.indexer.ts | 60 -- indexers/src/utils/constants.ts | 34 +- indexers/src/utils/deps.ts | 2 +- indexers/src/utils/helpers.ts | 27 +- indexers/src/utils/types.ts | 11 + 26 files changed, 1331 insertions(+), 686 deletions(-) delete mode 100644 .gitignore delete mode 100644 .vscode/settings.json create mode 100644 backend/drizzle/0002_fixed_whirlwind.sql create mode 100644 backend/drizzle/0003_silent_namor.sql create mode 100644 backend/drizzle/meta/0002_snapshot.json create mode 100644 backend/drizzle/meta/0003_snapshot.json create mode 100644 backend/src/db/int8range.ts delete mode 100644 indexers/.env.example create mode 100644 indexers/Dockerfile delete mode 100644 indexers/deno.json delete mode 100644 indexers/docker-compose.yml delete mode 100644 indexers/src/escrow-locked.indexer.ts delete mode 100644 indexers/src/escrow-unlocked.indexer.ts create mode 100644 indexers/src/liquidity-addition.ts delete mode 100644 indexers/src/revolut-liquidity-added.indexer.ts delete mode 100644 indexers/src/revolut-liquidity-locked.indexer.ts delete mode 100644 indexers/src/revolut-liquidity-retrieved.indexer.ts create mode 100644 indexers/src/utils/types.ts diff --git a/.gitignore b/.gitignore deleted file mode 100644 index eb5a316..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 4ae9f3f..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "deno.enablePaths": ["indexers"] -} diff --git a/backend/drizzle/0002_fixed_whirlwind.sql b/backend/drizzle/0002_fixed_whirlwind.sql new file mode 100644 index 0000000..c4ed7a8 --- /dev/null +++ b/backend/drizzle/0002_fixed_whirlwind.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS "registration" ( + "address" text PRIMARY KEY NOT NULL, + "revolut" text[] DEFAULT '{}'::text[] NOT NULL +); +--> statement-breakpoint +DROP TABLE "indexer_locked";--> statement-breakpoint +DROP TABLE "indexer_unlocked"; \ No newline at end of file diff --git a/backend/drizzle/0003_silent_namor.sql b/backend/drizzle/0003_silent_namor.sql new file mode 100644 index 0000000..a4708c1 --- /dev/null +++ b/backend/drizzle/0003_silent_namor.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS "liquidity" ( + "owner" text PRIMARY KEY NOT NULL, + "offchain_id" text PRIMARY KEY NOT NULL, + "locked" boolean, + "amount" boolean +); +--> statement-breakpoint +ALTER TABLE "registration" ALTER COLUMN "revolut" SET DEFAULT ARRAY[]::text[]; \ No newline at end of file diff --git a/backend/drizzle/meta/0002_snapshot.json b/backend/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..7172afb --- /dev/null +++ b/backend/drizzle/meta/0002_snapshot.json @@ -0,0 +1,502 @@ +{ + "id": "1fdda1f4-6d36-41d5-b53b-c8c7d114c594", + "prevId": "98ec95bb-ade2-4d5b-bac0-178f8e38a81a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.indexer_liquidity_added": { + "name": "indexer_liquidity_added", + "schema": "", + "columns": { + "_cursor": { + "name": "_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "network": { + "name": "network", + "type": "network_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "block_hash": { + "name": "block_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_number": { + "name": "block_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transaction_hash": { + "name": "transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_in_block": { + "name": "index_in_block", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ramp": { + "name": "ramp", + "type": "ramp_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "owner_address": { + "name": "owner_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "liquidity_added_cursor_idx": { + "name": "liquidity_added_cursor_idx", + "columns": [ + { + "expression": "_cursor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_added_token_idx": { + "name": "liquidity_added_token_idx", + "columns": [ + { + "expression": "ramp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_added_owner_idx": { + "name": "liquidity_added_owner_idx", + "columns": [ + { + "expression": "owner_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_added_offchain_id_idx": { + "name": "liquidity_added_offchain_id_idx", + "columns": [ + { + "expression": "offchain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.indexer_liquidity_locked": { + "name": "indexer_liquidity_locked", + "schema": "", + "columns": { + "_cursor": { + "name": "_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "network": { + "name": "network", + "type": "network_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "block_hash": { + "name": "block_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_number": { + "name": "block_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transaction_hash": { + "name": "transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_in_block": { + "name": "index_in_block", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ramp": { + "name": "ramp", + "type": "ramp_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "owner_address": { + "name": "owner_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "liquidity_locked_cursor_idx": { + "name": "liquidity_locked_cursor_idx", + "columns": [ + { + "expression": "_cursor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_locked_token_idx": { + "name": "liquidity_locked_token_idx", + "columns": [ + { + "expression": "ramp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_locked_owner_idx": { + "name": "liquidity_locked_owner_idx", + "columns": [ + { + "expression": "owner_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_locked_offchain_id_idx": { + "name": "liquidity_locked_offchain_id_idx", + "columns": [ + { + "expression": "offchain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.indexer_liquidity_retrieved": { + "name": "indexer_liquidity_retrieved", + "schema": "", + "columns": { + "_cursor": { + "name": "_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "network": { + "name": "network", + "type": "network_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "block_hash": { + "name": "block_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_number": { + "name": "block_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transaction_hash": { + "name": "transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_in_block": { + "name": "index_in_block", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ramp": { + "name": "ramp", + "type": "ramp_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "owner_address": { + "name": "owner_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "liquidity_retrieved_cursor_idx": { + "name": "liquidity_retrieved_cursor_idx", + "columns": [ + { + "expression": "_cursor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_retrieved_token_idx": { + "name": "liquidity_retrieved_token_idx", + "columns": [ + { + "expression": "ramp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_retrieved_owner_idx": { + "name": "liquidity_retrieved_owner_idx", + "columns": [ + { + "expression": "owner_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_retrieved_offchain_id_idx": { + "name": "liquidity_retrieved_offchain_id_idx", + "columns": [ + { + "expression": "offchain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.registration": { + "name": "registration", + "schema": "", + "columns": { + "address": { + "name": "address", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "revolut": { + "name": "revolut", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "public.network_type": { + "name": "network_type", + "schema": "public", + "values": [ + "mainnet", + "sepolia" + ] + }, + "public.ramp_type": { + "name": "ramp_type", + "schema": "public", + "values": [ + "Revolut" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/backend/drizzle/meta/0003_snapshot.json b/backend/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..903ec74 --- /dev/null +++ b/backend/drizzle/meta/0003_snapshot.json @@ -0,0 +1,536 @@ +{ + "id": "64667f6d-b60b-4d46-94b8-499979ab386b", + "prevId": "1fdda1f4-6d36-41d5-b53b-c8c7d114c594", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.liquidity": { + "name": "liquidity", + "schema": "", + "columns": { + "owner": { + "name": "owner", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.indexer_liquidity_added": { + "name": "indexer_liquidity_added", + "schema": "", + "columns": { + "_cursor": { + "name": "_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "network": { + "name": "network", + "type": "network_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "block_hash": { + "name": "block_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_number": { + "name": "block_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transaction_hash": { + "name": "transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_in_block": { + "name": "index_in_block", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ramp": { + "name": "ramp", + "type": "ramp_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "owner_address": { + "name": "owner_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "liquidity_added_cursor_idx": { + "name": "liquidity_added_cursor_idx", + "columns": [ + { + "expression": "_cursor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_added_token_idx": { + "name": "liquidity_added_token_idx", + "columns": [ + { + "expression": "ramp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_added_owner_idx": { + "name": "liquidity_added_owner_idx", + "columns": [ + { + "expression": "owner_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_added_offchain_id_idx": { + "name": "liquidity_added_offchain_id_idx", + "columns": [ + { + "expression": "offchain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.indexer_liquidity_locked": { + "name": "indexer_liquidity_locked", + "schema": "", + "columns": { + "_cursor": { + "name": "_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "network": { + "name": "network", + "type": "network_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "block_hash": { + "name": "block_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_number": { + "name": "block_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transaction_hash": { + "name": "transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_in_block": { + "name": "index_in_block", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ramp": { + "name": "ramp", + "type": "ramp_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "owner_address": { + "name": "owner_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "liquidity_locked_cursor_idx": { + "name": "liquidity_locked_cursor_idx", + "columns": [ + { + "expression": "_cursor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_locked_token_idx": { + "name": "liquidity_locked_token_idx", + "columns": [ + { + "expression": "ramp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_locked_owner_idx": { + "name": "liquidity_locked_owner_idx", + "columns": [ + { + "expression": "owner_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_locked_offchain_id_idx": { + "name": "liquidity_locked_offchain_id_idx", + "columns": [ + { + "expression": "offchain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.indexer_liquidity_retrieved": { + "name": "indexer_liquidity_retrieved", + "schema": "", + "columns": { + "_cursor": { + "name": "_cursor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "network": { + "name": "network", + "type": "network_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "block_hash": { + "name": "block_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_number": { + "name": "block_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transaction_hash": { + "name": "transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "index_in_block": { + "name": "index_in_block", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ramp": { + "name": "ramp", + "type": "ramp_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "owner_address": { + "name": "owner_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offchain_id": { + "name": "offchain_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "liquidity_retrieved_cursor_idx": { + "name": "liquidity_retrieved_cursor_idx", + "columns": [ + { + "expression": "_cursor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_retrieved_token_idx": { + "name": "liquidity_retrieved_token_idx", + "columns": [ + { + "expression": "ramp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_retrieved_owner_idx": { + "name": "liquidity_retrieved_owner_idx", + "columns": [ + { + "expression": "owner_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "liquidity_retrieved_offchain_id_idx": { + "name": "liquidity_retrieved_offchain_id_idx", + "columns": [ + { + "expression": "offchain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.registration": { + "name": "registration", + "schema": "", + "columns": { + "address": { + "name": "address", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "revolut": { + "name": "revolut", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "public.network_type": { + "name": "network_type", + "schema": "public", + "values": [ + "mainnet", + "sepolia" + ] + }, + "public.ramp_type": { + "name": "ramp_type", + "schema": "public", + "values": [ + "Revolut" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/backend/drizzle/meta/_journal.json b/backend/drizzle/meta/_journal.json index eef0414..d0f78a8 100644 --- a/backend/drizzle/meta/_journal.json +++ b/backend/drizzle/meta/_journal.json @@ -15,6 +15,20 @@ "when": 1726746693178, "tag": "0001_dusty_microbe", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1727185271445, + "tag": "0002_fixed_whirlwind", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1727271953035, + "tag": "0003_silent_namor", + "breakpoints": true } ] } \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index b97c223..3427fe5 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,7 +18,8 @@ "drizzle-orm": "^0.33.0", "fastify": "^4.28.1", "fastify-plugin": "^4.5.1", - "postgres": "^3.4.4" + "postgres": "^3.4.4", + "postgres-range": "^1.1.4" }, "devDependencies": { "@testcontainers/postgresql": "^10.13.0", diff --git a/backend/src/db/int8range.ts b/backend/src/db/int8range.ts new file mode 100644 index 0000000..4f174ca --- /dev/null +++ b/backend/src/db/int8range.ts @@ -0,0 +1,49 @@ +import { customType } from 'drizzle-orm/pg-core' +import type { Range } from 'postgres-range' +import { parse as rangeParse, serialize as rangeSerialize } from 'postgres-range' + +type Comparable = string | number + +type RangeBound = + | T + | { + value: T + inclusive: boolean + } + +class Int8Range { + constructor(public readonly range: Range) {} + + get start(): RangeBound | null { + return this.range.lower != null + ? { + value: this.range.lower, + inclusive: this.range.isLowerBoundClosed(), + } + : null + } + + get end(): RangeBound | null { + return this.range.upper != null + ? { + value: this.range.upper, + inclusive: this.range.isUpperBoundClosed(), + } + : null + } +} + +export const int8range = customType<{ + data: Int8Range +}>({ + dataType: () => 'int8range', + fromDriver: (value: unknown): Int8Range => { + if (typeof value !== 'string') { + throw new Error('Expected string') + } + + const parsed = rangeParse(value, (val) => Number.parseInt(val, 10)) + return new Int8Range(parsed) + }, + toDriver: (value: Int8Range): string => rangeSerialize(value.range), +}) diff --git a/backend/src/db/schema.ts b/backend/src/db/schema.ts index cc60816..2ce247e 100644 --- a/backend/src/db/schema.ts +++ b/backend/src/db/schema.ts @@ -1,128 +1,54 @@ -import { bigint, index, pgEnum, pgTable, text, timestamp } from 'drizzle-orm/pg-core' +import { sql } from 'drizzle-orm' +import { bigint, boolean, foreignKey, pgEnum, pgTable, primaryKey, text, timestamp } from 'drizzle-orm/pg-core' + +import { int8range } from './int8range' export const networkEnum = pgEnum('network_type', ['mainnet', 'sepolia']) export const rampEnum = pgEnum('ramp_type', ['Revolut']) -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const indexerCommonSchema = { - cursor: bigint('_cursor', { mode: 'number' }), - createdAt: timestamp('created_at', { mode: 'date', withTimezone: false }), - - network: networkEnum('network'), - blockHash: text('block_hash'), - blockNumber: bigint('block_number', { mode: 'number' }), - blockTimestamp: timestamp('block_timestamp', { - mode: 'date', - withTimezone: false, - }), - transactionHash: text('transaction_hash'), - indexInBlock: bigint('index_in_block', { mode: 'number' }), -} +export const registration = pgTable('registration', { + address: text('address').primaryKey(), + revolut: text('revolut') + .array() + .notNull() + .default(sql`ARRAY[]::text[]`), +}) -export const locked = pgTable( - 'indexer_locked', +export const liquidity = pgTable( + 'liquidity', { - ...indexerCommonSchema, - - id: text('id').primaryKey(), - - token: text('token_address'), - from: text('from_address'), - amount: text('amount'), - }, - (table) => { - return { - cursorIdx: index('locked_cursor_idx').on(table.cursor), - tokenIdx: index('locked_token_idx').on(table.token), - fromIdx: index('locked_from_idx').on(table.from), - } - }, -) - -export const unlocked = pgTable( - 'indexer_unlocked', - { - ...indexerCommonSchema, - - id: text('id').primaryKey(), - - token: text('token_address'), - from: text('from_address'), - to: text('to_address'), - amount: text('amount'), - }, - (table) => { - return { - cursorIdx: index('unlocked_cursor_idx').on(table.cursor), - tokenIdx: index('unlocked_token_idx').on(table.token), - fromIdx: index('unlocked_from_idx').on(table.from), - toIdx: index('unlocked_to_idx').on(table.to), - } - }, -) - -export const liquidityAdded = pgTable( - 'indexer_liquidity_added', - { - ...indexerCommonSchema, - - id: text('id').primaryKey(), - - ramp: rampEnum('ramp'), - owner: text('owner_address'), - offchainId: text('offchain_id'), - amount: text('amount'), - }, - (table) => { - return { - cursorIdx: index('liquidity_added_cursor_idx').on(table.cursor), - rampIdx: index('liquidity_added_token_idx').on(table.ramp), - ownerIdx: index('liquidity_added_owner_idx').on(table.owner), - offchainIdIdx: index('liquidity_added_offchain_id_idx').on(table.offchainId), - } - }, -) - -export const liquidityLocked = pgTable( - 'indexer_liquidity_locked', - { - ...indexerCommonSchema, - - id: text('id').primaryKey(), - - ramp: rampEnum('ramp'), - owner: text('owner_address'), + owner: text('owner'), offchainId: text('offchain_id'), + locked: boolean('locked').default(false), + amount: bigint('amount', { mode: 'bigint' }), + cursor: int8range('_cursor').notNull(), }, (table) => { return { - cursorIdx: index('liquidity_locked_cursor_idx').on(table.cursor), - rampIdx: index('liquidity_locked_token_idx').on(table.ramp), - ownerIdx: index('liquidity_locked_owner_idx').on(table.owner), - offchainIdIdx: index('liquidity_locked_offchain_id_idx').on(table.offchainId), + liquidityKey: primaryKey({ name: 'liquidity_key', columns: [table.owner, table.offchainId] }), } }, ) -export const liquidityRetrieved = pgTable( - 'indexer_liquidity_retrieved', +export const liquidityRequest = pgTable( + 'liquidity_request', { - ...indexerCommonSchema, - - id: text('id').primaryKey(), - - ramp: rampEnum('ramp'), - owner: text('owner_address'), + owner: text('owner'), offchainId: text('offchain_id'), - amount: text('amount'), + requestor: text('requestor'), + requestorOffchainId: text('requestor_offchain_id'), + amount: bigint('amount', { mode: 'bigint' }).notNull(), + expiresAt: timestamp('expires_at').notNull(), + cursor: bigint('_cursor', { mode: 'number' }), }, (table) => { return { - cursorIdx: index('liquidity_retrieved_cursor_idx').on(table.cursor), - rampIdx: index('liquidity_retrieved_token_idx').on(table.ramp), - ownerIdx: index('liquidity_retrieved_owner_idx').on(table.owner), - offchainIdIdx: index('liquidity_retrieved_offchain_id_idx').on(table.offchainId), + liquidityKey: foreignKey({ + columns: [table.owner, table.offchainId], + foreignColumns: [liquidity.owner, liquidity.offchainId], + name: 'liquidity_key', + }), } }, ) diff --git a/backend/yarn.lock b/backend/yarn.lock index b07aae1..1a3e632 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3417,6 +3417,11 @@ postcss@^8.4.43: picocolors "^1.0.1" source-map-js "^1.2.0" +postgres-range@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863" + integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== + postgres@^3.4.4: version "3.4.4" resolved "https://registry.yarnpkg.com/postgres/-/postgres-3.4.4.tgz#adbe08dc1fff0dea3559aa4f83ded70a289a6cb8" @@ -3854,8 +3859,16 @@ streamx@^2.15.0, streamx@^2.18.0: optionalDependencies: bare-events "^2.2.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3915,7 +3928,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== diff --git a/indexers/.env.example b/indexers/.env.example deleted file mode 100644 index 8f6f437..0000000 --- a/indexers/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -AUTH_TOKEN=dna_your_apibara_token -POSTGRES_CONNECTION_STRING=postgresql://admin:password@postgres:5432/zkramp diff --git a/indexers/.gitignore b/indexers/.gitignore index 9b1ee42..722d5e7 100644 --- a/indexers/.gitignore +++ b/indexers/.gitignore @@ -1,175 +1 @@ -# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore - -# Logs - -logs -_.log -npm-debug.log_ -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Caches - -.cache - -# Diagnostic reports (https://nodejs.org/api/report.html) - -report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json - -# Runtime data - -pids -_.pid -_.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover - -lib-cov - -# Coverage directory used by tools like istanbul - -coverage -*.lcov - -# nyc test coverage - -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) - -.grunt - -# Bower dependency directory (https://bower.io/) - -bower_components - -# node-waf configuration - -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) - -build/Release - -# Dependency directories - -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) - -web_modules/ - -# TypeScript cache - -*.tsbuildinfo - -# Optional npm cache directory - -.npm - -# Optional eslint cache - -.eslintcache - -# Optional stylelint cache - -.stylelintcache - -# Microbundle cache - -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history - -.node_repl_history - -# Output of 'npm pack' - -*.tgz - -# Yarn Integrity file - -.yarn-integrity - -# dotenv environment variable files - -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) - -.parcel-cache - -# Next.js build output - -.next -out - -# Nuxt.js build / generate output - -.nuxt -dist - -# Gatsby files - -# Comment in the public line in if your project uses Gatsby and not Next.js - -# https://nextjs.org/blog/next-9-1#public-directory-support - -# public - -# vuepress build output - -.vuepress/dist - -# vuepress v2.x temp and cache directory - -.temp - -# Docusaurus cache and generated files - -.docusaurus - -# Serverless directories - -.serverless/ - -# FuseBox cache - -.fusebox/ - -# DynamoDB Local files - -.dynamodb/ - -# TernJS port file - -.tern-port - -# Stores VSCode versions used for testing VSCode extensions - -.vscode-test - -# yarn v2 - -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# IntelliJ based IDEs -.idea - -# Finder (MacOS) folder config -.DS_Store +.vscode diff --git a/indexers/Dockerfile b/indexers/Dockerfile new file mode 100644 index 0000000..e5fa391 --- /dev/null +++ b/indexers/Dockerfile @@ -0,0 +1,14 @@ +# Notice that the base image is build from scratch (not an OS like Ubuntu), +# so the binary is in a location that depends on the build. +# For this reason we stick to a specific version and architecture. +# +# When updating the image you also need to update the entrypoint below. +# +# - docker image pull quay.io/apibara/sink-postgres:0.7.0-x86_64 +# - docker image inspect quay.io/apibara/sink-postgres:0.7.0-x86_64 | jq '.[].Config.Entrypoint' +FROM quay.io/apibara/sink-postgres:0.7.0-x86_64 + +WORKDIR /app +COPY ./src/* /app + +ENTRYPOINT ["/nix/store/rh1g8pb7wfnyr527jfmkkc5lm3sa1f0l-apibara-sink-postgres-0.7.0/bin/apibara-sink-postgres"] diff --git a/indexers/deno.json b/indexers/deno.json deleted file mode 100644 index df54125..0000000 --- a/indexers/deno.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "lint": { - "include": ["src/"], - "rules": { - "tags": ["recommended"], - "include": ["ban-untagged-todo"], - "exclude": ["no-unused-vars"] - } - }, - "fmt": { - "useTabs": false, - "lineWidth": 120, - "indentWidth": 2, - "semiColons": false, - "singleQuote": true, - "proseWrap": "preserve", - "include": ["src/"] - } -} diff --git a/indexers/docker-compose.yml b/indexers/docker-compose.yml deleted file mode 100644 index a69bbb7..0000000 --- a/indexers/docker-compose.yml +++ /dev/null @@ -1,66 +0,0 @@ -version: '3.8' - -services: - zkramp-locked-indexer: - environment: - - AUTH_TOKEN=${AUTH_TOKEN} - - POSTGRES_CONNECTION_STRING=${POSTGRES_CONNECTION_STRING} - image: quay.io/apibara/sink-postgres:latest - command: 'run ./indexer/escrow-locked.indexer.ts --connection-string ${POSTGRES_CONNECTION_STRING} -A ${AUTH_TOKEN}' - volumes: - - ./src:/indexer - networks: - - indexer - restart: on-failure - - zkramp-unlocked-indexer: - environment: - - AUTH_TOKEN=${AUTH_TOKEN} - - POSTGRES_CONNECTION_STRING=${POSTGRES_CONNECTION_STRING} - image: quay.io/apibara/sink-postgres:latest - command: 'run ./indexer/escrow-unlocked.indexer.ts --connection-string ${POSTGRES_CONNECTION_STRING} -A ${AUTH_TOKEN}' - volumes: - - ./src:/indexer - networks: - - indexer - restart: on-failure - - revolut-liquidity-added-indexer: - environment: - - AUTH_TOKEN=${AUTH_TOKEN} - - POSTGRES_CONNECTION_STRING=${POSTGRES_CONNECTION_STRING} - image: quay.io/apibara/sink-postgres:latest - command: 'run ./indexer/revolut-liquidity-added.indexer.ts --connection-string ${POSTGRES_CONNECTION_STRING} -A ${AUTH_TOKEN}' - volumes: - - ./src:/indexer - networks: - - indexer - restart: on-failure - - revolut-liquidity-locked-indexer: - environment: - - AUTH_TOKEN=${AUTH_TOKEN} - - POSTGRES_CONNECTION_STRING=${POSTGRES_CONNECTION_STRING} - image: quay.io/apibara/sink-postgres:latest - command: 'run ./indexer/revolut-liquidity-locked.indexer.ts --connection-string ${POSTGRES_CONNECTION_STRING} -A ${AUTH_TOKEN}' - volumes: - - ./src:/indexer - networks: - - indexer - restart: on-failure - - revolut-liquidity-retrieved-indexer: - environment: - - AUTH_TOKEN=${AUTH_TOKEN} - - POSTGRES_CONNECTION_STRING=${POSTGRES_CONNECTION_STRING} - image: quay.io/apibara/sink-postgres:latest - command: 'run ./indexer/revolut-liquidity-retrieved.indexer.ts --connection-string ${POSTGRES_CONNECTION_STRING} -A ${AUTH_TOKEN}' - volumes: - - ./src:/indexer - networks: - - indexer - restart: on-failure - -networks: - indexer: - driver: bridge diff --git a/indexers/src/escrow-locked.indexer.ts b/indexers/src/escrow-locked.indexer.ts deleted file mode 100644 index e561d56..0000000 --- a/indexers/src/escrow-locked.indexer.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { apibara, starknet } from './utils/deps.ts' -import { ESCROW_ADDRESS, ESCROW_STARTING_BLOCK } from './utils/constants.ts' -import { getCommonValues } from './utils/helpers.ts' - -const filter = { - header: { - weak: true, - }, - events: [ - { - fromAddress: ESCROW_ADDRESS, - keys: [starknet.hash.getSelectorFromName('Locked')], - includeReceipt: false, - }, - ], -} - -export const config = { - streamUrl: 'https://mainnet.starknet.a5a.ch', - startingBlock: ESCROW_STARTING_BLOCK, - network: 'starknet', - finality: 'DATA_STATUS_ACCEPTED', - filter, - sinkType: 'postgres', - sinkOptions: { - tableName: 'indexer_locked', - }, -} - -export default function transform({ header, events }: apibara.Block) { - return (events ?? []) - .map(({ event, transaction }) => { - if (!event.data || !event.keys) return null - - const eventId = `${transaction.meta.hash}_${event.index ?? 0}` - - const tokenAddress = event.keys[1] - const [fromAddress, amountLow, amountHigh] = event.data - - const amount = starknet.uint256.uint256ToBN({ - low: amountLow, - high: amountHigh, - }) - - return { - ...getCommonValues(header!, event, transaction), - - id: eventId, - - token_address: tokenAddress, - from_address: fromAddress, - amount: amount.toString(), - } - }) - .filter(Boolean) -} diff --git a/indexers/src/escrow-unlocked.indexer.ts b/indexers/src/escrow-unlocked.indexer.ts deleted file mode 100644 index 877a7fa..0000000 --- a/indexers/src/escrow-unlocked.indexer.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { apibara, starknet } from './utils/deps.ts' -import { ESCROW_ADDRESS, ESCROW_STARTING_BLOCK } from './utils/constants.ts' -import { getCommonValues } from './utils/helpers.ts' - -const filter = { - header: { - weak: true, - }, - events: [ - { - fromAddress: ESCROW_ADDRESS, - keys: [starknet.hash.getSelectorFromName('UnLocked')], - includeReceipt: false, - }, - ], -} - -export const config = { - streamUrl: 'https://mainnet.starknet.a5a.ch', - startingBlock: ESCROW_STARTING_BLOCK, - network: 'starknet', - finality: 'DATA_STATUS_ACCEPTED', - filter, - sinkType: 'postgres', - sinkOptions: { - tableName: 'indexer_unlocked', - }, -} - -export default function transform({ header, events }: apibara.Block) { - return (events ?? []) - .map(({ event, transaction }) => { - if (!event.data || !event.keys) return null - - const eventId = `${transaction.meta.hash}_${event.index ?? 0}` - - const tokenAddress = event.keys[1] - const [fromAddress, toAddress, amountLow, amountHigh] = event.data - - const amount = starknet.uint256.uint256ToBN({ - low: amountLow, - high: amountHigh, - }) - - return { - ...getCommonValues(header!, event, transaction), - - id: eventId, - - token_address: tokenAddress, - from_address: fromAddress, - to_address: toAddress, - amount: amount.toString(), - } - }) - .filter(Boolean) -} diff --git a/indexers/src/liquidity-addition.ts b/indexers/src/liquidity-addition.ts new file mode 100644 index 0000000..e619965 --- /dev/null +++ b/indexers/src/liquidity-addition.ts @@ -0,0 +1,95 @@ +import { apibara, starknet } from './utils/deps.ts' +import { LIQUIDITY_VAR_NAME, REVOLUT_ADDRESSES, SN_CHAIN_ID, STARTING_BLOCK, STREAM_URLS } from './utils/constants.ts' +import { getLiquidityKeyMapStorageLocation } from './utils/helpers.ts' +import { LiquidityKey } from './utils/types.ts'; + +const filter = { + header: { + weak: true, + }, + events: [ + { + fromAddress: REVOLUT_ADDRESSES[SN_CHAIN_ID], + keys: [starknet.hash.getSelectorFromName('LiquidityAdded')], + includeReceipt: false, + }, + ], + stateUpdate: { + storageDiffs: [{ contractAddress: REVOLUT_ADDRESSES[SN_CHAIN_ID] }], + }, +} + +const streamUrl = STREAM_URLS[SN_CHAIN_ID] +const startingBlock = STARTING_BLOCK + +export const config = { + streamUrl, + startingBlock, + network: 'starknet', + finality: 'DATA_STATUS_PENDING', + filter, + sinkType: 'postgres', + sinkOptions: { + tableName: 'liquidity', + entityMode: true, + }, +} + +function getLiquidity(storageMap: Map, liquidityKey: LiquidityKey): bigint { + const liquidityAmountLocation = getLiquidityKeyMapStorageLocation(LIQUIDITY_VAR_NAME, liquidityKey) + + const addressBalanceLow = storageMap.get(liquidityAmountLocation) + const addressBalanceHigh = storageMap.get(liquidityAmountLocation + 1n) + + return starknet.uint256.uint256ToBN({ + low: addressBalanceLow ?? 0n, + high: addressBalanceHigh ?? 0n, + }) +} + +export default function transform({ events, stateUpdate }: apibara.Block) { + // Step 1: map state updates. + const storageMap = new Map() + const storageDiffs = stateUpdate?.stateDiff?.storageDiffs ?? [] + + for (const storageDiff of storageDiffs) { + for (const storageEntry of storageDiff.storageEntries ?? []) { + if (!storageEntry.key || !storageEntry.value) { + continue + } + + const key = BigInt(storageEntry.key) + const value = BigInt(storageEntry.value) + + storageMap.set(key, value) + } + } + + // Step 2: aggregate everyting + return (events ?? []) + .map(({ event }) => { + if (!event.data || !event.keys) return null + + const [, owner, offchainIdPlateform, offchainIdValue] = event.keys + + const offchainId = `${offchainIdPlateform}@${offchainIdValue}` + + const liquidityKey = { + offchainId: { + plateform: +offchainIdPlateform, + id: offchainIdValue + }, + owner + } + + const amount = getLiquidity(storageMap, liquidityKey) + + return { + owner, + offchainId, + locked: false, + amount, + } + }) + .filter(Boolean) +} diff --git a/indexers/src/revolut-liquidity-added.indexer.ts b/indexers/src/revolut-liquidity-added.indexer.ts deleted file mode 100644 index e878556..0000000 --- a/indexers/src/revolut-liquidity-added.indexer.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { apibara, starknet } from './utils/deps.ts' -import { RAMPS, REVOLUT_ADDRESS, REVOLUT_STARTING_BLOCK } from './utils/constants.ts' -import { getCommonValues } from './utils/helpers.ts' - -const filter = { - header: { - weak: true, - }, - events: [ - { - fromAddress: REVOLUT_ADDRESS, - keys: [starknet.hash.getSelectorFromName('LiquidityAdded')], - includeReceipt: false, - }, - ], -} - -export const config = { - streamUrl: 'https://mainnet.starknet.a5a.ch', - startingBlock: REVOLUT_STARTING_BLOCK, - network: 'starknet', - finality: 'DATA_STATUS_ACCEPTED', - filter, - sinkType: 'postgres', - sinkOptions: { - tableName: 'indexer_liquidity_added', - }, -} - -export default function transform({ header, events }: apibara.Block) { - return (events ?? []) - .map(({ event, transaction }) => { - if (!event.data || !event.keys) return null - - const eventId = `${transaction.meta.hash}_${event.index ?? 0}` - - const [, owner, ramp_idx, offchain_id] = event.keys - const [amountLow, amountHigh] = event.data - - const ramp = Object.values(RAMPS)[Number(ramp_idx)] - if (!ramp) return null - - const amount = starknet.uint256.uint256ToBN({ - low: amountLow, - high: amountHigh, - }) - - return { - ...getCommonValues(header!, event, transaction), - - id: eventId, - - ramp, - owner_address: owner, - offchain_id, - amount: amount.toString(), - } - }) - .filter(Boolean) -} diff --git a/indexers/src/revolut-liquidity-locked.indexer.ts b/indexers/src/revolut-liquidity-locked.indexer.ts deleted file mode 100644 index 87fb5d8..0000000 --- a/indexers/src/revolut-liquidity-locked.indexer.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { apibara, starknet } from './utils/deps.ts' -import { RAMPS, REVOLUT_ADDRESS, REVOLUT_STARTING_BLOCK } from './utils/constants.ts' -import { getCommonValues } from './utils/helpers.ts' - -const filter = { - header: { - weak: true, - }, - events: [ - { - fromAddress: REVOLUT_ADDRESS, - keys: [starknet.hash.getSelectorFromName('LiquidityLocked')], - includeReceipt: false, - }, - ], -} - -export const config = { - streamUrl: 'https://mainnet.starknet.a5a.ch', - startingBlock: REVOLUT_STARTING_BLOCK, - network: 'starknet', - finality: 'DATA_STATUS_ACCEPTED', - filter, - sinkType: 'postgres', - sinkOptions: { - tableName: 'indexer_liquidity_locked', - }, -} - -export default function transform({ header, events }: apibara.Block) { - return (events ?? []) - .map(({ event, transaction }) => { - if (!event.data || !event.keys) return null - - const eventId = `${transaction.meta.hash}_${event.index ?? 0}` - - const [, owner, ramp_idx, offchain_id] = event.keys - - const ramp = Object.values(RAMPS)[Number(ramp_idx)] - if (!ramp) return null - - return { - ...getCommonValues(header!, event, transaction), - - id: eventId, - - ramp, - owner_address: owner, - offchain_id, - } - }) - .filter(Boolean) -} diff --git a/indexers/src/revolut-liquidity-retrieved.indexer.ts b/indexers/src/revolut-liquidity-retrieved.indexer.ts deleted file mode 100644 index 6f29cff..0000000 --- a/indexers/src/revolut-liquidity-retrieved.indexer.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { apibara, starknet } from './utils/deps.ts' -import { RAMPS, REVOLUT_ADDRESS, REVOLUT_STARTING_BLOCK } from './utils/constants.ts' -import { getCommonValues } from './utils/helpers.ts' - -const filter = { - header: { - weak: true, - }, - events: [ - { - fromAddress: REVOLUT_ADDRESS, - keys: [starknet.hash.getSelectorFromName('LiquidityRetrieved')], - includeReceipt: false, - }, - ], -} - -export const config = { - streamUrl: 'https://mainnet.starknet.a5a.ch', - startingBlock: REVOLUT_STARTING_BLOCK, - network: 'starknet', - finality: 'DATA_STATUS_ACCEPTED', - filter, - sinkType: 'postgres', - sinkOptions: { - tableName: 'indexer_liquidity_retrieved', - }, -} - -export default function transform({ header, events }: apibara.Block) { - return (events ?? []) - .map(({ event, transaction }) => { - if (!event.data || !event.keys) return null - - const eventId = `${transaction.meta.hash}_${event.index ?? 0}` - - const [, owner, ramp_idx, offchain_id] = event.keys - const [amountLow, amountHigh] = event.data - - const ramp = Object.values(RAMPS)[Number(ramp_idx)] - if (!ramp) return null - - const amount = starknet.uint256.uint256ToBN({ - low: amountLow, - high: amountHigh, - }) - - return { - ...getCommonValues(header!, event, transaction), - - id: eventId, - - ramp, - owner_address: owner, - offchain_id, - amount: amount.toString(), - } - }) - .filter(Boolean) -} diff --git a/indexers/src/utils/constants.ts b/indexers/src/utils/constants.ts index cfe4e8f..4500bc6 100644 --- a/indexers/src/utils/constants.ts +++ b/indexers/src/utils/constants.ts @@ -1,10 +1,32 @@ +import { starknet } from './deps.ts' + // Order should match the order in the contract -export enum RAMPS { - Revolut, +export enum Plateform { + Revolut = 0, +} + +export const STORAGE_ADDRESS_BOUND = 2n ** 251n + +type SupportedChainId = Exclude + +type AddressesMap = Record + +export const REVOLUT_ADDRESSES: AddressesMap = { + [starknet.constants.StarknetChainId.SN_MAIN]: '0x0', + [starknet.constants.StarknetChainId.SN_SEPOLIA]: '0x0', +} + +const DEFAULT_NETWORK_NAME = starknet.constants.NetworkName.SN_SEPOLIA + +export const SN_CHAIN_ID = + (starknet.constants.StarknetChainId[(Deno.env.get('SN_NETWORK') ?? '') as starknet.constants.NetworkName] ?? + starknet.constants.StarknetChainId[DEFAULT_NETWORK_NAME]) as SupportedChainId + +export const STREAM_URLS = { + [starknet.constants.StarknetChainId.SN_MAIN]: 'https://mainnet.starknet.a5a.ch', + [starknet.constants.StarknetChainId.SN_SEPOLIA]: 'https://sepolia.starknet.a5a.ch', } -export const ESCROW_ADDRESS = '0x0' -export const ESCROW_STARTING_BLOCK = 0 +export const STARTING_BLOCK = Number(Deno.env.get('STARTING_BLOCK')) ?? 0 -export const REVOLUT_ADDRESS = '0x0' -export const REVOLUT_STARTING_BLOCK = 0 +export const LIQUIDITY_VAR_NAME = 'liquidity' diff --git a/indexers/src/utils/deps.ts b/indexers/src/utils/deps.ts index c987452..1f0db3f 100644 --- a/indexers/src/utils/deps.ts +++ b/indexers/src/utils/deps.ts @@ -1,4 +1,4 @@ -export * as starknet from 'https://esm.sh/starknet@5.14' +export * as starknet from 'https://esm.sh/starknet@5.25' export * as viem from 'https://esm.sh/viem@1.4' export * as apibara from 'https://esm.sh/@apibara/indexer@0.3/starknet' diff --git a/indexers/src/utils/helpers.ts b/indexers/src/utils/helpers.ts index 6f2371a..b846ab7 100644 --- a/indexers/src/utils/helpers.ts +++ b/indexers/src/utils/helpers.ts @@ -1,22 +1,13 @@ -import { apibara } from './deps.ts' +import { STORAGE_ADDRESS_BOUND } from './constants.ts' +import { starknet } from './deps.ts' +import { LiquidityKey } from './types.ts' -export const getCommonValues = ( - header: apibara.BlockHeader, - event: apibara.Event, - transaction: apibara.Transaction, -) => { - const { blockNumber, blockHash, timestamp } = header +export function getLiquidityKeyMapStorageLocation(varName: string, liquidityKey: LiquidityKey) { + const hashedVarName = starknet.hash.getSelectorFromName(varName) + const serializedLiquidityKey = [liquidityKey.owner, liquidityKey.offchainId.plateform, liquidityKey.offchainId.id] - const transactionHash = transaction.meta.hash - const IndexInBlock = (transaction.meta.transactionIndex ?? 0) * 1_000 + (event.index ?? 0) + const location = BigInt([serializedLiquidityKey.length, ...serializedLiquidityKey] + .reduce((x, y) => starknet.ec.starkCurve.pedersen(x, y), hashedVarName)) - return { - created_at: new Date().toISOString(), - network: 'mainnet', - block_hash: blockHash, - block_number: +(blockNumber ?? 0), - block_timestamp: timestamp, - transaction_hash: transactionHash, - index_in_block: IndexInBlock, - } + return location >= STORAGE_ADDRESS_BOUND ? location - STORAGE_ADDRESS_BOUND : location } diff --git a/indexers/src/utils/types.ts b/indexers/src/utils/types.ts new file mode 100644 index 0000000..8c13195 --- /dev/null +++ b/indexers/src/utils/types.ts @@ -0,0 +1,11 @@ +import { Plateform } from './constants.ts'; + +export interface OffchainId { + plateform: Plateform + id: string +} + +export interface LiquidityKey { + owner: string + offchainId: OffchainId +}