Undergraduate research · 2025
Smart Contract Residual Attention NetSCRAN
A deep-learning model that flags eleven kinds of security vulnerability in Solidity smart contracts, scoring 0.87 macro F1 against 0.75 for the previously published model on the same dataset.
- Period
- December 2024 — May 2025
- My role
- First author; trained the PyTorch ensemble
- Built with
- PyTorch · multi-head attention · focal loss · SMOTE
- 0.87macro F1vs 0.75 for the previous model on the same data
- 95%of vulnerabilities caughtaverage across the eleven classes
- 0.997macro AUChow well it ranks risky contracts first
In brief
SCRAN reads a Solidity smart contract, reduced to 240 static features, and flags which of eleven vulnerability classes it contains. A contract can carry several at once, so each class gets its own independent answer.
The hard part is imbalance: some classes appear in under one percent of the 111,897 contracts in BCCC-SCsVul-2024. Focal loss, oversampling, and per-class thresholds do most of the work, and a three-model ensemble steadies the result. On a held-out test split that never saw resampling or threshold tuning, it reaches a macro F1 of 0.8703 and a macro AUC of 0.9973.
The domain is smart contracts; the skills are general. Learning from badly imbalanced data, choosing metrics that don't flatter the model, and reporting the trade-offs rather than hiding them carry over to any problem where rare events are the ones that matter.
Results
| Vulnerability | Precision to recall span | Precision | Recall | F1 | AUC |
|---|---|---|---|---|---|
| SecureNonVulnerable | 0.9775 | 0.9968 | 0.9871 | 0.9998 | |
| Integer under/overflowIntegerUO | 0.9563 | 0.9926 | 0.9741 | 0.9996 | |
| Denial of serviceDenialOfService | 0.9621 | 0.9857 | 0.9737 | 0.9995 | |
| Call to unknownCallToUnknown | 0.9509 | 0.9865 | 0.9684 | 0.9995 | |
| Re-entrancyReentrancy | 0.9567 | 0.9799 | 0.9681 | 0.9994 | |
| Weak access modifierWeakAccessMod | 0.8736 | 0.9896 | 0.9280 | 0.9999 | |
| Gas exceptionGasException | 0.7598 | 0.9425 | 0.8414 | 0.9954 | |
| External bugExternalBug | 0.7306 | 0.9254 | 0.8166 | 0.9967 | |
| Mishandled exceptionMishandledException | 0.7006 | 0.9277 | 0.7983 | 0.9940 | |
| Unused returnUnusedReturn | 0.6497 | 0.9519 | 0.7723 | 0.9969 | |
| Transaction order dependenceTOD | 0.6213 | 0.9161 | 0.7404 | 0.9942 | |
| Timestamp dependenceTimestamp | 0.5434 | 0.8914 | 0.6752 | 0.9924 | |
| Macro average12 classes | 0.8069 | 0.9572 | 0.8703 | 0.9973 |
The shape of that chart is the finding. Recall never drops below 0.89, so the model rarely misses a vulnerability that is present. Precision is what varies, and every long span is a class the model over-reports. Timestamp dependence is the worst at 0.5434 precision: close to one wrong flag for every right one.
AUC stays above 0.99 everywhere, including those weak classes. The ranking is good even where the decision is not, so the failure is in where the threshold lands rather than in what the network learned.
Against EGA profiling (HajiHosseinKhani et al., 2024), the previously published model on this same dataset, SCRAN raises macro F1 from 0.75 to 0.87 and has the higher F1 on every one of the twelve classes. EGA profiling keeps the edge on macro precision (0.86 against 0.81), and it was built to be explainable, which SCRAN is not.
One trade is worth stating: a single model scores a slightly higher macro F1 (0.8762) than the ensemble (0.8703). The ensemble was kept for its higher AUC and steadier behaviour on the rare classes.
Architecture
A contract arrives as 240 numbers. The question is which of eleven failure modes those numbers imply — and a contract can be guilty of several at once, so the eleven answers have to be independent rather than a single choice among classes.
01
Static features
x ∈ ℝ²⁴⁰
02
Residual FC ×2
LayerNorm · ReLU · Dropout
03
Multi-head attention
softmax(QKᵀ/√d)·V
04
Class-specific heads
αⱼ = softmax(aⱼ)
05
Per-class sigmoid
ŷ ∈ [0,1]¹¹
Ensemble
- γ = 2.0
- γ = 2.5
- γ = 3.0
Three instances of the network above, each trained under a different focal focusing parameter and learning rate. Their logits are combined with per-class weights taken from validation F1, so a model that is good at re-entrancy and bad at timestamps is trusted accordingly.
- 01Static features. Control-flow analysis and opcode statistics, z-score normalised per feature.
- 02Residual FC ×2. Two fully connected blocks with skip connections, projected when the dimensions do not line up.
- 03Multi-head attention. Positional encoding, scaled dot-product attention, then a residual LayerNorm and a GELU feed-forward block.
- 04Class-specific heads. Eleven attention heads, one per vulnerability, each weighting the feature dimensions independently.
- 05Per-class sigmoid. Thresholds are chosen per class off the precision–recall curve rather than left at 0.5.
Limits
The model never sees a contract, only 240 numbers extracted from one. That is why the classes it struggles with are the ones defined by execution context rather than structure. Dynamic traces or a graph encoder over the control-flow graph would give it what it is missing, and that is the first change I would make.
Every number here comes from one corpus, and SCRAN trades away some explainability: its attention heads say which features mattered, which is not a reason an auditor can act on. The paper covers both, along with the comparisons against prior work.
The paper
Acknowledgements
Written with Dr. Irfan Khan, who advised the work, and with help from graduate students Zahid Hussain and Shorya Agarwal.
Read the paper






