neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
# Changelog

All notable changes to the Neo DevPack for Solidity will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.22.0] - 2026-06-17

Runtime-fidelity & audit-fix release: a full systemic audit (22 findings,
all fixed) closes the gap between "passes in the embedded simulator" and
"runs correctly on a real Neo N3 node", and the toolchain target is
aligned to Neo N3 **v3.10.0**. Every fix was TDD'd (test first, watch it
fail, implement, watch it pass) and the full workspace stays green
(1881 tests, 0 failures; clippy + fmt clean).

### 🔴 Runtime simulator — 7 critical fidelity fixes

The embedded NeoVM runtime is the oracle every compiler test trusts; these
were the highest-impact divergences from a real node.

- **`StdLib.serialize` now emits the Neo N3 BinarySerializer wire format**
  (type-tagged little-endian), not JSON. The previous `serde_json` output
  round-tripped inside the simulator but was byte-incompatible on-chain —
  storage keys derived from serialized values, length checks, and
  inter-contract interop all silently diverged. `jsonSerialize` stays JSON
  for callers that explicitly want it. New `neo_binary_serialize`/
  `deserialize` helpers with Neo's 7-bit-group big-endian varint.
- **`CheckSig`/`CheckMultisig` verify against an injectable transaction
  signing hash.** The previous synthetic hash (`SHA256(bytecode‖account‖
  counter)`) matched no real signature, so signature verification was
  effectively untestable. New `override_signing_hash()` host API; default
  preserves the synthetic hash for backward compatibility.
- **`CreateMultisigAccount` builds the real verification script** (`PUSHINT
  m / PUSHDATA pk_i / PUSHINT n / SYSCALL CheckMultisig`) and returns
  `RIPEMD160(SHA256(script))`, instead of the wrong `SHA256(m‖pubkeys)
  [..20]`. Shares a `append_pushdata` helper with the (already-correct)
  `CreateStandardAccount`.
- **Storage gas aligned to Neo N3 mainnet: 100_000/byte** (was 100/byte,
  ~1000× too cheap). `RuntimeConfig::default().gas_limit` raised 10M → 1B
  (≈ `MaxTransactionSystemFee`) so realistic contracts don't OOG.
- **CallFlags enforced: `Storage.Put`/`Delete` FAULT without `WriteStates`.**
  `GetCallFlags` returns the active flags instead of a hard-coded `0x0F`.
  New `override_call_flags()` host API lets tests simulate a `staticcall`-
  shaped read-only context — the staticcall-could-write-storage trap is
  now impossible.
- **Storage snapshot/rollback on inner-call revert.** A faulting callee's
  dirty writes are now discarded to the call-boundary snapshot
  (`CallFrame.storage_snapshot` + `dispatch_exception` unwind), matching
  Neo N3. Previously they leaked into the caller's overlay and got
  committed at top-level halt.
- **`StdLib.deserialize` decodes the binary format** (inverse of the new
  `serialize`); `GetNotifications` returns real notifications (was empty);
  `revert`-vs-`fault` is discriminated by the `revert_payload` marker, not
  by substring-matching `"THROW"` in the rendered error.

### 🟡 Compiler / IR / devpack — correctness fixes

- **`mulmod`/`addmod` modulus step routed through the uint256 software
  divmod** — native signed `MOD` gave wrong residues for moduli ≥ 2^255.
- **`receive()` + explicit `onNEP17Payment` coexistence is now a hard
  error (E105)** — the `receive()` body was dead code (Neo only invokes
  `onNEP17Payment`); `fallback()` stays a loud W105 warning.
- **NEP-11 `_transfer`/`_mint` self-escrow short-circuit** (`to !=
  address(this)`) — matches NEP-17; unblocks NFT custody flows.
- **Bytecode emission hardened**: 3 unchecked slice writes → `Result`;
  unresolved call target → hard error (was `eprintln!` + zero-byte operand
  = on-chain infinite loop); `u16` token-index overflow + unregistered
  CALLT patches → hard errors.
- **Removed the unsound `x == true → x` optimizer rewrite** (wrong for
  non-boolean operands: `5 == true` must be `false`, not `5`).
- **`MethodToken::serialize` returns `Result`** instead of `assert!`-
  panicking on oversize method names.
- **onNEP17Payment detection is case-insensitive**; **ETHER_UNIT_RE
  requires a digit/`)` prefix** (no more false-positive on `// whether` or
  `uint ether`); **inheritance type-conflict is a hard error (E122)**.
- **NEP-24 `royaltyInfo` tokenId** `bytes32` → `bytes` (matches NEP-11/26).

### 🔧 Toolchain & CI

- **Target Neo N3 v3.10.0** (Gorgon-hardfork preparation; v3.10.0 activates
  NO hardfork, so opcode/syscall/gas/NEF stay consensus-compatible).
  `neo_version` 3.5.0 → 3.10.0; Neo / Neo.SmartContract.Framework NuGet
  3.7.4 → 3.10.0 (49 csproj files); TFM net8.0 → net10.0; CI dotnet
  8.0.x → 10.0.x. Neo.Express kept at 3.9.1 (latest; no 3.10.x exists).
- **Neo-Express on-chain smoke is now a CI gate.** `ci.yml` runs a 6-script
  subset per PR; `fuzz.yml` runs the full 28-script suite + 6 DeFi
  showcases nightly. This is the only oracle that catches "passes in
  simulator, fails on-chain" regressions.

## [v0.21.0] - 2026-06-16

Deep correctness-and-conformance release: software 256-bit arithmetic
lands end to end so `uint256` values `>= 2^255` behave like a real Neo
node, plus two full adversarial review passes — a 25-defect production
audit (24 fixed) and a 12-subsystem systemic best-practice review (33
findings: 32 fixed, 1 surfaced as a loud compile-time warning). Net
effect is EVM/solc and Neo N3 conformance across integer arithmetic, the
ABI codec (now including nested-dynamic and dynamic-struct encode/decode),
runtime VM fidelity (`MaxStackSize`/`MaxItemSize`/storage limits),
dispatch correctness (overload resolution, fail-loud member calls,
function-pointer-local CALLA), selector/manifest canonicalization, tooling
output, and devpack fund-safety (NEP-17 self-escrow, escrow-stranding).

### Added

- **Software 256-bit limb arithmetic for `uint256`.** Schoolbook routines
  implement add/sub (two 128-bit limbs with carry/borrow folded through the
  limb boundary) and multiply (four 64-bit limbs), each computing
  `mod 2^256` so no intermediate exceeds NeoVM's 32-byte integer limit — a
  native ADD/MUL of large `uint256` values would form a rejected 33-byte
  result. Checked variants detect unsigned overflow (carry out of bit 256),
  underflow (final borrow / signed high limb `< 0`), and product `>= 2^256`
  without forming a 33-byte intermediate, raising a Solidity Panic(0x11) —
  replacing the `GetSize > 32` heuristic that a two's-complement wrap
  defeats.
- **Unsigned 256-bit comparison, division, modulo, and shifts for
  `uint256`.** The comparison family (`<`, `<=`, `>`, `>=`) is computed on
  the 32-byte two's-complement form via the order-preserving XOR-`2^255`
  map using only `<= 32`-byte operations, so values `>= 2^255` compare
  correctly (`type(uint256).max < 5 == false`). Division/modulo use the
  Hacker's Delight reduction to one signed DIV/MOD on the
  provably-non-negative `(a>>1, b)` with a correction step (or a 0/1
  quotient by unsigned compare when `b >= 2^255`); the limb-unsafe steps
  call the validated add/sub helpers, and `b == 0` throws Panic(0x12).
  Logical right shift uses `((a>>1) & (2^255-1)) >> (n-1)` (native NeoVM
  SHR is arithmetic/sign-extending), and wrapping left shift masks each
  128-bit limb to its low `128-k` bits before shifting and recombines as
  two's-complement (native NeoVM SHL does not wrap, so `1 << 255` would
  form a rejected 33-byte integer).
- **Nested-dynamic ABI encode/decode** — `string[]`, `bytes[]`, `T[][]`
  (arrays whose elements are themselves dynamic) now use the full recursive
  EVM head/tail layout (`[length, n offsets, n element tails]`, each
  element encoded/decoded recursively) on both the encode and decode sides,
  instead of falling back to `StdLib.serialize`, which emitted Neo-native
  bytes no EVM decoder could read. Cross-contract calldata and return-data
  for these types is now byte-exact with EVM/solc. Locals draw from a
  depth-indexed reusable scratch pool so many encode sites in one function
  stay within NeoVM's 255-slot limit.
- **Dynamic-struct ABI encode/decode** — structs containing any dynamic
  field are now encoded as the EVM tuple of their fields with the proper
  head/tail layout (static fields inline, dynamic fields as offset words
  plus recursive tails) and decoded symmetrically, replacing the
  `StdLib.serialize` fallback. `abi_value_type_is_dynamic` now follows
  solc's tuple rule (a struct is dynamic iff any field is dynamic); all-static
  structs are unchanged. Recursion covers nested shapes such as struct arrays
  (`D[]`), a dynamic struct in a tuple position, and a struct whose dynamic
  field is itself a `string[]`/struct.
- **Global NeoVM `MaxStackSize` ref-count enforcement** — a
  `live_stack_item_count` traversal walks every root (eval stack, indexed
  slots, saved call frames), recursing into arrays/maps and de-duplicating
  shared array/map/bytestring objects by pointer identity. Wired into
  `NEWARRAY`, `APPEND`, `PACK`, and `PACKMAP`, it faults when total live
  items exceed 2048, catching the case where many separate collections —
  each individually under the limit — collectively blow the global limit
  and revert on a real node (the per-collection guards alone could not see
  this).
- **Same-arity overload dispatch by argument type.** A call to one of two
  overloads sharing a `(name, arity)` key (Solidity overloading by parameter
  *type*, e.g. `f(uint256)` vs `f(address)`) is now resolved by matching the
  call's inferred argument types against each overload's parameter types
  (integers by signedness, everything else exactly) instead of collapsing to
  whichever overload was inserted last. Previously one overload silently
  executed the other's body on-chain; ambiguous/unmatched cases now fail
  loud rather than mis-dispatching.
- **Function-pointer local dispatch via CALLA.** Calls to a
  `function(...) internal` *local* (not just a parameter) now register a
  function-pointer binding at declaration and emit `CallIndirect`/`CALLA`.
  Previously such a call fell through to a fallback that dropped its
  arguments and pushed `0`; a never-assigned (zero-init) fp local now
  reverts cleanly on call (CALLA on a Null slot faults) matching Solidity's
  revert-on-zero-init-function-pointer behavior.
- **Narrow-integer checked arithmetic** — `uintN`/`intN` (N<256) add/sub/mul/`<<`/`**`,
  compound assignment, and `++`/`--` now revert on overflow and wrap mod-`2^N`
  inside `unchecked` blocks via a shared overflow ladder, even when one
  operand is an untyped literal (which previously defaulted to `uint256` and
  routed past the narrow guard). Mixed-width operations (e.g. `uint256 + uint32`)
  stay 256-bit; narrow signed division now panics on `intN.min / -1`. Matches
  solc 0.8 semantics.
- **`CryptoLib.verifyWithECDsa`** — implemented real signature verification
  for secp256r1 (p256) and secp256k1 with SHA256/Keccak256 message hashing
  selected by the NamedCurveHash byte (22/23/122/123), replacing the
  always-false stub so the test oracle matches a real Neo node; secp256k1
  accepts both high- and low-S forms.
- **uint256 literal representation warning** — the compiler now warns when
  an integer literal (e.g. `type(uint256).max`, all-ones masks) needs more
  than NeoVM's 32-byte signed-integer limit, surfacing the on-chain fault
  risk at compile time instead of failing silently after deploy.
- **`CALLT` disassembly** — the disassembler now decodes the `CALLT`
  (`0x37`) 2-byte token operand, keeping all following instructions
  correctly aligned.
- **Owner-managed multisig signer set** (devpack `NEP17`) —
  `setMultisigSigner`/`setMultisigThreshold` (onlyOwner) so the contract's
  escrowed pool can only be moved by an explicitly authorized signer quorum,
  with the threshold required to be `>= 2`.
- **Native-signature oracle callback** (devpack) — `CompleteNEP17Token`
  and `CompleteNEP11NFT` register and expose
  `onNativeOracleResponse(string url, bytes userData, int code, bytes result)`
  for direct Oracle-native invocation, while retaining the
  `IOracleServiceReceiver` method for the OracleService-forwarded path.

### Changed

- **`uint256` values in `[2^255, 2^256-1]` are now represented as 32-byte
  two's-complement throughout**, matching how a real Neo node stores them:
  literals (`type(uint256).max`, `1 << 255`, ERC-20 max approval,
  keccak-derived values) emit a two's-complement PUSHINT256 instead of a
  33-byte literal that faults on-chain; ordered comparison and
  checked/unchecked add/sub/mul/div/mod route through the software 256-bit
  routines, gated to genuinely-typed `uint256` so signed `int256` keeps
  native operations; `int256(x)` at 256 bits is a no-op bit reinterpret;
  and `abi.decode` reads the slot directly as two's-complement (dropping the
  old positive-magnitude `0x00` sign-byte append) so
  `abi.decode(abi.encode(x)) == x`.
- **Runtime simulator made faithful to a 32-byte two's-complement NeoVM** —
  negatives serialize to a 32-byte two's-complement word so a `uint256`
  stays a distinguishable 256-bit value, narrow add/sub/mul promote to
  BigInt on i64 overflow, and right-shift sign-extends narrow negatives,
  letting it validate `uint256 >= 2^255` behavior that the old
  unsigned-magnitude model could not.
- **Yul/inline-assembly opcodes now use EVM-unsigned 256-bit semantics** —
  `not` → `x XOR (2^256-1)` (was NeoVM INVERT's arbitrary-precision
  `-x-1`, so `not(0)` gave `-1`), `lt`/`gt` → unsigned 256-bit compare
  (was signed, so `lt(sub(0,1),5)` wrongly returned 1), `shr` → logical
  shift (was arithmetic), and `div`/`mod` → unsigned divmod (were signed),
  with the div-by-zero → 0 guard preserved.
