// SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts ^5.6.0 pragma solidity ^0.8.27; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; /// @custom:security-contact security@example.com contract WCNPY is ERC20, Ownable2Step, Pausable { error ZeroAmount(); error ZeroCanopyAddress(); error RenounceOwnershipDisabled(); constructor(address initialOwner) ERC20("CNPY", "CNPY") Ownable(initialOwner) {} function pause() public onlyOwner { _pause(); } function unpause() public onlyOwner { _unpause(); } /// @notice Ownership renunciation is disabled to avoid bricking bridge administration. function renounceOwnership() public view override onlyOwner { revert RenounceOwnershipDisabled(); } /// @notice Mint wCNPY to a user after the bridge relayer confirms a CNPY deposit on Canopy. /// @dev Callable only by the owner. Reverts when paused. /// @param to Recipient on wCNPY. /// @param amount Amount of wCNPY to mint. function mint(address to, uint256 amount) external onlyOwner whenNotPaused { if (amount == 0) revert ZeroAmount(); _mint(to, amount); } /// @notice Returns the number of decimals used for wCNPY balances. function decimals() public pure override returns (uint8) { return 6; } /// @notice Burn wCNPY and request release of native CNPY on Canopy. /// @dev Callable by any holder. Burns from msg.sender and emits a redeem event. /// Reverts when paused. /// @param amount Amount of wCNPY to burn. /// @param canopyAddress Destination Canopy address to receive released CNPY. function redeem(uint256 amount, address canopyAddress) external whenNotPaused { if (amount == 0) revert ZeroAmount(); if (canopyAddress == address(0)) revert ZeroCanopyAddress(); _burn(msg.sender, amount); emit RedeemRequested(msg.sender, amount, canopyAddress); } /// @notice Emitted when a user burns wCNPY to redeem native CNPY on Canopy. /// @param from wCNPY address that burned the tokens. /// @param amount Amount burned. /// @param canopyAddress Destination Canopy address. event RedeemRequested(address indexed from, uint256 amount, address indexed canopyAddress); }