<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/feed.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
<title>Guillermo Salazar</title>
<link>https://guillesalazar.dev</link>
<description>Posts and projects on smart contracts, backend engineering, and AI development workflows.</description>
<language>en</language>
<lastBuildDate>Fri, 10 Apr 2026 21:35:13 GMT</lastBuildDate>
<atom:link href="https://guillesalazar.dev/feed.xml" rel="self" type="application/rss+xml" />
<image>
<url>https://guillesalazar.dev/apple-icon.png</url>
<title>Guillermo Salazar</title>
<link>https://guillesalazar.dev</link>
</image>
<item>
<title>Understanding `view` vs `pure` in Solidity: Semantics, Gas, and Best Practices</title>
<link>https://guillesalazar.dev/posts/understanding-view-vs-pure-in-solidity-semantics-gas-and-best-practices</link>
<guid isPermaLink="true">https://guillesalazar.dev/posts/understanding-view-vs-pure-in-solidity-semantics-gas-and-best-practices</guid>
<pubDate>Fri, 10 Apr 2026 21:35:13 GMT</pubDate>
<dc:creator>Guillermo</dc:creator>
<category domain="type">post</category>
<description>A practical guide to Solidity’s view and pure functions: what they actually guarantee, when they do and do not affect gas, and how to use them for clearer, safer smart contract design.</description>
<content:encoded><![CDATA[<p>In Solidity, <code>view</code> and <code>pure</code> are not gas-optimization keywords. They are <strong>state-interaction guarantees</strong>.</p>
<ul>
<li>A <code>view</code> function may read state, but must not modify it.</li>
<li>A <code>pure</code> function may neither read nor modify state.</li>
</ul>
<p>These modifiers matter for correctness, composability, and audits because they tell both the compiler and human readers what a function is allowed to depend on.</p>
<h2>What each modifier allows</h2>
<p>A <code>view</code> function can read:</p>
<ul>
<li>contract storage</li>
<li>external contract state through read-only calls</li>
<li>block and transaction context such as <code>block.timestamp</code>, <code>block.number</code>, or <code>msg.sender</code></li>
</ul>
<p>A <code>pure</code> function can depend only on its inputs and local variables. It cannot read storage, external state, or block/transaction context.</p>
<p>At the EVM level, read-only external calls are enforced with <code>STATICCALL</code>. That matters because <code>view</code> is partly a Solidity-level promise, while <code>STATICCALL</code> is an execution-level restriction.</p>
<h2>Code Examples</h2>
<pre><code class="language-typescript">// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ViewVsPure {
    uint256 public totalSupply = 1_000_000e18;
    mapping(address => uint256) public balances;

    // ── PURE: only uses input parameters and memory ──

    /// @notice Basis-point tax calculation (10000 = 100%)
    function calculateTax(uint256 amount, uint256 bps) external pure returns (uint256) {
        return (amount * bps) / 10_000;
    }

    /// @notice Deterministic hash — same input always yields same output
    function hashData(bytes memory data) external pure returns (bytes32) {
        return keccak256(data);
    }

    // ── VIEW: reads storage or block context ──

    /// @notice Single storage read
    function getBalance(address user) external view returns (uint256) {
        return balances[user];
    }

    /// @notice Reads block context — not allowed in pure
    function getCurrentTimestamp() external view returns (uint256) {
        return block.timestamp;
    }

    /// @notice Reads balances[user] and totalSupply
    function calculateUserShare(address user) external view returns (uint256) {
        uint256 supply = totalSupply;
        if (supply == 0) return 0;
        return (balances[user] * 1e18) / supply; // 18-decimal precision
    }

    // ── STATE-CHANGING: costs gas in every transaction context ──

    function updateBalance(address user, uint256 amount) external {
        balances[user] = amount;
    }
}
</code></pre>
<h2>Gas: When Read-Only Is Free — and When It Is Not</h2>
<p><code>view</code> and <code>pure</code> functions do <strong>not</strong> cost onchain gas when invoked off-chain with <code>eth_call</code>, because no transaction is broadcast or included in a block.</p>
<p>But when the same logic executes <strong>inside a transaction</strong>, it consumes gas like any other code path.</p>
<p>That means:</p>
<ul>
<li>calling a <code>view</code> function from your frontend is usually “free” in the onchain sense</li>
<li>calling a <code>view</code> function from inside a state-changing function is <strong>not</strong> free</li>
<li>marking a function <code>pure</code> instead of <code>view</code> does <strong>not</strong> magically lower gas; it only prevents state reads, which may indirectly avoid expensive operations like <code>SLOAD</code></li>
</ul>
<p>A useful mental model is:</p>
<blockquote>
<p><code>view</code> and <code>pure</code> describe what a function is allowed to read, not how the EVM prices its execution.</p>
</blockquote>
<p>Here is the common source of confusion:</p>
<pre><code class="language-solidity">contract GasExample {
    uint256 public price = 1 ether;

    function getPrice() public view returns (uint256) {
        return price;
    }

    function buy(uint256 qty) external payable {
        // This view call happens inside a transaction.
        // Its storage read still costs gas.
        uint256 cost = getPrice() * qty;
        require(msg.value >= cost, "Insufficient payment");
    }
}
</code></pre>
<p>An internal call to a <code>view</code> or <code>pure</code> function is just normal bytecode execution. It is not a special free operation.</p>
<h3>Opcode Cost Reference (Post-EIP-2929)</h3>
<table>
<thead>
<tr>
<th>Opcode</th>
<th>Cold</th>
<th>Warm</th>
<th>Typical meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>SLOAD</code></td>
<td>2100</td>
<td>100</td>
<td>storage read</td>
</tr>
<tr>
<td><code>BALANCE</code></td>
<td>2600</td>
<td>100</td>
<td>account balance read</td>
</tr>
<tr>
<td><code>EXTCODESIZE</code></td>
<td>2600</td>
<td>100</td>
<td>code existence / size check</td>
</tr>
<tr>
<td><code>STATICCALL</code></td>
<td>2600 + exec</td>
<td>100 + exec</td>
<td>read-only external call</td>
</tr>
<tr>
<td>stack / memory ops</td>
<td>~3–6</td>
<td>~3–6</td>
<td>typical pure-function work</td>
</tr>
</tbody>
</table>
<h2>Real-World Application Patterns</h2>
<p><strong>Use</strong> <code>pure</code> <strong>for:</strong></p>
<ul>
<li>mathematical libraries</li>
<li>encoding and decoding helpers</li>
<li>signature and hash construction</li>
<li>validation predicates that depend only on inputs</li>
</ul>
<p><strong>Use</strong> <code>view</code> <strong>for:</strong></p>
<ul>
<li>aggregated state queries</li>
<li>computed properties derived from storage</li>
<li>access-controlled reads involving <code>msg.sender</code> or role state</li>
<li>oracle or adapter reads from external contracts</li>
</ul>
<h2>Common Pitfalls</h2>
<pre><code class="language-typescript">// ❌ Compiler error: block context is not allowed in pure
function badPure() public pure returns (uint256) {
    return block.timestamp;
}

// ❌ Compiler error: state writes are not allowed in view
function badView() public view returns (uint256) {
    totalSupply = 1000;
    return totalSupply;
}

// ✅ Compiles, but the modifier is weaker than necessary
function add(uint256 a, uint256 b) public pure returns (uint256) {
    return a + b;
}

// ❌ Not pure: depends on external contract state
function externalSupply(IERC20 token) public view returns (uint256) {
    return token.totalSupply();
}
</code></pre>
<p>When a <code>view</code> function calls another contract, the compiler requires <code>view</code>, not <code>pure</code>, because the result depends on external state. That value may change across blocks, so frontend caching assumptions should reflect that.</p>
<h2>Security Considerations</h2>
<p><code>pure</code> functions reduce chain-state-related risk because they have no storage reads, no external state dependency, and no block-context dependency. That makes them easier to isolate, test, and audit. But they are not automatically “safe” — math bugs, encoding errors, and flawed assumptions can still exist inside pure logic.</p>
<p><code>view</code> functions carry more nuance:</p>
<ul>
<li>external read-only calls use <code>STATICCALL</code>, which the EVM enforces as non-state-modifying</li>
<li><code>private</code> only restricts Solidity-level access, not what the declaring contract itself can read internally</li>
<li>off-chain users can inspect storage slots directly regardless of visibility modifiers</li>
<li>external <code>view</code> calls are only as trustworthy as the contracts they read from</li>
</ul>
<p>Never treat onchain storage as secret just because a variable is marked <code>private</code>.</p>
<h2>Actionable Takeaways</h2>
<ol>
<li>Use <code>pure</code> when logic depends only on inputs and local computation.</li>
<li>Use <code>view</code> when logic needs storage, external read-only state, or block/transaction context.</li>
<li>Treat <code>view</code> and <code>pure</code> as correctness annotations first, not gas tricks.</li>
<li>Remember that <code>eth_call</code> avoids <strong>onchain</strong> gas, but the same logic still costs gas when executed during a transaction.</li>
<li>Split complex flows into pure computation plus state-reading wrappers where possible.</li>
<li>Be cautious with external <code>view</code> calls: read-only does not mean trustworthy.</li>
</ol>]]></content:encoded>
</item>
<item>
<title>Memory vs Storage in Solidity: When and Why It Matters</title>
<link>https://guillesalazar.dev/posts/memory-vs-storage-in-solidity-when-and-why-it-matters</link>
<guid isPermaLink="true">https://guillesalazar.dev/posts/memory-vs-storage-in-solidity-when-and-why-it-matters</guid>
<pubDate>Mon, 30 Mar 2026 21:04:45 GMT</pubDate>
<dc:creator>Guillermo</dc:creator>
<category domain="type">post</category>
<description>Master Solidity data locations to write gas-efficient smart contracts. Learn the critical differences between memory, storage, and calldata, when to use each, and optimization patterns that can save thousands of gas per transaction.</description>
<content:encoded><![CDATA[<p>Understanding the distinction between <strong>memory</strong>, <strong>storage</strong>, and<br>
<strong>calldata</strong> in Solidity is crucial for writing efficient, cost‑effective smart contracts. Data location directly affects <strong>gas usage, execution behavior, and security</strong>.</p>
<hr>
<h2>What's the Difference?</h2>
<h3>Storage</h3>
<p><strong>Storage</strong> is persistent data stored on the blockchain. It survives<br>
across transactions.</p>
<ul>
<li>Writing new storage slot ≈ 20,000 gas</li>
<li>Updating existing slot ≈ 5,000 gas</li>
<li>Reading storage ≈ expensive compared to memory</li>
<li>Clearing storage can generate gas refunds (subject to limits)</li>
</ul>
<p>Think of storage as your contract's <strong>hard drive</strong>.</p>
<h3>Memory</h3>
<p><strong>Memory</strong> exists only during function execution.</p>
<ul>
<li>Cheap to read/write</li>
<li>Cleared after execution</li>
<li>Used for temporary variables</li>
</ul>
<p>Think of memory as your contract's <strong>RAM</strong>.</p>
<h3>Calldata</h3>
<p><strong>Calldata</strong> is:</p>
<ul>
<li>Read‑only</li>
<li>Non‑persistent</li>
<li>Cheaper than memory</li>
<li>Available only in external function calls</li>
</ul>
<p>Best used for function parameters that should not be modified.</p>
<hr>
<h2>Code Example</h2>
<pre><code class="language-typescript">contract StorageVsMemory {

    string public storedData;
    uint[] public numbers;

    function demonstrateStorage() public {

        storedData = "This persists on blockchain";
        numbers.push(42);

    }

    function demonstrateMemory() public pure returns (string memory) {

        string memory tempData = "Temporary execution data";

        uint[] memory tempNumbers = new uint[](3);

        tempNumbers[0] = 1;
        tempNumbers[1] = 2;
        tempNumbers[2] = 3;

        return tempData;

    }

    function processArray(uint[] calldata inputArray) external {

        uint sum = 0;

        for (uint i = 0; i &#x3C; inputArray.length; i++) {
            sum += inputArray[i];
        }

        uint[] memory modifiableArray = inputArray;

        modifiableArray[0] = sum;

    }
}
</code></pre>
<hr>
<h2>Reference vs Copy Behavior (Critical Concept)</h2>
<p>Assignment behavior depends on the data location.</p>
<p>From → To           Behavior</p>
<p>storage → storage   reference<br>
storage → memory    copy<br>
memory → memory     reference<br>
calldata → memory   copy</p>
<p>Example:</p>
<pre><code class="language-typescript">uint[] public numbers;

function example() public {

    uint[] storage ref = numbers;

    ref[0] = 999;

    uint[] memory copy = numbers;

    copy[0] = 111;

}
</code></pre>
<p>Only the storage reference modifies the contract state.</p>
<hr>
<h2>Struct Example (Real‑World Pattern)</h2>
<pre><code class="language-solidity">struct User {
    uint balance;
}

mapping(address => User) public users;

function updateBalance(uint amount) public {

    User storage user = users[msg.sender];

    user.balance += amount;

}
</code></pre>
<p>Using <code>storage</code> avoids unnecessary copying.</p>
<hr>
<h2>Important Limitation: Mappings Exist Only in Storage</h2>
<p>Mappings cannot exist in memory.</p>
<p>Invalid:</p>
<pre><code class="language-solidity">mapping(address => uint) memory balances;
</code></pre>
<p>Mappings rely on hashed storage slots.</p>
<hr>
<h2>Internal vs External Functions and Calldata</h2>
<p>Valid:</p>
<pre><code class="language-solidity">function externalExample(uint[] calldata data) external {}
</code></pre>
<p>Invalid:</p>
<pre><code class="language-solidity">function internalExample(uint[] calldata data) internal {}
</code></pre>
<p>Calldata exists only for <strong>external call boundaries</strong>.</p>
<hr>
<h2>Storage Caching Optimization Pattern</h2>
<p>Avoid repeated storage reads.</p>
<p>Better:</p>
<pre><code class="language-solidity">uint value = storedValue;

for (uint i = 0; i &#x3C; 10; i++) {
    total += value;
}
</code></pre>
<p>Instead of:</p>
<pre><code class="language-solidity">for (uint i = 0; i &#x3C; 10; i++) {
    total += storedValue;
}
</code></pre>
<p>Storage reads are expensive (~2100 gas cold access).</p>
<hr>
<h2>Storage Pointer Aliasing (Advanced Gotcha)</h2>
<pre><code class="language-solidity">uint[] storage ref1 = numbers;
uint[] storage ref2 = numbers;
</code></pre>
<p>Both reference the same storage location.</p>
<p>Modifying one modifies the other.</p>
<hr>
<h2>Emerging Feature: Transient Storage (EIP‑1153)</h2>
<p>Transient storage behaves like storage during execution but resets after the transaction completes.</p>
<p>Use cases:</p>
<ul>
<li>Reentrancy guards</li>
<li>Temporary execution flags</li>
<li>Gas optimization helpers</li>
</ul>
<p>Example:</p>
<pre><code class="language-solidity">tstore(slot, value)
tload(slot)
</code></pre>
<hr>
<h2>Real‑World Impact</h2>
<p>Reading storage ≈ expensive</p>
<p>Memory operations ≈ cheap</p>
<p>Reducing storage access can save <strong>thousands of gas per transaction</strong>.</p>
<hr>
<h2>Security Considerations</h2>
<ul>
<li>Memory resets after execution</li>
<li>Storage persists permanently</li>
<li>Always enforce access control when modifying storage</li>
<li>Avoid unintended storage references</li>
</ul>
<hr>
<h2>Best Practices</h2>
<ol>
<li>Use <strong>storage references</strong> when modifying persistent state</li>
<li>Use <strong>memory</strong> for temporary calculations</li>
<li>Use <strong>calldata</strong> for external function parameters</li>
<li>Cache storage reads if reused multiple times</li>
<li>Avoid unnecessary struct and array copies</li>
<li>Always explicitly declare data locations</li>
</ol>
<hr>
<h2>Final Takeaway</h2>
<p>Mastering Solidity data locations is one of the highest‑impact gas<br>
optimization skills a smart contract developer can learn. Most inefficiencies come from unnecessary storage access or accidental data copying between locations.</p>]]></content:encoded>
</item>
<item>
<title>Portfolio Website</title>
<link>https://guillesalazar.dev/projects/portfolio-website</link>
<guid isPermaLink="true">https://guillesalazar.dev/projects/portfolio-website</guid>
<pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate>
<dc:creator>Guillermo Salazar</dc:creator>
<category domain="type">project</category>
<category>Next.js 15</category>
<category>TypeScript</category>
<category>Tailwind CSS v4</category>
<category>PostgreSQL</category>
<category>OpenRouter</category>
<category>Claude</category>
<category>CMS</category>
<category>Resend</category>
<category>Cloudflare Turnstile</category>
<category>pdf-lib</category>
<description>Full-stack Next.js portfolio with AI chatbot, resume access system, CMS integration, and advanced security features.</description>
<content:encoded><![CDATA[<p>A full-stack portfolio website featuring an AI-powered chatbot, gated resume access system, and content management capabilities. Built with Next.js 15 App Router, TypeScript, and Tailwind CSS v4, integrated with multiple third-party services for email, notifications, and database operations.</p>
<h2>Technical Architecture</h2>
<h3>Core Technologies</h3>
<ul>
<li><strong>Next.js 15</strong>: App Router with server components and streaming</li>
<li><strong>TypeScript</strong>: Strict mode with comprehensive type safety</li>
<li><strong>Tailwind CSS v4</strong>: CSS-first configuration with @theme directive</li>
<li><strong>Framer Motion</strong>: Smooth animations and page transitions</li>
<li><strong>PostgreSQL</strong>: Third-party database with Row Level Security (RLS)</li>
</ul>
<h3>AI Chatbot System</h3>
<p><strong>RAG Implementation</strong> (Retrieval-Augmented Generation):</p>
<ul>
<li>Document chunking with 300-word chunks and 30-word overlap</li>
<li>Keyword-based scoring for relevant context retrieval</li>
<li>2000-token budget management for optimal context injection</li>
<li>Session persistence (last 20 messages)</li>
</ul>
<p><strong>Multi-Provider Support</strong>:</p>
<ul>
<li>Primary: Claude Sonnet 5.5 via OpenRouter</li>
<li>Fallback: Claude Opus 5.5 (OpenRouter model routing)</li>
<li>Streaming responses over plain <code>fetch</code> (no SDK)</li>
<li>Configurable rate limiting per session</li>
</ul>
<p><strong>Context Sources</strong>:</p>
<ul>
<li>Professional bio and experience</li>
<li>Project descriptions and technical details</li>
<li>Skills and technologies</li>
<li>FAQ and common questions</li>
</ul>
<h3>Resume Access System</h3>
<p><strong>Workflow</strong>:</p>
<ol>
<li>User submits request (Cloudflare Turnstile protected)</li>
<li>Database record created</li>
<li>Telegram notification sent to admin</li>
<li>Admin approves via secure token link</li>
<li>Email sent with time-limited access token</li>
<li>Token validation on every access</li>
</ol>
<p><strong>Security Features</strong>:</p>
<ul>
<li>HMAC-signed tokens with configurable expiry</li>
<li>Server-side validation on all endpoints</li>
<li>Persistent rate limiting on access requests</li>
<li>IP-based tracking with SHA-256 hashing</li>
</ul>
<p><strong>PDF Generation</strong>:</p>
<ul>
<li>Dynamic metadata embedding (token, email, download date, URL)</li>
<li>Date-stamped filenames (YYYYMMDD_Resume_Guillermo_Salazar.pdf)</li>
<li>Private storage outside public directory</li>
<li>Authenticated download endpoint</li>
</ul>
<h3>Content Management</h3>
<p><strong>CMS</strong>:</p>
<ul>
<li>Git-based headless CMS</li>
<li>Markdown content with frontmatter</li>
<li>Direct repository commits</li>
<li>Blog posts and project showcase</li>
<li>No external database dependency</li>
</ul>
<h3>Integrations</h3>
<p><strong>Email Service</strong>:</p>
<ul>
<li>Transactional emails for resume approval</li>
<li>Contact form submissions</li>
<li>Graceful fallback when unavailable</li>
</ul>
<p><strong>Telegram Bot</strong>:</p>
<ul>
<li>Real-time notifications for resume requests</li>
<li>Contact form submissions</li>
<li>Admin alerts</li>
</ul>
<p><strong>Security (Cloudflare Turnstile)</strong>:</p>
<ul>
<li>Bot protection on all public forms</li>
<li>Contact form verification</li>
<li>Resume request validation</li>
</ul>
<h2>Security &#x26; Rate Limiting</h2>
<h3>Server-Side Rate Limiting</h3>
<p><strong>Implementation</strong>:</p>
<ul>
<li>Database-backed persistent rate limits for critical endpoints</li>
<li>IP hashing with configurable salt for privacy</li>
<li>Configurable time windows and request thresholds</li>
<li>Multiple storage strategies (in-memory and persistent)</li>
</ul>
<p><strong>Protected Features</strong>:</p>
<ul>
<li>AI chatbot interactions</li>
<li>Resume access requests</li>
<li>Public-facing API endpoints</li>
</ul>
<h3>Input Validation</h3>
<ul>
<li>Server-side validation on all user inputs</li>
<li>Length limits enforced</li>
<li>Type checking with TypeScript</li>
<li>Sanitization before database storage</li>
</ul>
<h3>Security Headers</h3>
<ul>
<li>Comprehensive Content Security Policy (CSP)</li>
<li>HTTP Strict Transport Security (HSTS)</li>
<li>X-Frame-Options and X-Content-Type-Options</li>
<li>Configured in next.config.mjs</li>
</ul>
<h2>Development Approach</h2>
<h3>AI-Assisted Development</h3>
<p><strong>Code Generation</strong>:</p>
<ul>
<li>API route scaffolding</li>
<li>Component structure and boilerplate</li>
<li>TypeScript interfaces from schemas</li>
<li>Test case generation</li>
</ul>
<p><strong>Optimization</strong>:</p>
<ul>
<li>Bundle size analysis</li>
<li>Performance profiling</li>
<li>Accessibility audits</li>
<li>Security vulnerability scanning</li>
</ul>
<p><strong>Documentation</strong>:</p>
<ul>
<li>Inline code comments</li>
<li>README with setup instructions</li>
<li>Environment variable documentation</li>
</ul>
<h3>Quality Assurance</h3>
<ul>
<li>TypeScript strict mode enabled</li>
<li>ESLint with Next.js rules</li>
<li>Prettier for consistent formatting</li>
<li>Build-time type checking</li>
</ul>
<h2>Performance &#x26; SEO</h2>
<h3>Optimization</h3>
<ul>
<li>Server components for reduced JavaScript</li>
<li>Image optimization with Next.js Image</li>
<li>Code splitting and lazy loading</li>
<li>Static generation where possible</li>
</ul>
<h3>Search Engine Optimization</h3>
<ul>
<li>JSON-LD structured data</li>
<li>Open Graph and Twitter Cards</li>
<li>Semantic HTML markup</li>
<li>Dynamic sitemap generation</li>
<li>Robots.txt configuration</li>
</ul>
<h2>Deployment &#x26; Monitoring</h2>
<ul>
<li>Deployment with automatic CI/CD</li>
<li>Environment variable management</li>
<li>Build validation before deployment</li>
<li>Production error tracking</li>
</ul>
<p>This portfolio demonstrates a production-ready full-stack application combining modern web technologies, AI capabilities, and robust security practices. The modular architecture allows graceful degradation when optional services are unavailable, ensuring core functionality remains accessible.</p>]]></content:encoded>
</item>
</channel>
</rss>