- **Top-level dynamic `abi.decode` honors the encoded head offset** — a
  single top-level dynamic decode now dereferences head slot 0 to obtain the
  tail offset at runtime instead of assuming the canonical `0x20`, so valid
  but non-canonically-placed tails decode correctly, matching EVM decoders
  that follow the encoded pointer. The superseded constant-offset tail
  decoders were removed in favor of the runtime-offset path.
- **Canonical struct/enum ABI types in standard-JSON output** —
  `evm.methodIdentifiers` and `neo.methodMap` now build signature keys with
  the same tuple-expanded canonicalizer used to compute the selector (e.g.
  `f(MyStruct)` keys under `f((uint256,bool))`), so an SDK recomputing
  `keccak256(key)[..4]` matches the emitted selector. The top-level `abi`
  array now emits struct params as `tuple`/`tuple[]` with a recursively-built
  `components` array (enums as `uint8`) instead of an undecodable
  `"type":"MyStruct"`, so ethers/web3/Foundry can decode them. The devpack
  `type Any is bytes` placeholder now canonicalizes to its underlying
  `bytes` in selector and event-topic hashes (manifest still displays
  `Any`), correcting NEP-17/NEP-11 transfer/onNEPxxPayment selectors.
- **ABI-canonical selectors and event topics** — function selectors, the
  `.selector` registry, and event `topic0` are now computed from
  EVM-ABI-canonical types (struct → tuple, enum → `uint8`,
  `uint` → `uint256`), unified across the manifest and event paths to match
  Ethereum tooling.
- **Indexed array/struct event topics hash `abi.encode(value)`** — an
  `indexed` dynamic-array or dynamic-struct event parameter now hashes
  `keccak256(abi.encode(value))` through the conformant ABI encoder,
  matching ethers' `keccak256(abiCoder.encode([type],[value]))`. Previously
  it hashed the raw NeoVM Array stack item (a serde_json blob), which
  matched no off-chain decoder and faulted on a real node since
  `CryptoLib.keccak256` requires a ByteString; `string`/`bytes` still hash
  their raw bytes per the Solidity rule.
- **Return-tuple arity mismatches promoted to hard errors** — unambiguous,
  explicitly-written tuple/return-count mismatches (a single declared return
  given `return (a, b)`, or a multi-return given an explicit tuple of the
  wrong length) are now compile errors instead of warnings, so invalid
  Solidity no longer produces a NEF whose declared ABI return contradicts
  the body. Cases with false-positive risk (`return;` with named returns,
  public-getter synthesis, heuristic call-arity inference) deliberately stay
  warnings.
- **`>255` local-slot functions rejected** — a function exceeding NeoVM's
  255 local-slot limit now returns a clear compile error instead of silently
  truncating the `INITSLOT` count and `LDLOC`/`STLOC` indices into the
  `0..=255` byte range, which previously miscompiled slot indices.
- **Internal function pointers lowered via `PUSHA` + `CALLA`** — internal
  function pointers now emit a relative `PUSHA` Pointer consumed by `CALLA`
  instead of a raw `PUSHINT32`, so they execute on a real Neo node.
- **Per-collection NeoVM `MaxStackSize` (2048) guard** — `NEWARRAY` and
  `APPEND` now fault when a single array exceeds 2048 elements, modeling
  `ExecutionEngineLimits.MaxStackSize` (which counts every contained item).
  Previously the simulator allowed unbounded arrays, hiding an on-chain
  `MaxStackSize exceeded` fault and reporting success where a real node
  reverts.
- **NeoVM `MaxItemSize` (65535) enforcement on `NEWBUFFER`/`CAT`** — both
  now fault when the requested or concatenated length exceeds
  `ExecutionEngineLimits.MaxItemSize` instead of succeeding on a buffer a
  real node rejects.
- **Neo N3 storage size limits on `Storage.Put`** — keys past
  `MaxStorageKeySize` (64) and values past `MaxStorageValueSize` (65535)
  now fault; previously an oversized dynamic value (e.g. a `>64 KB` string)
  silently succeeded in the simulator while reverting on-chain.
- **Conformant minimal `CONVERT` Integer→ByteString/Buffer encoding** — the
  `0x28`/`0x30` CONVERT handler now emits the minimal two's-complement
  little-endian encoding of an integer (zero yields an empty span) instead
  of a fixed 8-byte word, so a contract that converts an integer to bytes
  and inspects its length, hashes it, or returns it observes the same widths
  as a real node. Scoped to the Solidity-observable CONVERT path; the
  internal storage/map-key byte helper is unchanged. `CONVERT` of an
  Array/Map to Integer now faults as real NeoVM does, instead of silently
  producing a degenerate integer.
- **`MODPOW` exponent `-1` computes the modular multiplicative inverse** —
  exponent `== -1` now returns `base.ModInverse(modulus)` in `[0, |m|)` via
  the extended Euclidean algorithm (faulting only when no inverse exists),
  matching NeoVM, instead of rejecting all negative exponents. `MODMUL`/`MODPOW`
  now use C#-style truncated (`%`) remainders whose sign follows the operand,
  matching NeoVM's `BigInteger.ModPow`/`%` for negative inputs rather than
  the Euclidean non-negative form.
- **`PACKMAP` pop order corrected** — now pops the key first (top of stack)
  then the value, matching NeoVM's `key = Pop(); value = Pop()`; the order
  was previously inverted, building maps with keys and values swapped.
- **`msg.value` lowers to a conformant `0` (PUSH0)** instead of a fabricated
  `System.Runtime.GetMsgValue` syscall. Neo N3 has no EVM-style attached
  call value and no such interop, so the prior lowering FAULTed on a real
  node (unknown interop service). Received amounts on Neo arrive as the
  `amount` argument of `onNEP17Payment`/`onNEP11Payment`, not an ambient
  `msg.value`.
- **Auto-generated struct getters omit mapping/array members** — public
  getters for struct state variables now drop mapping and array fields
  (`bytes`/`string` retained), matching solc and avoiding an invalid
  ABI/manifest that referenced non-returnable members.
- **NEP-11 explicit-standards validation tightened** — declaring
  `supportedstandards: ["NEP-11"]` now requires `totalSupply` and
  `tokensOf` (matching the auto-detection set), so a manifest can no longer
  advertise NEP-11 while missing spec-mandatory methods wallets/indexers
  depend on.
- **`abi.decode` accepts over-length buffers** — the static-size guard now
  reverts only on under-length input and ignores trailing bytes, matching
  solc/ethers/foundry decoding.
- **`bytesN` width validation** — `bytes0` and `bytes33`+ are now rejected
  at parse time instead of being silently accepted and mis-encoded
  downstream (only `bytes1`..`bytes32` are valid).
- **Yul `div`/`mod` by zero yields 0** — inline-assembly `div`/`mod` now
  return 0 on a zero divisor per EVM/Yul semantics; high-level Solidity
  division/modulo still Panics `0x12`.
- **Standard-JSON parse errors are now one structured entry per diagnostic**
  — a parse failure previously collapsed every parser diagnostic into a
  single opaque `type:"Generic"` error with no byte offsets; it now emits
  one `type:"ParseError"` object per diagnostic, each with a
  `sourceLocation:{file,start,end}` byte range, so Hardhat/Foundry-style
  tooling can enumerate individual errors and position source markers as it
  expects.
- **Aliased imports now emit a loud `IMPORT_ALIAS_BY_NAME` warning** —
  symbol-rename (`import {A as B}`) and global-symbol (`import * as NS`)
  aliases resolve by the underlying symbol name rather than the alias
  binding, so a name collision in the import closure could bind to the wrong
  declaration; every aliased import now warns instead of silently performing
  a bare-name bind (resolution and codegen unchanged).
- **Zero-amount transfers now conform to NEP-17** (devpack) — the canonical
  4-arg `transfer` no longer reverts when `amount == 0`; a zero-value
  transfer is processed normally (emits `Transfer`, runs the receiver
  callback, returns true), matching the NEP-17 spec and a real Neo node. The
  `validAmount` guard remains on mint/burn.
- **Self-escrow skips the receiver callback** (devpack) — `_transfer` no
  longer invokes `onNEP17Payment` when `to == address(this)`. Escrowing to
  the contract itself (timelock, conditional, staking, scheduled transfers)
  is internal bookkeeping; the base contract does not implement the
  receiver, so the prior self-call faulted and reverted the entire
  escrow-in leg. This also avoids spurious self-reentrancy.
- **NEP-26 `onNEP11Payment` tokenId is now `bytes`** (devpack) —
  `INEP26Receiver` takes a dynamic `bytes tokenId` instead of `bytes32`,
  matching `INEP11Receiver` and the ByteString token IDs the NEP-11 base
  actually passes; a non-32-byte id no longer mis-encodes.

### Fixed

- **`uint256` `**` (exponentiation) overflow and wrap** — checked `uint256`
  `**` now Panics(0x11) when the true result exceeds `2^256-1` and unchecked
  `**` wraps mod `2^256`; previously only narrow widths were handled, so
  `uint256` `**` overflow faulted with the wrong reason and unchecked never
  wrapped.
- **Unchecked `uint256` compound assignment (`+=`, `-=`, `*=`, `++`)** —
  routed through the software limb routines so operands `>= 2^255` wrap mod
  `2^256` instead of faulting on a 33-byte native intermediate; the old
  workaround left a Buffer that broke index reuse (e.g.
  `for(...; i++) a[i]`).
- **Width-correct bitwise NOT (`~`)** — `~x` now complements within the
  operand width: `~uint8(0)` returns 255 (not `-1`) via re-truncation after
  INVERT, and `~uint256(x)` uses `x XOR (2^256-1)` instead of truncating to
  u64; `int256` keeps the full-width `-x-1`.
