diff --git a/.env.example b/.env.example index 28133e5..5e1e26b 100644 --- a/.env.example +++ b/.env.example @@ -21,10 +21,24 @@ MAIN_SAFE_ADDRESS= DAO_CONTRACT_ADDRESS= DAO_SHARE_TOKEN_ADDRESS= DAO_SHARE_THRESHOLD=100 +# Optional; defaults to GNOSIS_RPC_URL and Gnosis when unset. +DAO_SHARE_RPC_URL= +DAO_SHARE_CHAIN_ID= HATS_CONTRACT_ADDRESS= # Decimal or hex Hat ID values are both accepted. ANGRY_DWARF_HAT_ID= +# Machine API / x402 demo configuration +# Defaults to Base Sepolia chain ID 84532. +X402_ACCOUNTING_CHAIN_ID=84532 +X402_ACCOUNTING_PAY_TO_ADDRESS= +X402_ACCOUNTING_REPORT_PRICE=0.01 +X402_ACCOUNTING_MAX_TIMEOUT_SECONDS=60 +X402_FACILITATOR_URL=https://x402.org/facilitator +RG_DELEGATION_REGISTRY_CHAIN_ID=84532 +RG_DELEGATION_REGISTRY_RPC_URL= +RG_DELEGATION_REGISTRY_ADDRESS= + # External APIs # Optional; public CoinGecko price endpoint is used when unset. COINGECKO_API_KEY= diff --git a/drizzle/0019_magenta_deathbird.sql b/drizzle/0019_magenta_deathbird.sql new file mode 100644 index 0000000..98b2628 --- /dev/null +++ b/drizzle/0019_magenta_deathbird.sql @@ -0,0 +1,25 @@ +CREATE TABLE "machine_api_rate_limits" ( + "key" text PRIMARY KEY NOT NULL, + "count" integer DEFAULT 0 NOT NULL, + "reset_at" timestamp with time zone NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "machine_api_request_nonces" ( + "nonce" text PRIMARY KEY NOT NULL, + "agent_address" text NOT NULL, + "delegator_address" text NOT NULL, + "quarter_id" uuid, + "report_slice" text NOT NULL, + "expires_at" timestamp with time zone NOT NULL, + "consumed_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "machine_api_request_nonces" ADD CONSTRAINT "machine_api_request_nonces_quarter_id_quarters_id_fk" FOREIGN KEY ("quarter_id") REFERENCES "public"."quarters"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "machine_api_rate_limits_reset_at_idx" ON "machine_api_rate_limits" USING btree ("reset_at");--> statement-breakpoint +CREATE INDEX "machine_api_request_nonces_agent_idx" ON "machine_api_request_nonces" USING btree (lower("agent_address"));--> statement-breakpoint +CREATE INDEX "machine_api_request_nonces_delegator_idx" ON "machine_api_request_nonces" USING btree (lower("delegator_address"));--> statement-breakpoint +CREATE INDEX "machine_api_request_nonces_expires_at_idx" ON "machine_api_request_nonces" USING btree ("expires_at"); \ No newline at end of file diff --git a/drizzle/meta/0019_snapshot.json b/drizzle/meta/0019_snapshot.json new file mode 100644 index 0000000..1e2cf8c --- /dev/null +++ b/drizzle/meta/0019_snapshot.json @@ -0,0 +1,3675 @@ +{ + "id": "ad1a6acd-02d6-4107-9f5b-d13c98423842", + "prevId": "86c95f0e-bdf6-4e84-a481-25d5486b94bb", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.app_users": { + "name": "app_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "wallet_address": { + "name": "wallet_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name_encrypted": { + "name": "display_name_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "app_users_wallet_address_unique": { + "name": "app_users_wallet_address_unique", + "columns": [ + { + "expression": "lower(\"wallet_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_events": { + "name": "audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_wallet_address": { + "name": "actor_wallet_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "audit_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "subject_table": { + "name": "subject_table", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_id": { + "name": "subject_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_events_subject_idx": { + "name": "audit_events_subject_idx", + "columns": [ + { + "expression": "subject_table", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_events_actor_user_id_idx": { + "name": "audit_events_actor_user_id_idx", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_events_quarter_id_idx": { + "name": "audit_events_quarter_id_idx", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_events_created_at_idx": { + "name": "audit_events_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_events_actor_user_id_app_users_id_fk": { + "name": "audit_events_actor_user_id_app_users_id_fk", + "tableFrom": "audit_events", + "tableTo": "app_users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "audit_events_quarter_id_quarters_id_fk": { + "name": "audit_events_quarter_id_quarters_id_fk", + "tableFrom": "audit_events", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cleric_roles": { + "name": "cleric_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "wallet_address": { + "name": "wallet_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "granted_by_user_id": { + "name": "granted_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_by_user_id": { + "name": "revoked_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cleric_roles_wallet_address_idx": { + "name": "cleric_roles_wallet_address_idx", + "columns": [ + { + "expression": "lower(\"wallet_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cleric_roles_active_idx": { + "name": "cleric_roles_active_idx", + "columns": [ + { + "expression": "lower(\"wallet_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "revoked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cleric_roles_granted_by_user_id_app_users_id_fk": { + "name": "cleric_roles_granted_by_user_id_app_users_id_fk", + "tableFrom": "cleric_roles", + "tableTo": "app_users", + "columnsFrom": [ + "granted_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cleric_roles_revoked_by_user_id_app_users_id_fk": { + "name": "cleric_roles_revoked_by_user_id_app_users_id_fk", + "tableFrom": "cleric_roles", + "tableTo": "app_users", + "columnsFrom": [ + "revoked_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dao_proposals": { + "name": "dao_proposals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "dao_address": { + "name": "dao_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "proposal_id": { + "name": "proposal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposal_number": { + "name": "proposal_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_tx_hash": { + "name": "execution_tx_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "daohaus_url": { + "name": "daohaus_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "raw_metadata": { + "name": "raw_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dao_proposals_chain_dao_proposal_unique": { + "name": "dao_proposals_chain_dao_proposal_unique", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"dao_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "proposal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dao_proposals_chain_execution_tx_unique": { + "name": "dao_proposals_chain_execution_tx_unique", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"execution_tx_hash\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dao_proposals_executed_at_idx": { + "name": "dao_proposals_executed_at_idx", + "columns": [ + { + "expression": "executed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dao_proposals_status_idx": { + "name": "dao_proposals_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entities": { + "name": "entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "type": { + "name": "type", + "type": "entity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name_encrypted": { + "name": "name_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "website_encrypted": { + "name": "website_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "notes_encrypted": { + "name": "notes_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_member": { + "name": "is_member", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "entities_type_idx": { + "name": "entities_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "entities_archived_at_idx": { + "name": "entities_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entity_addresses": { + "name": "entity_addresses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "label_encrypted": { + "name": "label_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "entity_addresses_entity_id_idx": { + "name": "entity_addresses_entity_id_idx", + "columns": [ + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "entity_addresses_address_idx": { + "name": "entity_addresses_address_idx", + "columns": [ + { + "expression": "address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "entity_addresses_chain_address_unique": { + "name": "entity_addresses_chain_address_unique", + "columns": [ + { + "expression": "coalesce(\"chain_id\", -1)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "lower(\"address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "entity_addresses_entity_id_entities_id_fk": { + "name": "entity_addresses_entity_id_entities_id_fk", + "tableFrom": "entity_addresses", + "tableTo": "entities", + "columnsFrom": [ + "entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ledger_entries": { + "name": "ledger_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "ledger_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source_external_id": { + "name": "source_external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "ledger_category", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'uncategorized'" + }, + "verification_status": { + "name": "verification_status", + "type": "verification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'verified'" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tx_hash": { + "name": "tx_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "treasury_account_id": { + "name": "treasury_account_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "treasury_transaction_transfer_id": { + "name": "treasury_transaction_transfer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "asset_symbol": { + "name": "asset_symbol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "asset_amount": { + "name": "asset_amount", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": true + }, + "usd_amount": { + "name": "usd_amount", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": true + }, + "counterparty_entity_id": { + "name": "counterparty_entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "raid_id": { + "name": "raid_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "rip_id": { + "name": "rip_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notes_encrypted": { + "name": "notes_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source_metadata": { + "name": "source_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ledger_entries_quarter_id_idx": { + "name": "ledger_entries_quarter_id_idx", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_category_idx": { + "name": "ledger_entries_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_source_external_id_unique": { + "name": "ledger_entries_source_external_id_unique", + "columns": [ + { + "expression": "source_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_tx_hash_idx": { + "name": "ledger_entries_tx_hash_idx", + "columns": [ + { + "expression": "tx_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_treasury_transfer_unique": { + "name": "ledger_entries_treasury_transfer_unique", + "columns": [ + { + "expression": "treasury_transaction_transfer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_raid_id_idx": { + "name": "ledger_entries_raid_id_idx", + "columns": [ + { + "expression": "raid_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_rip_id_idx": { + "name": "ledger_entries_rip_id_idx", + "columns": [ + { + "expression": "rip_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ledger_entries_occurred_at_idx": { + "name": "ledger_entries_occurred_at_idx", + "columns": [ + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ledger_entries_quarter_id_quarters_id_fk": { + "name": "ledger_entries_quarter_id_quarters_id_fk", + "tableFrom": "ledger_entries", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ledger_entries_treasury_account_id_treasury_accounts_id_fk": { + "name": "ledger_entries_treasury_account_id_treasury_accounts_id_fk", + "tableFrom": "ledger_entries", + "tableTo": "treasury_accounts", + "columnsFrom": [ + "treasury_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ledger_entries_treasury_transaction_transfer_id_treasury_transaction_transfers_id_fk": { + "name": "ledger_entries_treasury_transaction_transfer_id_treasury_transaction_transfers_id_fk", + "tableFrom": "ledger_entries", + "tableTo": "treasury_transaction_transfers", + "columnsFrom": [ + "treasury_transaction_transfer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ledger_entries_counterparty_entity_id_entities_id_fk": { + "name": "ledger_entries_counterparty_entity_id_entities_id_fk", + "tableFrom": "ledger_entries", + "tableTo": "entities", + "columnsFrom": [ + "counterparty_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ledger_entries_raid_id_raids_id_fk": { + "name": "ledger_entries_raid_id_raids_id_fk", + "tableFrom": "ledger_entries", + "tableTo": "raids", + "columnsFrom": [ + "raid_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ledger_entries_rip_id_rips_id_fk": { + "name": "ledger_entries_rip_id_rips_id_fk", + "tableFrom": "ledger_entries", + "tableTo": "rips", + "columnsFrom": [ + "rip_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.machine_api_rate_limits": { + "name": "machine_api_rate_limits", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reset_at": { + "name": "reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "machine_api_rate_limits_reset_at_idx": { + "name": "machine_api_rate_limits_reset_at_idx", + "columns": [ + { + "expression": "reset_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.machine_api_request_nonces": { + "name": "machine_api_request_nonces", + "schema": "", + "columns": { + "nonce": { + "name": "nonce", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "agent_address": { + "name": "agent_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "delegator_address": { + "name": "delegator_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "report_slice": { + "name": "report_slice", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "machine_api_request_nonces_agent_idx": { + "name": "machine_api_request_nonces_agent_idx", + "columns": [ + { + "expression": "lower(\"agent_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "machine_api_request_nonces_delegator_idx": { + "name": "machine_api_request_nonces_delegator_idx", + "columns": [ + { + "expression": "lower(\"delegator_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "machine_api_request_nonces_expires_at_idx": { + "name": "machine_api_request_nonces_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "machine_api_request_nonces_quarter_id_quarters_id_fk": { + "name": "machine_api_request_nonces_quarter_id_quarters_id_fk", + "tableFrom": "machine_api_request_nonces", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.membership_activities": { + "name": "membership_activities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dao_proposal_id": { + "name": "dao_proposal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "membership_activity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "dao_address": { + "name": "dao_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "member_address": { + "name": "member_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recipient_address": { + "name": "recipient_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tx_hash": { + "name": "tx_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "proposal_id": { + "name": "proposal_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposal_title": { + "name": "proposal_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "asset_address": { + "name": "asset_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "asset_symbol": { + "name": "asset_symbol", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "asset_amount": { + "name": "asset_amount", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": false + }, + "usd_amount": { + "name": "usd_amount", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": false + }, + "shares": { + "name": "shares", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": false + }, + "loot": { + "name": "loot", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": false + }, + "raw_metadata": { + "name": "raw_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "membership_activities_chain_tx_type_member_unique": { + "name": "membership_activities_chain_tx_type_member_unique", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tx_hash\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"member_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "membership_activities_quarter_id_idx": { + "name": "membership_activities_quarter_id_idx", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "membership_activities_dao_proposal_id_idx": { + "name": "membership_activities_dao_proposal_id_idx", + "columns": [ + { + "expression": "dao_proposal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "membership_activities_executed_at_idx": { + "name": "membership_activities_executed_at_idx", + "columns": [ + { + "expression": "executed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "membership_activities_type_idx": { + "name": "membership_activities_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "membership_activities_member_address_idx": { + "name": "membership_activities_member_address_idx", + "columns": [ + { + "expression": "member_address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "membership_activities_quarter_id_quarters_id_fk": { + "name": "membership_activities_quarter_id_quarters_id_fk", + "tableFrom": "membership_activities", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "membership_activities_dao_proposal_id_dao_proposals_id_fk": { + "name": "membership_activities_dao_proposal_id_dao_proposals_id_fk", + "tableFrom": "membership_activities", + "tableTo": "dao_proposals", + "columnsFrom": [ + "dao_proposal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.quarter_balance_snapshots": { + "name": "quarter_balance_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "treasury_account_id": { + "name": "treasury_account_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "boundary": { + "name": "boundary", + "type": "quarter_balance_boundary", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "account_address": { + "name": "account_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "block_number": { + "name": "block_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "block_timestamp": { + "name": "block_timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "symbol": { + "name": "symbol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decimals": { + "name": "decimals", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "raw_amount": { + "name": "raw_amount", + "type": "numeric(78, 0)", + "primaryKey": false, + "notNull": true + }, + "balance": { + "name": "balance", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": true + }, + "usd_price": { + "name": "usd_price", + "type": "numeric(18, 8)", + "primaryKey": false, + "notNull": true + }, + "usd_value": { + "name": "usd_value", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": true + }, + "price_source": { + "name": "price_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "quarter_balance_snapshots_quarter_boundary_idx": { + "name": "quarter_balance_snapshots_quarter_boundary_idx", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "boundary", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "quarter_balance_snapshots_unique": { + "name": "quarter_balance_snapshots_unique", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "boundary", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"account_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "symbol", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "quarter_balance_snapshots_quarter_id_quarters_id_fk": { + "name": "quarter_balance_snapshots_quarter_id_quarters_id_fk", + "tableFrom": "quarter_balance_snapshots", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "quarter_balance_snapshots_treasury_account_id_treasury_accounts_id_fk": { + "name": "quarter_balance_snapshots_treasury_account_id_treasury_accounts_id_fk", + "tableFrom": "quarter_balance_snapshots", + "tableTo": "treasury_accounts", + "columnsFrom": [ + "treasury_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.quarter_balance_validations": { + "name": "quarter_balance_validations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "quarter_balance_validation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "checked_count": { + "name": "checked_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "variance_count": { + "name": "variance_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "excluded_count": { + "name": "excluded_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_variance_usd": { + "name": "total_variance_usd", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "acknowledgement_note_encrypted": { + "name": "acknowledgement_note_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "acknowledged_by_wallet_address": { + "name": "acknowledged_by_wallet_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acknowledged_at": { + "name": "acknowledged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "source_sync_run_id": { + "name": "source_sync_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "quarter_balance_validations_quarter_id_unique": { + "name": "quarter_balance_validations_quarter_id_unique", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "quarter_balance_validations_status_idx": { + "name": "quarter_balance_validations_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "quarter_balance_validations_quarter_id_quarters_id_fk": { + "name": "quarter_balance_validations_quarter_id_quarters_id_fk", + "tableFrom": "quarter_balance_validations", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.quarter_sync_statuses": { + "name": "quarter_sync_statuses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "quarter_id": { + "name": "quarter_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "overall_status": { + "name": "overall_status", + "type": "quarter_sync_overall_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "current_step": { + "name": "current_step", + "type": "quarter_sync_step", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "transactions_status": { + "name": "transactions_status", + "type": "quarter_sync_step_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "proposals_status": { + "name": "proposals_status", + "type": "quarter_sync_step_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "membership_status": { + "name": "membership_status", + "type": "quarter_sync_step_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "balances_status": { + "name": "balances_status", + "type": "quarter_sync_step_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "finalize_status": { + "name": "finalize_status", + "type": "quarter_sync_step_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "transactions_started_at": { + "name": "transactions_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "transactions_completed_at": { + "name": "transactions_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "proposals_started_at": { + "name": "proposals_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "proposals_completed_at": { + "name": "proposals_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "membership_started_at": { + "name": "membership_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "membership_completed_at": { + "name": "membership_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "balances_started_at": { + "name": "balances_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "balances_completed_at": { + "name": "balances_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finalize_started_at": { + "name": "finalize_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finalize_completed_at": { + "name": "finalize_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "transactions_error": { + "name": "transactions_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposals_error": { + "name": "proposals_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "membership_error": { + "name": "membership_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "balances_error": { + "name": "balances_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finalize_error": { + "name": "finalize_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "imported_transactions": { + "name": "imported_transactions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "imported_transfers": { + "name": "imported_transfers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scanned_transfers": { + "name": "scanned_transfers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sync_error_count": { + "name": "sync_error_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "proposal_linked_transactions": { + "name": "proposal_linked_transactions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "proposal_matches": { + "name": "proposal_matches", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "membership_activities": { + "name": "membership_activities", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "quarter_sync_statuses_quarter_id_unique": { + "name": "quarter_sync_statuses_quarter_id_unique", + "columns": [ + { + "expression": "quarter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "quarter_sync_statuses_overall_status_idx": { + "name": "quarter_sync_statuses_overall_status_idx", + "columns": [ + { + "expression": "overall_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "quarter_sync_statuses_last_synced_at_idx": { + "name": "quarter_sync_statuses_last_synced_at_idx", + "columns": [ + { + "expression": "last_synced_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "quarter_sync_statuses_quarter_id_quarters_id_fk": { + "name": "quarter_sync_statuses_quarter_id_quarters_id_fk", + "tableFrom": "quarter_sync_statuses", + "tableTo": "quarters", + "columnsFrom": [ + "quarter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.quarters": { + "name": "quarters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quarter": { + "name": "quarter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "starts_on": { + "name": "starts_on", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "ends_on": { + "name": "ends_on", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "quarter_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reopened_at": { + "name": "reopened_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "quarters_year_quarter_unique": { + "name": "quarters_year_quarter_unique", + "columns": [ + { + "expression": "year", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "quarter", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "quarters_status_idx": { + "name": "quarters_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.raids": { + "name": "raids", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_entity_id": { + "name": "client_entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name_encrypted": { + "name": "name_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "notes_encrypted": { + "name": "notes_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "raids_client_entity_id_idx": { + "name": "raids_client_entity_id_idx", + "columns": [ + { + "expression": "client_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "raids_archived_at_idx": { + "name": "raids_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "raids_client_entity_id_entities_id_fk": { + "name": "raids_client_entity_id_entities_id_fk", + "tableFrom": "raids", + "tableTo": "entities", + "columnsFrom": [ + "client_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_assistant_rate_limits": { + "name": "report_assistant_rate_limits", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reset_at": { + "name": "reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_assistant_rate_limits_reset_at_idx": { + "name": "report_assistant_rate_limits_reset_at_idx", + "columns": [ + { + "expression": "reset_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rips": { + "name": "rips", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title_encrypted": { + "name": "title_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "url_encrypted": { + "name": "url_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by_wallet_address": { + "name": "created_by_wallet_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rips_created_at_idx": { + "name": "rips_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rips_created_by_wallet_address_idx": { + "name": "rips_created_by_wallet_address_idx", + "columns": [ + { + "expression": "lower(\"created_by_wallet_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.treasury_accounts": { + "name": "treasury_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name_encrypted": { + "name": "name_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "treasury_account_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "is_dao_controlled": { + "name": "is_dao_controlled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "notes_encrypted": { + "name": "notes_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "treasury_accounts_chain_address_unique": { + "name": "treasury_accounts_chain_address_unique", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "address", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_accounts_type_idx": { + "name": "treasury_accounts_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_accounts_archived_at_idx": { + "name": "treasury_accounts_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.treasury_balance_assets": { + "name": "treasury_balance_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "symbol": { + "name": "symbol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decimals": { + "name": "decimals", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "raw_amount": { + "name": "raw_amount", + "type": "numeric(78, 0)", + "primaryKey": false, + "notNull": true + }, + "balance": { + "name": "balance", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": true + }, + "usd_price": { + "name": "usd_price", + "type": "numeric(18, 8)", + "primaryKey": false, + "notNull": true + }, + "usd_value": { + "name": "usd_value", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "treasury_balance_assets_snapshot_id_idx": { + "name": "treasury_balance_assets_snapshot_id_idx", + "columns": [ + { + "expression": "snapshot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_balance_assets_snapshot_symbol_unique": { + "name": "treasury_balance_assets_snapshot_symbol_unique", + "columns": [ + { + "expression": "snapshot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "symbol", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "treasury_balance_assets_snapshot_id_treasury_balance_snapshots_id_fk": { + "name": "treasury_balance_assets_snapshot_id_treasury_balance_snapshots_id_fk", + "tableFrom": "treasury_balance_assets", + "tableTo": "treasury_balance_snapshots", + "columnsFrom": [ + "snapshot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.treasury_balance_snapshots": { + "name": "treasury_balance_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "account_address": { + "name": "account_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "treasury_snapshot_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "total_usd": { + "name": "total_usd", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": true + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "treasury_balance_snapshots_chain_account_synced_idx": { + "name": "treasury_balance_snapshots_chain_account_synced_idx", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_address", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "synced_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_balance_snapshots_synced_at_idx": { + "name": "treasury_balance_snapshots_synced_at_idx", + "columns": [ + { + "expression": "synced_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.treasury_transaction_transfers": { + "name": "treasury_transaction_transfers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "treasury_transaction_id": { + "name": "treasury_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "treasury_account_id": { + "name": "treasury_account_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "transfer_id": { + "name": "transfer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "treasury_transfer_direction", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "transfer_type": { + "name": "transfer_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_address": { + "name": "account_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tx_hash": { + "name": "tx_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "from_address": { + "name": "from_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_address": { + "name": "to_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_address": { + "name": "token_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "asset_symbol": { + "name": "asset_symbol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "asset_name": { + "name": "asset_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decimals": { + "name": "decimals", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "raw_amount": { + "name": "raw_amount", + "type": "numeric(78, 0)", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "numeric(36, 18)", + "primaryKey": false, + "notNull": true + }, + "usd_price": { + "name": "usd_price", + "type": "numeric(18, 8)", + "primaryKey": false, + "notNull": false + }, + "usd_amount": { + "name": "usd_amount", + "type": "numeric(18, 2)", + "primaryKey": false, + "notNull": false + }, + "raw_metadata": { + "name": "raw_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "treasury_transaction_transfers_chain_transfer_unique": { + "name": "treasury_transaction_transfers_chain_transfer_unique", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"account_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "transfer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transaction_transfers_transaction_id_idx": { + "name": "treasury_transaction_transfers_transaction_id_idx", + "columns": [ + { + "expression": "treasury_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transaction_transfers_treasury_account_id_idx": { + "name": "treasury_transaction_transfers_treasury_account_id_idx", + "columns": [ + { + "expression": "treasury_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transaction_transfers_tx_hash_idx": { + "name": "treasury_transaction_transfers_tx_hash_idx", + "columns": [ + { + "expression": "tx_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transaction_transfers_executed_at_idx": { + "name": "treasury_transaction_transfers_executed_at_idx", + "columns": [ + { + "expression": "executed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "treasury_transaction_transfers_treasury_transaction_id_treasury_transactions_id_fk": { + "name": "treasury_transaction_transfers_treasury_transaction_id_treasury_transactions_id_fk", + "tableFrom": "treasury_transaction_transfers", + "tableTo": "treasury_transactions", + "columnsFrom": [ + "treasury_transaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "treasury_transaction_transfers_treasury_account_id_treasury_accounts_id_fk": { + "name": "treasury_transaction_transfers_treasury_account_id_treasury_accounts_id_fk", + "tableFrom": "treasury_transaction_transfers", + "tableTo": "treasury_accounts", + "columnsFrom": [ + "treasury_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.treasury_transactions": { + "name": "treasury_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "dao_proposal_id": { + "name": "dao_proposal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "treasury_account_id": { + "name": "treasury_account_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "ledger_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "account_address": { + "name": "account_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chain_id": { + "name": "chain_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tx_hash": { + "name": "tx_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "safe_transaction_hash": { + "name": "safe_transaction_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transaction_type": { + "name": "transaction_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "block_number": { + "name": "block_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "raw_metadata": { + "name": "raw_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "imported_at": { + "name": "imported_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "treasury_transactions_chain_account_tx_unique": { + "name": "treasury_transactions_chain_account_tx_unique", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"account_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "lower(\"tx_hash\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transactions_tx_hash_idx": { + "name": "treasury_transactions_tx_hash_idx", + "columns": [ + { + "expression": "tx_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transactions_account_executed_idx": { + "name": "treasury_transactions_account_executed_idx", + "columns": [ + { + "expression": "chain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_address", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "executed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transactions_treasury_account_id_idx": { + "name": "treasury_transactions_treasury_account_id_idx", + "columns": [ + { + "expression": "treasury_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "treasury_transactions_dao_proposal_id_idx": { + "name": "treasury_transactions_dao_proposal_id_idx", + "columns": [ + { + "expression": "dao_proposal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "treasury_transactions_dao_proposal_id_dao_proposals_id_fk": { + "name": "treasury_transactions_dao_proposal_id_dao_proposals_id_fk", + "tableFrom": "treasury_transactions", + "tableTo": "dao_proposals", + "columnsFrom": [ + "dao_proposal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "treasury_transactions_treasury_account_id_treasury_accounts_id_fk": { + "name": "treasury_transactions_treasury_account_id_treasury_accounts_id_fk", + "tableFrom": "treasury_transactions", + "tableTo": "treasury_accounts", + "columnsFrom": [ + "treasury_account_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.audit_action": { + "name": "audit_action", + "schema": "public", + "values": [ + "create", + "update", + "delete", + "import", + "classify", + "publish", + "reopen", + "grant_role", + "revoke_role" + ] + }, + "public.entity_type": { + "name": "entity_type", + "schema": "public", + "values": [ + "client", + "provider", + "subcontractor" + ] + }, + "public.ledger_category": { + "name": "ledger_category", + "schema": "public", + "values": [ + "raid_revenue", + "raid_spoils", + "subcontractor_payout", + "rip_expense", + "provider_expense", + "member_dues", + "ragequit", + "treasury_transfer", + "uncategorized" + ] + }, + "public.ledger_source": { + "name": "ledger_source", + "schema": "public", + "values": [ + "main_safe", + "side_vault", + "operator", + "manual", + "bank_csv", + "dao_proposal" + ] + }, + "public.membership_activity_type": { + "name": "membership_activity_type", + "schema": "public", + "values": [ + "join", + "ragequit" + ] + }, + "public.quarter_balance_boundary": { + "name": "quarter_balance_boundary", + "schema": "public", + "values": [ + "opening", + "closing" + ] + }, + "public.quarter_balance_validation_status": { + "name": "quarter_balance_validation_status", + "schema": "public", + "values": [ + "not_ready", + "needs_review", + "validated", + "acknowledged" + ] + }, + "public.quarter_status": { + "name": "quarter_status", + "schema": "public", + "values": [ + "draft", + "ready_for_review", + "published", + "reopened" + ] + }, + "public.quarter_sync_overall_status": { + "name": "quarter_sync_overall_status", + "schema": "public", + "values": [ + "idle", + "running", + "success", + "partial", + "failed" + ] + }, + "public.quarter_sync_step": { + "name": "quarter_sync_step", + "schema": "public", + "values": [ + "transactions", + "proposals", + "membership", + "balances", + "finalize" + ] + }, + "public.quarter_sync_step_status": { + "name": "quarter_sync_step_status", + "schema": "public", + "values": [ + "pending", + "running", + "success", + "failed" + ] + }, + "public.treasury_account_type": { + "name": "treasury_account_type", + "schema": "public", + "values": [ + "main_safe", + "side_vault", + "operator", + "bank" + ] + }, + "public.treasury_snapshot_status": { + "name": "treasury_snapshot_status", + "schema": "public", + "values": [ + "pending_live_sync", + "synced", + "stale_syncing", + "partial", + "failed" + ] + }, + "public.treasury_transfer_direction": { + "name": "treasury_transfer_direction", + "schema": "public", + "values": [ + "inflow", + "outflow", + "internal" + ] + }, + "public.verification_status": { + "name": "verification_status", + "schema": "public", + "values": [ + "verified", + "unverified" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 97ea464..1b228f7 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -134,6 +134,13 @@ "when": 1782744358925, "tag": "0018_workable_miss_america", "breakpoints": true + }, + { + "idx": 19, + "version": "7", + "when": 1782756454663, + "tag": "0019_magenta_deathbird", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index a30a392..025f3b5 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "@neondatabase/serverless": "^1.1.0", "@tanstack/react-query": "^5.100.14", "@vercel/analytics": "^2.0.1", + "@x402/core": "^2.17.0", + "@x402/evm": "^2.17.0", + "@x402/next": "^2.17.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "drizzle-orm": "^0.45.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dda8a47..fe40f26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,15 @@ importers: '@vercel/analytics': specifier: ^2.0.1 version: 2.0.1(next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + '@x402/core': + specifier: ^2.17.0 + version: 2.17.0 + '@x402/evm': + specifier: ^2.17.0 + version: 2.17.0(typescript@5.9.3) + '@x402/next': + specifier: ^2.17.0 + version: 2.17.0(ethers@6.16.0)(next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -786,105 +795,89 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -958,28 +951,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@next/swc-linux-arm64-musl@16.2.6': resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@next/swc-linux-x64-gnu@16.2.6': resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@next/swc-linux-x64-musl@16.2.6': resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@next/swc-win32-arm64-msvc@16.2.6': resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==} @@ -1040,6 +1029,20 @@ packages: '@scure/bip39@1.6.0': resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@signinwithethereum/siwe-parser@4.2.0': + resolution: {integrity: sha512-e3edh8XpZrEjbzVYc0BZ4ySFOa8RKTZOQTafSf1E6ejCB5XcBH93jEpYmjzry1EEocgMNq4KlB3YunsFNCXakQ==} + + '@signinwithethereum/siwe@4.2.0': + resolution: {integrity: sha512-6W+oyKgMFUZRJI4o9P9mTMzrokkXfu3tq1/CfOJj9QrMqyPqujJqv1xnxUAqDQwWVyCA8TMg0Fxk/+gXrDg2Nw==} + peerDependencies: + ethers: ^5.7.0 || ^6.13.0 + viem: ^2.7.0 + peerDependenciesMeta: + ethers: + optional: true + viem: + optional: true + '@spruceid/siwe-parser@3.0.0': resolution: {integrity: sha512-Y92k63ilw/8jH9Ry4G2e7lQd0jZAvb0d/Q7ssSD0D9mp/Zt2aCXIc3g0ny9yhplpAx1QXHsMz/JJptHK/zDGdw==} @@ -1096,28 +1099,24 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.3.0': resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.3.0': resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.3.0': resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.3.0': resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} @@ -1288,61 +1287,51 @@ packages: resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} cpu: [arm64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.12.2': resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} cpu: [arm64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} cpu: [loong64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-loong64-musl@1.12.2': resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} cpu: [loong64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} cpu: [ppc64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} cpu: [riscv64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} cpu: [riscv64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} cpu: [s390x] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.12.2': resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} cpu: [x64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.12.2': resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} cpu: [x64] os: [linux] - libc: [musl] '@unrs/resolver-binding-openharmony-arm64@1.12.2': resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} @@ -1447,6 +1436,24 @@ packages: typescript: optional: true + '@x402/core@2.17.0': + resolution: {integrity: sha512-BBooe9Kewl5KLaKadElFwhZzZ+n/X/m2djzGtc7OY51D8fk2vHTVvDZtBFEJYdJxtQoUZMKX73hew0ss81FENQ==} + + '@x402/evm@2.17.0': + resolution: {integrity: sha512-MjeouRdVJ1Y20Ikk03RHlnyOuo2FRkh0Nnrxt8aku2qt5fr5M7U8pUGn5iR6aFfRK3xlh48bp0Kypv1zjKuegw==} + + '@x402/extensions@2.17.0': + resolution: {integrity: sha512-oYQPLJkRWiSutEVAsyNxwKh4EyviG//s8Ms7Jwkx4J8csm1gd7TfCQTsLwDmieYA5B2Qp8ajebdTkL+bT9/sQA==} + + '@x402/next@2.17.0': + resolution: {integrity: sha512-bVIifpst8kuTMtuwIkdQCus/fq7IkT93f9jKHeX122weRTAXn7tWzKernE9X3bXXWf9X8+ZEVacXu/q2RUJCgQ==} + peerDependencies: + '@x402/paywall': ^2.17.0 + next: '>=16.2.6' + peerDependenciesMeta: + '@x402/paywall': + optional: true + abitype@1.2.3: resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} peerDependencies: @@ -1474,6 +1481,9 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -2071,6 +2081,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -2383,6 +2396,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2401,6 +2417,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2476,28 +2495,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -2868,6 +2883,10 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + reselect@5.2.0: resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} @@ -3112,6 +3131,9 @@ packages: tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -3285,6 +3307,9 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -3985,6 +4010,18 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 + '@signinwithethereum/siwe-parser@4.2.0': + dependencies: + '@noble/hashes': 1.8.0 + apg-js: 4.4.0 + + '@signinwithethereum/siwe@4.2.0(ethers@6.16.0)(viem@2.51.3(typescript@5.9.3)(zod@4.4.3))': + dependencies: + '@signinwithethereum/siwe-parser': 4.2.0 + optionalDependencies: + ethers: 6.16.0 + viem: 2.51.3(typescript@5.9.3)(zod@4.4.3) + '@spruceid/siwe-parser@3.0.0': dependencies: '@noble/hashes': 1.8.0 @@ -4306,6 +4343,53 @@ snapshots: - react - use-sync-external-store + '@x402/core@2.17.0': + dependencies: + zod: 3.25.76 + + '@x402/evm@2.17.0(typescript@5.9.3)': + dependencies: + '@x402/core': 2.17.0 + viem: 2.51.3(typescript@5.9.3)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@x402/extensions@2.17.0(ethers@6.16.0)(typescript@5.9.3)': + dependencies: + '@noble/curves': 1.9.1 + '@scure/base': 1.2.6 + '@signinwithethereum/siwe': 4.2.0(ethers@6.16.0)(viem@2.51.3(typescript@5.9.3)(zod@4.4.3)) + '@x402/core': 2.17.0 + ajv: 8.20.0 + jose: 5.10.0 + tweetnacl: 1.0.3 + viem: 2.51.3(typescript@5.9.3)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - ethers + - typescript + - utf-8-validate + + '@x402/next@2.17.0(ethers@6.16.0)(next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)': + dependencies: + '@x402/core': 2.17.0 + '@x402/extensions': 2.17.0(ethers@6.16.0)(typescript@5.9.3) + next: 16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - bufferutil + - ethers + - typescript + - utf-8-validate + + abitype@1.2.3(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + abitype@1.2.3(typescript@5.9.3)(zod@4.4.3): optionalDependencies: typescript: 5.9.3 @@ -4326,6 +4410,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -5115,6 +5206,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.3: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -5433,6 +5526,8 @@ snapshots: jiti@2.7.0: {} + jose@5.10.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -5445,6 +5540,8 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: @@ -5724,6 +5821,21 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + ox@0.14.25(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + ox@0.14.25(typescript@5.9.3)(zod@4.4.3): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -5889,6 +6001,8 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + require-from-string@2.0.2: {} + reselect@5.2.0: {} resolve-from@4.0.0: {} @@ -6188,6 +6302,8 @@ snapshots: tw-animate-css@1.4.0: {} + tweetnacl@1.0.3: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -6313,6 +6429,23 @@ snapshots: uuid@8.3.2: {} + viem@2.51.3(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.20.1) + ox: 0.14.25(typescript@5.9.3)(zod@3.25.76) + ws: 8.20.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.51.3(typescript@5.9.3)(zod@4.4.3): dependencies: '@noble/curves': 1.9.1 @@ -6424,6 +6557,8 @@ snapshots: dependencies: zod: 4.4.3 + zod@3.25.76: {} + zod@4.4.3: {} zustand@5.0.0(@types/react@19.2.15)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)): diff --git a/src/app/.well-known/agents.json/route.ts b/src/app/.well-known/agents.json/route.ts new file mode 100644 index 0000000..de3fe20 --- /dev/null +++ b/src/app/.well-known/agents.json/route.ts @@ -0,0 +1,127 @@ +import { NextResponse } from "next/server"; + +import { ACCOUNTING_DATA_REQUEST_TYPES } from "@/lib/machine-api/auth"; +import { getPublicAccountingX402Config } from "@/lib/machine-api/x402"; +import { + DELEGATION_REGISTRY_ABI, + getDelegationRegistryConfig, +} from "@/lib/machine-api/registry"; +import { MACHINE_REPORT_SLICES } from "@/lib/machine-api/report-slices"; + +type AgentsCapability = { + auth: { + delegation: { + registry: ReturnType; + type: "evm-contract"; + }; + payment: { + facilitatorUrl: string; + network: string; + payTo: `0x${string}`; + price: string; + protocol: "x402"; + scheme: "exact"; + }; + signature: { + domain: { + chainId: number; + name: string; + verifyingContract: `0x${string}`; + version: string; + }; + primaryType: "AccountingDataRequest"; + types: typeof ACCOUNTING_DATA_REQUEST_TYPES; + }; + }; + endpointTemplate: string; + id: string; + methods: ["POST"]; + privacy: { + excludes: string[]; + visibility: string; + }; + provenance: string[]; + reportSlices: typeof MACHINE_REPORT_SLICES; +}; + +function getRegistryDiscovery() { + const config = getDelegationRegistryConfig(); + + return { + address: config.address, + chainId: config.chainId, + lookup: { + abi: DELEGATION_REGISTRY_ABI, + method: "delegatorOf(address)", + }, + }; +} + +export function GET() { + let capabilities: AgentsCapability[] = []; + + try { + const x402 = getPublicAccountingX402Config(); + const registry = getRegistryDiscovery(); + + capabilities = [ + { + auth: { + delegation: { + registry, + type: "evm-contract", + }, + payment: { + facilitatorUrl: x402.facilitatorUrl, + network: x402.network, + payTo: x402.payTo, + price: x402.price, + protocol: "x402", + scheme: "exact", + }, + signature: { + domain: { + chainId: registry.chainId, + name: "RaidGuild Member API Demo", + verifyingContract: registry.address, + version: "1", + }, + primaryType: "AccountingDataRequest", + types: ACCOUNTING_DATA_REQUEST_TYPES, + }, + }, + endpointTemplate: "/api/machine/reports/quarters/{quarterId}", + id: "raidguild.accounting.published-reports", + methods: ["POST"], + privacy: { + excludes: [ + "draft data", + "reopened unpublished changes", + "audit-only metadata", + "decrypted private notes", + "raw bank memo data", + "admin-only records", + ], + visibility: "published member-visible accounting data only", + }, + provenance: [ + "quarter", + "quarterId", + "publishedAt", + "reportExportVersion", + "reportSlice", + ], + reportSlices: MACHINE_REPORT_SLICES, + }, + ]; + } catch { + capabilities = []; + } + + return NextResponse.json({ + capabilities, + demo: true, + name: "RaidGuild Accounting", + schema: "https://raidguild.org/schemas/agents-discovery-demo-v1", + }); +} diff --git a/src/app/api/machine/reports/quarters/[id]/route.ts b/src/app/api/machine/reports/quarters/[id]/route.ts new file mode 100644 index 0000000..be5d905 --- /dev/null +++ b/src/app/api/machine/reports/quarters/[id]/route.ts @@ -0,0 +1,133 @@ +import { withX402 } from "@x402/next"; +import { NextRequest, NextResponse } from "next/server"; + +import { isQuarterExportReady } from "@/lib/quarter-export-readiness"; +import { getQuarterReportData } from "@/lib/quarter-report"; +import { listQuarterReportingPeriods } from "@/lib/quarters"; +import { + MachineApiAuthError, + verifyMachineApiRequest, +} from "@/lib/machine-api/auth"; +import { + MachineApiNonceError, +} from "@/lib/machine-api/nonces"; +import { + MachineApiRateLimitError, + checkMachineApiRateLimit, +} from "@/lib/machine-api/rate-limit"; +import { getMachineReportSlice } from "@/lib/machine-api/report-slices"; +import { createAccountingX402Server } from "@/lib/machine-api/x402"; + +type RouteParams = { + params: Promise<{ id: string }>; +}; + +const RATE_LIMIT_WINDOW_MS = 60_000; +const RATE_LIMIT_MAX_REQUESTS = 30; + +function errorResponse(message: string, status = 400) { + return NextResponse.json({ error: message }, { status }); +} + +function getMachineApiErrorResponse(error: unknown) { + if (error instanceof MachineApiAuthError) { + return errorResponse(error.message, error.status); + } + + if (error instanceof MachineApiNonceError) { + return errorResponse(error.message, 409); + } + + if (error instanceof MachineApiRateLimitError) { + return errorResponse(error.message, 429); + } + + if (error instanceof Error) { + console.error("Machine API request failed", error); + return errorResponse("Machine API request failed.", 500); + } + + return errorResponse("Machine API request failed.", 500); +} + +async function handler(request: NextRequest, { params }: RouteParams) { + const { id } = await params; + const quarter = (await listQuarterReportingPeriods()).find( + (item) => item.id === id, + ); + + if (!quarter) { + return errorResponse("Report not found.", 404); + } + + if (quarter.status !== "published" || !isQuarterExportReady(quarter)) { + return errorResponse("Only published reports are available.", 403); + } + + try { + const payload = (await request.json().catch(() => null)) as { + auth?: unknown; + reportSlice?: unknown; + } | null; + + if (!payload || typeof payload.auth !== "object" || !payload.auth) { + return errorResponse("Machine API auth payload is required.", 400); + } + + const resource = `${request.nextUrl.pathname}${request.nextUrl.search}`; + const verified = await verifyMachineApiRequest({ + auth: payload.auth, + method: request.method, + quarterId: quarter.id, + reportSlice: payload.reportSlice, + resource, + }); + + await checkMachineApiRateLimit({ + key: `${verified.delegator}:${verified.agent}`, + maxRequests: RATE_LIMIT_MAX_REQUESTS, + windowMs: RATE_LIMIT_WINDOW_MS, + }); + + const report = await getQuarterReportData(quarter); + const response = getMachineReportSlice({ + quarter, + report, + reportSlice: verified.reportSlice, + }); + + return NextResponse.json({ + ...response, + access: { + agent: verified.agent, + delegatedBy: verified.delegator, + }, + }); + } catch (error) { + return getMachineApiErrorResponse(error); + } +} + +export async function POST(request: NextRequest, context: RouteParams) { + try { + const { config, server } = createAccountingX402Server(); + const paidHandler = withX402( + (paidRequest) => handler(paidRequest, context), + { + accepts: { + maxTimeoutSeconds: config.maxTimeoutSeconds, + network: config.network, + payTo: config.payTo, + price: config.price, + scheme: "exact", + }, + description: config.description, + }, + server, + ); + + return await paidHandler(request); + } catch (error) { + return getMachineApiErrorResponse(error); + } +} diff --git a/src/db/schema.ts b/src/db/schema.ts index 0ed0590..fdaaebf 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -143,6 +143,17 @@ export const reportAssistantRateLimits = pgTable( ], ); +export const machineApiRateLimits = pgTable( + "machine_api_rate_limits", + { + key: text("key").primaryKey(), + count: integer("count").default(0).notNull(), + resetAt: timestamp("reset_at", { withTimezone: true }).notNull(), + ...timestamps, + }, + (table) => [index("machine_api_rate_limits_reset_at_idx").on(table.resetAt)], +); + export const appUsers = pgTable( "app_users", { @@ -204,6 +215,33 @@ export const quarters = pgTable( ], ); +export const machineApiRequestNonces = pgTable( + "machine_api_request_nonces", + { + nonce: text("nonce").primaryKey(), + agentAddress: text("agent_address").notNull(), + delegatorAddress: text("delegator_address").notNull(), + quarterId: uuid("quarter_id").references(() => quarters.id, { + onDelete: "set null", + }), + reportSlice: text("report_slice").notNull(), + expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), + consumedAt: timestamp("consumed_at", { withTimezone: true }) + .defaultNow() + .notNull(), + ...timestamps, + }, + (table) => [ + index("machine_api_request_nonces_agent_idx").on( + sql`lower(${table.agentAddress})`, + ), + index("machine_api_request_nonces_delegator_idx").on( + sql`lower(${table.delegatorAddress})`, + ), + index("machine_api_request_nonces_expires_at_idx").on(table.expiresAt), + ], +); + export const quarterSyncStatuses = pgTable( "quarter_sync_statuses", { diff --git a/src/lib/auth/permissions.ts b/src/lib/auth/permissions.ts index ff9db13..42e9340 100644 --- a/src/lib/auth/permissions.ts +++ b/src/lib/auth/permissions.ts @@ -3,6 +3,7 @@ import "server-only"; import { and, isNull, sql } from "drizzle-orm"; import { createPublicClient, + defineChain, getAddress, http, isAddress, @@ -44,20 +45,60 @@ const HATS_ABI = [ }, ] as const; +function getEvmReadClient({ + chainId, + rpcUrl, +}: { + chainId: number; + rpcUrl: string; +}) { + return createPublicClient({ + chain: defineChain({ + id: chainId, + name: `Chain ${chainId}`, + nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" }, + rpcUrls: { + default: { + http: [rpcUrl], + }, + }, + }), + transport: http(rpcUrl), + }); +} + function getGnosisClient() { - const rpcUrl = process.env.GNOSIS_RPC_URL; + const daoShareRpcUrl = process.env.DAO_SHARE_RPC_URL; + const configuredChainId = process.env.DAO_SHARE_CHAIN_ID; + const rpcUrl = daoShareRpcUrl ?? process.env.GNOSIS_RPC_URL; if (!rpcUrl) { - throw new Error("GNOSIS_RPC_URL is required for permission checks"); + throw new Error( + "DAO_SHARE_RPC_URL or GNOSIS_RPC_URL is required for permission checks", + ); } - return createPublicClient({ - chain: gnosis, - transport: http(rpcUrl), - }); + if (daoShareRpcUrl && !configuredChainId) { + throw new Error("DAO_SHARE_CHAIN_ID is required when DAO_SHARE_RPC_URL is set"); + } + + if (!configuredChainId) { + return createPublicClient({ + chain: gnosis, + transport: http(rpcUrl), + }); + } + + const chainId = Number(configuredChainId); + + if (!Number.isInteger(chainId) || chainId <= 0) { + throw new Error("DAO_SHARE_CHAIN_ID must be a positive integer"); + } + + return getEvmReadClient({ chainId, rpcUrl }); } -async function hasDaoShares(address: `0x${string}`) { +export async function hasDaoShares(address: `0x${string}`) { const shareTokenAddress = process.env.DAO_SHARE_TOKEN_ADDRESS; if (!shareTokenAddress || !isAddress(shareTokenAddress)) { diff --git a/src/lib/machine-api/auth.ts b/src/lib/machine-api/auth.ts new file mode 100644 index 0000000..a3d2dd6 --- /dev/null +++ b/src/lib/machine-api/auth.ts @@ -0,0 +1,183 @@ +import "server-only"; + +import { getAddress, isAddress, verifyTypedData } from "viem"; + +import { hasDaoShares } from "@/lib/auth/permissions"; +import { consumeMachineApiNonce } from "@/lib/machine-api/nonces"; +import { getDelegationRegistryConfig, getRegistryDelegator } from "@/lib/machine-api/registry"; +import { + isMachineReportSlice, + type MachineReportSlice, +} from "@/lib/machine-api/report-slices"; + +export type MachineApiAuthPayload = { + agent?: unknown; + delegator?: unknown; + expiresAt?: unknown; + nonce?: unknown; + signature?: unknown; +}; + +export type VerifiedMachineApiRequest = { + agent: `0x${string}`; + delegator: `0x${string}`; + expiresAt: Date; + nonce: string; + reportSlice: MachineReportSlice; +}; + +export class MachineApiAuthError extends Error { + constructor( + message: string, + public status = 401, + ) { + super(message); + this.name = "MachineApiAuthError"; + } +} + +const ACCOUNTING_DATA_REQUEST_TYPES = { + AccountingDataRequest: [ + { name: "agent", type: "address" }, + { name: "delegator", type: "address" }, + { name: "method", type: "string" }, + { name: "resource", type: "string" }, + { name: "quarterId", type: "string" }, + { name: "reportSlice", type: "string" }, + { name: "nonce", type: "string" }, + { name: "expiresAt", type: "uint256" }, + { name: "chainId", type: "uint256" }, + ], +} as const; + +function requireString(value: unknown, field: string) { + if (typeof value !== "string" || value.trim().length === 0) { + throw new MachineApiAuthError(`${field} is required.`, 400); + } + + return value.trim(); +} + +function normalizeAddress(value: unknown, field: string) { + const address = requireString(value, field); + + if (!isAddress(address)) { + throw new MachineApiAuthError(`${field} must be a valid EVM address.`, 400); + } + + return getAddress(address); +} + +function parseExpiry(value: unknown) { + const expiresAt = Number(value); + + if (!Number.isInteger(expiresAt) || expiresAt <= 0) { + throw new MachineApiAuthError("expiresAt must be a Unix timestamp.", 400); + } + + if (expiresAt <= Math.floor(Date.now() / 1000)) { + throw new MachineApiAuthError("Machine API request has expired.", 401); + } + + return { + date: new Date(expiresAt * 1000), + timestamp: BigInt(expiresAt), + }; +} + +function parseSignature(value: unknown) { + const signature = requireString(value, "signature"); + + if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) { + throw new MachineApiAuthError( + "signature must be a 65-byte hex EVM signature.", + 400, + ); + } + + return signature as `0x${string}`; +} + +export async function verifyMachineApiRequest({ + auth, + method, + quarterId, + reportSlice, + resource, +}: { + auth: MachineApiAuthPayload; + method: string; + quarterId: string; + reportSlice: unknown; + resource: string; +}): Promise { + if (!isMachineReportSlice(reportSlice)) { + throw new MachineApiAuthError("Unsupported report slice.", 400); + } + + const registryConfig = getDelegationRegistryConfig(); + const agent = normalizeAddress(auth.agent, "agent"); + const delegator = normalizeAddress(auth.delegator, "delegator"); + const nonce = requireString(auth.nonce, "nonce"); + const signature = parseSignature(auth.signature); + const expiresAt = parseExpiry(auth.expiresAt); + + const validSignature = await verifyTypedData({ + address: agent, + domain: { + chainId: registryConfig.chainId, + name: "RaidGuild Member API Demo", + verifyingContract: registryConfig.address, + version: "1", + }, + message: { + agent, + chainId: BigInt(registryConfig.chainId), + delegator, + expiresAt: expiresAt.timestamp, + method, + nonce, + quarterId, + reportSlice, + resource, + }, + primaryType: "AccountingDataRequest", + signature, + types: ACCOUNTING_DATA_REQUEST_TYPES, + }).catch(() => { + throw new MachineApiAuthError("Invalid agent signature."); + }); + + if (!validSignature) { + throw new MachineApiAuthError("Invalid agent signature."); + } + + const currentDelegator = await getRegistryDelegator(agent); + + if (!currentDelegator || currentDelegator !== delegator) { + throw new MachineApiAuthError("Agent is not delegated by this member."); + } + + if (!(await hasDaoShares(delegator))) { + throw new MachineApiAuthError("Delegator is not a current member.", 403); + } + + await consumeMachineApiNonce({ + agentAddress: agent, + delegatorAddress: delegator, + expiresAt: expiresAt.date, + nonce, + quarterId, + reportSlice, + }); + + return { + agent, + delegator, + expiresAt: expiresAt.date, + nonce, + reportSlice, + }; +} + +export { ACCOUNTING_DATA_REQUEST_TYPES }; diff --git a/src/lib/machine-api/nonces.ts b/src/lib/machine-api/nonces.ts new file mode 100644 index 0000000..74080ee --- /dev/null +++ b/src/lib/machine-api/nonces.ts @@ -0,0 +1,57 @@ +import "server-only"; + +import { sql } from "drizzle-orm"; + +import { getDb } from "@/db"; + +export class MachineApiNonceError extends Error { + constructor(message = "Machine API request nonce has already been used.") { + super(message); + this.name = "MachineApiNonceError"; + } +} + +export async function consumeMachineApiNonce({ + agentAddress, + delegatorAddress, + expiresAt, + nonce, + quarterId, + reportSlice, +}: { + agentAddress: string; + delegatorAddress: string; + expiresAt: Date; + nonce: string; + quarterId: string; + reportSlice: string; +}) { + const result = await getDb().execute<{ nonce: string }>(sql` + with cleanup as ( + delete from machine_api_request_nonces + where expires_at < now() + ) + insert into machine_api_request_nonces ( + nonce, + agent_address, + delegator_address, + quarter_id, + report_slice, + expires_at + ) + values ( + ${nonce}, + ${agentAddress}, + ${delegatorAddress}, + ${quarterId}, + ${reportSlice}, + ${expiresAt} + ) + on conflict (nonce) do nothing + returning nonce + `); + + if (result.rows.length === 0) { + throw new MachineApiNonceError(); + } +} diff --git a/src/lib/machine-api/rate-limit.ts b/src/lib/machine-api/rate-limit.ts new file mode 100644 index 0000000..9b0eeea --- /dev/null +++ b/src/lib/machine-api/rate-limit.ts @@ -0,0 +1,50 @@ +import "server-only"; + +import { sql } from "drizzle-orm"; + +import { getDb } from "@/db"; + +export class MachineApiRateLimitError extends Error { + constructor() { + super("Machine API rate limit reached. Try again later."); + this.name = "MachineApiRateLimitError"; + } +} + +export async function checkMachineApiRateLimit({ + key, + maxRequests, + windowMs, +}: { + key: string; + maxRequests: number; + windowMs: number; +}) { + const result = await getDb().execute<{ + count: number; + }>(sql` + with next_reset as ( + select now() + (${windowMs} * interval '1 millisecond') as value + ) + insert into machine_api_rate_limits (key, count, reset_at) + values (${key}, 1, (select value from next_reset)) + on conflict (key) do update + set + count = case + when machine_api_rate_limits.reset_at <= now() then 1 + else machine_api_rate_limits.count + 1 + end, + reset_at = case + when machine_api_rate_limits.reset_at <= now() + then (select value from next_reset) + else machine_api_rate_limits.reset_at + end, + updated_at = now() + returning count + `); + const [row] = result.rows; + + if (Number(row?.count ?? 0) > maxRequests) { + throw new MachineApiRateLimitError(); + } +} diff --git a/src/lib/machine-api/registry.ts b/src/lib/machine-api/registry.ts new file mode 100644 index 0000000..cf5b43f --- /dev/null +++ b/src/lib/machine-api/registry.ts @@ -0,0 +1,65 @@ +import "server-only"; + +import { createPublicClient, defineChain, getAddress, http, isAddress } from "viem"; + +const DELEGATION_REGISTRY_ABI = [ + { + inputs: [{ internalType: "address", name: "agent", type: "address" }], + name: "delegatorOf", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, +] as const; + +const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; + +export function getDelegationRegistryConfig() { + const address = process.env.RG_DELEGATION_REGISTRY_ADDRESS; + const rpcUrl = process.env.RG_DELEGATION_REGISTRY_RPC_URL; + const chainIdValue = process.env.RG_DELEGATION_REGISTRY_CHAIN_ID ?? "84532"; + + if (!address || !isAddress(address)) { + throw new Error("RG_DELEGATION_REGISTRY_ADDRESS must be a valid EVM address"); + } + + if (!rpcUrl) { + throw new Error("RG_DELEGATION_REGISTRY_RPC_URL is required"); + } + + const chainId = Number(chainIdValue); + + if (!Number.isInteger(chainId) || chainId <= 0) { + throw new Error("RG_DELEGATION_REGISTRY_CHAIN_ID must be a positive integer"); + } + + return { + address: getAddress(address), + chainId, + rpcUrl, + }; +} + +export async function getRegistryDelegator(agent: `0x${string}`) { + const config = getDelegationRegistryConfig(); + const client = createPublicClient({ + chain: defineChain({ + id: config.chainId, + name: `Chain ${config.chainId}`, + nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" }, + rpcUrls: { default: { http: [config.rpcUrl] } }, + }), + transport: http(config.rpcUrl), + }); + const delegator = await client.readContract({ + abi: DELEGATION_REGISTRY_ABI, + address: config.address, + args: [agent], + functionName: "delegatorOf", + }); + const normalizedDelegator = getAddress(delegator); + + return normalizedDelegator === ZERO_ADDRESS ? null : normalizedDelegator; +} + +export { DELEGATION_REGISTRY_ABI }; diff --git a/src/lib/machine-api/report-slices.ts b/src/lib/machine-api/report-slices.ts new file mode 100644 index 0000000..a823fe2 --- /dev/null +++ b/src/lib/machine-api/report-slices.ts @@ -0,0 +1,151 @@ +import "server-only"; + +import type { QuarterReportData } from "@/lib/quarter-report"; +import type { QuarterSummary } from "@/lib/quarters"; + +export const MACHINE_REPORT_SLICES = [ + "quarter-summary", + "taxable-revenue", + "client-revenue", + "raid-revenue", + "provider-expenses", + "full-ledger", +] as const; + +export type MachineReportSlice = (typeof MACHINE_REPORT_SLICES)[number]; + +export type MachineReportResponse = { + data: unknown; + provenance: { + publishedAt: string; + quarter: string; + quarterId: string; + reportExportVersion: string; + reportSlice: MachineReportSlice; + source: "published-quarter-report"; + }; +}; + +export function isMachineReportSlice( + value: unknown, +): value is MachineReportSlice { + return ( + typeof value === "string" && + MACHINE_REPORT_SLICES.includes(value as MachineReportSlice) + ); +} + +function toNumber(value: string | null | undefined) { + if (!value) { + return 0; + } + + const number = Number(value); + + if (!Number.isFinite(number)) { + throw new Error(`Invalid usdAmount: ${value}`); + } + + return number; +} + +function sumUsd(rows: QuarterReportData["ledgerRows"]) { + return rows.reduce((total, row) => total + toNumber(row.usdAmount), 0); +} + +export function getMachineReportSlice({ + quarter, + report, + reportSlice, +}: { + quarter: QuarterSummary; + report: QuarterReportData; + reportSlice: MachineReportSlice; +}): MachineReportResponse { + const base = { + provenance: { + publishedAt: quarter.publishedAt ?? quarter.updatedAt, + quarter: quarter.label, + quarterId: quarter.id, + reportExportVersion: "2026-q1-v1", + reportSlice, + source: "published-quarter-report" as const, + }, + }; + + if (reportSlice === "quarter-summary") { + return { + ...base, + data: { + balances: report.balances, + expenseBreakdown: report.expenseBreakdown, + metrics: report.metrics, + providerExpenses: report.providerExpenses, + raidEconomics: report.raidEconomics, + ripExpenses: report.ripExpenses, + topRaids: report.topRaids, + }, + }; + } + + if (reportSlice === "taxable-revenue") { + return { + ...base, + data: { + metric: "taxableRevenue", + totalUsd: report.metrics.revenue, + rows: report.ledgerRows.filter((row) => + ["member_dues", "raid_revenue"].includes(row.category ?? ""), + ), + }, + }; + } + + if (reportSlice === "client-revenue") { + const rows = report.ledgerRows.filter( + (row) => row.category === "member_dues", + ); + + return { + ...base, + data: { + metric: "memberDuesRevenue", + rows, + totalUsd: sumUsd(rows), + }, + }; + } + + if (reportSlice === "raid-revenue") { + const rows = report.ledgerRows.filter( + (row) => row.category === "raid_revenue", + ); + + return { + ...base, + data: { + ledgerRows: rows, + metric: "raidRevenue", + rows: report.topRaids, + totalUsd: sumUsd(rows), + }, + }; + } + + if (reportSlice === "provider-expenses") { + return { + ...base, + data: { + rows: report.providerExpenses, + totalUsd: report.expenseBreakdown.providerExpenses, + }, + }; + } + + return { + ...base, + data: { + rows: report.ledgerRows, + }, + }; +} diff --git a/src/lib/machine-api/x402.ts b/src/lib/machine-api/x402.ts new file mode 100644 index 0000000..bc9016e --- /dev/null +++ b/src/lib/machine-api/x402.ts @@ -0,0 +1,77 @@ +import "server-only"; + +import { HTTPFacilitatorClient, x402ResourceServer } from "@x402/core/server"; +import { registerExactEvmScheme } from "@x402/evm/exact/server"; +import { getAddress, isAddress } from "viem"; + +type AccountingX402Server = ReturnType; + +let cachedServer: AccountingX402Server | null = null; + +export function getAccountingX402Config() { + const payTo = process.env.X402_ACCOUNTING_PAY_TO_ADDRESS; + + if (!payTo || !isAddress(payTo)) { + throw new Error("X402_ACCOUNTING_PAY_TO_ADDRESS must be a valid EVM address"); + } + + const chainIdValue = process.env.X402_ACCOUNTING_CHAIN_ID ?? "84532"; + const chainId = Number(chainIdValue); + + if (!Number.isInteger(chainId) || chainId <= 0) { + throw new Error("X402_ACCOUNTING_CHAIN_ID must be a positive integer"); + } + + const maxTimeoutSeconds = Number( + process.env.X402_ACCOUNTING_MAX_TIMEOUT_SECONDS ?? 60, + ); + + if (!Number.isInteger(maxTimeoutSeconds) || maxTimeoutSeconds <= 0) { + throw new Error( + "X402_ACCOUNTING_MAX_TIMEOUT_SECONDS must be a positive integer", + ); + } + + return { + chainId, + description: + process.env.X402_ACCOUNTING_DESCRIPTION ?? + "Access published RaidGuild accounting report data", + facilitatorUrl: + process.env.X402_FACILITATOR_URL ?? "https://x402.org/facilitator", + maxTimeoutSeconds, + network: `eip155:${chainId}` as const, + payTo: getAddress(payTo), + price: process.env.X402_ACCOUNTING_REPORT_PRICE ?? "0.01", + }; +} + +export function getPublicAccountingX402Config() { + const config = getAccountingX402Config(); + + return { + chainId: config.chainId, + facilitatorUrl: config.facilitatorUrl, + network: config.network, + payTo: config.payTo, + price: config.price, + }; +} + +function buildAccountingX402Server() { + const config = getAccountingX402Config(); + const facilitatorClient = new HTTPFacilitatorClient({ + url: config.facilitatorUrl, + }); + const server = new x402ResourceServer(facilitatorClient); + + registerExactEvmScheme(server, { networks: [config.network] }); + + return { config, server }; +} + +export function createAccountingX402Server() { + cachedServer ??= buildAccountingX402Server(); + + return cachedServer; +}