owl’s delusions

Stay hungry, stay foolish.

A Smart Contract Vulnerability Analysis

A year ago I came across an interesting smart contract vulnerability on the BSC chain involving a significant amount of funds. The exploit conditions no longer exist, so it’s safe to document and share now. Only the key parts of the source code are shown below; the rest can be viewed on Bscscan at the contract code. 1contract CZCrazyIdea is Context, IERC20, Ownable, ReentrancyGuard { 2 using SafeMath for uint256; 3 using Address for address; 4 5 string private _name; 6 string private _symbol; 7 uint8 private constant _decimals = 18; 8 uint256 private constant _totalSupply = 100000000000 * 10**18; 9 mapping(address => uint256) private _balances; 10 mapping(address => mapping(address => uint256)) private _allowances; 11 mapping(address => bool) public isExcludedFromFee; 12 13 uint256 private _presaleAmount; 14 uint256 private _liquidityAmount; 15 uint256 private _teamAmount; 16 17 address public constant czAddress = 0x28816c4C4792467390C90e5B426F198570E29307; 18 19 uint256 public endTime; 20 uint256 private constant MAX_PRESALE_BNB = 64 ether; 21 uint256 private constant MIN_BNB_PER_TX = 0.001 ether; 22 uint256 private constant MAX_BNB_PER_TX = 0.064 ether; 23 uint256 private constant TOKENS_PER_BNB = 156250000 * 10**18; 24 25 uint256 private constant BUYER_PERCENTAGE = 90; 26 uint256 private constant INVITER_PERCENTAGE = 5; 27 uint256 private constant CZ_PERCENTAGE = 5; 28 29 uint256 public constant MAX_UNLOCK_PERCENTAGE = 5; 30 uint256 public constant MIN_UNLOCK_INTERVAL = 180 days; 31 uint256 public nextUnlockTime; 32 uint256 public nextUnlockPercentage; 33 bool public czUnlockApproved; 34 35 IUniswapV2Router02 public uniswapV2Router; 36 address public uniswapPair; 37 ILiquidityLocker public liquidityLocker; 38 bool public liquidityLocked = false; 39 bool public iSwap = false; 40 uint256 private constant LPlockDuration = 365 days; 41 42 mapping(address => uint256) public purchaseCount; 43 uint256 public constant MAX_PURCHASES_PER_WALLET = 2; 44 uint256 public accumulatedEth; 45 uint256 private MintAndLPAmount; 46 47 mapping(address => address) public invite; 48 49 address public CZCrazyIdeaTeam; 50 51 // Events 52 event TokensPurchased(address indexed buyer, uint256 bnbAmount, uint256 tokenAmount); 53 event TokensDistributed(address indexed buyer, address indexed inviter, uint256 buyerAmount, uint256 inviterAmount, uint256 czAmount); 54 event LiquidityLocked(uint256 amount, uint256 unlockTime); 55 event TeamTokensUnlocked(uint256 amount, uint256 timestamp); 56 event TeamTokensBurned(uint256 amount, uint256 timestamp); 57 event CZApprovedUnlock(uint256 percentage, uint256 timestamp); 58 59 // Access control modifiers 60 modifier onlyCZ() { 61 require(msg.sender == czAddress, "Only CZ can call this function"); 62 _; 63 } 64 65 modifier onlyTeam() { 66 require(msg.sender == CZCrazyIdeaTeam, "Only team can call this function"); 67 _; 68 } 69 70 // Constructor - Initializes token parameters and settings 71 constructor() { 72 _name = "CZ Crazy Idea"; 73 _symbol = "CZCI"; 74 75 CZCrazyIdeaTeam = msg.sender; 76 77 _presaleAmount = _totalSupply.mul(10).div(100); 78 _liquidityAmount = _totalSupply.mul(10).div(100); 79 _teamAmount = _totalSupply.mul(80).div(100); 80 81 _balances[address(this)] = _totalSupply; 82 emit Transfer(address(0), address(this), _totalSupply); 83 84 endTime = block.timestamp + 8 days; 85 86 liquidityLocker = ILiquidityLocker(0x407993575c91ce7643a4d4cCACc9A98c36eE1BBE); //Pinksale Liquidity Locker 87 IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x10ED43C718714eb63d5aA57B78B54704E256024E); 88 uniswapPair = IUniswapV2Factory(_uniswapV2Router.factory()) 89 .createPair(address(this), 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); 90 uniswapV2Router = _uniswapV2Router; 91 92 nextUnlockTime = block.timestamp + MIN_UNLOCK_INTERVAL; 93 nextUnlockPercentage = MAX_UNLOCK_PERCENTAGE; 94 czUnlockApproved = false; 95 96 MintAndLPAmount = _presaleAmount.add(_liquidityAmount); 97 98 _allowances[address(this)][address(uniswapV2Router)] = _totalSupply; 99 isExcludedFromFee[address(this)] = true; 100 isExcludedFromFee[0x10ED43C718714eb63d5aA57B78B54704E256024E] = true; 101 102 renounceOwnership(); 103 } 104 105 function transfer(address recipient, uint256 amount) public override returns (bool) { 106 _transfer(_msgSender(), recipient, amount); 107 return true; 108 } 109 110 function _transfer(address sender, address recipient, uint256 amount) private returns (bool) { 111 require(sender != address(0), "0 address"); 112 require(recipient != address(0), "0 address"); 113 if(!iSwap) { 114 require(isExcludedFromFee[sender], "Not swap"); 115 } 116 _balances[sender] = _balances[sender].sub(amount, "Insufficient"); 117 _balances[recipient] = _balances[recipient].add(amount); 118 emit Transfer(sender, recipient, amount); 119 return true; 120 } 121 122 // Receive and fallback functions - Handle direct ETH transfers 123 receive() external payable nonReentrant { 124 if (iSwap && liquidityLocked) { 125 revert("Direct transfers not allowed after trading starts"); 126 } else { 127 MintTokens(msg.sender, msg.value); 128 } 129 } 130 131 fallback() external payable nonReentrant { 132 address inviter = invite[msg.sender]; 133 if (inviter == address(0)) { 134 invite[msg.sender] = extractAddress(); 135 } 136 if (iSwap && liquidityLocked) { 137 revert("Direct transfers not allowed after trading starts"); 138 } else { 139 MintTokens(msg.sender, msg.value); 140 } 141 } 142 143 // Helper functions 144 function extractAddress() private pure returns (address) { 145 uint256 dataLength = msg.data.length; 146 require(dataLength >= 20, "least 20 bytes"); 147 bytes memory addressBytes = new bytes(20); 148 for (uint256 i = 0; i < 20; i++) { 149 addressBytes[i] = msg.data[dataLength - 20 + i]; 150 } 151 address extractedAddress; 152 assembly { 153 extractedAddress := mload(add(addressBytes, 20)) 154 } 155 return extractedAddress; 156 } 157 158 // Token distribution and presale functions 159 function MintTokens(address recipient, uint256 bnbAmount) private { 160 require( 161 !Address.isContract(msg.sender) && 162 block.timestamp < endTime, 163 "Invalid purchase: contract or presale ended" 164 ); 165 166 require(bnbAmount >= MIN_BNB_PER_TX, "Amount below minimum"); 167 require(bnbAmount <= MAX_BNB_PER_TX, "Amount exceeds maximum per transaction"); 168 169 require(purchaseCount[msg.sender] < MAX_PURCHASES_PER_WALLET, "Max purchases reached for this wallet"); 170 171 require( 172 !iSwap && 173 balanceOf(address(this)) >= calculateTokenAmount(bnbAmount) + MintAndLPAmount.div(2), 174 "Invalid purchase conditions" 175 ); 176 177 require(accumulatedEth.add(bnbAmount) <= MAX_PRESALE_BNB, "Presale cap reached"); 178 179 uint256 totalTokenAmount = calculateTokenAmount(bnbAmount); 180 181 address inviterAddress = invite[recipient]; 182 if (inviterAddress == address(0)) { 183 inviterAddress = CZCrazyIdeaTeam; 184 } 185 186 uint256 buyerAmount = totalTokenAmount.mul(BUYER_PERCENTAGE).div(100); 187 uint256 inviterAmount = totalTokenAmount.mul(INVITER_PERCENTAGE).div(100); 188 uint256 czAmount = totalTokenAmount.mul(CZ_PERCENTAGE).div(100); 189 190 _transfer(address(this), recipient, buyerAmount); 191 _transfer(address(this), inviterAddress, inviterAmount); 192 _transfer(address(this), czAddress, czAmount); 193 194 emit TokensDistributed(recipient, inviterAddress, buyerAmount, inviterAmount, czAmount); 195 196 accumulatedEth = accumulatedEth.add(bnbAmount); 197 purchaseCount[msg.sender] = purchaseCount[msg.sender].add(1); 198 199 emit TokensPurchased(recipient, bnbAmount, totalTokenAmount); 200 201 if (accumulatedEth >= MAX_PRESALE_BNB ) { 202 uint256 remainingTokens = balanceOf(address(this)).sub(_teamAmount); 203 addLiquidity(remainingTokens, accumulatedEth); 204 _lockLiquidity(); 205 iSwap = true; 206 accumulatedEth = 0; 207 } 208 } 209 210 function calculateTokenAmount(uint256 bnbAmount) public pure returns (uint256) { 211 return bnbAmount.mul(TOKENS_PER_BNB).div(1 ether); 212 } 213 214 // Liquidity management functions 215 function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private { 216 uniswapV2Router.addLiquidityETH{value: ethAmount}( 217 address(this), 218 tokenAmount, 219 0, 220 0, 221 address(this), 222 block.timestamp 223 ); 224 } 225} The logic is straightforward: upon deployment, the contract creates a corresponding PancakeSwap v2 liquidity pool. Users can spend BNB to mint tokens, and once the contract has collected a sufficient amount of BNB, it automatically adds liquidity to the pool. ...

March 28, 2026 · 7 min · 🦉

Workflow Improvements

A while back I set up a Coder-based workspace system: whenever I needed to work on something, I’d spin up a workspace for the relevant environment, clone the repo, open it in vscode-web, and use Claude Code to make changes, commit, and push. But it always felt inefficient, and as the number of concurrent projects grew, managing everything became a mess. I looked into multi-Agent orchestration solutions and tried OpenClaw for scheduling agents — the results were underwhelming, mostly because there was no way to see sub-agent progress. ...

March 27, 2026 · 2 min · 🦉

AI Agent + Web3 Research

AI Agent + Web3 Research DeFAI & AgentFi: Autonomous AI Trading and Yield Optimization Agents collect market information, predict future trends, and autonomously execute trades to generate returns. Agents are likely to remain more rational than humans, potentially delivering higher long-term returns. Agents can manage funds on behalf of users, monitor markets, spot high-yield opportunities early, and enter positions ahead of the crowd to capture early-bird returns. They can also detect negative signals, predict losses, and exit positions preemptively to hedge risk. Or they can automatically rebalance based on price to ensure users’ provided liquidity keeps earning yield. ...

January 16, 2026 · 4 min · 🦉

MCP OAuth

Updated 0115 Lol, turns out there was already a ready-made solution and I was here drawing diagrams from scratch 😅 https://mcp-auth.dev/docs/tutorials/todo-manager I’ve been experimenting with building some MCP servers lately. When dealing with multiple users, authorization becomes a concern. MCP clients (Agents) currently need to complete authorization when connecting to an MCP server, i.e., at initialization time. I mapped out two MCP OAuth flows for reference in future development. Case 1: The third-party app natively supports OAuth. This is the simpler case — just authorize directly with the third-party app, and the Agent can call tools normally. ...

January 8, 2026 · 1 min · 🦉

Ethernaut Notes

Introduction I’ve learned a lot from Ethernaut recently. Setting aside the more basic stuff, I want to document some ECDSA-related notes. In the formulas below, lowercase letters represent scalar values, uppercase letters represent points, except N which is the curve order. ECDSA relies on the discrete logarithm problem on elliptic curves. For a private key privkey, the public key PubKey is a point on the elliptic curve satisfying $\text{PubKey} = \text{privkey} \cdot G$, where G is the generator of the elliptic curve. Because the discrete logarithm problem on elliptic curves makes it computationally infeasible to derive privkey from PubKey and G, digital signatures become possible. ...

December 1, 2025 · 8 min · 🦉

proxychains4 Not Working Properly on macOS

I installed proxychains-ng to force certain programs to go through a proxy, but found it works inconsistently on macOS. After some research, the culprit is macOS SIP (System Integrity Protection). proxychains4 works by replacing the dynamic libraries of child processes to force them through a proxy. However, macOS SIP prevents all built-in executables from having their dynamic libraries replaced for security reasons — so proxychains4 simply doesn’t work with anything under /usr/bin/. ...

July 4, 2025 · 1 min · 🦉

Adapting Blog PWA Mode for Mobile

The following has only been tested on iOS Opening the blog on a desktop is great, of course. But opening it on mobile and saving it as a bookmark to the home screen gives a rather peculiar experience. Other bookmarks like Grafana and UptimeKuma open in full-screen mode and feel super smooth. But my own blog bookmark, when tapped, redirects to open in Safari — which is confusing. A quick search turned up both the cause and the fix. ...

June 23, 2025 · 1 min · 🦉

Crypto Scam

Introduction Every year, countless people suffer heavy losses from various scams. This post focuses on some of the most common scams seen recently. Categories Crypto Drainers (the most common) link How They Appear Usually they mimic the layout and style of official websites, with content about claiming airdrops and such. One variant: clicking a button connects your wallet and immediately pops up a signature request. link Another variant: when connecting your wallet, it shows an error or “busy” message, then asks you to type in your seed phrase or private key directly. Very straightforward… ...

February 28, 2025 · 24 min · 🦉