<?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: NSNAS Pay</title>
    <description>The latest articles on DEV Community by NSNAS Pay (@nsnascoin).</description>
    <link>https://dev.to/nsnascoin</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3904989%2F994fa30c-9868-4b12-a394-da167c1a0216.png</url>
      <title>DEV Community: NSNAS Pay</title>
      <link>https://dev.to/nsnascoin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nsnascoin"/>
    <language>en</language>
    <item>
      <title>Building a Reflection Token on BSC with OpenZeppelin v5 — Architecture &amp; Security Patterns</title>
      <dc:creator>NSNAS Pay</dc:creator>
      <pubDate>Wed, 29 Apr 2026 20:10:40 +0000</pubDate>
      <link>https://dev.to/nsnascoin/building-a-reflection-token-on-bsc-with-openzeppelin-v5-architecture-security-patterns-3n8i</link>
      <guid>https://dev.to/nsnascoin/building-a-reflection-token-on-bsc-with-openzeppelin-v5-architecture-security-patterns-3n8i</guid>
      <description>&lt;h1&gt;
  
  
  Building a Reflection Token on BSC with OpenZeppelin v5 — Architecture &amp;amp; Security Patterns
&lt;/h1&gt;

&lt;p&gt;When we set out to build &lt;strong&gt;NSNAS Token 2.0&lt;/strong&gt;, our goal wasn't just to create another BSC token. We wanted to build a token that was &lt;strong&gt;provably safe&lt;/strong&gt; — where every security claim could be verified by reading the source code.&lt;/p&gt;

&lt;p&gt;This article walks through the architecture decisions, security patterns, and lessons learned building a production reflection token on BNB Smart Chain.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Contract Inheritance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NSNAS Token 2.0
├── ERC20 (OpenZeppelin)
├── ERC20Pausable (OpenZeppelin)
├── Ownable (OpenZeppelin)
├── ReentrancyGuard (OpenZeppelin)
└── Custom Logic
    ├── Reflection System
    ├── Auto-Liquidity
    ├── Auto-Burn
    ├── Anti-Whale
    └── Timelock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We chose &lt;strong&gt;OpenZeppelin v5.0.0&lt;/strong&gt; as our foundation. Every security-critical function — token transfers, access control, reentrancy protection — comes from battle-tested code that manages billions in value across the ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why v5 Over v4?
&lt;/h3&gt;

&lt;p&gt;blockchain,&lt;br&gt;
OpenZeppelin v5 introduced several improvements we leveraged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namespaced storage&lt;/strong&gt; — Better upgrade patterns (though our contract is not upgradeable by design)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom errors&lt;/strong&gt; — Gas savings over string-based reverts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stricter access control&lt;/strong&gt; — &lt;code&gt;Ownable&lt;/code&gt; now requires explicit initial owner in constructor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modernized patterns&lt;/strong&gt; — Better alignment with latest Solidity features&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Security Pattern 1: Immutable Constants
&lt;/h2&gt;

&lt;p&gt;The most important security decision was making critical parameters &lt;strong&gt;immutable&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;uint256 public constant MAX_SUPPLY = 10_000_000 * 10**18;
uint256 public constant MIN_SUPPLY = 1_000 * 10**18;
uint256 public constant MAX_TAX = 500; // 5% in basis points
uint256 public constant TIMELOCK_DELAY = 48 hours;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Constants Matter
&lt;/h3&gt;

&lt;p&gt;In Solidity, &lt;code&gt;constant&lt;/code&gt; values are embedded directly in the bytecode at compile time. They don't occupy storage slots. They can't be modified by any function, any role, or any governance mechanism.&lt;/p&gt;

