<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Guardian</title>
        <link>https://paragraph.com/@guardian</link>
        <description>Devastatingly effective Smart Contract security, for when it has to be right the first time. Guarded $7 Billion.

https://guardianaudits.com</description>
        <lastBuildDate>Wed, 08 Apr 2026 15:09:20 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Guardian</title>
            <url>https://storage.googleapis.com/papyrus_images/6fcb262ba8f67ebb72b679623e40e190aa67fbac65acac3f5ed735c95c85e943.png</url>
            <link>https://paragraph.com/@guardian</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Web2: The Hidden Layer of DeFi Risk]]></title>
            <link>https://paragraph.com/@guardian/web2-the-hidden-layer-of-defi-risk</link>
            <guid>MdBbX4AtA9sEhpjL33T9</guid>
            <pubDate>Sun, 18 May 2025 15:48:21 GMT</pubDate>
            <description><![CDATA[Web2 Vulnerabilities, Web3 RiskWeb2 and Web3 security are often treated as separate domains. Labels like “Web2 Security Researcher” and “Web3 Security Researcher” create a false dichotomy — when in reality, modern DApps are deeply integrated across both layers. Smart contracts may be secure in isolation, but DeFi’s composability introduces new, dangerous risks. Whoever controls the calldata controls the world. We’ve seen billion-dollar exploits stem from frontend misconfigurations or compromi...]]></description>
            <content:encoded><![CDATA[<h2 id="h-web2-vulnerabilities-web3-risk" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Web2 Vulnerabilities, Web3 Risk</h2><p>Web2 and Web3 security are often treated as separate domains. Labels like “Web2 Security Researcher” and “Web3 Security Researcher” create a false dichotomy — when in reality, modern DApps are deeply integrated across both layers.</p><p>Smart contracts may be secure in isolation, but DeFi’s composability introduces new, dangerous risks. <strong>Whoever controls the calldata controls the world.</strong> We’ve seen <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rekt.news/bybit-rekt">billion-dollar exploits</a> stem from frontend misconfigurations or compromised libraries that injected malicious calldata into trusted contracts.</p><h2 id="h-why-web2-security-matters-more-than-ever" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Why Web2 Security Matters More Than Ever</h2><p>When people think about securing a DApp, they often stop at smart contracts. But the reality is that much of the DApp’s behavior is governed by what happens <em>offchain</em> — in backends, frontends, and CI/CD pipelines. This is the “Web2 layer” of crypto.</p><p>And it’s increasingly where attackers are looking.</p><p>Unlike smart contracts, which are immutable and reviewed line-by-line, Web2 systems are mutable, fast-moving, and rarely audited with the same scrutiny. They often hold the keys to core logic:</p><ul><li><p>Who can call sensitive contract functions</p></li><li><p>What calldata gets passed in</p></li><li><p>When transactions are triggered</p></li></ul><p>If an attacker compromises any of those systems — by injecting fake data into a backend, modifying frontend scripts, or abusing deployment infrastructure — they can bypass the &quot;secure&quot; onchain logic entirely.</p><p>The issue isn’t always that smart contracts are vulnerable. It’s that everything surrounding them can make them insecure — and most teams don’t realize it until it’s too late.</p><p>Let’s examine a real-world example Guardian uncovered, where the smart contracts were sound — but the <strong>calldata</strong> passed through a Web2 backend made the DApp vulnerable.</p><h2 id="h-example-of-a-web2-attack" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Example of a Web2 Attack</h2><p>When a user registers on this website, their data is stored in the application’s backend:</p><pre data-type="codeBlock" text="async handler(ctx, { data }) {
    let account = await accountByExternalId(ctx, data.id);
    if (account === null) {
      const newAccount = await ctx.db.insert(&quot;accounts&quot;, {
        displayName: `${data.username}`,
        externalId: data.id,
        profileImage: data.image_url,
        preferences: {
          theme: &quot;DEFAULT&quot;,
          settingA: false,
          settingB: false,
          showValuesInUnits: false,
        },
        socialHandle: maybeGetSocialHandle(data),
      });
      account = (await ctx.db.get(newAccount)) as Doc&lt;&quot;accounts&quot;&gt;;
    }
"><code>async handler(ctx, { data }) {
    let account <span class="hljs-operator">=</span> await accountByExternalId(ctx, data.id);
    <span class="hljs-keyword">if</span> (account <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> null) {
      const newAccount <span class="hljs-operator">=</span> await ctx.db.insert(<span class="hljs-string">"accounts"</span>, {
        displayName: `${data.username}`,
        externalId: data.id,
        profileImage: data.image_url,
        preferences: {
          theme: <span class="hljs-string">"DEFAULT"</span>,
          settingA: <span class="hljs-literal">false</span>,
          settingB: <span class="hljs-literal">false</span>,
          showValuesInUnits: <span class="hljs-literal">false</span>,
        },
        socialHandle: maybeGetSocialHandle(data),
      });
      account <span class="hljs-operator">=</span> (await ctx.db.get(newAccount)) <span class="hljs-keyword">as</span> Doc<span class="hljs-operator">&#x3C;</span><span class="hljs-string">"accounts"</span><span class="hljs-operator">></span>;
    }
</code></pre><p>A cron job runs every 30 minutes to flush tokens held in user-specific “forwarder” contracts to the protocol’s treasury:</p><pre data-type="codeBlock" text="function batchFlushERC20(
        address[] calldata forwarderAddresses,
        address tokenContractAddress
    ) public {
        for (uint256 i = 0; i &lt; forwarderAddresses.length; i++) {
            address forwarderAddress = forwarderAddresses[i];
            Forwarder forwarder = Forwarder(forwarderAddress);
            forwarder.flushERC20(tokenContractAddress);
        }
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchFlushERC20</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> forwarderAddresses,
        <span class="hljs-keyword">address</span> tokenContractAddress
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> forwarderAddresses.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">address</span> forwarderAddress <span class="hljs-operator">=</span> forwarderAddresses[i];
            Forwarder forwarder <span class="hljs-operator">=</span> Forwarder(forwarderAddress);
            forwarder.flushERC20(tokenContractAddress);
        }
    }
</code></pre><p>From a Web3 security perspective, this function looks secure, and would most likely pass a review. A web3 security researcher might check edge cases like zero addresses or reentrancy — but that’s not where the real risk lies.</p><p>Here’s how the Web2 backend calls this function:</p><pre data-type="codeBlock" text="export const flushExternalDeposit = internalAction({
  args: {},
  handler: async (ctx) =&gt; {
    const allUserIds = await ctx.runQuery(internal.deposits.allUserIds);
    const forwarderAddresses: string[] = [];
    for (const userId of allUserIds) {
      const address = getPredeployForUser(userId, BASE_CHAIN_ID);
      if (address !== &quot;test&quot;) {
        forwarderAddresses.push(address);
      }
    }

    const queueId = await contractWrite({
      contractAddress: TREASURIES[BASE_CHAIN_ID],
      functionName:
        &quot;function batchFlushERC20(address[] forwarderAddresses, address tokenContractAddress)&quot;,
      args: [forwarderAddresses, BASE_USDC],
      abi: [
        {
          type: &quot;function&quot;,
          name: &quot;batchFlushERC20&quot;,
          inputs: [
            { type: &quot;address[]&quot;, name: &quot;forwarderAddresses&quot; },
            { type: &quot;address&quot;, name: &quot;tokenContractAddress&quot; },
          ],
          outputs: [],
        },
      ],
      chainId: BASE_CHAIN_ID,
    });
  },
});
"><code>export const flushExternalDeposit <span class="hljs-operator">=</span> internalAction({
  args: {},
  handler: async (ctx) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const allUserIds <span class="hljs-operator">=</span> await ctx.runQuery(<span class="hljs-keyword">internal</span>.deposits.allUserIds);
    const forwarderAddresses: <span class="hljs-keyword">string</span>[] <span class="hljs-operator">=</span> [];
    <span class="hljs-keyword">for</span> (const userId of allUserIds) {
      const <span class="hljs-keyword">address</span> <span class="hljs-operator">=</span> getPredeployForUser(userId, BASE_CHAIN_ID);
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">address</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"test"</span>) {
        forwarderAddresses.<span class="hljs-built_in">push</span>(<span class="hljs-keyword">address</span>);
      }
    }

    const queueId <span class="hljs-operator">=</span> await contractWrite({
      contractAddress: TREASURIES[BASE_CHAIN_ID],
      functionName:
        <span class="hljs-string">"function batchFlushERC20(address[] forwarderAddresses, address tokenContractAddress)"</span>,
      args: [forwarderAddresses, BASE_USDC],
      <span class="hljs-built_in">abi</span>: [
        {
          <span class="hljs-keyword">type</span>: <span class="hljs-string">"function"</span>,
          name: <span class="hljs-string">"batchFlushERC20"</span>,
          inputs: [
            { <span class="hljs-keyword">type</span>: <span class="hljs-string">"address[]"</span>, name: <span class="hljs-string">"forwarderAddresses"</span> },
            { <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span>, name: <span class="hljs-string">"tokenContractAddress"</span> },
          ],
          outputs: [],
        },
      ],
      chainId: BASE_CHAIN_ID,
    });
  },
});
</code></pre><p>Every 30 minutes, the application fetches <strong>all</strong> users who have ever registered and passes their addresses to the <code>batchFlushERC20</code> function as <code>forwarderAddresses</code>.</p><p>This creates a unique problem: blockchains have a <strong>block gas limit</strong>. As more users register, the calldata grows — and so does the gas required to process each flush. Every additional user adds another external call to a forwarder contract.</p><p>Eventually, as the user base grows — or if a malicious actor spams thousands of fake registrations — the function call will exceed the block gas limit. At that point, the flushing mechanism fails entirely, <strong>denying service to a critical part of the DApp’s asset flow</strong>.</p><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>As DApps evolve, core logic is increasingly pushed off-chain — and attackers know it. Smart contract audits alone are no longer enough.</p><p>Guardian provides <strong>full-stack security</strong> — auditing not just your contracts, but everything connected to them. If you’re serious about shipping a secure product, we’ll help you find and fix protocol-killing issues at every layer.</p><p>Book an audit with Guardian <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://guardianaudits.com/">here</a>, to secure every layer of your stack.</p>]]></content:encoded>
            <author>guardian@newsletter.paragraph.com (Guardian)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/1f26be5c96550866681c3ab4625973148daebb324935c0c7fcf65aaad4ebe5ae.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>