<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Coins N Coinsequences</title>
    <description>The latest articles on DEV Community by Coins N Coinsequences (@vaultvult).</description>
    <link>https://dev.to/vaultvult</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4205582%2F1d8ea25c-31da-4309-bfab-62f3fcf7cf78.jpg</url>
      <title>DEV Community: Coins N Coinsequences</title>
      <link>https://dev.to/vaultvult</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vaultvult"/>
    <language>en</language>
    <item>
      <title>Delta Analysis: How to Audit a Uniswap Fork Without Wasting a Week</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Sun, 01 Nov 2026 11:33:06 +0000</pubDate>
      <link>https://dev.to/vaultvult/delta-analysis-how-to-audit-a-uniswap-fork-without-wasting-a-week-31k</link>
      <guid>https://dev.to/vaultvult/delta-analysis-how-to-audit-a-uniswap-fork-without-wasting-a-week-31k</guid>
      <description>&lt;h1&gt;
  
  
  Delta Analysis: How to Audit a Uniswap Fork Without Wasting a Week
&lt;/h1&gt;

&lt;p&gt;Every DeFi protocol on an EVM chain is, statistically, a fork of something. A Uniswap V2 fork here, a PancakeSwap V3 fork there, a Compound fork in the lending corner. Bug bounty programs list these contracts as critical targets, and researchers — reasonably — point their tools at them.&lt;/p&gt;

&lt;p&gt;Then nothing happens. Slither is silent. The audit report from 2021 says "no critical findings." The code has been live for years, processing billions in volume, with no exploit.&lt;/p&gt;

&lt;p&gt;The mistake is treating an audited fork as a fresh codebase. The productive approach is &lt;strong&gt;delta analysis&lt;/strong&gt;: identify exactly what the fork changed, and aim only at the delta. Everything else is a waste of time.&lt;/p&gt;

&lt;p&gt;This post walks through the methodology I use, with public examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why "just run Slither" fails
&lt;/h2&gt;

&lt;p&gt;Static analysis tools are pattern matchers. They catch reentrancy, unchecked math, unsafe external calls — the classes of bug that have existed since 2016. On an audited fork, those patterns have already been flagged and resolved.&lt;/p&gt;

&lt;p&gt;The bugs that survive audits are &lt;strong&gt;not pattern-detectable&lt;/strong&gt;. They live in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The fork's own additions (a reward mechanism, a migration path, a proxy layer)&lt;/li&gt;
&lt;li&gt;Interactions between two audited contracts&lt;/li&gt;
&lt;li&gt;Economic invariants that break under specific, legal user behavior&lt;/li&gt;
&lt;li&gt;Access control that is &lt;em&gt;present&lt;/em&gt; but &lt;em&gt;wrong&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Slither cannot find these. You have to read the code — but you have to read the &lt;em&gt;right&lt;/em&gt; code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The five-phase methodology
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1 — Recon and scope mapping
&lt;/h3&gt;

&lt;p&gt;Before any review, answer these questions in writing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Which contracts are in scope?&lt;/strong&gt; Copy the addresses verbatim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which of them are proxies?&lt;/strong&gt; Run &lt;code&gt;cast codesize&lt;/code&gt; on every address. Anything under ~1KB is a proxy; you need the implementation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who controls authority?&lt;/strong&gt; Call &lt;code&gt;owner()&lt;/code&gt;, &lt;code&gt;admin()&lt;/code&gt;, &lt;code&gt;getOwners()&lt;/code&gt;, &lt;code&gt;getThreshold()&lt;/code&gt;. Then call &lt;code&gt;cast codesize&lt;/code&gt; on &lt;em&gt;that&lt;/em&gt; address. If it returns &lt;code&gt;0&lt;/code&gt;, the owner is an EOA — log it, but do not chase it. Bounty programs almost universally exclude "attacks requiring privileged addresses."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What does the protocol hold?&lt;/strong&gt; For each contract, call &lt;code&gt;balanceOf&lt;/code&gt; on the reward or value token. That number is your maximum damage estimate, and it tells you where to spend time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; a &lt;code&gt;SCOPE.md&lt;/code&gt; with all of the above. This document travels with you through the rest of the review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2 — Delta analysis against the canonical source
&lt;/h3&gt;

&lt;p&gt;For each contract, identify its upstream parent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uniswap V2 → &lt;code&gt;Uniswap/v2-core&lt;/code&gt;, &lt;code&gt;Uniswap/v2-periphery&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PancakeSwap V3 → &lt;code&gt;pancakeswap/pancake-v3-contracts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Compound → &lt;code&gt;compound-finance/compound-protocol&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;WETH9 → &lt;code&gt;gnosis/weth9&lt;/code&gt; (canonical reference: &lt;code&gt;etherscan.io/address/0xC02aaA39...&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clone both the fork and the parent, then diff the function signatures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;grep -E &lt;span class="s2"&gt;"^    function"&lt;/span&gt; fork/Contract.sol&lt;span class="o"&gt;)&lt;/span&gt; \
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;grep -E &lt;span class="s2"&gt;"^    function"&lt;/span&gt; parent/Contract.sol&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Critical Caveat:&lt;/strong&gt; This fast-pass only checks &lt;em&gt;function signatures&lt;/em&gt;, not the code &lt;em&gt;inside&lt;/em&gt; the function body. A fork can keep the exact same function names but change a single number inside the logic, and this &lt;code&gt;grep&lt;/code&gt; command will return zero differences. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;A perfect historical example is **Uranium Finance&lt;/em&gt;* (April 2021, ~$50M loss). The developers changed the fee multiplier inside the &lt;code&gt;swap()&lt;/code&gt; function from &lt;code&gt;1000&lt;/code&gt; to &lt;code&gt;10000&lt;/code&gt;, but left the K-invariant check at &lt;code&gt;1000**2&lt;/code&gt;. Because the function signature didn't change, signature-only diffing missed it entirely.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For core math functions (&lt;code&gt;swap&lt;/code&gt;, &lt;code&gt;mint&lt;/code&gt;, &lt;code&gt;burn&lt;/code&gt;), you &lt;strong&gt;must&lt;/strong&gt; perform a full file diff or manually read the function bodies. Also, never forget to compare the &lt;code&gt;INIT_CODE_PAIR_HASH&lt;/code&gt; (the &lt;code&gt;pairFor&lt;/code&gt; init code hash) used in the Router—if that doesn't match the Factory, routing will break or be exploitable.&lt;/p&gt;