&lt;p&gt;When we say "tax can never exceed 5%," it's not a governance promise — it's a mathematical certainty enforced by the EVM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function setTaxRates(uint256 _buyTax, uint256 _sellTax) external onlyOwner {
    require(_buyTax &amp;lt;= MAX_TAX, "Buy tax exceeds 5%");
    require(_sellTax &amp;lt;= MAX_TAX, "Sell tax exceeds 5%");
    // ... timelock logic
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if the private key is compromised, even if governance is attacked — the tax cannot exceed &lt;code&gt;MAX_TAX&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Pattern 2: Timelock Mechanism
&lt;/h2&gt;

&lt;p&gt;For parameters that need operational flexibility (like adjusting tax split between reflection, liquidity, and burn), we implemented a timelock:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 public constant TIMELOCK_DELAY = 48 hours;

struct PendingTaxChange {
    uint256 newBuyTax;
    uint256 newSellTax;
    uint256 executeAfter;
    bool pending;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Owner proposes a change → recorded on-chain with &lt;code&gt;block.timestamp + TIMELOCK_DELAY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;48 hours must pass — visible to everyone on BscScan&lt;/li&gt;
&lt;li&gt;Owner executes the change after the delay&lt;/li&gt;
&lt;li&gt;Anyone monitoring the blockchain can see the pending change and react&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern is used by major DeFi protocols (Compound, Uniswap governance) and provides a critical trust guarantee: &lt;strong&gt;no surprises&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Pattern 3: Anti-Whale Protection
&lt;/h2&gt;

&lt;p&gt;Large holders can manipulate markets. We prevent this at the contract level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 public maxTransactionAmount = 5_000 * 10**18;
uint256 public maxWalletAmount = 10_000 * 10**18;
uint256 public constant COOLDOWN_TIME = 30;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementation Details
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;_update&lt;/code&gt; function (OpenZeppelin v5's replacement for &lt;code&gt;_transfer&lt;/code&gt;) checks these limits on every transfer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _update(address from, address to, uint256 amount) internal override {
    // Anti-whale checks
    if (!isExcludedFromLimits[to]) {
        require(amount &amp;lt;= maxTransactionAmount, "Exceeds max tx");
        require(balanceOf(to) + amount &amp;lt;= maxWalletAmount, "Exceeds max wallet");
    }

    // Cooldown check
    if (!isExcludedFromLimits[from]) {
        require(block.timestamp &amp;gt;= lastTransferTimestamp[from] + COOLDOWN_TIME, "Cooldown active");
        lastTransferTimestamp[from] = block.timestamp;
    }

    super._update(from, to, amount);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key design decision:&lt;/strong&gt; The contract owner and PancakeSwap pair are excluded from these limits. Without this, adding liquidity or collecting taxes would fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reflection System
&lt;/h2&gt;

&lt;p&gt;Reflection tokens distribute a portion of every transaction to all holders proportionally. Here's the high-level flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User trades NSNAS on PancakeSwap
    │
    ├── 1-2% → Distributed to ALL holders (reflection)
    ├── 1-2% → Added to PancakeSwap LP (auto-liquidity)
    └── 1%   → Permanently burned (deflation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Challenge: Gas Efficiency
&lt;/h3&gt;

&lt;p&gt;Distributing tokens to every holder on every transaction would cost millions in gas. The standard approach — iterating through all holders — doesn't scale.&lt;/p&gt;

&lt;p&gt;Instead, we use a &lt;strong&gt;virtual balance&lt;/strong&gt; system where reflection rewards are calculated lazily. Each holder's "real" balance includes their base tokens plus accumulated reflections, calculated on-demand rather than distributed per-transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-Liquidity Mechanism
&lt;/h2&gt;

&lt;p&gt;A percentage of every transaction is converted to liquidity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Half the liquidity tax is swapped to BNB via PancakeSwap&lt;/li&gt;
&lt;li&gt;The other half remains as NSNAS&lt;/li&gt;
&lt;li&gt;Both are added to the NSNAS/BNB liquidity pool&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This creates a continuously growing liquidity floor, making the token more stable over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reentrancy Protection
&lt;/h3&gt;

&lt;p&gt;The swap-and-liquify function interacts with PancakeSwap (an external contract), creating a reentrancy vector. We protect against this with OpenZeppelin's &lt;code&gt;ReentrancyGuard&lt;/code&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 swapAndLiquify() private nonReentrant {
    // Safe from reentrancy
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, we use a boolean flag to prevent recursive calls during the swap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool private inSwap;
modifier lockTheSwap {
    inSwap = true;
    _;
    inSwap = false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Burn Floor: Preventing Token Death
&lt;/h2&gt;

&lt;p&gt;Deflationary tokens face an existential risk: what if they burn to zero? We implemented a floor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 public constant MIN_SUPPLY = 1_000 * 10**18;

function _burn(address account, uint256 amount) internal override {
    if (totalSupply() - amount &amp;lt; MIN_SUPPLY) {
        // Don't burn, skip the burn portion
        return;
    }
    super._burn(account, amount);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the token can never be burned below 1,000 NSNAS, regardless of how many transactions occur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment &amp;amp; Verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Constructor Design
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor(
    address _taxWallet,
    address initialOwner,
    address _routerAddress
) ERC20("NSNAS Token", "NSNAS") Ownable(initialOwner) {
    // Setup PancakeSwap pair
    IUniswapV2Router02 router = IUniswapV2Router02(_routerAddress);
    uniswapV2Pair = IUniswapV2Factory(router.factory())
        .createPair(address(this), router.WETH());

    // Mint total supply to deployer
    _mint(msg.sender, MAX_SUPPLY);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three parameters are passed at deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tax wallet address&lt;/li&gt;
&lt;li&gt;Initial owner address&lt;/li&gt;
&lt;li&gt;PancakeSwap V2 Router address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The constructor is the &lt;strong&gt;only place&lt;/strong&gt; &lt;code&gt;_mint&lt;/code&gt; is called. After deployment, no new tokens can ever be created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification on BscScan
&lt;/h3&gt;

&lt;p&gt;We verified the contract using BscScan's compiler verification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiler: Solidity v0.8.20&lt;/li&gt;
&lt;li&gt;Optimization: Yes (200 runs)&lt;/li&gt;
&lt;li&gt;EVM Version: Paris&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The verified source code matches the deployed bytecode exactly — anyone can read and audit it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use constants aggressively.&lt;/strong&gt; Any parameter that should never change after deployment should be a &lt;code&gt;constant&lt;/code&gt;. Don't rely on governance promises.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Timelock everything else.&lt;/strong&gt; Parameters that need flexibility should have a mandatory delay. 48 hours is a good minimum.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't write security code yourself.&lt;/strong&gt; OpenZeppelin exists for a reason. Use &lt;code&gt;ReentrancyGuard&lt;/code&gt;, not custom mutex patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test with anti-whale limits.&lt;/strong&gt; Our first testnet deployment failed because the anti-whale limits blocked the initial liquidity add. Exclude necessary addresses from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify your source code immediately.&lt;/strong&gt; An unverified contract is a red flag. Verify on BscScan the same day you deploy.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contract:&lt;/strong&gt; &lt;a href="https://bscscan.com/address/0x0c2009d3ee9090c1192353527c09c217b4e32cf4#code" rel="noopener noreferrer"&gt;View on BscScan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenZeppelin v5 Docs:&lt;/strong&gt; &lt;a href="https://docs.openzeppelin.com/contracts/5.x/" rel="noopener noreferrer"&gt;docs.openzeppelin.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://nsnas.net" rel="noopener noreferrer"&gt;nsnas.net&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Questions about the implementation? Drop a comment below or reach out on &lt;a href="https://x.com/nsnascoin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>solidity</category>
      <category>web3</category>
      <category>defi</category>
    </item>
  </channel>
</rss>
