Skip to content

PT721 (contract)

ERC-721 principal token for arbitrary-maturity positions. Singleton — one contract holds all PT721 positions for the Venice path.

contracts/src/platforms/venice/PT721.sol

Inherits

ERC721 (OpenZeppelin), Ownable2Step

Constants

NameValue
ROLE_DELAY7 days

Immutables

NameType
underlyingaddress (kDIEM)

Mutable state

NamePurpose
lockerSole authorized caller of deposit
pendingLocker, lockerEffectiveAtRotation buffer
nextTokenIdMonotonically incremented (starts at 1)
_positions[tokenId]Position { principal: uint128, maturity: uint64 }

Wiring setters

solidity
function setLockerInitial(address locker_) external onlyOwner;
function proposeLocker(address locker_) external onlyOwner;
function activateLocker() external;
function cancelLockerChange() external onlyOwner;

Deposit (mint)

solidity
function deposit(uint256 amount, uint64 maturity, address recipient)
    external onlyLocker
    returns (uint256 tokenId);
  • amount > 0, recipient != 0, maturity > block.timestamp.
  • Pulls amount underlying from msg.sender.
  • tokenId = nextTokenId++ (starts at 1).
  • Stores _positions[tokenId] = (uint128(amount), maturity).
  • Mints tokenId to recipient.

Redeem (burn)

solidity
function redeem(uint256 tokenId, address to, address from)
    external
    returns (uint256 underlyingAmount);
  • from == ownerOf(tokenId) required (WrongOwner).
  • msg.sender must be owner OR approved for tokenId OR approved-for-all by owner.
  • block.timestamp >= _positions[tokenId].maturity (NotMatured).
  • Reads principal, deletes position, burns NFT, transfers principal underlying to to.

Views

solidity
function position(uint256 tokenId)
    external view
    returns (uint256 principal, uint64 maturity);

Plus standard ERC-721 surface: ownerOf, balanceOf, approve, setApprovalForAll, getApproved, isApprovedForAll, transferFrom, safeTransferFrom, name, symbol, tokenURI (default null).

Events

Deposited(tokenId, payer, recipient, principal, maturity)
Redeemed(tokenId, owner, recipient, principal)
LockerActivated, LockerProposed, LockerChangeCancelled

Errors

ZeroAmount, ZeroAddress, MaturityInPast, NotMatured,
NotAuthorized, WrongOwner, NotLocker,
AlreadySet, NoPending, TooEarly

Position encoding

principal is uint128 (≤ 3.4 × 10^38 wei — sufficient for any realistic amount). maturity is uint64 (valid through year ~2554). Both packed into one storage slot.

Locker rotation

Unlike PT20 (which has immutable locker per-deploy), PT721's locker is mutable and singleton. Rotation via 7-day timelock affects all subsequent mints. Existing positions are unaffected (only their original owner can transfer, and only after-maturity redemption is needed to unwrap).

safeMint vs _mint

PT721 uses _mint (not _safeMint). Rationale: _safeMint calls onERC721Received on the recipient, allowing a receiver contract to revert and grief the Locker. Since this NFT is created by Locker as part of an atomic lockPT721, we don't want any external callback to be able to abort the whole transaction.

If the recipient is a contract that can't handle ERC-721, the position is still mintable but the owner contract just can't transferFrom it via safe paths. Owner can always burn-and-redeem at maturity.

No tokenURI / metadata

Default ERC-721 returns empty tokenURI. NFT marketplaces can read position(tokenId) directly for (principal, maturity) display. A future metadata service (on-chain SVG, off-chain JSON) can be wired without breaking the standard interface.

Released under the MIT License.