&lt;p&gt;Every line with &lt;code&gt;&amp;lt;&lt;/code&gt; or &lt;code&gt;&amp;gt;&lt;/code&gt; in a &lt;strong&gt;full diff&lt;/strong&gt; is a candidate. Three outcomes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Diff shape&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No diff in function signatures&lt;/td&gt;
&lt;td&gt;Likely canonical — but verify the math inside &lt;code&gt;swap&lt;/code&gt;/&lt;code&gt;mint&lt;/code&gt;/&lt;code&gt;burn&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Renamed function, identical body&lt;/td&gt;
&lt;td&gt;Dead — probably a rebrand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New function, or changed logic&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;This is the target&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a slower but more thorough pass, run &lt;code&gt;git log&lt;/code&gt; on the parent repo to find the exact commit that matches the fork's deployment date, then diff full file contents. For an even faster first look, extract the deployed function selectors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cast code 0x&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;CONTRACT&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; \
  grep -oE &lt;span class="s2"&gt;"63[0-9a-f]{8}"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;'s/^63//'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; sort -u &lt;span class="o"&gt;|&lt;/span&gt; \
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; sel&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="s2"&gt;"0x$sel -&amp;gt; "&lt;/span&gt;
  cast 4byte &lt;span class="s2"&gt;"0x$sel"&lt;/span&gt; 2&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"(custom)"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selectors that 4byte-decodes to &lt;strong&gt;known&lt;/strong&gt; functions tell you what the contract claims to be. Selectors that come back as &lt;code&gt;(custom)&lt;/code&gt; are either compiler error strings pushed into the bytecode (a common source of false positives — look for byte patterns like &lt;code&gt;0x61&lt;/code&gt; &lt;code&gt;0x6c&lt;/code&gt; &lt;code&gt;0x6c&lt;/code&gt; which spell &lt;code&gt;"all"&lt;/code&gt; ASCII) or genuine fork additions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3 — Invariant testing on a Foundry fork
&lt;/h3&gt;