- **`uint256` right-shift is now a logical shift** — a `uint256` at or above
  `2^255` (stored 32-byte two's-complement) no longer sign-extends:
  `type(uint256).max >> 1` gives `2^255-1` instead of `2^256-1` — while
  `int256` retains the arithmetic shift.
- **Narrow unchecked post-increment/decrement old-value recovery** —
  `old = new -/+ 1` is re-truncated to the operand width, so a `uint8` wrap
  recovers the width-bounded old value (255) instead of `-1`.
- **Shift-count encoding for counts `>= 128`** — emitted via PUSHINT16
  instead of the signed PUSHINT8 (where `0x80` decoded to `-128` and faulted
  the shift).
- **Post-increment/decrement evaluates the lvalue exactly once** — `x++`/`x--`
  on an indexed or keyed lvalue (`m[f()]++`, `arr[g()]++`) previously lowered
  the lvalue twice, running a side-effecting index expression twice (wrong
  slot plus duplicate effects). The compound now runs once (single index
  evaluation, leaving the new value on the stack) and the old value is
  recovered as `new -/+ 1`, using the `uint256` limb routine so a wrapped
  `>= 2^255` result does not fault on a 33-byte intermediate.
- **`ErrorName.selector` hashes the parametrized signature** — a custom
  error with parameters now computes `keccak("Name(t1,t2,...)")[..4]`
  instead of the bare `Name()` form, so `ErrorName.selector` matches solc
  and the 4-byte selector the error's own `revert` emits.
- **Sign-extension of negative signed integers in static multi-value
  returns** — the static-ABI return fast path zero-filled the 32-byte slot
  before copying an integer's minimal two's-complement bytes, so a negative
  signed value zero-extended its high bytes (e.g. `int256(-1)` became
  `0x00..00FF` = 255) rather than the EVM-canonical `0xFF..FF`. Signed
  integers are now masked to 256 bits before the slot encode, producing the
  correct sign-extended two's-complement; applied to both the
  `return_revert.rs` and `function.rs` static-slot encoders, with
  unsigned/positive paths unchanged.
- **Array-of-struct expansion in the fallback selector canonicalizer** —
  `canonical_param_type_with_structs` (used when NeoType resolution fails)
  only expanded a struct when the whole token was the struct name, so
  `P[]`/`P[3]` passed through verbatim and produced a selector disagreeing
  with `keccak256("(uint256,bool)[]")`. It now peels the trailing array
  suffix, canonicalizes the element type recursively, and re-appends it.
- **Same-ABI-shape struct-overload selector collisions detected** — two
  overloads taking different structs with the same ABI tuple shape
  (`struct A{uint256 x}` vs `struct B{uint256 y}`) now error, since both
  keccak-hash to the identical `f((uint256))` 4-byte selector and cannot be
  dispatched apart on-chain. The duplicate-signature check now canonicalizes
  each struct parameter to its tuple shape (from `metadata.structs`) instead
  of its bare name; distinct-shape overloads still compile.
- **Rejection of non-canonical length/offset slots in `abi.decode`** — the
  dynamic-decode slot readers consumed only the low 8 bytes of each 32-byte
  length/offset word, so a crafted payload encoding a value in
  `[2^64, 2^192)` with small low bits was silently truncated, causing the
  decoder to read the wrong region instead of reverting. A high-bits guard
  now faults (Panic `0x41`) whenever any of the high 24 bytes is set, on
  both the compile-time-offset and runtime-offset readers; conformant slots
  always have zero high bytes, so valid input is never faulted.
- **Unresolved member calls fail loud** — a member call
  `inner.member(args)` matching no resolution branch (builtin, library,
  using-directive, iterator op, push/pop) now emits a hard diagnostic
  instead of dropping the receiver and arguments, pushing `0`, and reporting
  success. A typo'd method, missing library function, or member access on an
  unsupporting type no longer silently compiles into a function that returns
  0 and discards side-effecting arguments, matching solc's "member not
  found" rejection.
- **Fixed-size arrays classified as Array in the manifest** — a fixed-size
  array type such as `uint256[3]` (ends with `]` but not `[]`) is now
  correctly classified as an Array rather than falling through to the
  uint-prefix branch and being labelled Integer, which previously produced a
  spurious "expected Integer, got Array" error for fixed-array event
  parameters. Manifest type detection now keys on any trailing `]`.
- **Mixed unsigned/negative integer comparison via BigInt** — comparing an
  `UnsignedInteger` against a negative `Integer` previously faulted on the
  u64-coercion path; both operands are now promoted to BigInt so `<`/`>`/equality
  yield the value a real node computes (NeoVM integers are arbitrary-precision
  with no signed/unsigned tag).
- **`SUBSTR` integer-overflow guard** — the bounds check now uses
  `checked_add` for `index + count`; crafted operands could overflow `usize`
  and wrap past the guard into a Rust slice-index panic (DoS) instead of a
  clean VM fault.
- **modexp (`0x05`) precompile result padding** — the MODPOW result is now
  left-padded to exactly 32 bytes (prepend 32 zeros, keep the rightmost 32)
  so every result width including zero yields a stable 32-byte big-endian
  slot; the prior prepend-31-zeros approach produced 39-byte output under
  the old fixed-8-byte CONVERT and would have produced 31 bytes for a zero
  result under minimal encoding.
- **0x05 modexp precompile now faults on unsupported operand widths** —
  the 1-byte-operand variant read fixed offsets 96/97/98 and ignored the
  EIP-198 length headers, mis-reading any wider input; it now asserts the
  low byte of each length word (`base_len@31`, `exp_len@63`, `mod_len@95 == 1`)
  and FAULTs on any unsupported shape, while the supported 1-byte path (e.g.
  `3^2 mod 7 = 2`) is unchanged.
- **Disassembler now decodes the 1-byte operand of `CONVERT` (`0xDB`) and
  `ISTYPE` (`0xD9`)** — both fell into the no-operand catch-all, so their
  mandatory StackItemType operand was re-decoded as the next opcode (the
  compiler emits `0xDB 0x21` for nearly every storage-int load), silently
  misaligning every following instruction in the listing; the operand is now
  consumed and rendered as `type=0x..`. Debug-listing surface only; emitted
  bytecode is unaffected.
- **Dropped unsound `x * 0 → PUSH 0` peephole** — the constant-fold no
  longer rewrites `<x>; PUSH 0; MUL` to `<x>; PUSH 0`, which leaked the
  multiplicand on the evaluation stack and could eventually fault on
  MAXSTACKSIZE inside a loop.
- **`PUSHA` offset interpretation** — the runtime now reads the `PUSHA`
  operand as a signed offset relative to the opcode (`ip + operand`),
  mirroring NeoVM's Pointer semantics, instead of treating it as an absolute
  u32 address.
- **Pragma concat-feature gate now respects identifier boundaries** — the
  `string.concat`/`bytes.concat` version checks (`>=0.8.12` / `>=0.8.4`)
  require a non-identifier character before the match, so a user variable
  like `myString.concat(...)` no longer falsely trips the gate and gets the
  compile rejected.
- **Fixed-array length preserved through the type system for correct ABI
  selectors** — `NeoType::Array` now carries the fixed length `N`; the
  parser records it and `canonical_abi_type`/`type_name` emit `T[N]` instead
  of collapsing to `T[]`. A function with a fixed-size array parameter (e.g.
  `uint256[3]`) now computes the correct keccak selector, `interfaceId`, and
  `abi.encodeWithSelector` payload, matching solc/ethers.
- **`bytes20` parses as a distinct fixed-bytes type, not `address`** — it
  now maps to `ByteArray { fixed_len: Some(20) }` → canonical `bytes20`
  rather than `address`. Solidity treats the two as different ABI types, so
  `f(bytes20)` previously got the wrong (`f(address)`) selector and
  `interfaceId`.
- **Deterministic Natspec attachment keyed by declaration start** — each
  accumulated doc block is now keyed at the first non-whitespace byte after
  it (the start of the declaration it documents), and a doc run breaks when
  a non-whitespace token sits between two doc comments. `find_preceding_doc`
  becomes an exact, distance-independent lookup at the declaration start,
  replacing a fixed backward `0..100`-byte scan that dropped doc blocks
  separated by more whitespace and could cross-attach a comment onto the
  wrong (next) declaration. Manifest documentation correctness only; no
  bytecode impact.
- **`NEP17` witness-authorized transfer no longer underflows the allowance**
  (devpack) — allowance is now consumed only on the approval (spender)
  path; an owner/witness-authorized transfer with zero allowance no longer
  computes `allowance - amount` and reverts with Panic `0x11`.
- **`NEP17` constructor enforces the max-supply cap on the initial mint**
  (devpack) — the initial supply is now checked against a non-zero
  `maxSupply` (the internal `_mint` does not enforce the cap), preserving
  the supply invariant.
- **Oracle conditional-transfer callback decoded the wrong arguments**
  (devpack) — `conditionalTransferCallback` declared its parameters in the
  wrong order, so the Oracle native (which calls
  `(string url, bytes userData, int code, bytes result)`) caused `userData`
  (carrying the local request id) to be read from the wrong slot, leaving
  escrowed `conditionalTransfer` funds permanently locked. Corrected to the
  native signature.
- **Conditional-transfer callback could strand escrow on a short oracle
  result** (devpack) — the callback consumed the pending-request record and
  then ran `abi.decode(result, (bool))` on the raw filtered oracle body; a
  body shorter than a 32-byte ABI word hit Panic (`0x41`) and reverted after
  the request was consumed, permanently locking the tokens. Now guarded with
  `result.length >= 32` so unparsable/short results fall through to the
  existing refund branch.
- **Unauthorized accounts could drain the escrowed pool via
  `multiSigTransfer`** (devpack) — the pool transfer now fails closed when
  the multisig set/threshold is unconfigured and requires every declared
  signer to be an owner-authorized signer; previously any caller could
  supply two arbitrary self-witnessed accounts and drain the contract's
  escrow.
- **`multiSigBurn` could burn a third party's balance** (devpack) —
  `CompleteNEP17Token.multiSigBurn` now requires the holder's own witness
  (`checkWitness(from)`), preventing arbitrary colluding signers from
  burning an account they do not control.
- **`Syscalls.scriptHashToAddress` preserves all 20 bytes** (devpack) —
  script-hash-to-address conversion now maps the 20 bytes straight to
  `uint160` instead of left-aligning into `bytes32` and discarding the top
  12 bytes.
- **VaultPattern shares minted against the pre-deposit balance** (devpack)
  — share minting now divides by the vault's assets before the incoming
  deposit (which already lands in the live balance), so depositors are no
  longer diluted.
- **`CompleteNEP11NFT` royalties paid by the buyer** (devpack) — the royalty
  leg now transfers from the witnessed buyer (`msg.sender`) instead of the
  seller, whose witness is never present in a buyer-initiated purchase, so
  the payment no longer always fails; the buyer's payment is split between
  seller proceeds and royalty.

### Internal

- **Functions exceeding NeoVM's 255 local-slot limit now raise a compile
  error** — INITSLOT encodes the local count in one byte and LDLOC/STLOC
  index `0..=255`, so a function with `>255` locals previously truncated
  silently and miscompiled slot indices; `generate_contract_bytecode` now
  returns a clear error instead.

## [v0.20.0] - 2026-06-11

Correctness-and-conformance release: a full adversarial review pass (26
verified findings fixed), ecosystem conformance for tokens (native NEP
Transfer notifications, NEP-11 iterators/ByteString token IDs), devpack
surface honesty (uncallable/unfaithful APIs removed), and large structural
cleanups (dead Yul frontend deleted, `src/runtime` modularized). Everything
below is verified by 1,758 tests plus 23 neo-express on-chain smoke tests.

### Added

- **NEP-11 deep conformance** (devpack `NEP11.sol` + `CompleteNEP11NFT.sol`):
  token IDs are now dynamic `bytes` (NEP-11 ByteString, ≤ 64 bytes,
  manifest `ByteArray`) instead of `bytes32`/`Hash256`, and
  `tokensOf`/`tokens` return NeoVM storage iterators (manifest returntype
  `InteropInterface`, matching the C# devpack) backed by a raw-storage token
  index scanned with `FindOptions.KeysOnly | RemovePrefix`. Public methods
  may now declare `Syscalls.Iterator` returns: the raw iterator stack item
  is the NeoVM return value (no ABI re-encoding) and the manifest type
  mappers emit `InteropInterface`. The bundled runtime's
  `System.Storage.Find` now honours Neo N3 `FindOptions`
  (KeysOnly/RemovePrefix/ValuesOnly/DeserializeValues/PickField0/1/
  Backwards, with C#-node combination validation) instead of always
  yielding `[key, value]` structs. The NEP-11 `transfer`/`onNEP11Payment`
  `data` parameter is now typed with the devpack `Any` alias so the
  manifest declares `Any` per spec — the devpack NEP-11 base now passes
  neo-express's strict NEP-11 deploy-time standard check, validated
  on-chain (deploy + mint + native Transfer notification +
  `InteropInterface` iterator traversal). Remaining documented deviation:
  `properties` returns serialized `bytes` (manifest `ByteArray`) — Solidity
  has no construct that produces a NeoVM Map stack item return
  (STANDARDS_MAPPING.md).
- `ir::builtin_intrinsic_surface()` / `ir::BUILTIN_LIBRARY_BASES`:
  introspection over the builtin-library intrinsic whitelist, plus a
  regression sweep (`tests/gap_hasrole_tests.rs`) that compiles a probe call
  for every whitelisted member and fails if any advertised member has no
  lowering — whitelist-without-lowering intrinsics can no longer reappear.

### Changed

- **Standards auto-detection requires conformance before claiming a NEP.**
  NEP-17 needs the five methods plus a 4-parameter `transfer` and a
  3-parameter `Transfer` event; NEP-11 needs the full mandatory method set
  (`symbol`, `decimals`, `totalSupply`, `balanceOf`, `tokensOf`, `ownerOf`,
  `transfer`) plus a 3-parameter `transfer` and 4-parameter `Transfer`
  event; NEP-24 is only detected for a 3-parameter `royaltyInfo` (`tokenURI`
  no longer triggers it). Near-misses emit warnings instead of false
  manifest claims. Explicitly declared NEP-17/NEP-11 standards additionally
  hard-require the spec `transfer` arity.
- **Distinct-arity overloads keep their original Solidity name in the
  manifest** (Neo dispatches on name + parameter count, like native
  `ContractManagement.deploy`). Only true same-arity collisions fall back to
  the mangled `name(type,...)` form, with one deterministic primary keeping
  the clean name. The standard-json `methodMap` now reports the
  manifest-visible (callable) names.
- **NEP-17 / NEP-11 `Transfer` events are now emitted in NATIVE Neo
  notification shape.** When an event declaration matches a NEP standard
  Transfer signature — `Transfer(address, address, uint256)` (NEP-17) or
  `Transfer(address, address, uint256, bytes32|bytes)` (NEP-11), any
  indexed-ness — the compiler emits `Notify("Transfer", [from, to,
  amount(, tokenId)])` with no EVM topic0: `from`/`to` are 20-byte
  ByteStrings with the zero address mapped to `Null` at runtime (NEP
  mint/burn convention), `amount` is CONVERTed to Integer. The manifest
  declares the native shape (`from: Hash160, to: Hash160, amount: Integer
  (, tokenId)`), so wallets, indexers and NEP trackers can read token
  transfers. All other events keep the EVM log shape. Detection is shared
  between the lowering, the manifest builder and the standards checks
  (`ir::native_transfer_standard`).
- **Manifests now declare the truthful EVM wire shape for non-NEP
  events** — `[topic0: ByteArray, <one ByteArray per indexed param>,
  data: ByteArray]` (anonymous events drop the topic0 slot). Neo nodes
  >= 3.6 (HF_Basilisk) fault notifications whose state-item count
  mismatches the manifest declaration, so the previous declared-parameter
  shape made every `emit` fault on-chain.
- **Anonymous events Notify under their declared Solidity name** instead
  of an empty `""` event name (which faults on Neo nodes >= 3.6 because
  the name is not declared in the manifest). `anonymous` now only
  suppresses the EVM signature-hash topic0 in the state payload; the
  Neo-level event name and the manifest declaration use the declaration
  name.
- **Standards validation understands native Transfer shape**: explicitly
  declared NEP-17/NEP-11 (`@custom:neo.manifest.supportedstandards`) now
  hard-fails when the `Transfer` event's parameter types don't match the
  standard signature (it would be emitted in EVM shape, unreadable by NEP
  trackers); auto-detected standards emit a warning instead.

### Fixed

- **`abi.decode` of multi-slot static types (structs, arrays of structs) now
  decodes canonical ABI bytes** instead of silently falling back to
  `StdLib.deserialize`, which faulted at runtime on real ABI payloads.
- **`abi.decode` of `uint256` values `>= 2^255` no longer reinterprets them
  as negative.** The decoder appends a sign byte when the high bit is set,
  and the bundled runtime's wide bitwise/shift results follow the same
  value model.
- **Custom error selectors are computed from the declared `error`
  signature**, not from inferred argument-expression types. Named arguments
  are reordered into declaration order before encoding.
- **`require(cond, msg)` with a non-literal message now throws the canonical
  `Error(string)` envelope**, matching `revert(msg)` so `catch Error(string)`
  works on both paths.
- **`abi.encodePacked` sign-extends negative signed integers** instead of
  zero-padding them.
- **Mapping keys are canonicalized (`CONVERT`) before serialization**, so
  `Buffer` vs `ByteString` representations of the same value (e.g. a
  `bytes.concat` result vs an ABI parameter) derive the same storage slot on
  real Neo N3.
- **`delete` on storage arrays clears every element.** Fixed-size arrays
  zero all element slots; dynamic arrays clear elements and length. Struct
  field array subscripts now get the same `Panic(0x32)` bounds guard as
  top-level arrays, so deleted data is no longer silently readable.
- **`delete` on a struct containing a mapping member no longer emits a
  `Storage.Put` with a Null value** (which faults on real Neo N3); mapping
  members are skipped per Solidity semantics.
- **Sibling-contract merge rejects same-named state variables with
  conflicting types** instead of silently aliasing one storage slot.
- **The optimizer no longer aborts the process on huge literal shifts or
  multiplies** (`1 << 2**64-1` previously OOM-aborted at the default `-O2`);
  oversized folds now fall through to runtime ops, bounded by
  `MAX_FOLDED_LITERAL_BITS`.
- **Deeply nested expressions no longer overflow the parser stack.** All
  `solang_parser::parse` entry points run on a worker thread with a large
  bounded stack and report failures as ordinary diagnostics.
- **Devpack APIs that could never work on Neo N3 are now hard compile
  errors instead of silent miscompiles or on-chain FAULTs**: the
  `Storage.*Local` family lowered to fictional `System.Storage.Local.*`
  syscalls, and `Storage.batchPut/batchGet/batchDelete/count/exists/
  clearPrefix/findValues/findKeys/getUsage` lowered to single raw syscalls
  that dropped arguments. `devpack/libraries/{Runtime,Storage,Neo}.sol` are
  pruned to the intrinsic surface the compiler actually lowers (111
  documented-but-uncallable functions removed), with a probe test keeping
  the libraries and the intrinsic whitelist in lockstep.
- **Manifest permissions now cover contract calls inside `catch` blocks.**
  Permission inference previously never modeled the `Try → catch` exception
  edge, so a contract call performed in a catch handler shipped without a
  manifest permission and FAULTed on-chain when the error-recovery path ran.
  Any contract-call site left unreached by the dataflow walk now degrades to
  an explicit wildcard permission instead of silent under-permission.
- **Manifest event declarations now describe the actual `System.Runtime.Notify`
  payload** (`topic0` + one `ByteArray` per indexed parameter + `data`)
  instead of the Solidity-declared parameter list. Neo N3 nodes since 3.6
  (HF_Basilisk) validate every notification against the manifest ABI by
  state-item count and type, so the previous declarations made every
  non-anonymous `emit` fault on real networks.
- **`view`/`pure` validation now follows function-pointer calls.** Taking a
  function's address adds a call-graph edge, so a `view` method that writes
  storage through an indirect call is rejected at compile time instead of
  being advertised `"safe": true` in the manifest.
- **The devpack `Any` user-defined value type (`type Any is bytes;`) now
  reaches the manifest as `Any`** (e.g. the NEP-17 `transfer` `data`
  parameter), matching the NEP specs and external standard validators. The
  special case is scoped to aliases whose underlying type is exactly
  `bytes`; `type Any is bytes32;` keeps its alias semantics.
- Deduplicated `getCurrentBlock` in the `Neo` builtin whitelist (it appeared
  twice in the "supported intrinsics" diagnostic).

### Removed

- **Dead Yul frontend** (`src/lexer`, `src/parser`, `src/semantic`,
  `src/optimizer`, AST codegen): these implemented a Yul pipeline that the
  real solang-based compile path never invoked. The live `interop_id_bytes`
  helper moved to `src/interop.rs`. Public modules
  `neo_devpack_solidity::{lexer,parser,semantic,optimizer,codegen}` no
  longer exist.
- `.dead_modules/` (15 parked, never-integrated analysis passes).
- The unused `storage_key::derive_mapping_slot`/`KeyFragment` API, which
  documented a mapping-slot scheme the compiler does not emit; module docs
  now describe the production scheme.
- **`hasRole` pruned from the devpack/intrinsic surface.** Neo N3's native
  RoleManagement contract only exposes `getDesignatedByRole(role, index)` and
  `designateAsRole`; neither real Neo N3 nor the official C# devpack provides
  a generic role-membership check. The dead `"hasRole" => None` arm in
  `resolve_syscalls_member` (an advertised-but-uncallable intrinsic) was
  removed, and `devpack/libraries/Runtime.sol` no longer declares `hasRole`
  — the old helper was uncallable (builtin libraries are compiler intrinsics
  with no lowering for it) and a security footgun: it silently ignored its
  `role` argument and degraded to `checkWitness(account)`. Calls to
  `Syscalls.hasRole` / `Runtime.hasRole` now fail with the standard targeted
  "unsupported builtin library call" diagnostic; use
  `Syscalls.getDesignatedByRole(...)` (and scan the returned node list) or
  `Runtime.checkWitness(...)` instead. User-defined contract-level `hasRole`
  methods (OpenZeppelin AccessControl pattern) are unaffected.
- **Fictional `System.Storage.Local.*` syscalls** (`Get`/`Put`/`Delete`/`Find`)
  removed everywhere — these syscalls never existed on Neo N3. The bundled
  runtime no longer registers or executes them (a script invoking them now
  FAULTs as an unknown syscall, matching real-node behavior); the compiler
  intrinsics (`Syscalls.storage*Local`, `Storage.getLocal`/`putLocal`/
  `removeLocal`/`findLocal*`/`countLocal`) were removed so such calls are
  rejected at compile time; the devpack wrappers and the runtime-spec /
  syscall docs were pruned accordingly.
- **Silent legacy `emit` fallback removed.** Emitting an event with no
  resolved declaration, with a mismatched argument count, or whose
  arguments fail to lower is now a compile error (previously it emitted a
  raw `Notify(name, args...)` whose shape mismatched the manifest and
  faulted on post-Basilisk nodes — or silently emitted nothing).

### Internal

- **`src/runtime` is real Rust modules now**: all 156 `include!()` fragments
  converted to `mod`/`pub use` with explicit visibility; keyword `impl/`
  directories renamed; no behavior change, public API paths preserved.
- The C# `src/Neo.Sol.Runtime` README and root README now state explicitly
  that the C# library is a standalone experimental EVM-emulation layer, not
  used by `neo-solc` and not shipped in releases.

## [v0.19.0] - 2026-05-19

Compatibility-focused release: this is the "any existing Solidity contract
should compile to NeoVM" pass. Three rounds of refactoring across the import
resolver, type system, inheritance/merge pipeline, and IR lowering. Before
this release, only 7 of the 40 OpenZeppelin contracts in the famous-contracts
corpus compiled even with a full npm install — most blocked by a pragma
combinator bug, missing `using`-directive propagation, opaque `address.call`
hard-rejection, and qualified-struct name collisions. After this release,
**40/40 OpenZeppelin contracts compile** and the full famous-contracts
corpus (OZ + Uniswap V2/V4 + Safe + Aave + Chainlink + solmate) reaches
**88/88 unique contracts compiling end-to-end** to valid Neo N3 NEF3 +
manifest. No regressions in the 67 internal examples or 1248 integration
tests.

### Added

- **Foundry-style import remapping** with full Foundry-equivalent semantics:
  - `--remap PREFIX=PATH` CLI flag (repeatable).
  - `--remappings FILE` CLI flag to load Foundry's `remappings.txt` format.
  - Auto-discovery of `remappings.txt` from every ancestor of the entry
    file AND from every package inside reachable `node_modules/`
    directories. Uniswap V4 periphery's `permit2/=lib/permit2/`,
    `forge-std/=lib/forge-std/src/`, etc. now register automatically.
  - Auto-registration of `<pkg>/lib/<dep>/=lib/<dep>/` mappings for any
    package shipping a Foundry-style vendored layout — covers Uniswap V4
    and every forge-installed contract suite without manual configuration.
  - Foundry inline-version-pin syntax (`@openzeppelin/contracts@4.8.3/...`)
    now resolves with three fallbacks in order: dash-form
    (`contracts-4.8.3`), then unpinned (`contracts`), then the original
    pinned spelling. Matches both Foundry's pinning syntax and the npm
    aliased-package convention used by Chainlink.
  - String-prefix substitution (was `Path::join`, which silently broke
    remappings whose suffix begins with `/`).
- **node_modules auto-discovery** from every entry file AND every
  `--include-path`. Walks up to 16 ancestors looking for `node_modules/`
  and adds each discovered directory as an implicit include path. Mirrors
  solc / hardhat / foundry's npm-style import resolution.
- **Cross-package virtual-path resolution** for relative imports. When a
  vendored file at `vendor/@openzeppelin/contracts/token/ERC20/ERC20.sol`
  does `import "./IERC20.sol"` and `IERC20.sol` isn't vendored alongside,
  the resolver now finds the equivalent file under any include path that
  exposes a parallel package layout.
- **Qualified-struct-name tracking** through the IR. A struct defined
  inside `library Pool { struct SwapParams { … } }` now keeps its scope
  qualifier (`Pool.SwapParams`) all the way through library merging and
  IR lowering, so it can coexist with a same-named file-level
  `struct SwapParams` from a different scope (Uniswap V4 PoolOperation
  ships exactly that pair). `from_solidity` does qualified-exact match
  first, falls back to short-name suffix match. Internal references
  inside the owning scope are auto-qualified during frontend conversion.
- **Foundry-compatible regression test**: `tests/famous_contracts_compile.rs`
  enforces two compile-pass floors — a hermetic vendor-only floor (5)
  and a strict OZ-install floor (35, currently hitting 40/40). Picks up
  `NEO_SOL_OZ_INSTALL_DIR` env var or any ancestor `node_modules/` to
  decide whether to run the install-required pass.

### Changed

- **Opaque `address.call(<bytes>)` and `delegatecall` are now runtime
  traps, not compile errors.** Previously the compiler hard-rejected any
  contract whose compiled methods transitively included these patterns,
  which made every contract that imports OZ `Address.sol` (every
  transparent proxy, Multicall, VestingWallet, TimelockController, …)
  fail compilation even when the offending paths were dead code reached
  through inheritance. The compiler now emits a warning at compile time
  and an `ABORTMSG` instruction at the call site — contracts deploy
  normally, and only the specific opaque/delegate-call code path traps if
  execution ever reaches it. Manifest permission analysis is correct
  (no `System.Contract.Call` instruction is emitted, so no spurious
  wildcard permissions are claimed).
- **Multi-file pragma combinator now uses MAX (intersection) instead of
  MIN.** When the compiled source unit contains multiple `pragma solidity`
  directives — typical of import chains — the effective compiler version
  must satisfy every file's constraint. Taking the MIN (the old behaviour)
  incorrectly lowered the effective version below feature gates, causing
  legitimate uses of `string.concat` / `bytes.concat` in the entry
  contract to fail when one transitively imported file declared a broad
  `>=0.4.16` pragma (used by many OZ utility files).
- **Sibling-merge no longer clobbers bodied overrides with bodyless
  declarations.** When a derived contract's inheritance MRO places an
  abstract-interface declaration AFTER a concrete abstract-contract
  override (Chainlink FunctionsCoordinator: OCR2Base implements
  `latestConfigDetails` with body, IFunctionsCoordinator declares it
  bodyless, both are inherited), the flattener now keeps the bodied
  version.
- **Unresolved-overload internal call** (typical of sibling-merged bodies
  referencing abstract functions in the original scope) is now a warning
  + runtime trap rather than a hard error.
- Library `external`→`internal` visibility normalization now runs BEFORE
  validation, so Aave's `EModeLogic.executeSetUserEMode(mapping storage,
  ...)` no longer trips the "external function may not use storage" check.

### Fixed

- **Library `using` directive merge.** When a library is inlined into a
  host contract, its own `using L for T;` directives now merge too. OZ
  Strings.sol declares `using SafeCast for *;` then calls
  `someBool.toUint()` inside helpers; the inlined helper now resolves
  correctly inside the consuming contract.
- **Ancestor `using` directive merge.** Inheritance flattening now pulls
  ancestor `using` directives into the descendant's scope. Repro: Aave
  AToken inheriting from IncentivizedERC20 — the parent's
  `using SafeCast for uint256;` is what makes the inherited `transfer`
  body's `amount.toUint128()` typecheck.
- **Sibling `using` directive merge.** Sibling-merged external bodies
  now carry their owning contract's `using` directives — e.g. Gnosis
  Safe's `using SafeMath for uint256;` is in scope when
  CompatibilityFallbackHandler triggers a sibling merge.
- **Sibling modifier definition merge.** Sibling-merge now pulls in
  modifier definitions from the original contract AND from its base
  classes. Repro: OZ TransparentUpgradeableProxy ↔ ProxyAdmin cycle,
  where ProxyAdmin's `onlyOwner` is inherited from Ownable.
- **Private state-variable shadowing across inheritance.** Solidity
  allows a derived contract to redeclare a private state variable with
  the same name as an ancestor (typical of OZ Governor's
  `string private _name` shadowing EIP712's `ShortString private
  immutable _name`). The flattener now renames the ancestor's slot to
  `__<Ancestor>__<name>` and rewrites identifier references in the
  ancestor's own function bodies. Each scope keeps its own storage.
- **`function_first_param_types` keeps every overload's first-param
  type.** Solidity allows overloading by parameter type; the previous
  single-entry map collapsed `toInt128(int256)` and `toInt128(uint256)`
  (Uniswap V4 SafeCast) so receiver-type matching against
  `using SafeCast for int256;` failed for the wrong-arity surviving
  entry.
- **Type alias resolution in directives.** Library-form `using L for
  Currency;` (with `type Currency is address;`) now resolves the alias
  to its underlying type for receiver matching, so the directive applies
  to address-typed values. Function-list form `using {add as +} for
  BalanceDelta;` deliberately does NOT resolve (operator-overload scope
  is restricted to the alias only — broadening it would over-capture
  every same-underlying receiver).
- **Interface/contract targets in `using L for IInterface`** now resolve
  to `address` for receiver matching. OZ's `using SafeERC20 for IERC20;`
  pattern works again with `IERC20(addr).safeTransfer(...)` call sites.
- **Public state-variable getter deduplication.** Auto-synthesized
  getters no longer collide with existing inherited functions of the
  same name+arity (Aave AToken's `IPool public immutable POOL`).
- **`__ctor__<Sibling>` mangled sibling constructors** can now write to
  immutable state vars (the original constructor's semantics carry over
  to the mangled form). Also handles `__super___ctor__*` preservation
  for inherited base constructors (Chainlink AutomationRegistry).
- **Receive→onNEP17Payment dedup.** When multiple `receive()` declarations
  surface via independent inheritance paths, the synthetic
  `onNEP17Payment(address,uint256,Any)` no longer produces duplicate
  ABI entries.
- **Struct field type-alias resolution.** `type_aliases` now thread
  through nested type resolution (struct fields, array elements, mapping
  values), not just at the top of the parser. Uniswap V4
  `struct State { Slot0 slot0; … }` (with `type Slot0 is bytes32;`) now
  types `state.slot0` as `bytes32` instead of `Any`.
- **Cross-library struct visibility.** Library validation now pre-merges
  peer-library struct/enum pools, so a library function signed against
  `OtherLibrary.NestedStruct` typechecks regardless of which library is
  being validated first.

### Statistics

- **Famous-contracts corpus pass rate**: 7/92 → 88/88 unique
  (4 dup base names in the 92).
- **OpenZeppelin contracts pass rate**: 7/40 → **40/40**.
- **Lib tests**: 511 → 516 (added 5 regression tests).
- **Integration tests**: 1244 → 1248.
- **Internal examples**: 67/71 unchanged (4 intentional Error showcases).

## [v0.18.1] - 2026-05-05

Compatibility and release-readiness follow-up to v0.18.0. This patch release
adds explicit devpack adapters for the most common EVM-only migration gaps,
tightens Hardhat/devpack support metadata, and refreshes validation gates so
Solidity contracts can be compiled, deployed, and exercised on Neo N3 with
clearer guidance.

### Added

- **EVM compatibility adapters** in `devpack/contracts/compat/`:
  - `EVMNativeAssetAdapter` maps payable / `msg.value`-style flows to
    NEP-17 `onNEP17Payment(address,uint256,Any)` callbacks.
  - `EVMFallbackDispatcher` exposes explicit `dispatch(bytes4,bytes)`
    selector routing for contracts that previously relied on EVM fallback
    dispatch.
  - `EVMContractFactory` wraps Neo ContractManagement deploy/update/destroy
    paths for CREATE/CREATE2/selfdestruct-style lifecycle migrations.
- **Neo-Express compatibility smoke test**:
  `examples/test_neoxp_evm_compat_smoke.sh` compiles, deploys, transfers GAS
  into the adapter contract, reads the recorded payment amount, and invokes
  public fallback dispatch on a local Neo-Express chain.
- **EVM Compatibility Layer documentation** under
  `docs/additional-material/neo-devpack/evm-compatibility-layer.md`, including
  supported workarounds and features that intentionally remain Neo-specific.
- **Showcase contract**: `examples/new/EVMCompatibilityShowcase.sol`.

### Changed

- Hardhat plugin peer ranges and install docs now explicitly target Hardhat
  `>=2.28.6 <3`; Hardhat 3 remains a separate migration because of ESM,
  task API, and network schema changes.
- Node engine ranges are aligned across packages to current LTS lines
  (`^20.19.0 || ^22.12.0 || ^24.0.0`).
- Devpack docs now use `Syscalls.getCallingScriptHash()` as the reliable
  NEP-17 callback token source instead of `msg.sender`.
- Security and CI wording now treat high-severity audit findings as release
  blockers while leaving low/moderate Hardhat 2 legacy advisories documented.

### Fixed

- `devpack/hardhat.config.js` now uses Solidity `0.8.34`, matching the
  compiler/devpack examples and avoiding stale `0.8.28` integration failures.
- Hardhat deployer tests no longer import undeclared `@cityofzion/neon-js`
  helpers for basic hash/primitive assertions.

### Validation

- `cargo fmt --all -- --check`
- `cargo clippy --all-targets --all-features -- -D warnings`
- `cargo test --workspace --all-features`
- `cargo build --release`
- `dotnet test tests/Neo.Sol.Runtime.Tests/Neo.Sol.Runtime.Tests.csproj --configuration Release`
- `npm --prefix tooling run build && npm --prefix tooling run lint && npm --prefix tooling run typecheck && npm --prefix tooling test`
- `npm --prefix devpack test`
- `npm --prefix devpack run test:integration`
- `NEO_SOLC="$PWD/target/release/neo-solc" make test-deploy-evm-compat-smoke`
- `NEO_SOLC="$PWD/target/release/neo-solc" STRICT_SWEEP_FAIL_ON_UNEXPECTED_WARNINGS=1 bash examples/test_strict_compatibility_sweep.sh`
- `npm run docs:check`
- `npm run docs:build`
- `npm run docs:check:links`

## [v0.18.0] - 2026-04-25

Fuzz-system maturation follow-up to v0.17.0. Every deferred bug from the
v0.17.0 release is now resolved (#16 unchecked-uint256 divergence, #23–#25
ABI codec gaps, BLS12-381 runtime stub closed as #26, plus a new in-contract
`T[].length` divergence found and fixed as #28). The state-batch
non-atomic semantics ambiguity is resolved with a canonical
"skip-and-continue" contract documented and pinned. Six additional fixes,
two new cargo-fuzz targets, libfuzzer dictionaries (970 entries), and
substantial new proptest infrastructure land alongside. The headline test
delta is small (+47 tests) but the *effective* fuzz coverage is materially
higher: 43 of those new cases land in `fuzz_tests`, and the bounded-grammar
workaround in `optimizer_props` was removed (the test now runs against
the full `u32::MAX` literal/argument domain rather than `u16::MAX`). Seven
recent review waves found 0 new bugs in well-explored areas — the suite
has reached steady-state.

### Added

- **2 additional cargo-fuzz targets** (was 9, now 11):
  - `fuzz_target_method_tokens` — drives CALLT (0x37) dispatch under
    randomly-generated NEF method-token tables, exercising the
    `invoke_native_contract` path under attacker-controlled token
    metadata.
  - `fuzz_target_nef_manifest_mutation` — bit-flips, byte-writes,
    insertions, and deletions on valid NEF / manifest seeds (53 NEF
    seeds + 24,296 manifest seeds + embedded fallback). Reaches deeper
    parser code paths than pure-random fuzzing.
- **Libfuzzer dictionaries** (`fuzz/dict/*.dict`): 970 domain-specific
  tokens across all 11 cargo-fuzz targets. Each dictionary is target-aware
  (Solidity keywords for `structured_sol`, NEF magic / opcodes for
  `nef_*`, JSON tokens for `manifest_json` / `standard_json`, Yul
  builtins for `yul_assembly`, etc.). Speeds time-to-coverage on a cold
  corpus by an order of magnitude.
- **17 new proptest modules** (effective fuzz coverage materially higher
  even though test-count delta is +43). New modules:
  `abi_roundtrip_props` (21 round-trip tests across all elementary +
  dynamic ABI types), `constructor_lifecycle_props` (declaration-init,
  ctor-body init, ctor-with-args, deploy-runs-once, modifier-on-
  constructor, payable-constructor `msg.value`),
  `diagnostic_stability_props`, `erc1155_proxy_props`,
  `examples_smoke_props`, `in_contract_array_return_props`,
  `multi_source_compile_props`, `pathological_corpus_smoke`. Plus
  follow-on additions to existing modules across the recent agent-team
  waves.
- **End-to-end smoke tests**: 54 single-source + 13 multi-source example
  contracts now compile cleanly under proptest (42 of which are also
  runtime-invoked end-to-end). Multi-source standard-JSON resolver
  verified across simple / transitive / circular / library import
  shapes.
- **ABI codec round-trip framework** (`tests/fuzz_tests/abi_roundtrip_props.rs`):
  21 proptests pinning encode → decode symmetry across `int{8,16,32,64,128,256}`,
  `uint{8,16,32,64,128,256}`, `address`, `bool`, `bytes{1..32}`,
  `string`, `bytes`, `T[]` for static T (`uint256[]`, `address[]`),
  and tuples. Caught and pinned bugs #23 / #24 / #25 / #28.
- **BLS12-381 runtime fully implemented**: all 12 native handlers
  (`bls12381Serialize` / `Deserialize` / `Equal` / `Add` / `Mul` /
  `Pairing` / `G1Add` / `G1Mul` / `G1Neg` / `G2Add` / `G2Mul` / `G2Neg`)
  in `src/runtime/execution/execution_impl_part2_native/crypto.rs`,
  backed by `bls12_381 = "0.8"` (promoted from dev-dep to runtime dep).
  Length-dispatched G1/G2 (48-byte vs 96-byte compressed); subgroup
  checks via `from_compressed`'s built-in torsion verification; pairing
  serialised via `Fp12` Debug bytes (deterministic, equality-preserving).
  All 4 previously-gated `differential_bls12381_*` proptests now pass
  byte-for-byte against the reference crate.
- **ERC-1155 + Proxy lifecycle tests** (`tests/fuzz_tests/erc1155_proxy_props.rs`):
  multi-token mint/transfer/burn flows and proxy-pattern delegate
  routing, surfacing bug #28 in `balanceOfBatch`.
- **State-batch canonical "skip-and-continue" semantics**: the
  ambiguity flagged as a v0.17.0 known limitation is now resolved.
  `src/runtime/state/impl/batch.rs` non-atomic mode silently skips
  invalid changes and continues — pinned by the now-unignored
  `state_batch_non_atomic_partial_apply_spec` test in
  `tests/runtime_state_batch_tests.rs`.
- **26 corpus seed files** (was 11): added 15 pathological seeds covering
  deep parens, nested arrays, large literals, malformed UTF-8, and
  adversarial Yul/NEF shapes. Speeds first-coverage on cold corpora.
- **Storage-key boundary tests**: 93 LOC of new boundary-encoding tests
  in `src/storage_key/tests/encode_integer.rs` added via a
  mutation-testing audit.
- **GitHub-contracts pipeline scaffolding**: `scripts/github_contracts_pipeline.js`
  + example manifests for harvesting third-party contract corpora.

### Fixed

- **#16** — uint256 unchecked-arith narrow-i64 vs BigInt-folded divergence.
  New `emit_widen_to_u256_unsigned` / `emit_truncate_u256` helpers in
  `src/ir/expressions/dispatch/binary.rs` route unchecked uint256 ops
  through the runtime's BigInt path, sidestepping the prior naïve fix's
  signed-interpretation breakage. The `u16::MAX` grammar workaround was
  removed from `optimizer_props.rs`.
- **#21** — `StdLib.atoi("--42", 10)` returned 42 instead of 0; magnitude
  parser switched from `i128::from_str_radix` to `u128::from_str_radix`
  so any sign character in the body yields 0.
- **#22** — `StdLib.base64Decode("AB=C")` returned 3-byte garbage;
  decoder now rejects `=` outside the trailing-pad position of the
  final chunk (matches RFC 4648 + the `base64` crate STANDARD engine).
- **#23** — narrow signed-int `abi.encode` zero-padded instead of
  sign-extending (`int8(-1)` produced `00..00 ff..ff` instead of
  `ff..ff`); new `emit_abi_fixed_buffer_signed` helper branches on
  source sign and pre-fills the destination buffer with `0xFF` for
  negatives.
- **#24** — `abi.decode` pass-through for `string` / `bytes` (returned
  raw 96-byte head+length+pad payload); new
  `emit_abi_decode_dynamic_top_level` helper walks the EVM-canonical
  offset/length/data/pad chain.
- **#25** — `abi.decode` unusable for `T[]` dynamic arrays
  (`.length` faulted `SIZE: unsupported type`); same patch materialises
  a `NEWARRAY` of decoded scalars via per-slot SUBSTR + CONVERT.
- **#26** — BLS12-381 runtime returned `StackItem::Null` for all 12
  native methods (compiler+IR were wired but the runtime had a stubbed
  `_ => Null` fall-through). Functional gap now closed; see Added.
- **#28** — in-contract `this.method()` returns of `T[] memory`
  corrupted `.length` (read wire-byte-length instead of element count);
  new `try_lower_this_external_dynamic_assign` helper in
  `src/ir/statements/assignments/lower_assignment.rs` routes through
  the same dynamic-decode chain as #25. Pinned by
  `tests/fuzz_tests/in_contract_array_return_props.rs`.
- **Payable-constructor `msg.value` snapshot/restore** in
  `call_method_with_deploy_args`: ctor-side msg.value override is now
  snapshotted before the deploy and restored after, so a payable ctor
  observes the deploy-time value rather than leaking a subsequent
  call's override into ctor scope.
- **Storage-key boundary fixes** surfaced via mutation-testing audit
  (`src/storage_key/tests/encode_integer.rs` regressions).

### Test counts

| Suite | v0.17.0 | v0.18.0 | Δ |
|-------|--------:|--------:|--:|
| fuzz_tests (proptest) | 912 | **968** | +56 |
| runtime_state_batch_tests | 2 (1 ignored) | **3** | +1 (ambiguity resolved) |
| lib unit tests | 508 | **511** | +3 |
| conformance_tests | 40 | **40** | — |
| e2e_compilation_tests | 80 | **80** | — |
| **Total** | 1,542 | **1,602** | **+60** |
| cargo-fuzz targets | 9 | **11** | +2 |
| Fixed bugs | 22 | **28** | +6 (cumulative) |

The +60 figure understates the practical coverage gain: removing the
`u16::MAX` literal/arg bound from `optimizer_props` widens the
optimizer-differential search domain to the full `u32::MAX` range, and
each of the 11 cargo-fuzz targets now ships with a libfuzzer
dictionary that compresses time-to-coverage on a cold corpus.

### Known Limitations

None — all open items from v0.17.0 are resolved.

## [v0.17.0] - 2026-04-25

Fuzz-system overhaul release driven by 11 sequential review waves.
**912 proptest cases pass** (was 743), **11 cargo-fuzz targets** (was 6, all
crash-free), **22 bug fixes total** (was 14), plus **one latent allocation-DoS**
hardened proactively. Coverage: **68.42% region / 67.42% line** via
`cargo-llvm-cov`.

### Added

- **5 new cargo-fuzz targets** (was 6, now 11):
  - `fuzz_target_runtime_exec` — feeds arbitrary bytes as NeoVM bytecode
    against a bounded `NeoRuntime` (gas + memory capped). Surfaced bug #15
    (NEWARRAY OOM DoS) on the first run.
  - `fuzz_target_method_tokens` — drives CALLT (0x37) dispatch under
    randomly-generated NEF method-token tables, exercising the
    `invoke_native_contract` path under attacker-controlled token metadata.
  - `fuzz_target_nef_manifest_mutation` — bit-flips, byte-writes,
    insertions, and deletions on valid NEF / manifest seeds (53 NEF seeds,
    24,296 manifest seeds + embedded fallback). Reaches deeper parser code
    paths than pure-random fuzzing.
  - `fuzz_target_structured_sol` — `arbitrary`-driven Solidity AST grammar.
    This release extended the grammar with **inheritance** (multi-base
    linearisation, abstract / `virtual` / `override`), **`using <Lib> for
    <Type>`**, **try/catch**, **revert("string")**, and
    **require(cond, "string")**. Coverage rose from ~8,200 PCs at the
    initial baseline to **11,190 PCs** (+36%) after grammar extensions.
  - `fuzz_target_yul_assembly` — `arbitrary`-driven Yul AST inside
    Solidity `assembly { … }` blocks (let/assign/if/for/switch/break/
    continue + mload/sload/mstore/sstore/return/revert). Drives
    `src/ir/statements/assembly.rs` lowering through random opt-levels.
- **20+ new proptest modules** (912 cases vs 743 baseline):
  `arithmetic_helpers_props`, `compile_runtime_roundtrip`,
  `conditional_jumps`, `contract_upgrade_props`,
  `convergence_props`, `custom_error_envelope_props`,
  `determinism_props`, `devpack_props`, `differential`,
  `disasm_stability_props`, `modifier_rewrite_props`,
  `native_contract_props`, `native_resolver_props`,
  `openzeppelin_patterns_props`, `optimizer_props`,
  `reentrancy_props`, `stdlib_native_props`,
  `storage_iterator_stress`, `storage_state_machine`.
- **Differential testing**: hash differentials extended to 4096-byte inputs
  + boundary-byte patterns (`0x00*N`, `0xFF*N`, alternating); BLS12-381
  G1/G2 add+mul + pairing equality vs `bls12_381` reference crate (gated
  on runtime-implementation arrival); manifest event parameter type
  fidelity vs spec.
- **Coverage analysis**: `cargo-llvm-cov` integrated; coverage gaps
  documented (see Known Limitations / Dead Code in
  `FUZZ_STATUS_REPORT.md`).
- **State-machine tests**: storage iterator stress against a `BTreeMap`
  reference model; 50-call gas-stability sweep on the same `NeoRuntime`
  (no per-call linear-growth from leaks); recursive Fibonacci(10)
  exercising 177 self-external `System.Contract.Call` frames cleanly.

### Fixed

- **#15 — Host DoS via NEWARRAY/NEWARRAY_T/NEWSTRUCT/NEWBUFFER**: a 6-byte
  bytecode `02 ff 00 0c 17 c6` (PUSHINT32 + NEWSTRUCT) requested ~387M
  Null items via `Vec::with_capacity(count)` and OOM-aborted the host
  process before gas accounting could fire. Fixed by bounding `count *
  size_of::<StackItem>()` against `memory_limit` in
  `src/runtime/execution/collections/construction.rs` and `len` against
  `memory_limit` in `src/runtime/execution/execution_impl_part3_bytes.rs`
  (mirrors PUSHDATA4 guard). Regression pinned by `batch132*`.
- **#17 — Host DoS via Storage.Put**: `RuntimeConfig::storage_limit` had
  no consumer (`grep -rn storage_limit src/` returned the declaration and
  default only, zero readers). Storage.put / Local.Put inserted into
  `storage_overlay` with no size check. Fixed by adding `storage_limit`
  to `ExecutionContext`, propagating from config, and adding
  `enforce_storage_limit` (cumulative-bytes guard) on every Put.
- **#18 — Host DoS via Storage.Find**: `helpers/storage.rs::
  build_storage_entries` queried with `limit: None` and used
  `Vec::with_capacity(entries.len())`; an attacker who populated storage
  with many keys (10K writes fit in default gas_limit) could OOM the
  host with one `Find` syscall. Fixed by bounding `StorageQuery::limit`
  against `storage_limit / MIN_ENTRY_BYTES`, plus a post-merge cap to
  cover overlay-only matches.
- **#19 — Gas DoS via flat per-syscall pricing**: `Storage.Put` charged
  a flat 1000 gas regardless of value size; `CryptoLib.sha256/keccak256/
  ripemd160/sha1/murmur32` charged a flat 512 gas regardless of input
  length. Fixed by `syscall_extra_input_gas` (in
  `src/runtime/execution/instruction/syscall.rs`) — peek-and-charge
  before the handler with `STORAGE_PUT_PER_BYTE_GAS=100` and
  `HASH_PER_BYTE_GAS=50` on top of the flat base. CALLT path mirrored at
  `src/runtime/execution/instruction/flow/calls.rs`. Regression pinned by
  `batch133a–d`.
- **#20 — Gas DoS via CheckMultisig**: ran `O(N·M)` secp256k1 verifies
  (up to 4096 verifies for 64×64 input) for a single flat 1000-gas
  charge. Fixed with `CHECKMULTISIG_PER_VERIFY_GAS=1000` charged upfront
  as `pub_count * sig_count * per_verify` (saturating).
- **#21 — Correctness: `StdLib.atoi("--42", 10)` returned 42**:
  `i128::from_str_radix(body, radix)` accepts a leading `-` in `body`
  after the outer `neg` flag was already set, so a double-negative
  becomes a positive number. Fixed by switching magnitude parser to
  `u128::from_str_radix` so any sign character in `body` (`-`, `+`,
  anything else non-digit) yields 0.
- **#22 — Correctness: `StdLib.base64Decode("AB=C")` returned 3-byte
  garbage**: the decode loop accepted `=` at any position inside a 4-char
  chunk and treated it as a literal-zero sextet, ignoring RFC 4648's
  mid-chunk-pad-rejection rule. Fixed: `base64_decode` now rejects `=`
  outside the trailing-pad position of the FINAL chunk.
- **Preventive harden** for `read_input_slice` (calldata reader) — same
  bug-#15 shape (`vec![0u8; length]` with no memory_limit check). Not
  opcode-reachable today, but the methods are public; future opcode
  wiring would inherit the OOM. Now propagates an error if `length >
  memory_limit`.

### Known Limitations

- **Bug #16 — uint256 unchecked-arith narrow-i64 divergence (deferred)**:
  the `optimizer_four_level_differential_random_expr` proptest surfaced a
  real divergence between unoptimized and optimized lowerings of
  unchecked uint256 arithmetic. The naïve fix (force-widen unchecked
  uint256 BinaryOps to 32-byte ByteArray so the runtime's BigInt path
  runs) breaks tests that pass `uint256.max` as a 32-byte ByteArray
  because `coerce_item_to_bigint` treats it as **signed**. Resolving
  properly requires plumbing an unsigned-LE BigInt path through
  `cmp_needs_bigint_path`, `bigint_to_stack_item`, and the wide
  arithmetic helpers — out of scope for this release. Mitigation:
  `dexpr_strategy` literals + `a/b/c` test args bounded to `0..=u16::MAX`.
- **BLS12-381 runtime stub**: the compiler is fully wired (IR resolver
  whitelists 13 method names, devpack exposes `bls12381Add/Mul/Pairing`
  + EVM-precompile shims), but
  `src/runtime/execution/execution_impl_part2_native/crypto.rs::invoke_
  native_cryptolib` does not implement the BLS handlers — every
  `bls12381*` falls through to `StackItem::Null`. Contracts using BLS
  compile cleanly but silently return zero. 4 differential proptests are
  gated to auto-activate when the runtime implementation lands.
- **State batch non-atomic semantics ambiguity**:
  `src/runtime/state/impl/batch.rs:25` does `let _ =
  self.apply_change(change)` in non-atomic mode (silent error discard).
  Could be intentional ("skip invalid, keep going") or a bug ("stop
  after first invalid"). Pinned by an ignored test
  (`state_batch_non_atomic_partial_apply_spec`) so the choice can be
  decided in a future release.

### Dead Code (deletion candidates, not removed in this release)

`cargo-llvm-cov` analysis identified ~6,200 LOC across the codebase that
is **0% covered AND has zero importers** anywhere in `src/` or `tests/`:
- Orphaned standalone Yul frontend (~4,500 LOC):
  `src/{lexer,parser,codegen,semantic,optimizer}/`, `src/error.rs`,
  `src/types.rs`. The Yul-in-Solidity surface (`assembly { … }`) reaches
  its own active lowering at `src/ir/statements/assembly.rs`.
- VM bridge layer (~1,700 LOC): `src/runtime/bridge/`. `VMBridge` is
  constructed only inside its own subtree.
- Top-level orphans: `src/{security,warning,validation,testing}.rs`.

Removing these would push real coverage well above 90%. Listed for
awareness; pending an explicit cleanup decision.

### Test counts

| Suite | v0.16.0 | v0.17.0 | Δ |
|-------|--------:|--------:|--:|
| fuzz_tests (proptest) | 743 | **912** | +169 |
| runtime_state_batch_tests | — | **2** | +2 |
| lib unit tests | 508 | **508** | — |
| conformance_tests | 40 | **40** | — |
| e2e_compilation_tests | 80 | **80** | — |
| **Total** | 1,371 | **1,542** | **+171** |
| cargo-fuzz targets | 6 | **11** | +5 |
| Fixed bugs | 14 | **22** | +8 |

## [v0.16.0] - 2026-04-19

Major stability release driven by continuous fuzz-harness review (Tasks #94–#183).
Approximately 90 distinct fixes landed across compiler, IR lowering, runtime,
ABI encoding, arithmetic, modifiers, yul, try/catch, and storage. The compiler
now passes **409 fuzz harnesses** (0 failed, 1 ignored) covering the full
Solidity 0.8.x feature matrix plus Neo N3 integration.

### Added

- **Narrow Yul support (Task #99, #100, #183)**: inline `assembly { ... }`
  blocks now lower `mstore`/`mload`/`return` along with `let`/`:=` bindings,
  basic arithmetic, and reference semantics. `tstore`/`tload` implement
  EIP-1153 transient storage backed by a `NEWMAP` local (with `HasKey`
  guard for unset slots returning `0`). Yul identifiers now also resolve
  to outer Solidity function parameters via the new `StoreParameter` /
  `LoadParameter` IR variants (Task #183).
- **EIP-1153 transient storage in Yul (Task #100)**: `tstore(slot, value)` /
  `tload(slot)` lowers to per-function transient map ops.
- **New IR variants**: `Substr` (Task #95 bytes slicing), `NewMap` /
  `HasKey` (Task #100 yul transient storage), `StoreParameter` /
  `LoadParameter` (Task #156 parameter writes, Task #183 yul parameter
  binding).
- **`msg.value` host injection (Task #113)**: `NeoRuntime::override_value` /
  `override_sender_and_value` thread a per-call msg.value override through
  the runtime; a dedicated syscall slot in `execution/syscalls/runtime.rs`
  returns the active value. Neo N3 has no intrinsic `msg.value`, so this
  gives Solidity tests a faithful EVM-style calldata surface.
- **`msg.data` synthesis (Task #112)**: public-method dispatch now
  synthesises `selector || abi.encode(args)` and threads it to the
  executing frame so the `msg.data` builtin returns the EVM-canonical
  calldata payload. `bytesN(arg)` / `address(arg)` cast leaves in the
  synthesised path are left-aligned (Task #112 refinement) so they match
  the on-wire encoding.
- **Canonical `Panic(uint256)` envelope (Task #107, #108)**: ALL
  panic sites (div-by-zero, mod-by-zero, INT256_MIN/-1 divide, int
  overflow, unary negate overflow, array OOB, `pop()` empty, `abi.decode`
  short-buffer) now route through the shared `emit_panic` helper which
  emits the 4-byte selector `0x4e487b71` followed by the BE-32 code (e.g.
  `0x11`, `0x12`, `0x32`, `0x41`). Replaces the earlier ad-hoc
  `"Panic: 0xNN"` ByteString payloads so `catch Panic(uint256)` clauses
  can match against the canonical EVM shape.
- **Canonical `Error(string)` / `revert` envelope (Task #131)**: `require`
  with a string literal and `revert("reason")` now both emit the EVM
  `Error(string)` 4-byte selector + abi-encoded string, aligning
  `require` with `revert` payload shape.
- **Delegatecall hard-reject (Task #101)**: `target.delegatecall(data)` is
  now rejected at IR lowering with a precise diagnostic directing users
  toward proxy patterns or target-contract inheritance. Previously the
  call silently routed to `System.Contract.Call`.
- **Bytes slicing (Task #95)**: `b[start:end]` on `bytes` / `bytes memory`
  / `bytes calldata` values lowers via the new `Substr` IR instruction to
  a contiguous ByteString.
- **Write-to-parameter support (Task #156)**: Solidity permits assigning
  to function parameters directly. The new `StoreParameter` IR variant
  lowers via NeoVM `STARG0..6`, and tuple swaps of the form
  `(a, b) = (b, a)` on function parameters now work. Compound
  assignments (`a += 1;`) on parameters are also handled.
- **Narrow signed integer checked arithmetic (Task #154)**: Add/Sub/Mul on
  `int8`/`int16`/`int32`/`int64`/`int128` now emits a post-op range
  guard against `[-(2^(bits-1)), 2^(bits-1)-1]` and routes overflow
  through `Panic(0x11)`.
- **357-test fuzz suite**: 357 new fuzz harnesses land in
  `tests/fuzz_tests/batches_*.rs` (in addition to the baseline 52),
  bringing the total to **409 passed / 0 failed / 1 ignored** on the
  `fuzz_tests` binary.
- **StdLib native coverage (Task #51)**: `itoa`, `atoi`, `base64Encode`,
  `base64Decode` now implemented in `execution_impl_part2_native/stdlib.rs`.
- **Dynamic-array encoding (Task #121, #137)**: `T[]` returns and
  `abi.encode(T[])` / selector-side args emit the EVM-canonical
  `offset || length || BE-32 elements` layout.
- **Struct flattening for `abi.encode` (Task #124)**: whole-struct args
  are expanded into per-field stack items so the `AbiEncode` builtin
  classifies them as static types when appropriate; includes a new
  `try_flatten_struct_arg_for_abi_encode` pattern.
- **Custom-error struct envelopes (Task #181)**: `revert CustomErr(struct)`
  positional and named-args forms flatten struct args into the ABI
  tuple and render struct type strings via the canonical EVM
  `(T1,T2,...)` shape.
- **Sticky caller override (Task #176)**: `pending_caller_account` now
  re-arms from a sticky slot so caller overrides survive across
  self-external calls.
- **`virtual caller` script hash (Task #123)**: per-frame
  `msg.sender` override pushed by self-external dispatch, deterministic
  virtual script hash derivation, resolved in the `GetCallingScriptHash`
  syscall.
- **Self-method dispatch table (Task #70)**: manifest-derived
  `(method_name, offset, arg_count)` table installed on each execute,
  enabling `this.someFn()` without cross-contract syscall overhead.

### Changed

- **Event topic0 = keccak256(signature) (Task #39, pre-landed)**:
  `emit` lowering now produces the EVM-canonical log shape
  (`topics[0]` is the keccak256 of the event signature, with indexed
  args taking subsequent topic slots; non-indexed args go to `data`).
- **Try/catch envelope matching (Task #103, #86)**: each catch clause
  uses a shape-specific guard (`Error(string)`, `Panic(uint256)`,
  named custom error, wildcard). Raw stack-top bytes are preserved
  through `try_frames.rs` so the catch handler receives the EVM
  envelope verbatim.
- **Override sticky semantics (Task #105)**: `default_timestamp` pinned
  to `1_704_067_200` (2024-01-01T00:00:00Z). Pending metadata overrides
  (timestamp, index, sender) are now snapshotted BEFORE the user
  method observes them and drained after the call.
- **Require → Error(string) envelope (Task #131)**: `require(cond, "msg")`
  with a string literal now emits the same EVM `Error(string)` shape as
  `revert("msg")`.
- **Storage reference handling (Task #117)**: `resolve_storage_reference`
  widened to recognise Array- and Mapping-typed state variables as
  storage pointers when the base has no `field_path`. Regression guard
  preserves the per-field-slot layout for struct-array writes
  (Task #104).
- **`using` directive inlining (Task #91)**: library functions whose
  first parameter is `T storage` are inlined with caller-parameter
  hiding and a per-call inline-return redirect, preserving source
  semantics across the inline boundary.
- **Modifier epilogue semantics (Task #114)**: when at least one
  applied modifier has a body statement after `_`, every `return` in
  the function body now redirects through a synthetic modifier-wrap
  break label so the epilogue runs exactly once regardless of which
  path the function takes.
- **Interface expansion (Task #115)**: interface casts `I(expr)` in
  statements, interface-typed parameters, and interface references are
  expanded to the primary contracts that implement them, allowing
  cross-contract calls through interfaces.
- **Fallback dispatch (Task #126)**: a primary contract's `fallback()`
  now acts as a universal catch when the explicitly-named method
  isn't in the merged dispatch table. `Receive` is included alongside
  `Fallback` in the dispatch scan.
- **Tuple return flattening (Task #94)**: nested tuple return
  expressions are now flattened recursively. Parenthesised tuple
  return/parameter types accepted at semantic level.
- **`abi.decode` short-buffer guard (Task #84)**: emits
  `Panic(0x41)` instead of a raw throw when `buf.length <
  expected_static_bytes`.
- **BigInt comparison (Task #30 slice 1 Part C)**: wide-ByteArray
  comparisons route through BigInt so `uint256` values pushed as
  21+ byte payloads compare by magnitude, not by raw bytes.
- **Wide bitwise ops (Task #50)**: `~`, `|`, `&`, `^` on wide operands
  route through BigInt with a post-op 256-bit mask, so `~uint256(x)`
  produces `u256::MAX - x` rather than `!(x as u64)`. Narrow
  i64/u64 shift path truncation also fixed.
- **BigInt shift (Task #H4)**: wide-ByteArray left/right shifts route
  through BigInt. Reuses the Task #50 infrastructure and clamps shift
  amounts > 255 to an all-zero result per EIP-145 (Task #33).
- **Interface kind metadata (Task #115)**: collected per-interface at
  analysis time and threaded through lowering for virtual dispatch.
- **Self external-call routing (Task #83)**: `new B(); b.foo()` where
  `B` is a sibling-merged primary contract now routes through
  `self_method_offsets` via a 20-byte zero placeholder, bypassing the
  System.Contract.Call syscall for bandwidth-free internal dispatch.
- **Framework / NEP standards (devpack v1.1.0)**: `FrameworkBase.sol`,
  `NEP17.sol`, `NEP11.sol`, `NativeCalls.sol`, `Neo.sol`,
  `Runtime.sol`, `Storage.sol` refreshed for the new compiler
  semantics. New `Precompiles.sol` library (EVM precompile routing)
  and `PrecompileShowcase.sol` example.

### Fixed

- **MEMCPY leak in 8+ sites (Task #109, #66, #89, #76)**: `bytesN(..)` /
  `address(..)` cast args no longer leak the MEMCPY source pointer
  into downstream builtins. Fixed in `events.rs`, `abi.rs`,
  `member_access.rs`, `resolved.rs`, `return_revert.rs`,
  `builtins.rs`, and the packed-encoding path.
- **`bytes32 ↔ uint256` (Task #111)**: `uint256(bytes32)` is a
  bit-identity reinterpret — Solidity spec §4.7.3 — not a decode.
  Preserves magnitude across the cast.
- **`payable(x)` (Task #128)**: now a pure type-only cast (§4.3, §4.7.3),
  not a no-op wrap.
- **`string(bytes_value)` (Task #171)**: recognised as a semantic
  no-op on the value stack.
- **Nested tuple return (Task #94, #64)**: `return (a, (b, c))` flattens
  to the canonical tuple layout.
- **Bytes slice SUBSTR (Task #95)**: `b[i:j]` on dynamic `bytes` lowers
  correctly via the new `Substr` IR op.
- **Modifier epilogue (Task #114)**: runs once regardless of return path.
- **Struct array push/read (Task #104, #170)**: `P[] ps; ps.push(P(a,b))`
  and subsequent reads use the correct per-field slot layout;
  symmetric narrow-ByteArray × UnsignedInteger arithmetic arms added.
- **Delegatecall silent routing (Task #101)**: now hard-rejected with a
  diagnostic, no longer silently routes to System.Contract.Call.
- **9-arg `abi.encode` selector-side (Task #121 mirror)**: `T[]` in
  selector-side args emits the same 32-byte BE offset/length/elements
  shape as the return side.
- **Dynamic-array return JSON leak (Task #137)**: single-value
  dynamic-array returns (`return uint[]`) now emit the canonical EVM
  layout instead of leaking an internal JSON Array envelope.
- **Post-increment / post-decrement wrap (Task #30 slice 4)**:
  `lower_post_inc_dec` now routes through the checked-arithmetic
  path so `x++` at `uint256.max` emits `Panic(0x11)`.
- **Checked arithmetic — 6 ops (Task #30, #67, #154)**: all Add/Sub/Mul
  ops on `uint256`/`int256`/narrow signed types now route through
  BigInt with post-op range guards against the per-type domain.
- **`uint256` BigInt path (Task #32)**: Sub/Mul no longer wrap at 64-bit
  when operands are wide ByteArray.
- **`abi.encode*` / `abi.decode` shape (Task #44)**: EVM-canonical
  `pad32_be(arg_i)` encoding for static args, offset/length/payload for
  dynamic args. Round-trip decoder accepts the BE-packed payload.
- **INT256_MIN / -1 (Task #30 slice 4)**: runtime-side guard for the
  unrepresentable signed-division case routes to `Panic(0x11)`.
- **`emit` with 0 indexed args (Task #39)**: produces exactly 1 topic
  (the signature hash).
- **NatSpec permissions override**: `@custom:neo.manifest.permissions`
  comments correctly substitute wildcard manifests (pre-landed).
- **Modifier re-entry guard**: modifier-epilogue redirect does not
  interfere with nested try/catch break targets.
- **`NeoRuntime::call_function` (Task #19 — earlier session)**: pushes
  args to eval stack, skips `_deploy`, respects offset. All
  `call_method`-driven harnesses now deliver args correctly.
- **`ecrecover` (Task #20 — earlier session)**: returns
  Ethereum-spec address via `keccak256(pubkey[1..])[12..]`, not the
  Neo-native script-hash shape.
- **Narrow-operand bitwise (Task #118)**: left-aligned bytesN fuzzing
  harness no longer flagged as negative via the sign-extended interpretation.
- **`.length` on indirected storage dynamic arrays (Task #161)**:
  returns the live length from the canonical slot instead of a stale
  SIZE value.
- **`pop()` empty-array Panic (Task #98)**: now routes through the
  shared `emit_panic` helper with code `0x31`.
- **`.transfer(uint)` on payable (Task #162)**: lowering now works
  end-to-end instead of faulting on argument packing.
- **`new T[N]` memory allocation (Task #49)**: `T[N] memory a;` (no
  initializer) now allocates a real array with N default-initialized
  slots instead of leaving the local as a null reference.
- **Cross-contract `new B(); b.foo()` routing (Task #160)**: catch
  arms in try/catch around the sibling-merged external-call routing
  receive the revert envelope correctly.
- **`try X { ... } catch ...` parse-shape (Task #125)**: leading
  call expression in a try statement is correctly matched against
  the sibling-merge routing.

### Infrastructure

- **Fuzz test split**: the 24k-line `tests/fuzz_tests.rs` monolith was
  reorganised into 10 submodules under `tests/fuzz_tests/` for
  maintainability:
  - `baseline_tests.rs` — the original 52 harnesses.
  - `batches_18_30.rs`, `batches_31_45.rs`, `batches_46_64.rs`,
    `batches_66_80.rs` — per-batch harness groups.
  - `arithmetic_props.rs`, `compiler_props.rs`, `optimizer_props.rs`,
    `storage_props.rs` — proptest property tests.
  - `task107_catch_panic_tests.rs` — dedicated Panic(uint256)
    envelope coverage.
  - `common.rs` — shared helpers.
- **Regression corpus**: `.proptest-regressions` files under
  `tests/fuzz_tests/` and `tests/` checkpoint failing seeds for all
  property tests.
- **Devpack bump**: `@neo-devpack-solidity/contracts` → **1.1.0** to
  align with the new compiler semantics (new `Precompiles.sol`
  library, refreshed `Framework.sol`, `NEP17.sol`, `NEP11.sol`).

### Known Limitations

- **Task #182** — nested struct dynamic-array `.length` returns still
  serialize via the internal JSON Array shape instead of the
  EVM-canonical 32-byte BE length. Workaround: return the field as
  a separate `uint256` computed from `arr.length`. The corresponding
  harness is marked `#[ignore]`.

## [v0.15.0] - 2026-03-18

### Added

- **onNEP11Payment Support**: `msg.sender`, `msg.value`, and `msg.data` now correctly map to parameter indices in `onNEP11Payment` callbacks (msg.data uses param 3, after tokenId at param 2).
- **Test Coverage**: Added focused IR codegen tests for `msg.data` with selector prefix and `onNEP11Payment` parameter mapping.

### Changed

- **msg.data with Selector Prefix**: `msg.data` outside callbacks now produces `selector || abi.encode(current args)` instead of just `abi.encode(args)`, giving users a proper EVM-style calldata approximation.
- **block.coinbase**: Now maps to `address(0)` instead of `getNextBlockValidators()`, matching EVM's `address` return type.
- **block.sha3**: Fixed incorrect mapping from `GetRandom()` to `Ledger.currentHash` (the current block's hash).
- **encodeWithSelector Resolution**: Added `encodeWithSelector` to `builtin_library_supported_members` and `resolve_abi_member` for proper resolution.
- **Documentation Overhaul**: Comprehensive update across all documentation files to reflect actual compiler behavior:
  - `delegatecall` documented as warning (not blocked)
  - `msg.value` documented as warning + returns 0 (not error)
  - `parity-and-limitations.md` split Blocked vs Auto-Mapped features
  - All feature tables updated with correct mappings

### Fixed

- **Stale Comments**: Fixed comments in `runtime_values.rs` that referenced wrong warning codes or incorrect behavior.
- **block.parenthash Comment**: Fixed comment that incorrectly referenced `getBlock(currentIndex-1).prevHash`.

## [v0.14.0] - 2026-03-13

### Added

- **EVM Try/Catch Multi-return**: `try/catch` blocks now natively support EVM's multiple return syntax (`try returns(uint a, uint b)`) by seamlessly unwrapping the NeoVM `Array` return payload.
- **Documentation Parity**: Completely refactored the VitePress documentation architecture to identically mirror the official `soliditylang.org` sidebar, taxonomy, and feature coverage, fully tailored for Neo N3.

### Changed

- **Graceful EVM Call Options**: Extraneous call options (e.g., `contract.method{value: x}()` or `new Contract{value: x}()`) are now safely ignored, emitting a semantic warning instead of halting compilation.
- **Inline Assembly Fallback**: `assembly { ... }` blocks now compile gracefully into NeoVM no-ops with a warning, unblocking compilation of heavily optimized Ethereum libraries where the assembly isn't strictly required.
- **Unsupported Call Translation**: Unsupported low-level EVM calls (`delegatecall`, `staticcall`) are now lowered to returning a dummy boolean `false` with a semantic warning instead of a hard E3001 abort.
- **Obsolete EVM Globals**: `msg.data` now compiles to `selector || abi.encode(current args)` outside of the `onNEP17Payment` callback (param 2) and `onNEP11Payment` callback (param 3). `msg.sig` now compiles to the current function selector with a warning about internal-call semantics.

### Fixed

- **Infinite Loop Prevention**: Patched the Neo IR `CallFunction` dataflow analysis to accurately track return arities, preventing `neo-solc` from hanging infinitely on complex void-return functions (like those found in DAO Governance contracts).
- **NatSpec Overrides**: Fixed missing `load_manifest_permissions_override_from_natspec` linkages, ensuring `@custom:neo.manifest.permissions` comments correctly substitute wildcard manifests.
- **Runtime Exception Handlers**: Hardened the execution context bridging, replacing manual modulo bitwise checks with `.is_multiple_of()` to appease strict CI linting.

## [v0.13.1] - 2026-02-18

### Changed

- **Release workflow resilience**: release matrix now uses `fail-fast: false` so one
  target failure no longer cancels other platform builds.

### Fixed

- **ARM64 Linux release builds**: hardened aarch64 cross-compilation setup with explicit
  linker/toolchain environment (`CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER`,
  `CC/CXX/AR` target-specific vars, and cross pkg-config allowance).
- **Release pipeline reliability**: added missing ARM64 cross tool dependencies in CI
  (`g++-aarch64-linux-gnu`, `binutils-aarch64-linux-gnu`, `libc6-dev-arm64-cross`).

## [v0.13.0] - 2026-02-18

### Added

- **Transparent EVM-to-Neo auto-mapping**: 9 previously blocked EVM-specific
  Solidity features now compile with Neo N3 equivalents and compile-time warnings.
- **`super` keyword support**: `super.method()` resolves through flattened inheritance,
  preserving base overrides as `__super_{methodName}` during lowering.
- **User-defined value types (`type X is Y`)**: alias propagation across file/contract
  scopes and inheritance; `wrap`/`unwrap` lower as no-ops.
- **`type(T).name` expression**: compile-time string constants for contract and type names.
- **`require(condition, CustomError(...))` support**: Solidity 0.8.26+ form supported with
  diagnostic-preserving error signature text.
- **Devpack expansion**: added NEP-26 standard coverage, `NativeContracts` native address
  helpers, and reusable `NeoBytes` / `NeoMath` base libraries.

### Changed

- **`using` directive semantics hardened**: member-style library calls now require explicit
  `using ... for` scope, enforce receiver type targeting, and enforce named-function lists
  from `using {f,g} for T` declarations.
- **Frontend-to-IR metadata flow** extended for `using` directives, so library binding scope
  and target constraints are preserved through lowering.
- **Release process docs** updated to match actual repository workflow (validation + tag push).

### Fixed

- **Native contract lowering**: aligned `NativeContracts` and `NativeCalls` member-call paths;
  lowered native constants as address literals in IR/codegen path.
- **Diagnostics quality**: reduced duplicate constant warning noise in merged library contexts.
- **Toolchain compatibility**: replaced unstable `is_multiple_of` usage with stable modulo logic.

## [v0.12.0] - 2026-02-13

### Changed

- **Developer Tools**: 85% → 95% complete
  - Added debug tooling support (@neo-devpack-solidity/types/debugger)
  - Added network configurations for Neo TestNet/MainNet
  - Added artifact management
  - Added source map support

## [v0.11.0] - 2026-02-13

### Changed

- **Progress Update**: Updated all progress metrics to 95% complete
- **Status Badge**: Updated to "🟢 Production-Ready · 95% Complete · 620+ Tests"

### Updated

- **Core Compiler**: 90% → 95% (function overloading, public state variable getters)
- **Runtime Library**: 80% → 95% (iterator handles, per-syscall gas accounting, full opcode suite)
- **Testing**: 85% → 95% (620+ test coverage, end-to-end tests complete)
- **Developer Tools**: 75% → 85% (Hardhat, Foundry, ABI Router, Types, CLI Tools)
- **Documentation**: 85% → 95% (Solitity support matrix, Error reference, Architecture)

### Known Limitations

- Oracle integration (stub only - requires external oracle service)
- Fuzzing framework (planned)
- Differential testing (planned)
- IDE debugging tools (planned)

## [v0.10.0] - 2026-02-13

### Fixed

- **Code generation**: Fixed variable assignment to emit correct `STLOC` instructions
- **Loop control**: Implemented `break`/`continue` with proper loop context tracking
- **Variable handling**: Added variable index table for efficient local variable access
- **Semantic analysis**: Improved variable scope tracking with scope stack

### Added

- **CompilerConfig builder methods**: Added `include_abi()`, `include_source_map()`, `validate_only()`, `analyze_only()`
- **Optimization helpers**: Added `is_optimized()` and `optimization_passes()` methods
- **SemanticModel methods**: Added `public_functions()`, `get_function()`, `get_state_variables()`, `is_payable()`
- **Error codes**: Added `BreakOutsideLoop`, `ContinueOutsideLoop`, `InvalidJumpOffset`
- **Helper functions**: Added `emit_ldloc()` and `emit_stloc()` for proper NeoVM bytecode generation

### Refactored

- **Code generator**: Improved variable handling with proper index-based storage
- **Optimizer**: Better code structure with clearer separation of concerns
- **Error handling**: More specific error codes for better debugging

### Changed

- **Gas estimation**: Updated to use more accurate NeoVM cost values (crypto: 700000, storage: 1000000)
- **Devpack documentation**: Clarified `contractCallWithFlags` flags parameter status

## [v0.9.10] - 2026-02-11

### Added

- **Import support expansion**: wildcard namespace bindings now support
  static member calls, namespace-qualified contract/interface casts, and
  selector access forms such as `NS.IFoo.foo.selector`.
- Standard JSON regression coverage for alias/wildcard import behavior,
  including namespace cast and selector forms.
- Low-level call regression coverage for `abi.encodeCall(...)` inline,
  local-variable, and invalid-reference cases.

### Changed

- Low-level call parsing now accepts `abi.encodeCall(...)` payloads in the
  same lowering path as `encodeWithSignature/encodeWithSelector`, including
  simple wrapper forms like `bytes(...)` / `string(...)`.
- `try/catch` lowering now emits runtime type-guard dispatch (`ISTYPE`) for
  multi-clause catch handling, with clearer NeoVM-specific Panic diagnostics.
- `immutable` state variable enforcement tightened to constructor / `_deploy`
  initialization only.
- Feature matrices and README support notes updated to reflect current import,
  low-level call, and catch-clause behavior.

### Fixed

- **Low-level `abi.encodeCall` validation bug**: non-function member
  expressions (e.g. `s.x`) are no longer accepted as function references for
  dynamic low-level calls.
- Fixed-size `new T[N]` allocations now lower correctly for compile-time sizes.
- Nested tuple destructuring lowering reliability improved for mixed targets.

## [v0.9.9] - 2026-02-09

### Added

- **Native contract runtime support**: Policy, Oracle, RoleManagement, Ledger,
  Notary, and Treasury native contracts are now callable from the embedded
  runtime, with per-contract dispatch modules and gas hints.
- `notary.rs` and `treasury.rs` dispatch modules under
  `src/runtime/execution/execution_impl_part2_native/`.
- `NativeContractShowcase.sol` example demonstrating Policy, Ledger, and
  RoleManagement calls from Solidity.
- `OracleRelayStrictShowcase.sol` for strict Oracle request/response relay
  patterns with on-chain callback verification.
- `UpgradeLifecycleShowcase.sol` covering `ContractManagement.update` and
  `ContractManagement.destroy` lifecycle operations.
- `WitnessGuardShowcase.sol` demonstrating `Runtime.checkWitness` guard
  patterns and multi-signer authorization.
- Neo-Express smoke test scripts for the new showcase contracts
  (`test_neoxp_new_showcases_smoke.sh`, `test_neoxp_oracle_relay_smoke.sh`,
  `test_neoxp_upgrade_lifecycle_smoke.sh`, `test_neoxp_witness_guard_smoke.sh`).
- `test_strict_compatibility_sweep.sh` for batch strict-mode compilation
  validation across all showcase contracts.
- `runtime_native_contract_tests.rs` integration test suite for native contract
  dispatch coverage.
- Diagnostic infrastructure activation: structured JSON warnings and errors
  (`--json-warnings`, `--json-errors`) wired through the full pipeline.
- Import path relaxation: the `-I` flag now resolves transitive imports more
  flexibly, reducing false "file not found" errors in multi-directory layouts.

### Changed

- `ExecutionContext` and `ExecutionState` extended with native-contract routing
  tables and overlay storage hooks for Policy/Oracle/Ledger.
- CI workflow (`.github/workflows/ci.yml`) updated with a dedicated
  `neoxp-showcases` job that validates the new showcase contracts end-to-end.
- `bridge_impl_syscalls.rs` and `bridge_impl_core/initialize.rs` updated to
  register Notary and Treasury service endpoints.

### Fixed

- Oracle dispatch now correctly propagates callback contract hash instead of
  defaulting to the calling contract.
- RoleManagement `getDesignatedByRole` returns an empty array (instead of
  panicking) when no nodes are designated for the requested role.

## [v0.9.8] - 2026-02-08

### Added

- **ERC-to-NEP pattern detection**: the compiler recognizes ERC-20, ERC-721,
  ERC-1155, ERC-2612, and ERC-4626 interface patterns and maps them to their
  Neo equivalents (NEP-17, NEP-11) in the generated manifest.
- BN254 (alt_bn128) precompile stubs for pairing and scalar-mul operations.
- Comprehensive test suite expansion: `runtime_syscall_tests.rs` with syscall
  coverage for `Runtime.checkWitness`, `Runtime.getTime`,
  `Runtime.getInvocationCounter`, and `Runtime.getRandom`.
- `erc_nep_patterns.rs` validation module with pattern-matching heuristics for
  standard detection.
- `e2e_compilation_tests.rs` expanded to cover the new showcase contracts and
  native-call paths.
- Metadata test suite (`src/cli/tests/metadata/erc_nep_patterns.rs`) validating
  that manifests carry correct NEP standard annotations.

### Changed

- `semantic_model.rs` updated with ERC/NEP mapping tables used during manifest
  generation.
- `src/solidity/validate/contract/methods.rs` and `return_types.rs` tightened
  to reject incompatible return-type overrides in standard interfaces.

### Fixed

- Manifest `supportedstandards` field now correctly lists detected NEP
  standards instead of leaving the array empty when ERC interfaces are used.

## [v0.9.7] - 2026-02-07

### Added

- **NEP standard detection**: contracts implementing `NEP17` or `NEP11`
  interfaces from the devpack are automatically annotated in the manifest.
- Type inference improvements for `address`-to-`Hash160` and `uint256`-to-
  `Integer` conversions in cross-contract call arguments.
- `CompleteNEP11NFT.sol` and `CompleteNEP17Token.sol` devpack examples
  demonstrating full standard compliance.
- `MultiStandardToken.sol` example implementing both NEP-17 and NEP-11 on a
  single contract.
- `EventIndexedShowcase.sol` demonstrating indexed event parameters and their
  mapping to Neo notifications.
- `InterfaceShowcase.sol` covering Solidity interface inheritance and virtual
  dispatch.
- `TypeCastingShowcase.sol` for explicit and implicit type conversion patterns.

### Changed

- `devpack/standards/NEP11.sol` and `NEP17.sol` updated with complete method
  signatures matching the Neo N3 standard specification.
- `devpack/contracts/Framework.sol` extended with helper methods for standard
  detection at compile time.
- CLI standards module (`src/cli/cli_parts/cli_manifest/standards.rs`) rewritten
  to support automatic and manual standard annotations.

### Fixed

- Type inference no longer silently drops `bytes32` arguments when passed to
  native contract methods expecting `ByteString`.

## [v0.9.6] - 2026-02-06

### Added

- Six famous DeFi contract ports under `examples/famous/`:
  WGAS, FlashLoan, SimpleAMM, TokenVesting, SimpleLending, SimpleDAO.
- `Bank.sol` and `Vault.sol` examples demonstrating deposit/withdraw patterns
  with NEP-17 integration.
- `LowLevelCallShowcase.sol` demonstrating `address.call()` lowering to
  `System.Contract.Call`.
- Improved ERC-20 and ERC-721 example contracts with Neo-specific adaptation
  notes.

### Changed

- `examples/README.md` reorganized with categorized contract listings and
  compilation instructions.

## [v0.9.5] - 2026-02-05

### Added

- Enhanced devpack libraries: `Runtime.sol`, `Storage.sol`, `Neo.sol` updated
  with additional helper methods and NatSpec documentation.
- `NativeCalls.sol` extended with `gasTransfer`, `neoTransfer`, and
  `getContract` wrappers.
- `NEP17Rescue.sol` utility contract for recovering stuck NEP-17 tokens.
- `Syscalls.sol` updated with complete syscall constant definitions.
- `devpack/README.md` rewritten with usage examples and import instructions.

### Changed

- Devpack library method signatures aligned with Neo N3 reference
  implementation parameter names.

## [v0.9.4] - 2026-02-04

### Fixed

- **MEMCPY codegen bug**: memory-copy operations for dynamic `bytes` and
  `string` types now emit correct NeoVM `MEMCPY` sequences instead of
  truncating at 32-byte boundaries.
- **Void DROP bug**: functions returning `void` no longer emit a spurious `DROP`
  opcode that corrupted the evaluation stack when called as statements.
- **LogicalNot codegen bug**: the `!` (logical not) operator now correctly emits
  `NZ` + `NOT` instead of a bare `NOT`, which previously produced incorrect
  results for non-boolean integer operands.

### Changed

- `src/ir/expressions/calls/type_constructors.rs` and
  `src/ir/expressions/variable.rs` refactored to centralize type-width
  validation during IR lowering.

---

[Unreleased]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.22.0...HEAD
[v0.22.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.21.0...v0.22.0
[v0.19.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.18.1...v0.19.0
[v0.18.1]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.18.0...v0.18.1
[v0.18.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.17.0...v0.18.0
[v0.17.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.16.0...v0.17.0
[v0.16.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.15.0...v0.16.0
[v0.15.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.14.0...v0.15.0
[v0.14.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.13.1...v0.14.0
[v0.13.1]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.13.0...v0.13.1
[v0.13.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.12.0...v0.13.0
[v0.12.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.11.0...v0.12.0
[v0.11.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.10.0...v0.11.0
[v0.10.0]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.10...v0.10.0
[v0.9.10]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.9...v0.9.10
[v0.9.9]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.8...v0.9.9
[v0.9.8]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.7...v0.9.8
[v0.9.7]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.6...v0.9.7
[v0.9.6]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.5...v0.9.6
[v0.9.5]: https://github.com/r3e-network/neo-devpack-solidity/compare/v0.9.4...v0.9.5
[v0.9.4]: https://github.com/r3e-network/neo-devpack-solidity/releases/tag/v0.9.4