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
3.7 KiB
Task 3: Establish the V1 proxy, initialization, views, and administration
Files:
- Create:
src/BankV1.sol - Create:
test/helpers/BankTestBase.sol - Create:
test/BankV1Admin.t.sol
Interfaces:
-
Produces
initialize(address,address),asset(),balanceOf(address),totalLiabilities(),contractVersion(),pause(),unpause(), and owner-authorized UUPS upgrades. -
Consumes
MockUSDCand OpenZeppelinUpgrades.deployUUPSProxy. -
Freezes V1 application storage as
_asset,_balances,_totalLiabilities, thenuint256[47] __gap. -
Create
BankTestBase.solwith deterministicowner,alice,bob, andstrangeraddresses frommakeAddr; deployMockUSDC; deploy the proxy with:
proxy = Upgrades.deployUUPSProxy(
"BankV1.sol:BankV1",
abi.encodeCall(BankV1.initialize, (address(token), owner))
);
bank = BankV1(proxy);
All bank calls in tests target proxy; retain implementation = Upgrades.getImplementationAddress(proxy) only for assertions.
-
Write failing admin tests for:
- initialized asset, owner, unpaused state, zero liabilities, and version
1; - initialization with zero asset;
- initialization with zero owner through
OwnableInvalidOwner; - proxy double initialization;
- direct implementation initialization;
- only owner can pause/unpause;
- views still work while paused;
_authorizeUpgraderejects a non-owner throughupgradeToAndCall;- implementation
proxiableUUID()is available directly while the proxy-context call reverts.
- initialized asset, owner, unpaused state, zero liabilities, and version
-
Run the focused suite and observe red:
npm_config_offline=true forge test --match-path test/BankV1Admin.t.sol -vvv --force
Expected red: BankV1 is missing.
- Implement the minimum V1 skeleton with these imports and inheritance order:
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
contract BankV1 is
Initializable,
UUPSUpgradeable,
OwnableUpgradeable,
PausableUpgradeable,
ReentrancyGuard
{
IERC20 internal _asset;
mapping(address account => uint256 balance) internal _balances;
uint256 internal _totalLiabilities;
uint256[47] private __gap;
}
Use error InvalidAsset(address asset);. The initializer checks the asset before calling only __Ownable_init(initialOwner) and __Pausable_init(). Initializable, UUPSUpgradeable, and ReentrancyGuard are stateless/shared in pinned OpenZeppelin 5.6.1 and have no initializer calls.
- Add the only permitted validator annotation and no other bypass:
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
-
Add
pause/unpausewithonlyOwner, simple views,contractVersion() public pure virtual returns (uint256), and_authorizeUpgrade(address) internal override onlyOwner {}. Do not add deposits or withdrawals yet. -
Run green verification and inspect storage:
forge fmt
npm_config_offline=true forge test --match-path test/BankV1Admin.t.sol -vvv --force
forge inspect BankV1 storage-layout
Expected: tests pass and the application fields appear in the frozen order. OpenZeppelin namespaced/stateless internals must not be mistaken for permission to reorder application fields.
- Commit:
git add src/BankV1.sol test/helpers/BankTestBase.sol test/BankV1Admin.t.sol
git commit -m "feat: establish UUPS bank V1"