&lt;p&gt;This is where most of the value is. Canonical code has known invariants. Your fork may break them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function test_Invariant() public {
    vm.createSelectFork(vm.envString("RPC"), BLOCK_NUMBER);
    // Assert something that should always be true for a correct implementation
    assertLe(protocol.value(), protocol.maxValue(), "invariant broken");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a Uniswap V3 pool, the invariants are well-documented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feeGrowthGlobal0X128&lt;/code&gt; and &lt;code&gt;feeGrowthGlobal1X128&lt;/code&gt; are &lt;strong&gt;non-decreasing&lt;/strong&gt; over time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slot0.sqrtPriceX96&lt;/code&gt; is within &lt;code&gt;[MIN_SQRT_RATIO, MAX_SQRT_RATIO]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slot0.unlocked == true&lt;/code&gt; after any completed transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a MasterChef-style farm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;totalSupply&lt;/code&gt; of the receipt token equals &lt;code&gt;balanceOf&lt;/code&gt; of the staking contract&lt;/li&gt;
&lt;li&gt;Sum of &lt;code&gt;user.rewardDebt&lt;/code&gt; equals &lt;code&gt;totalRewardsDistributed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;emergencyWithdraw&lt;/code&gt; does not leave residual claims&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a proxy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;implementation.slot0&lt;/code&gt; is initialized (i.e., &lt;code&gt;_disableInitializers()&lt;/code&gt; was called)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;implementation.owner()&lt;/code&gt; is either non-zero or &lt;code&gt;upgradeTo&lt;/code&gt; requires &lt;code&gt;onlyProxy&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When an invariant fails, &lt;strong&gt;you have a lead&lt;/strong&gt;. When all invariants pass, the canonical claim holds and you can move on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4 — Proxy and authority analysis
&lt;/h3&gt;

&lt;p&gt;Modern contracts are almost always behind a proxy. Three patterns exist:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;How to detect&lt;/th&gt;
&lt;th&gt;Attack surface&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TransparentUpgradeableProxy&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;EIP-1967&lt;/code&gt; admin slot ≠ 0&lt;/td&gt;
&lt;td&gt;Admin is a contract (timelock/multisig) or EOA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UUPS (ERC-1822)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;EIP-1967&lt;/code&gt; admin slot = 0, impl has &lt;code&gt;proxiableUUID&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Implementation initialization&lt;/strong&gt; — CVE-2021-41251&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beacon&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;EIP-1967&lt;/code&gt; beacon slot ≠ 0&lt;/td&gt;
&lt;td&gt;Beacon upgrade gate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The UUPS case is the most dangerous because the &lt;em&gt;implementation&lt;/em&gt; holds the initializer state. If &lt;code&gt;_disableInitializers()&lt;/code&gt; was not called in the constructor, an attacker can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;initialize()&lt;/code&gt; on the implementation directly&lt;/li&gt;
&lt;li&gt;Become the implementation's owner&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;upgradeToAndCall(malicious, "")&lt;/code&gt; on the implementation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;malicious.selfdestruct()&lt;/code&gt; → implementation has no code → proxy bricked&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is CVE-2021-41251, and it has destroyed production protocols.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cast call &lt;span class="nv"&gt;$IMPL&lt;/span&gt; &lt;span class="s2"&gt;"owner()(address)"&lt;/span&gt; --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt;      &lt;span class="c"&gt;# should be 0x0&lt;/span&gt;
cast storage &lt;span class="nv"&gt;$IMPL&lt;/span&gt; 0 --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt;                   &lt;span class="c"&gt;# should be 0x...ff&lt;/span&gt;
cast call &lt;span class="nv"&gt;$IMPL&lt;/span&gt; &lt;span class="s2"&gt;"initialize()"&lt;/span&gt; --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt;         &lt;span class="c"&gt;# should revert&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;initialize()&lt;/code&gt; succeeds on the implementation, stop everything and write the PoC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 5 — The discipline of stopping
&lt;/h3&gt;

&lt;p&gt;This is the phase no blog post talks about, and it is the most important one.&lt;/p&gt;

&lt;p&gt;The instinct after 20 dead ends is to lower the bar: "well, this receipt-token bug &lt;em&gt;could&lt;/em&gt; matter if someone deployed a market tomorrow." That is how researchers waste submissions on findings the triager rejects in 30 seconds.&lt;/p&gt;

&lt;p&gt;The bar is &lt;strong&gt;not&lt;/strong&gt; "is this technically a bug?" The bar is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is there real value at risk right now, on-chain?&lt;/strong&gt; Not "could be in the future."&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does the exploit work in a single transaction, on a fork, with the current state?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is the impact measurable in tokens held by the protocol, not just in emitted events?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it fall outside the program's explicit exclusions?&lt;/strong&gt; Most programs exclude "privileged addresses," "devaluing protocol incentive rewards," and "design issues." Read those clauses &lt;em&gt;before&lt;/em&gt; writing the PoC, not after.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any of those four answers is "no," the finding is a portfolio piece, not a submission.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two public case studies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Case study 1: The four.meme pool pre-creation (BSC, 2025)
&lt;/h3&gt;

&lt;p&gt;A launchpad migrated liquidity to PancakeSwap V3 via &lt;code&gt;createAndInitializePoolIfNecessary&lt;/code&gt;. This function only initializes the pool if &lt;code&gt;slot0.sqrtPriceX96 == 0&lt;/code&gt;. An attacker pre-created the pool and initialized it at an extreme price. The migration succeeded silently, and the attacker drained the pool for ~$183,000.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The delta:&lt;/strong&gt; the launchpad's code assumed "create" meant "initialize at a fair price." The canonical function only meant "initialize if empty."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; when a fork calls a canonical function, check what the canonical function &lt;em&gt;actually&lt;/em&gt; guarantees. The fork author's mental model is not the source of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case study 2: UUPS uninitialized implementation (CVE-2021-41251)
&lt;/h3&gt;

&lt;p&gt;Multiple protocols deployed their UUPS implementations without calling &lt;code&gt;_disableInitializers()&lt;/code&gt; in the constructor. Attackers called &lt;code&gt;initialize()&lt;/code&gt; on the implementation directly, became the owner, and upgraded to a selfdestructing contract — bricking the proxy and locking all funds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The delta:&lt;/strong&gt; nothing in the &lt;em&gt;proxy&lt;/em&gt; was wrong. The bug was in the &lt;em&gt;implementation's constructor&lt;/em&gt;, which is invisible from the proxy address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; audit both layers of every proxy. The proxy's bytecode is short and easy to verify, but the implementation is where the actual state lives — including the initializer.&lt;/p&gt;




&lt;h2&gt;
  
  
  A small tooling checklist
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Recon&lt;/span&gt;
cast codesize &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;addr&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt;             &lt;span class="c"&gt;# proxy detection&lt;/span&gt;
cast call &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;addr&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"owner()(address)"&lt;/span&gt; --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt;
cast storage &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;addr&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 0 --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt;             &lt;span class="c"&gt;# slot-0 fingerprint&lt;/span&gt;
cast code &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;addr&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; --rpc-url &lt;span class="nv"&gt;$RPC&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; \
  grep -oE &lt;span class="s2"&gt;"63[0-9a-f]{8}"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;'s/^63//'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; \
  xargs -I&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; cast 4byte &lt;span class="s2"&gt;"0x{}"&lt;/span&gt;                   &lt;span class="c"&gt;# selector extraction&lt;/span&gt;

&lt;span class="c"&gt;# Fork testing&lt;/span&gt;
forge &lt;span class="nb"&gt;test&lt;/span&gt; --match-contract &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;Name&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; -vvvv         &lt;span class="c"&gt;# with trace&lt;/span&gt;

&lt;span class="c"&gt;# Diff&lt;/span&gt;
diff &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;grep -E &lt;span class="s2"&gt;"^    function"&lt;/span&gt; a.sol&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;grep -E &lt;span class="s2"&gt;"^    function"&lt;/span&gt; b.sol&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Foundry's &lt;code&gt;-vvvv&lt;/code&gt; trace is the single most useful tool in the workflow. It shows every &lt;code&gt;SSTORE&lt;/code&gt;, &lt;code&gt;CALL&lt;/code&gt;, and &lt;code&gt;LOG&lt;/code&gt; in a call. A function that "succeeds" for a random caller but has zero &lt;code&gt;SSTORE&lt;/code&gt; is a &lt;strong&gt;no-op&lt;/strong&gt;, not a bug. The trace proves it in five seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Most of a smart contract review is proving what is &lt;strong&gt;not&lt;/strong&gt; vulnerable. The findings are a byproduct of the process, not the process itself.&lt;/p&gt;

&lt;p&gt;The five phases — scope, delta, invariants, proxies, discipline — are not novel. They are the same approach professional audit firms use. What separates a productive hunt from a frustrating one is &lt;strong&gt;resisting the urge to test code you know is clean&lt;/strong&gt;. Aim at the delta. Test the invariants. Check the proxies. Then stop.&lt;/p&gt;

&lt;p&gt;If nothing is vulnerable, you have still produced a documented security review — which is worth more to your portfolio than a single rejected submission.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Methodology refined over multiple bounty programs. Public examples referenced: the four.meme incident (February 2025), CVE-2021-41251, and the canonical Uniswap V2 / PancakeSwap V3 source repositories.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>web3</category>
      <category>solidity</category>
      <category>foundry</category>
    </item>
    <item>
      <title>Why Unauthenticated API Endpoints are Still a Major Threat</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Sat, 31 Oct 2026 21:10:57 +0000</pubDate>
      <link>https://dev.to/vaultvult/why-unauthenticated-api-endpoints-are-still-a-major-threat-2knc</link>
      <guid>https://dev.to/vaultvult/why-unauthenticated-api-endpoints-are-still-a-major-threat-2knc</guid>
      <description>&lt;h1&gt;
  
  
  Why Unauthenticated API Endpoints are Still a Major Threat
&lt;/h1&gt;

&lt;p&gt;In the world of modern web development, we often focus heavily on securing the front door. We implement complex login flows, enforce multi-factor authentication (MFA), and integrate Web3 wallet signatures. &lt;/p&gt;

&lt;p&gt;But while the front door is locked tight, the back door is often left wide open.&lt;/p&gt;

&lt;p&gt;In this article, we are going to talk about &lt;strong&gt;Unauthenticated API Endpoints&lt;/strong&gt; and &lt;strong&gt;Broken Access Control (BAC)&lt;/strong&gt;. It is one of the most common vulnerabilities found in bug bounties today, yet it is frequently dismissed by developers as "just data" or "low risk." &lt;/p&gt;

&lt;p&gt;Here is why an unauthenticated endpoint can be a massive threat to your infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Anatomy of a Broken Endpoint
&lt;/h2&gt;

&lt;p&gt;When a user logs into your application, they are usually granted a JWT (JSON Web Token) or a session cookie. Every subsequent request they make to your backend API should include this token in the headers (e.g., &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The backend server is supposed to validate this token. If the token is missing, expired, or invalid, the server must reject the request with a &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, during our recent research on a Web3 frontend, we discovered an API endpoint that was missing this basic check. &lt;/p&gt;

&lt;p&gt;We sent a simple &lt;code&gt;GET&lt;/code&gt; request to a route designed to list platform assets. We did not include a token. We did not include a cookie. We didn't even include an API key.&lt;/p&gt;

&lt;p&gt;The server responded with &lt;strong&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Payload: What Did We Get?
&lt;/h2&gt;

&lt;p&gt;The request didn't just return a small list of public tokens. It returned a &lt;strong&gt;487 Kilobyte JSON payload&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When we analyzed the data, we found a treasure trove of internal information that was never meant to be exposed to the public:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Internal Testnet Addresses:&lt;/strong&gt; The exact smart contract addresses used for testing environments. &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Hidden Assets:&lt;/strong&gt; Tokens that were explicitly flagged in the code with &lt;code&gt;"hide": true&lt;/code&gt;, meaning they were meant to be invisible to the frontend.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Business Logic:&lt;/strong&gt; Internal transfer limits, fee structures, and exchange routing paths.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rate Limits &amp;amp; KYC Tiers:&lt;/strong&gt; The exact limits applied to different KYC levels (e.g., limits for unverified users vs. fully verified users).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The "It's Just Data" Fallacy
&lt;/h2&gt;

&lt;p&gt;A common response from developers when they see this is: &lt;em&gt;"It's public blockchain data anyway,"&lt;/em&gt; or &lt;em&gt;"It's just a list of tokens, nobody can steal funds with this."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While it’s true that the attacker might not be able to drain a wallet directly with this information, &lt;strong&gt;Information Disclosure is the first step in a larger attack chain.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is what an attacker does with leaked data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Mapping the Infrastructure:&lt;/strong&gt; By finding internal testnet addresses, an attacker can pivot to the testnet environment. Testnets are notoriously less secure than mainnets. If they find a vulnerability there, they can often apply it to the mainnet.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Understanding the Business Logic:&lt;/strong&gt; Exposing the fee structures and KYC tiers allows an attacker to understand exactly how much they can move before triggering an alert, or how to bypass certain limits using multiple accounts.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Finding Hidden Features:&lt;/strong&gt; If a token is marked &lt;code&gt;"hide": true&lt;/code&gt;, an attacker knows to look for hidden endpoints or UI bypasses to interact with that asset.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to Fix Unauthenticated Endpoints
&lt;/h2&gt;

&lt;p&gt;If you are a developer building Web3 applications (or any web application), here is how you prevent this:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Enforce Authentication Globally
&lt;/h3&gt;

&lt;p&gt;Never rely on the frontend to hide an API call. Even if a UI component isn't rendered, the endpoint still exists on the backend. Use a global middleware on your API gateway that intercepts every request and checks for a valid token, unless the route is explicitly whitelisted as public (like &lt;code&gt;/login&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Minimize Response Data
&lt;/h3&gt;

&lt;p&gt;Even if an endpoint &lt;em&gt;is&lt;/em&gt; meant to be public, do not send the entire database record to the client. The API should only return the specific fields the frontend actually needs to render the UI. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Implement Rate Limiting
&lt;/h3&gt;

&lt;p&gt;If an attacker is probing your API, rate limiting (like a WAF) will slow them down and generate alerts. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug Hunter's Mindset
&lt;/h2&gt;

&lt;p&gt;For security researchers, this is a reminder to always look at the JavaScript. The frontend is a map to the backend. When you find an API route, always test it without your authentication headers. You might just find a 487KB breadcrumb trail.&lt;/p&gt;

&lt;p&gt;Authentication is not just about logging a user in; it's about verifying &lt;em&gt;who&lt;/em&gt; is asking for &lt;em&gt;what&lt;/em&gt; on every single request.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>api</category>
      <category>webdev</category>
      <category>bugbounty</category>
    </item>
    <item>
      <title>Bypassing Android SSL Pinning: A Journey with mitmproxy and APK-Mitm</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Sat, 31 Oct 2026 21:07:22 +0000</pubDate>
      <link>https://dev.to/vaultvult/bypassing-android-ssl-pinning-a-journey-with-mitmproxy-and-apk-mitm-2eko</link>
      <guid>https://dev.to/vaultvult/bypassing-android-ssl-pinning-a-journey-with-mitmproxy-and-apk-mitm-2eko</guid>
      <description>&lt;h1&gt;
  
  
  Bypassing Android SSL Pinning: A Journey with mitmproxy and APK-Mitm
&lt;/h1&gt;

&lt;p&gt;When you start reverse-engineering Web3 applications, you quickly realize that the web frontend is only half the story. Often, the most sensitive API endpoints are locked behind the mobile app. &lt;/p&gt;

&lt;p&gt;If you want to intercept the traffic from an Android app to see how it communicates with the backend, the first tool you’ll reach for is a proxy like &lt;code&gt;mitmproxy&lt;/code&gt; or Burp Suite. &lt;/p&gt;

&lt;p&gt;But here is the problem: &lt;strong&gt;Modern apps don't want to be intercepted.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through the methodology of intercepting Android traffic, hitting the dreaded SSL Pinning wall, and how to use &lt;code&gt;apk-mitm&lt;/code&gt; to bypass it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: The Setup (mitmproxy)
&lt;/h2&gt;

&lt;p&gt;Our journey starts by routing the Android device’s traffic through our machine. We fire up &lt;code&gt;mitmproxy&lt;/code&gt; and configure the phone's Wi-Fi to use our local IP as the proxy. &lt;/p&gt;

&lt;p&gt;To decrypt HTTPS traffic, we download the &lt;code&gt;mitmproxy&lt;/code&gt; CA certificate to the device and install it. For older Android versions, this was enough. We could see every request in plain text.&lt;/p&gt;

&lt;p&gt;However, when we opened our target app, the terminal spat out an error: &lt;br&gt;
&lt;code&gt;Client TLS handshake failed. The client does not trust the proxy's certificate.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: The Wall (SSL Pinning)
&lt;/h2&gt;

&lt;p&gt;Why did the app reject the proxy? &lt;/p&gt;

&lt;p&gt;There are two reasons. First, starting with &lt;strong&gt;Android 7 (Nougat)&lt;/strong&gt;, Google changed the security model. Apps no longer trust user-installed CA certificates by default; they only trust system certificates. &lt;/p&gt;

&lt;p&gt;Second, and more importantly, the app likely uses &lt;strong&gt;SSL Pinning&lt;/strong&gt;. This is a security mechanism where the app is hardcoded to only accept a specific, cryptographic signature (the server's public key). Even if you install the proxy certificate to the system store, the app will reject it because it doesn't match the hardcoded pin.&lt;/p&gt;

&lt;p&gt;So, how do we bypass this? We have to change the app itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: Extracting the APK
&lt;/h2&gt;

&lt;p&gt;We can't modify the app while it's installed. We need to pull the raw &lt;code&gt;.apk&lt;/code&gt; file from the device to our computer. &lt;/p&gt;

&lt;p&gt;Using Android Debug Bridge (&lt;code&gt;adb&lt;/code&gt;), we connect our phone via USB (or wireless debugging) and locate the path of the installed package. We then pull the base APK to our local machine. &lt;/p&gt;

&lt;p&gt;Once we have the &lt;code&gt;app.apk&lt;/code&gt; file, we are ready to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 4: The Bypass (apk-mitm)
&lt;/h2&gt;

&lt;p&gt;We could manually decompile the APK using &lt;code&gt;apktool&lt;/code&gt;, hunt through the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; and smali code to find the pinning logic, modify it, and re-sign the app. But that takes hours.&lt;/p&gt;

&lt;p&gt;Instead, we use a brilliant open-source tool called &lt;strong&gt;apk-mitm&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;apk-mitm&lt;/code&gt; automates the entire process. You feed it the APK, and it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decodes the APK.&lt;/li&gt;
&lt;li&gt;Applies a patch to the network security configuration (allowing user certificates).&lt;/li&gt;
&lt;li&gt;Attempts to disable common SSL pinning implementations (like OkHttp).&lt;/li&gt;
&lt;li&gt;Re-encodes and re-signs the APK.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The tool runs automatically on the extracted file&lt;/span&gt;
npx apk-mitm app.apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the tool finishes, it spits out a new file: &lt;code&gt;app-patched.apk&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 5: The Catch (App Bundles)
&lt;/h2&gt;

&lt;p&gt;Here is where modern Android development throws a curveball. &lt;/p&gt;

&lt;p&gt;When we tried to install the patched APK onto the test device, the installation failed with: &lt;code&gt;INSTALL_FAILED_MISSING_SPLIT&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This happens because modern Android apps are distributed as &lt;strong&gt;Android App Bundles (AAB)&lt;/strong&gt;, not single APKs. The &lt;code&gt;base.apk&lt;/code&gt; we pulled from the phone is just the skeleton. It relies on "split APKs" for specific screen densities, architectures (ARM64, x86), and languages.&lt;/p&gt;

&lt;p&gt;If you patch only the base APK, the app won't run. To fix this, you have to use a tool like &lt;strong&gt;SAI (Split APKs Installer)&lt;/strong&gt; on the Android device to install the patched base APK alongside the original split dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson for Developers
&lt;/h2&gt;

&lt;p&gt;SSL Pinning is a fantastic defense-in-depth mechanism. It stops 99% of casual attackers from using a proxy to read the traffic. &lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;client-side security is never absolute&lt;/strong&gt;. If an attacker has physical access to the device, they can extract the APK, remove the pinning, and re-sign it. &lt;/p&gt;

&lt;p&gt;To truly secure your mobile app, you need more than just SSL pinning:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Root/Jailbreak Detection:&lt;/strong&gt; Stop the app from running on compromised devices.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Tamper Detection:&lt;/strong&gt; Verify the app's signature at runtime. If someone re-signs the app (like &lt;code&gt;apk-mitm&lt;/code&gt; does), the app should refuse to launch.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Code Obfuscation:&lt;/strong&gt; Make it as difficult as possible for an attacker to find your pinning logic in the first place.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Intercepting mobile traffic is a puzzle, but with tools like &lt;code&gt;mitmproxy&lt;/code&gt; and &lt;code&gt;apk-mitm&lt;/code&gt;, the puzzle is solvable. &lt;/p&gt;

</description>
      <category>android</category>
      <category>cybersecurity</category>
      <category>reverseengineering</category>
      <category>bugbounty</category>
    </item>
    <item>
      <title>How to Reverse Engineer a Web3 Frontend to Find Hidden API Endpoints</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Sat, 31 Oct 2026 20:59:19 +0000</pubDate>
      <link>https://dev.to/vaultvult/how-to-reverse-engineer-a-web3-frontend-to-find-hidden-api-endpoints-3jan</link>
      <guid>https://dev.to/vaultvult/how-to-reverse-engineer-a-web3-frontend-to-find-hidden-api-endpoints-3jan</guid>
      <description>&lt;h1&gt;
  
  
  How to Reverse Engineer a Web3 Frontend to Find Hidden API Endpoints
&lt;/h1&gt;

&lt;p&gt;When we interact with Web3 applications—wallets, decentralized exchanges, or NFT marketplaces—we often assume everything is happening on the blockchain. But here is the reality: &lt;strong&gt;the blockchain is slow and expensive&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To provide a seamless user experience, most Web3 applications rely on centralized backend APIs. These APIs handle everything from fetching token prices and calculating fees to managing user profiles and transaction histories. &lt;/p&gt;

&lt;p&gt;Because the frontend is usually built with modern frameworks like React or Next.js, this logic is hidden inside minified JavaScript bundles. Developers sometimes assume that because the code is hard to read, the API is secure. &lt;/p&gt;

&lt;p&gt;In this article, I’m going to walk you through the methodology of reverse-engineering a Web3 frontend to find its hidden API, and how this process often leads to the discovery of unauthenticated endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Illusion of the Frontend
&lt;/h2&gt;

&lt;p&gt;When you visit a Web3 application, your browser downloads a massive bundle of JavaScript. This code is responsible for rendering the UI, connecting to your crypto wallet (like MetaMask), and communicating with the backend.&lt;/p&gt;

&lt;p&gt;The first step in our journey is realizing that &lt;strong&gt;the frontend is a map to the backend&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If you open your browser's Developer Tools and look at the &lt;strong&gt;Network&lt;/strong&gt; tab, you’ll see a flurry of requests. However, filtering through the noise can be exhausting. A more efficient approach is to pull the JavaScript bundles directly and analyze the source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the Hidden Gateway
&lt;/h2&gt;

&lt;p&gt;Once we extract the JavaScript files, we aren't looking for UI logic. We are looking for &lt;strong&gt;strings&lt;/strong&gt;. Specifically, we are looking for hardcoded URLs. &lt;/p&gt;

&lt;p&gt;Developers often hardcode their API gateways directly into the frontend code. During our recon, by extracting and searching through the frontend bundles, we found a specific API gateway (let's call it &lt;code&gt;api.web3wallet.com/v2&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Once you have the base API URL, the game completely changes. You no longer need to click around the web app; you can talk directly to the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping the Endpoints
&lt;/h2&gt;

&lt;p&gt;With the API gateway in hand, the next step is to discover the routes. Web3 apps have predictable naming conventions. If the frontend shows a "Wallet" page, there is almost certainly an endpoint like &lt;code&gt;/wallet/balance&lt;/code&gt; or &lt;code&gt;/user/transactions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By analyzing how the frontend JavaScript calls the API, we can map out the entire backend architecture. During our investigation, we found routes for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  User profiles&lt;/li&gt;
&lt;li&gt;  Token listings&lt;/li&gt;
&lt;li&gt;  Gas fee calculations&lt;/li&gt;
&lt;li&gt;  Transaction values&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Broken Access Control (BAC) Discovery
&lt;/h2&gt;

&lt;p&gt;Here is where the vulnerability usually hides. &lt;/p&gt;

&lt;p&gt;When an API endpoint is called, it should always verify &lt;em&gt;who&lt;/em&gt; is making the request. This is done using an &lt;code&gt;Authorization&lt;/code&gt; header, usually containing a Bearer Token (a JWT). &lt;/p&gt;

&lt;p&gt;During our testing, we sent requests to several endpoints. Some correctly returned a &lt;code&gt;401 Unauthorized&lt;/code&gt;, proving that the backend was checking for a valid session.&lt;/p&gt;

&lt;p&gt;However, we found an endpoint designed to list all available tokens in the ecosystem. When we sent a simple &lt;code&gt;GET&lt;/code&gt; request without any tokens, cookies, or authorization headers, the server responded with &lt;strong&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Not only was it accessible, but it returned a massive payload—nearly &lt;strong&gt;500 Kilobytes of JSON data&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why Information Disclosure Matters
&lt;/h2&gt;

&lt;p&gt;Some developers might look at a leaked JSON file and think, &lt;em&gt;"It's just a list of tokens, what's the harm?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But looking closely at the data, we found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Internal Testnet Addresses:&lt;/strong&gt; Smart contract addresses used for testing, which could be targeted for testnet-specific exploits.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Hidden Tokens:&lt;/strong&gt; Assets explicitly marked with &lt;code&gt;"hide": true&lt;/code&gt; in the JSON, meaning they shouldn't be visible to the public.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Business Logic:&lt;/strong&gt; Internal fee structures, transfer limits, and rate limits based on KYC tiers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the hands of an attacker, this data is gold. It allows them to map the internal infrastructure, understand the platform's logic, and find other vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson for Developers
&lt;/h2&gt;

&lt;p&gt;The biggest takeaway from this methodology is simple: &lt;strong&gt;Never trust the client.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Secure Your APIs:&lt;/strong&gt; Just because an endpoint is buried in minified React code does not mean it is private. If an endpoint shouldn't be public, enforce authentication and authorization on the backend.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Minimize Data Exposure:&lt;/strong&gt; Don't send your entire database schema to the frontend. Only send the specific data the user needs to see. Remove hidden tokens, internal testnet addresses, and sensitive configuration data from public API responses.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Audit Your Frontend:&lt;/strong&gt; Regularly review your JavaScript bundles to ensure you aren't leaking API keys, admin routes, or sensitive gateway information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reverse-engineering a Web3 frontend isn't about being a wizard; it's about understanding how the pieces connect. By following the trail of JavaScript, you can often find the hidden doors that developers forgot to lock.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>cybersecurity</category>
      <category>bugbounty</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Real-Time Funding Rate Monitor for DreamDEX (Somnia Testnet)</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Fri, 30 Oct 2026 14:25:30 +0000</pubDate>
      <link>https://dev.to/vaultvult/building-a-real-time-funding-rate-monitor-for-dreamdex-somnia-testnet-1pgn</link>
      <guid>https://dev.to/vaultvult/building-a-real-time-funding-rate-monitor-for-dreamdex-somnia-testnet-1pgn</guid>
      <description>&lt;p&gt;As part of the &lt;strong&gt;DreamDEX Perps Builder Bounty&lt;/strong&gt;, I set out to solve a simple problem for perpetual traders: &lt;strong&gt;staring at funding rate dashboards 24/7 is exhausting.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Traders need to know when a funding rate flips (from positive to negative, or vice versa), but they shouldn't have to monitor it manually. &lt;/p&gt;

&lt;p&gt;To solve this, I built the &lt;strong&gt;DreamDEX Funding Monitor&lt;/strong&gt; — a lightweight, automated alert bot and web dashboard that monitors the Somnia Hideki Testnet in real-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗️ The Architecture
&lt;/h2&gt;

&lt;p&gt;I wanted to keep the architecture clean and decoupled. The project is split into two distinct parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Engine:&lt;/strong&gt; A background worker that polls the &lt;code&gt;@somnia-chain/markets-sdk&lt;/code&gt; every 60 seconds to fetch live rates, check for flips, and fire off Discord alerts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Frontend:&lt;/strong&gt; A live Express.js-powered dashboard that visualizes the data without ever touching the engine's core logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend / Engine:&lt;/strong&gt; Node.js &amp;amp; TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Server:&lt;/strong&gt; Express.js + CORS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web3 Integration:&lt;/strong&gt; &lt;code&gt;@somnia-chain/markets-sdk&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; &lt;code&gt;dotenv&lt;/code&gt; (to keep Discord Webhook URLs safe)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Vanilla HTML/CSS/JS with a custom dark-mode grid layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧠 How the Engine Works
&lt;/h2&gt;

&lt;p&gt;The core engine (&lt;code&gt;funding-alert-bot.ts&lt;/code&gt;) connects to the Somnia Testnet Indexer. Smart contracts return raw integers, so I had to handle the decimal precision to make the data human-readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The SDK returns raw integers (e.g., 100000000000000)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;rawRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fundingRate&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentFundingRate&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Dividing by 1e18 converts it to a standard percentage (0.0001 = 0.01%)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;currentRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawRate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1e18&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot stores the previous funding rates in memory. When it detects a sign flip (e.g., &lt;code&gt;prevRate &amp;gt; 0&lt;/code&gt; and &lt;code&gt;currentRate &amp;lt; 0&lt;/code&gt;), it triggers an Axios POST request to a Discord Webhook, instantly pinging the trader.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Security First
&lt;/h2&gt;

&lt;p&gt;Because this is a public hackathon repository, I couldn't hardcode my Discord Webhook URL into the code. I used &lt;code&gt;dotenv&lt;/code&gt; to load environment variables, ensuring the secret stays hidden from GitHub crawlers and malicious actors.&lt;/p&gt;

&lt;h2&gt;
  
  
  📸 The Dashboard
&lt;/h2&gt;

&lt;p&gt;By decoupling the engine, I was able to spin up an Express server that serves a live-updating, dark-mode dashboard. It refreshes every 5 seconds to pull the latest state from the background engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 The Core Engine X (Twitter) Post
&lt;/h2&gt;

&lt;p&gt;I've been documenting the build process on X/Twitter. You can check out the exact post about the core engine, Express.js decoupling, and &lt;code&gt;dotenv&lt;/code&gt; integration here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://x.com/TrAdArSOY/status/2116170770464526425" rel="noopener noreferrer"&gt;https://x.com/TrAdArSOY/status/2116170770464526425&lt;/a&gt;&lt;/strong&gt; 👈&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Check out the Code
&lt;/h2&gt;

&lt;p&gt;The full MVP is open-source and available on GitHub.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/vaultvult/dreamdex-testnet-proof" rel="noopener noreferrer"&gt;https://github.com/vaultvult/dreamdex-testnet-proof&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built for the @DreamDEXSomnia Perps Builder Bounty.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>How I Built a DePIN Solar Energy Protocol for the W3Node Hackathon</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Tue, 27 Oct 2026 22:35:43 +0000</pubDate>
      <link>https://dev.to/vaultvult/how-i-built-a-depin-solar-energy-protocol-for-the-w3node-hackathon-4kk2</link>
      <guid>https://dev.to/vaultvult/how-i-built-a-depin-solar-energy-protocol-for-the-w3node-hackathon-4kk2</guid>
      <description>&lt;p&gt;The Web3 ecosystem is rapidly shifting from speculative finance to real-world infrastructure. For the W3Node Hackathon, I decided to tackle one of the most critical real-world problems: Energy Access in Africa.&lt;/p&gt;

&lt;p&gt;Africa needs over 46,000 new mini-grids by 2030 to power rural communities. The hardware is ready, but the financial rails to manage, verify, and pay for this distributed infrastructure don't exist.&lt;/p&gt;

&lt;p&gt;To solve this, I built the SHS DePIN Network (Solar Home System Decentralized Physical Infrastructure Network). &lt;/p&gt;

&lt;p&gt;Here is a deep dive into how it was built, the architecture, and how it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏗️ The Architecture
&lt;/h3&gt;

&lt;p&gt;The protocol consists of two main smart contracts deployed on the Base Sepolia Testnet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;MockUSDC: A standard ERC-20 stablecoin used for payments.&lt;/li&gt;
&lt;li&gt;EnergyMarket: The core engine that handles device registration, consumer payments, machine-to-machine (M2M) reward distribution, and operator withdrawals.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the frontend, I used Next.js, Wagmi, and Viem to build a sleek, dark-mode dashboard that connects directly to MetaMask.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔒 Smart Contract Security: The Reentrancy Fix
&lt;/h3&gt;

&lt;p&gt;During the build, Foundry's linter flagged a critical vulnerability in the &lt;code&gt;purchaseEnergy&lt;/code&gt; function. The original code made an external call (&lt;code&gt;transferFrom&lt;/code&gt;) &lt;em&gt;before&lt;/em&gt; updating the state variables. &lt;/p&gt;

&lt;p&gt;To make this hackathon project production-ready, I refactored the contract to follow the Checks-Effects-Interactions pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 1. EFFECTS: Update state first
device.totalRewardsEarned += _amount;
operatorBalances[device.operator] += _amount;

// 2. INTERACTIONS: External call last
require(paymentToken.transferFrom(msg.sender, address(this), _amount), "Payment failed");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This completely eliminates the risk of a reentrancy attack, ensuring that the funds are safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔮 The Future: Real-Time IoT Data
&lt;/h3&gt;

&lt;p&gt;For the MVP, I built an Oracle Simulator in the UI. It allows the admin to inject simulated solar generation data on-chain. &lt;/p&gt;

&lt;p&gt;In the real world, this contract is ready to be connected to physical IoT hardware (like an ESP32 chip on a solar inverter) or decentralized oracle networks like Chainlink, transforming it from a simulation into a real-time, automated machine-to-machine economy.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔗 Live Links
&lt;/h3&gt;

&lt;p&gt;🎥 Watch the 2-Minute Demo: &lt;a href="https://youtu.be/xfYc27v8Qho" rel="noopener noreferrer"&gt;https://youtu.be/xfYc27v8Qho&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 View the Code: &lt;a href="https://github.com/vaultvult/shs-depin-mvp" rel="noopener noreferrer"&gt;https://github.com/vaultvult/shs-depin-mvp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⛓️ View the Contract on BaseScan: &lt;a href="https://sepolia.basescan.org/address/0x6309C657B4fc06B39Ac9Bcb040156898A4F23629" rel="noopener noreferrer"&gt;https://sepolia.basescan.org/address/0x6309C657B4fc06B39Ac9Bcb040156898A4F23629&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🐦 Follow the Build on Twitter: &lt;a href="https://x.com/TrAdArSOY/status/2115206565959524372?a=xyQIUZ1igLA1bC4e-CI&amp;amp;s=20" rel="noopener noreferrer"&gt;https://x.com/TrAdArSOY/status/2115206565959524372?a=xyQIUZ1igLA1bC4e-CI&amp;amp;s=20&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building this MVP proved to me that DePIN is the future of infrastructure. The rails are built, and the future of energy is decentralized.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>solidity</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Building Sentinel AI: A Security-First AI Trading Agent for HSK Chain</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:51:29 +0000</pubDate>
      <link>https://dev.to/vaultvult/building-sentinel-ai-a-security-first-ai-trading-agent-for-hsk-chain-15ld</link>
      <guid>https://dev.to/vaultvult/building-sentinel-ai-a-security-first-ai-trading-agent-for-hsk-chain-15ld</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Blind AI Trading
&lt;/h2&gt;

&lt;p&gt;In the recent HSK Chain Horizon Hackathon, developers were tasked with building AI agents for DeFi. But as a smart contract auditor, I saw a massive flaw in the standard approach: &lt;strong&gt;AI trading bots are blind to security.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;They will happily execute a trade on a malicious contract if the API tells them to, draining user funds in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Sentinel AI
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;Sentinel AI&lt;/strong&gt;, a security-first trading agent deployed on the HSK Mainnet. It acts as a gatekeeper. Before any swap is executed on HSKSwap, the agent queries the blockchain RPC to verify the target contract's bytecode. &lt;/p&gt;

&lt;p&gt;If the address is fake or malicious, the trade is instantly blocked. If safe, the AI executes the trade and applies a strict -5% Stop-Loss framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ How I Built It (Tech Stack)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend / AI Agent:&lt;/strong&gt; Python, Web3.py, Flask&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Contracts:&lt;/strong&gt; Solidity (AuditRegistry deployed on HSK Mainnet)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Custom HTML/CSS/JS Dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚙️ The Core Logic
&lt;/h2&gt;

&lt;p&gt;Instead of relying on an LLM to make financial decisions, the agent uses a deterministic security check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It checks if the address is a valid EVM format.&lt;/li&gt;
&lt;li&gt;It calls &lt;code&gt;web3.eth.get_code()&lt;/code&gt; to ensure it's a real contract and not a spoofed wallet.&lt;/li&gt;
&lt;li&gt;It logs the immutable audit proof to a custom Solidity contract on HSK Mainnet before execution.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🔗 Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Demo Video:&lt;/strong&gt; &lt;a href="https://youtu.be/Gjwo1exBdbA?si=qLSPNF9X4_xCaGq0" rel="noopener noreferrer"&gt;https://youtu.be/Gjwo1exBdbA?si=qLSPNF9X4_xCaGq0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/vaultvult/sentinel-ai-hsk" rel="noopener noreferrer"&gt;https://github.com/vaultvult/sentinel-ai-hsk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mainnet Contract:&lt;/strong&gt; &lt;code&gt;0xb4FD99d390683DFA2eAE95EbcdDDAa6CF786edc7&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building this taught me a lot about the intersection of AI and security. AI agents are the future of DeFi, but they &lt;em&gt;must&lt;/em&gt; be built with strict security guardrails. &lt;/p&gt;

</description>
      <category>web3</category>
      <category>ai</category>
      <category>security</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>How I Found a High-Severity Rounding Vulnerability in Morpho Midnight (Tenor Labs)</title>
      <dc:creator>Coins N Coinsequences</dc:creator>
      <pubDate>Sat, 24 Oct 2026 23:52:44 +0000</pubDate>
      <link>https://dev.to/vaultvult/how-i-found-a-high-severity-rounding-vulnerability-in-morpho-midnight-tenor-labs-35c2</link>
      <guid>https://dev.to/vaultvult/how-i-found-a-high-severity-rounding-vulnerability-in-morpho-midnight-tenor-labs-35c2</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Smart contract security is often about finding the invisible edge cases. While participating in a recent bug bounty for Tenor Labs (a protocol built on top of Morpho Midnight), I discovered a high-severity rounding vulnerability in their migration callbacks.&lt;/p&gt;

&lt;p&gt;If exploited, this flaw allows an attacker to artificially inflate a user's Loan-to-Value (LTV) ratio to infinity, triggering an immediate liquidation of the user's pre-existing collateral.&lt;/p&gt;

&lt;p&gt;In this article, I’ll break down the vulnerability, explain the math behind the exploit, and provide the Foundry Proof of Concept (PoC) that proves it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: Tenor Labs &amp;amp; Morpho Midnight
&lt;/h2&gt;

&lt;p&gt;Tenor Labs is a suite of non-custodial smart contracts that extends the Morpho Midnight protocol. One of its core features is the ability for borrowers to seamlessly migrate their debt and collateral between different markets (e.g., moving a position from Market A to Market B).&lt;/p&gt;

&lt;p&gt;This is handled by &lt;code&gt;BorrowMidnightRenewalCallback.sol&lt;/code&gt; and the &lt;code&gt;CollateralTransferLib&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When a position is migrated, the protocol calculates how much collateral needs to move from the source market to the target market. It does this pro-rata, based on the amount of debt being repaid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vulnerability: &lt;code&gt;mulDivDown&lt;/code&gt; Rounding to Zero
&lt;/h2&gt;

&lt;p&gt;The core of the bug lies in the &lt;code&gt;transferCollaterals&lt;/code&gt; function within &lt;code&gt;CollateralTransferLib.sol&lt;/code&gt;. The code uses &lt;code&gt;mulDivDown&lt;/code&gt; to calculate the collateral to transfer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (sourceDebtBefore != repaidUnits) {
    collateralToTransfer = collateralToTransfer.mulDivDown(repaidUnits, sourceDebtBefore);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mulDivDown&lt;/code&gt; performs multiplication followed by division, &lt;strong&gt;always rounding down&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Here is the fatal flaw: &lt;strong&gt;In DeFi, debt accrues interest.&lt;/strong&gt; If a borrower has 1000e18 in collateral and 1000e18 in debt, after one block, their debt might be 1000e18 + 1 wei due to interest.&lt;/p&gt;

&lt;p&gt;If an attacker performs a "dust fill" on the migration offer (repaying exactly &lt;strong&gt;1 wei&lt;/strong&gt;), the math looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1000e18 * 1 / (1000e18 + 1) = 0.999...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;mulDivDown&lt;/code&gt; rounds down to the nearest integer, the &lt;code&gt;collateralToTransfer&lt;/code&gt; becomes &lt;strong&gt;0&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Exploit Scenario
&lt;/h2&gt;

&lt;p&gt;The developers actually left a comment in the code acknowledging this rounding behavior, noting that it "temporarily increases the target position's LTV until the position is fully migrated." &lt;/p&gt;

&lt;p&gt;However, this assumption is fatally flawed. An attacker can weaponize this "temporary" state:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Setup:&lt;/strong&gt; A borrower creates a SELL offer to migrate a large position from Market A to Market B. The borrower also has some pre-existing collateral on Market B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Dust Fill:&lt;/strong&gt; An attacker (or a malicious bot) intentionally fills a tiny fraction of that offer (e.g., 1 wei).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Rounding:&lt;/strong&gt; The contract repays 1 wei of debt on Market A, but transfers &lt;strong&gt;0 collateral&lt;/strong&gt; to Market B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Kill:&lt;/strong&gt; Market B now records a new debt for the borrower, but 0 new collateral backed it. The LTV on Market B is instantly pushed to infinity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Liquidation:&lt;/strong&gt; The attacker immediately liquidates the borrower on Market B, stealing the liquidation penalty and the borrower's pre-existing collateral.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Proof of Concept (Foundry)
&lt;/h2&gt;

&lt;p&gt;To prove this, I wrote a Foundry test that replicates the math. I mocked the Midnight interface and set up a scenario where a user has 1000e18 collateral but 1000e18 + 1 debt (simulating accrued interest).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.34;

import {Test, console} from "forge-std/Test.sol";
import {CollateralTransferLib} from "../src/libraries/CollateralTransferLib.sol";
import {Market, CollateralParams, IMidnight} from "@midnight/interfaces/IMidnight.sol";

contract DustFillExploit is Test {
    // ... Mocks setup ...

    function testDustFillRoundsCollateralToZero() public {
        // 1. Borrower has 1000e18 collateral on Market A
        midnight.setCollateral(sourceMarketId, borrower, 0, 1000e18);

        // 2. Debt is 1000e18 + 1 (due to accrued interest)
        uint128 sourceDebtBefore = 1000e18 + 1;

        // 3. Attacker does a dust fill of 1 wei
        uint256 repaidUnits = 1;

        // 4. Call the vulnerable library function directly
        (address[] memory tokens, uint256[] memory amounts) = CollateralTransferLib.transferCollaterals(
            IMidnight(address(midnight)), 
            sourceMarket,
            targetMarket,
            borrower,
            sourceMarketId,
            sourceDebtBefore,
            repaidUnits
        );

        console.log("Collateral transferred to target:", amounts[0]);

        // 5. The transferred collateral will be 0, proving the vulnerability
        assertEq(amounts[0], 0, "Vulnerability proven: collateral rounded to zero!");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[PASS] testDustFillRoundsCollateralToZero() (gas: 65335)
Logs:
  Collateral transferred to target: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Fix It
&lt;/h2&gt;

&lt;p&gt;To mitigate this vulnerability, the &lt;code&gt;transferCollaterals&lt;/code&gt; function should enforce a minimum transfer amount. If the pro-rata calculation rounds down to zero, the transaction should revert to prevent unbacked debt from being created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (sourceDebtBefore != repaidUnits) {
    collateralToTransfer = collateralToTransfer.mulDivDown(repaidUnits, sourceDebtBefore);
    // Add this check:
    if (collateralToTransfer == 0 &amp;amp;&amp;amp; repaidUnits &amp;gt; 0) {
        revert CallbackLib.ZeroAmount();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This bug is a perfect example of why rounding directions and edge cases matter so much in DeFi. A simple &lt;code&gt;mulDivDown&lt;/code&gt; combined with accrued interest created a path to drain user funds. &lt;/p&gt;

&lt;p&gt;If you are a developer, always assume debt and balances will have dust amounts, and protect your math accordingly. If you are a researcher, always read the developer comments—they often point you directly to the attack surface!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can check out more of my security research and PoCs on my &lt;a href="https://github.com/vaultvult" rel="noopener noreferrer"&gt;GitHub Portfolio&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>security</category>
      <category>solidity</category>
      <category>smartcontracts</category>
    </item>
  </channel>
</rss>
