Files
uupl-smart-contract/.superpowers/sdd/2026-08-17-uups-bank-demo/review-a6f9533..1f4175b.diff
T
golemandClaude Opus 5 fa36215def docs: track superpowers working documents in git
The uups-bank-demo wave's SDD records (ledger, six task briefs and reports,
review diffs) and the brainstorm design mockups were git-ignored, so they
existed only on one sandbox VM and reached no remote — this repo had no remote
at all until now.

Removes `.superpowers/` from .gitignore and the `*` .gitignore the superpowers
plugin writes inside .superpowers/sdd/; the second blocks the directory even
with the first removed.

Excluded as ephemeral local-server state, and now ignored by name:
.last-port, .last-token (a 64-char session token for a brainstorm server on a
port that is long gone), and the per-session state/ directories.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnwzj6McD6kSkXwjUKFKxe
2026-08-20 14:38:00 -06:00

102 lines
3.2 KiB
Diff

# Review package: a6f9533aacd4d6cdbbf9682bfd8482ced065ebf5..1f4175ba729fbbab58538cd3ac02b5459e014c86
## Commits
1f4175b feat: add valueless mock USDC
## Files changed
src/MockUSDC.sol | 18 ++++++++++++++++
test/MockUSDC.t.sol | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+)
## Diff
diff --git a/src/MockUSDC.sol b/src/MockUSDC.sol
new file mode 100644
index 0000000..96ca297
--- /dev/null
+++ b/src/MockUSDC.sol
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.35;
+
+import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+
+/// @notice Educational mock token with no monetary value. Never use as real USDC.
+contract MockUSDC is ERC20, Ownable {
+ constructor(address initialOwner) ERC20("Mock USD Coin", "mUSDC") Ownable(initialOwner) {}
+
+ function decimals() public pure override returns (uint8) {
+ return 6;
+ }
+
+ function mint(address to, uint256 amount) external onlyOwner {
+ _mint(to, amount);
+ }
+}
diff --git a/test/MockUSDC.t.sol b/test/MockUSDC.t.sol
new file mode 100644
index 0000000..028c1a0
--- /dev/null
+++ b/test/MockUSDC.t.sol
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.35;
+
+import {Test} from "forge-std/Test.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+import {MockUSDC} from "../src/MockUSDC.sol";
+
+contract MockUSDCTest is Test {
+ MockUSDC internal token;
+ address internal constant STRANGER = address(0xBEEF);
+ address internal constant RECIPIENT = address(0xCAFE);
+ address internal constant SPENDER = address(0xD00D);
+
+ function setUp() public {
+ token = new MockUSDC(address(this));
+ }
+
+ function testMetadataUsesSixDecimalMockUSDC() public view {
+ assertEq(token.name(), "Mock USD Coin");
+ assertEq(token.symbol(), "mUSDC");
+ assertEq(token.decimals(), 6);
+ }
+
+ function testInitialSupplyIsZero() public view {
+ assertEq(token.totalSupply(), 0);
+ }
+
+ function testMintMintsToRecipientWhenCalledByOwner() public {
+ token.mint(RECIPIENT, 1_250_000);
+
+ assertEq(token.totalSupply(), 1_250_000);
+ assertEq(token.balanceOf(RECIPIENT), 1_250_000);
+ }
+
+ function testMintRevertsWhenCalledByNonOwner() public {
+ vm.prank(STRANGER);
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, STRANGER));
+ token.mint(RECIPIENT, 1);
+ }
+
+ function testTransferMovesMintedBalance() public {
+ token.mint(address(this), 1_250_000);
+
+ token.transfer(RECIPIENT, 250_000);
+
+ assertEq(token.balanceOf(address(this)), 1_000_000);
+ assertEq(token.balanceOf(RECIPIENT), 250_000);
+ }
+
+ function testApproveSetsAllowanceForSpender() public {
+ token.approve(SPENDER, 750_000);
+
+ assertEq(token.allowance(address(this), SPENDER), 750_000);
+ }
+
+ function testConstructorRevertsForZeroInitialOwner() public {
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, address(0)));
+ new MockUSDC(address(0));
+ }
+}