0.7.53
This commit is contained in:
128
docs/reports/DEX_COVERAGE_GLOBAL_WATCHLIST_0_7_53.md
Normal file
128
docs/reports/DEX_COVERAGE_GLOBAL_WATCHLIST_0_7_53.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# DEX coverage global watchlist — `0.7.53`
|
||||
|
||||
## Objet
|
||||
|
||||
Ce rapport accompagne la clôture `0.7.53 pump_swap`. Il sépare les anomalies bloquantes des files de travail futures.
|
||||
|
||||
`pump_swap` est clos côté transaction/log decoder et matérialisation métier. Les lignes restantes de surveillance globale ne sont pas des erreurs PumpSwap.
|
||||
|
||||
## Résultat de clôture PumpSwap
|
||||
|
||||
Validation rapportée :
|
||||
|
||||
```text
|
||||
cargo test -p kb_lib -> 421 passed / 0 failed
|
||||
cargo clippy -p kb_lib --all-targets -- -D warnings -> OK
|
||||
```
|
||||
|
||||
Replay élargi rapporté :
|
||||
|
||||
```text
|
||||
1189 replayed
|
||||
0 decode skipped
|
||||
1189 ledger upserts
|
||||
967 unsafe ledger rows
|
||||
928 trades
|
||||
13 liquidity
|
||||
8 lifecycle
|
||||
0 tokenAccount
|
||||
3700 candle upserts
|
||||
instructionObservations = 25724
|
||||
resetDeleted = 5622
|
||||
catalog = 76 tokens / 80 pools / 80 pairs
|
||||
```
|
||||
|
||||
Checks PumpSwap attendus vides :
|
||||
|
||||
- decoded events sans coverage ;
|
||||
- fallback `upstream_git` couvert localement ;
|
||||
- successful trade candidates sans trade et sans raison explicite ;
|
||||
- failed transaction matérialisée en business trade ;
|
||||
- non-swap matérialisé en trade ;
|
||||
- multi-target materialization.
|
||||
|
||||
`buy_exact_quote_in` :
|
||||
|
||||
```text
|
||||
pump_swap_anchor_buy_event -> 168 decoded / 167 trades / 1 failed tx
|
||||
instruction_bounds_only -> decoded-only, sans trade
|
||||
```
|
||||
|
||||
## Non-régression Raydium
|
||||
|
||||
Les checks ciblés Raydium AMM v4 / CLMM / CPMM normalisés sont vides. Il n'y a pas de correction Raydium à inclure dans `0.7.53`.
|
||||
|
||||
Lecture correcte :
|
||||
|
||||
- `raydium_amm_v4.swap_base_in_v2` : observé et matérialisé dans le corpus courant ;
|
||||
- `raydium_clmm.swap_v2` : observé et matérialisé dans le corpus courant ;
|
||||
- `raydium_cpmm.swap_base_input` : observé et matérialisé dans le corpus courant ;
|
||||
- les entrées `upstream_git_mapped_unverified` avec `observed_count=0` sont des entrées registry non observées, pas des régressions.
|
||||
|
||||
## Gaps locaux reportés
|
||||
|
||||
La requête globale `local decoded events without coverage` retourne des gaps Meteora connus et reportés :
|
||||
|
||||
```text
|
||||
meteora_dlmm.swap
|
||||
meteora_damm_v2.instruction_audit
|
||||
meteora_damm_v2.swap
|
||||
```
|
||||
|
||||
Décision : ne pas les corriger dans `0.7.53`. Ils seront repris dans les tranches Meteora futures.
|
||||
|
||||
## Backlog upstream observé
|
||||
|
||||
La file `upstream_git.instruction_match` indique les prochaines surfaces à prioriser. Au moment de la clôture :
|
||||
|
||||
| Priorité | Surface | Indice principal | Décision |
|
||||
|---:|---|---|---|
|
||||
| 1 | `pump_fees` | `get_fees` très fréquent | Prochaine tranche recommandée ; aucun trade/candle direct attendu. |
|
||||
| 2 | `pump_fun` | creator fees, migrate, set/admin creator | Tranche launch/bonding séparée. |
|
||||
| 3 | `jupiter_swap` | `route_v2`, `create_token_account`, `route` | Agrégateur/router ; éviter double-count DEX effectifs. |
|
||||
| 4 | `dflow_aggregator_v4`, `onchain_labs_dex_v2` | swaps/route helpers | Surfaces secondaires après Pump/Meteora. |
|
||||
| 5 | `orca_whirlpools` | faibles occurrences | À reprendre plus tard avec corpus dédié. |
|
||||
|
||||
## Observations non attribuées
|
||||
|
||||
Les observations avec `decoder_code` vide ne doivent pas être traitées comme des discriminants Anchor confirmés. Elles exigent d'abord :
|
||||
|
||||
1. inspection de `sample_signature` ;
|
||||
2. extraction du `program_id` effectif ;
|
||||
3. comparaison aux IDL locales `idls/` et aux sources Git ;
|
||||
4. ajout d'un decoder ou d'un statut explicite seulement si le programme est identifié.
|
||||
|
||||
## Source IDL locale
|
||||
|
||||
Le répertoire `idls/` est désormais une source locale de savoir. Les IDL téléchargées depuis Solscan doivent être utilisées en plus des liens Git, avec la règle suivante : une IDL donne une surface possible, mais la matérialisation métier exige corpus, replay et SQL.
|
||||
|
||||
## Vérification `raydium_pool_v4.json`
|
||||
|
||||
Fichier Git vérifié : `0xfnzero/sol-parser-sdk/idls/raydium_pool_v4.json`.
|
||||
|
||||
Constats :
|
||||
|
||||
- la page GitHub annonce `3400` lignes / environ `101 KB` ;
|
||||
- le fichier expose notamment `swapBaseIn`, `addLiquidity` et des comptes OpenBook/Serum (`OpenBookMarket`, `OpenBookBids`, `OpenBookAsks`, etc.) ;
|
||||
- la chaîne du program id AMM v4 `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` n'apparaît pas dans la page GitHub consultée ;
|
||||
- aucun fichier local `idls/raydium_*.json` ne porte le nom `raydium_pool_v4` ;
|
||||
- les fichiers Raydium locaux présents sont `raydium_clmm`, `raydium_cpmm`, `raydium_launchlab` et `raydium_lock`.
|
||||
|
||||
Table locale :
|
||||
|
||||
| Fichier local | Name IDL | Address IDL | Taille | SHA-256 |
|
||||
|---|---|---|---:|---|
|
||||
| `idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json` | `raydium_clmm` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | 86240 | `d3f265adc9e4d7b1a28641444800a60bcdeb921ef136855fe8dea4532afa18bd` |
|
||||
| `idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json` | `raydium_cp_swap` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | 32066 | `5b709dfe7b0df67c9e29655307ae877244d3bb8022056ee01c754f0e36264d9c` |
|
||||
| `idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json` | `raydium_launchpad` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | 68052 | `c1465a10f38912413f55f04a68536c989992add1b3d48cba86dca97212a482da` |
|
||||
| `idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json` | `raydium_liquidity_locking` | absent | 10621 | `da45fd5eaf726e5e2ead7280c04588ebd4224d203304cdc8217b012b8130e1f1` |
|
||||
|
||||
Décision : `raydium_pool_v4.json` ne correspond à aucun fichier JSON local actuel. Il reste une source annexe d'audit AMM v4/strategy/wrapper à traiter plus tard, sans bloquer `0.7.53`.
|
||||
|
||||
## SQL associé
|
||||
|
||||
Voir :
|
||||
|
||||
```text
|
||||
validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql
|
||||
```
|
||||
166
docs/reports/PUMP_SWAP_EVENT_COVERAGE_REPORT.md
Normal file
166
docs/reports/PUMP_SWAP_EVENT_COVERAGE_REPORT.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# PumpSwap event coverage report — `0.7.53`
|
||||
|
||||
## Scope
|
||||
|
||||
- Version cible : `0.7.53`.
|
||||
- Surface unique : `pump_swap`.
|
||||
- Program id unique : `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA`.
|
||||
- Phasage : une version = un `program_id`.
|
||||
- `raydium_pool_v4.json` reste repoussé vers la fin du phasage et ne bloque pas cette tranche.
|
||||
|
||||
## Sources vérifiées / à vérifier pendant la fermeture
|
||||
|
||||
- `kb_lib/src/constants.rs`, notamment `PUMP_SWAP_PROGRAM_ID` et `SOLSCAN_ACCOUNT_SOURCES`.
|
||||
- Registre upstream local généré : `kb_lib/src/upstream_registry_generated.rs`.
|
||||
- Sources Git/IDL externes disponibles : Pump public docs / IDL, Carbon, fnzero, Pinax, HODL Warden si présents dans le registre.
|
||||
- IDL locale : `idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json`, téléchargée depuis Solscan et versionnée dans le workspace.
|
||||
- Solscan IDL du program id PumpSwap si disponible.
|
||||
- Corpus local Demo3 + backfills signature/pool.
|
||||
- `k_sol_dex_event_coverage_entries` après replay forcé.
|
||||
|
||||
Les sources Git/IDL/Solscan sont des indices. La fermeture métier exige corpus local, replay et SQL.
|
||||
|
||||
## Couverture locale ajoutée
|
||||
|
||||
Le decoder local `kb_lib/src/dex/pump_swap.rs` ne se limite plus à `buy`/`sell`. Il reconnaît maintenant les discriminants d’instruction suivants et produit un event spécialisé `pump_swap.<entry_name>`.
|
||||
|
||||
| Entry | Kind | Discriminator | Target attendu | Matérialisation |
|
||||
|---|---|---:|---|---|
|
||||
| `admin_set_coin_creator` | instruction | `f228759149606968` | admin/config | decoded specialized non-trade |
|
||||
| `admin_update_token_incentives` | instruction | `d10b7357d5177ccc` | reward/admin | decoded specialized non-trade |
|
||||
| `buy` | instruction | `66063d1201daebea` | trade | trade/candle seulement depuis montants exacts |
|
||||
| `buy_exact_quote_in` | instruction | `c62e1552b4d9e870` | trade conditionnel | matérialisé seulement si un `BuyEvent` Anchor exact est présent (`amountSource=pump_swap_anchor_buy_event`) ; sinon decoded-only `instruction_bounds_only` avec `skipTradeReason` |
|
||||
| `claim_cashback` | instruction | `253a237ebe35e4c5` | reward | decoded specialized non-trade |
|
||||
| `claim_token_incentives` | instruction | `1004471ccc01281b` | reward | decoded specialized non-trade |
|
||||
| `close_user_volume_accumulator` | instruction | `f945a4da9667548a` | reward | decoded specialized non-trade |
|
||||
| `collect_coin_creator_fee` | instruction | `a039592ab58b2b42` | fee | decoded specialized non-trade |
|
||||
| `create_config` | instruction | `c9cff3724b6f2fbd` | admin/config | decoded specialized non-trade |
|
||||
| `create_pool` | instruction | `e992d18ecf6840bc` | pool lifecycle | decoded specialized non-trade |
|
||||
| `deposit` | instruction | `f223c68952e1f2b6` | liquidity add | decoded specialized non-trade |
|
||||
| `disable` | instruction | `b9adbb5ad80feee9` | admin/config | decoded specialized non-trade |
|
||||
| `extend_account` | instruction | `ea66c2cb96483ee5` | admin/config | decoded specialized non-trade |
|
||||
| `init_user_volume_accumulator` | instruction | `5e06ca73ff60e8b7` | reward | decoded specialized non-trade |
|
||||
| `migrate_pool_coin_creator` | instruction | `d0089f044aaf103a` | admin/migration | decoded specialized non-trade |
|
||||
| `sell` | instruction | `33e685a4017f83ad` | trade | trade/candle seulement depuis montants exacts |
|
||||
| `set_coin_creator` | instruction | `d295802dbc3a4eaf` | admin/config | decoded specialized non-trade |
|
||||
| `set_reserved_fee_recipients` | instruction | `6faca2e87259d58e` | admin/config | decoded specialized non-trade |
|
||||
| `set_reserved_fee_recipient` | instruction | `cfbdb247a77a44b4` | admin/config | local log proof only: `Instruction: SetReservedFeeRecipient` ; absent from checked Solscan IDL raw ; decoded specialized non-trade |
|
||||
| `sync_user_volume_accumulator` | instruction | `561fc057a3574fee` | reward | decoded specialized non-trade |
|
||||
| `toggle_cashback_enabled` | instruction | `7367e0ffbd5956c3` | admin/config | decoded specialized non-trade |
|
||||
| `toggle_mayhem_mode` | instruction | `01096fd0641fffa3` | admin/config | decoded specialized non-trade |
|
||||
| `transfer_creator_fees_to_pump` | instruction | `8b348655e4e56cf1` | fee | decoded specialized non-trade |
|
||||
| `transfer_creator_fees_to_pump_v2` | instruction | `01214eb921432c5c` | fee | Solscan IDL proof + local log proof: `transfer_creator_fees_to_pump_v2` / `Instruction: TransferCreatorFeesToPumpV2` ; decoded specialized non-trade |
|
||||
| `update_admin` | instruction | `a1b028d53cb8b3e4` | admin/config | decoded specialized non-trade |
|
||||
| `update_buyback_config` | instruction | `fbe0ab92a01a71e9` | admin/config | Solscan IDL proof + local log proof: `update_buyback_config` / `Instruction: UpdateBuybackConfig` ; decoded specialized non-trade |
|
||||
| `update_fee_config` | instruction | `68b867f258976b14` | admin/config | decoded specialized non-trade |
|
||||
| `withdraw` | instruction | `b712469c946da122` | liquidity remove | decoded specialized non-trade |
|
||||
|
||||
## Upstream Program-data events à statut explicite
|
||||
|
||||
Ces events sont listés dans le registre upstream. Ils doivent apparaître avec un statut explicite dans la coverage DB après sync/replay. Ils ne doivent pas créer de doublons trade/candle tant que l’instruction locale spécialisée couvre déjà le DEX effectif.
|
||||
|
||||
| Event | Discriminator | Statut `0.7.53` |
|
||||
|---|---:|---|
|
||||
| `admin_set_coin_creator_event` | `2ddc5d181961ac68` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `admin_update_token_incentives_event` | `93fa6c78f71d43de` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `buy_event` | `67f4521f2cf57777` | upstream listed ; audit/decoded-only to avoid duplicate trade |
|
||||
| `claim_cashback_event` | `e2d6f62107f293e5` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `claim_token_incentives_event` | `4facf631cd5bcee8` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `close_user_volume_accumulator_event` | `929fbdac925838f4` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `collect_coin_creator_fee_event` | `e8f5c2eeeada3a59` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `create_config_event` | `6b34598137e25116` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `create_pool_event` | `b1310cd2a076a774` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `deposit_event` | `78f83d531f8e6b90` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `disable_event` | `6bfdc14ce4ca1b68` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `extend_account_event` | `6161d7905d92167c` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `init_user_volume_accumulator_event` | `86240d48e86582d8` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `migrate_pool_coin_creator_event` | `aadd52c793a5f72e` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `reserved_fee_recipients_event` | `2bbcfa12dd4bbb5f` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `sell_event` | `3e2f370aa503dc2a` | upstream listed ; audit/decoded-only to avoid duplicate trade |
|
||||
| `set_bonding_curve_coin_creator_event` | `f2e7eb664163bdd3` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `set_metaplex_coin_creator_event` | `966bc77b7ccf66e4` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `sync_user_volume_accumulator_event` | `c57aa77c74515bff` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `update_admin_event` | `e198ab57f63f42ea` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `update_fee_config_event` | `5a1741233ef4bcd0` | upstream listed ; decoded-only/status explicite après replay |
|
||||
| `withdraw_event` | `1609851aa02c47c0` | upstream listed ; decoded-only/status explicite après replay |
|
||||
|
||||
## Matérialisation
|
||||
|
||||
- `pump_swap.buy`, `pump_swap.sell` et `pump_swap.buy_exact_quote_in` peuvent alimenter `k_sol_trade_events`, mais seulement depuis des montants exacts.
|
||||
- `pump_swap.buy_exact_quote_in` est matérialisé quand le log Anchor `BuyEvent` fournit `baseAmountOutRaw` et `userQuoteAmountInRaw` pour `ixName=buy_exact_quote_in`. Les rows `instruction_bounds_only` restent decoded-only avec `skipTradeReason` explicite.
|
||||
- Les bornes d’instruction (`maxQuoteAmountIn`, `spendableQuoteAmountIn`, `minQuoteAmountOut`, `minBaseAmountOut`) ne sont pas considérées comme des montants exacts suffisants pour créer un trade/candle.
|
||||
- La matérialisation doit passer par les résolutions exactes existantes : token transfer deltas, vault/account balance deltas ou autre source d’amount explicitement prouvée.
|
||||
- Les transactions failed restent decoded-only via l’actionability `failed_transaction`.
|
||||
- Les non-trades restent hors `k_sol_trade_events` et hors `k_sol_pair_candles`.
|
||||
|
||||
## SQL de fermeture
|
||||
|
||||
Le fichier dédié est :
|
||||
|
||||
```text
|
||||
validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql
|
||||
```
|
||||
|
||||
Les requêtes d’anomalies attendues vides couvrent :
|
||||
|
||||
- fallback upstream résiduel sur instruction couverte localement ;
|
||||
- failed tx avec trade ;
|
||||
- non-swap avec trade ;
|
||||
- decoded without coverage ;
|
||||
- successful non-materialized inexpliqué ;
|
||||
- multi-target materialization ;
|
||||
- successful swap exploitable sans trade et sans raison explicite.
|
||||
|
||||
## Delta post-replay `0.7.53-pump_swap-delta-1`
|
||||
|
||||
Le replay local a révélé trois écarts corrigés par ce delta :
|
||||
|
||||
- `pump_swap.toggle_cashback_enabled` était classé à la fois `reward` et `admin`; il devient admin-only pour respecter l’invariant single-target.
|
||||
- `pump_swap.buy_exact_quote_in` réussi non matérialisé reçoit maintenant un `skipTradeReason` explicite et reste decoded-only tant que les montants exacts ne sont pas prouvés.
|
||||
- `k_sol_instruction_observations.instruction_name` reçoit un mapping PumpSwap par discriminant pour que la requête de coverage locale ne sorte plus des noms vides. Le discriminant `e445a52e51cb9a1d` reste marqué comme transport technique Anchor self-CPI. Les trois discriminants initialement inconnus ne doivent plus sortir en `observed_unknown_*` : deux sont confirmés par le raw Solscan IDL et un reste conservé comme local-log-only.
|
||||
|
||||
`pump_swap.migrate_pool_coin_creator` est également forcé en admin/config, pas lifecycle, car il modifie l’attribution coin creator et ne représente pas une migration de pool échangeable.
|
||||
|
||||
## Delta post-replay `0.7.53-pump_swap-delta-2`
|
||||
|
||||
- Les trois discriminants initialement inconnus (`01214eb921432c5c`, `fbe0ab92a01a71e9`, `cfbdb247a77a44b4`) ont été provisoirement couverts decoded-only pour supprimer les gaps locaux sans inventer de métier.
|
||||
- `toggle_cashback_enabled` est corrigé aussi dans la famille coverage (`admin_config`) afin de refléter la matérialisation admin-only.
|
||||
|
||||
## Delta post-replay `0.7.53-pump_swap-delta-4`
|
||||
|
||||
- Le corpus local donne maintenant le nom métier via logs Anchor : `TransferCreatorFeesToPumpV2`, `UpdateBuybackConfig`, `SetReservedFeeRecipient`.
|
||||
- Ces trois discriminants deviennent des instructions locales spécialisées : `pump_swap.transfer_creator_fees_to_pump_v2`, `pump_swap.update_buyback_config`, `pump_swap.set_reserved_fee_recipient`.
|
||||
- Recomparaison raw Solscan IDL : `transfer_creator_fees_to_pump_v2` et `update_buyback_config` sont présents dans l’IDL. `set_reserved_fee_recipient` n’est pas listé dans ce raw ; il est gardé sur preuve log locale, possiblement instruction historique/supprimée ou non exposée par cette version IDL.
|
||||
- `transfer_creator_fees_to_pump_v2` est fee/non-trade ; `update_buyback_config` et `set_reserved_fee_recipient` sont admin/config non-trade.
|
||||
|
||||
|
||||
|
||||
## Clôture finale `0.7.53`
|
||||
|
||||
La tranche est clôturée après les deltas de consolidation :
|
||||
|
||||
- `buy_exact_quote_in` route désormais vers la matérialisation pool/pair/trade quand `amountSource=pump_swap_anchor_buy_event`;
|
||||
- les montants du `BuyEvent` sont normalisés par rapport à l’ordre local de la paire pour éviter les inversions base/quote ;
|
||||
- les events Anchor PumpSwap sont décodés comme events autonomes audit-only ;
|
||||
- `claim_token_incentives_event` possède un test synthétique de matérialisabilité reward si un corpus réussi apparaît ;
|
||||
- `sync_user_volume_accumulator_event` reste implémenté mais non observé malgré un backfill élargi sur l’instruction ;
|
||||
- les tests synthétiques couvrent les instructions/events IDL non observés localement ;
|
||||
- `cargo test -p kb_lib` a été validé à `421 passed / 0 failed` et clippy est OK côté utilisateur.
|
||||
|
||||
Résultats de validation rapportés après corpus élargi :
|
||||
|
||||
```text
|
||||
pump_swap decoded without coverage = vide
|
||||
pump_swap upstream fallback couvert localement = vide
|
||||
successful trade candidates sans trade = vide
|
||||
failed tx avec business trade = vide
|
||||
non-swap matérialisé en trade = vide
|
||||
multi-target materialization = vide
|
||||
buy_exact_quote_in / pump_swap_anchor_buy_event = 168 decoded / 167 trades / 1 failed tx
|
||||
```
|
||||
|
||||
Les fichiers de surveillance à conserver sont :
|
||||
|
||||
- `validation_sql/SQL_VALIDATION_PUMP_SWAP_0_7_53.sql` ;
|
||||
- `validation_sql/SQL_VALIDATION_DEX_COVERAGE_GLOBAL_0_7_53.sql` ;
|
||||
- `docs/reports/DEX_COVERAGE_GLOBAL_WATCHLIST_0_7_53.md`.
|
||||
Reference in New Issue
Block a user