the receipt IS the position. — every open mints an ERC721 · transfer it, trade it on OpenSea, use it as collateral, gift it.
🪙 trade positions on OpenSea
your 15× ETH long up 18% is a token. someone can offer you 90% of your current PnL right now to take over the rest of the trade — you exit without closing, they take on the future risk + the closing fee.
🏛 compose with DAOs & multisigs
a DAO treasury can hold a perp position the same way it holds USDC. a Safe multisig can custody a directional bet. the position has an address-agnostic identity.
📊 tax accounting becomes trivial
each receipt is one trade, one mint, one burn. your tax software reads the ERC721 transfer log instead of guessing from contract events.
🎁 gift / inherit / split
transfer a winning position to a friend. split a large notional across multiple receipts. inherit a position from a multisig that's being wound down.
🖼 live artwork = free marketing
every receipt's tokenURI renders an animated SVG with live entry / leverage / liq / PnL. every screenshot on Twitter advertises HELIX. organic loop built into the primitive.
🔗 use as collateral elsewhere
once a receipt is a token, every lending protocol that takes ERC721s (Blur, Arcade, NFTfi) can accept it. open a perp, borrow against the perp, recycle the margin. capital efficiency unlock.
open()
you call open(marketId, side, margin, notional). the contract opens the position AND mints an ERC721 to msg.sender. tokenId = positionId.
uint256 tokenId = _mint(msg.sender, positionId);
tokenURI(id) renders live
tokenURI returns an inline SVG showing entry, mark, leverage, liq price and current PnL. updates every block because it reads live state.
return Base64.encode(svg(positionId));
transfer = position handoff
transferring the NFT updates positionOwner[id]. the recipient inherits margin + unrealized PnL + future risk. no closing fee paid.
_transfer(from, to, id);
positionOwner[id] = to;
close() burns
the current owner closes the position. realized PnL goes to the owner, NFT is burned. no orphan receipts.
_burn(id);
USDC.transfer(owner, realizedPnl);
// 1. inherit ERC721 (no upgrade key, no governance — same as HELIX promise) contract HelixPerps is IHooks, ERC721 { // 2. mint inside open() function open(bytes32 marketId, uint8 side, uint256 margin, uint256 notional) external returns (uint256 tokenId) { // ... existing position-open logic ... tokenId = ++_nextTokenId; _mint(msg.sender, tokenId); positionOf[tokenId] = Position({...}); } // 3. tokenURI renders live SVG function tokenURI(uint256 tokenId) public view override returns (string memory) { return _renderReceipt(tokenId); } // 4. transfer hook to keep margin → owner mapping in sync function _update(address to, uint256 tokenId, address auth) internal override returns (address) { address from = super._update(to, tokenId, auth); if (from != address(0) && to != address(0)) { positionOf[tokenId].trader = to; emit PositionTransferred(tokenId, from, to); } return from; } // 5. close burns function close(uint256 tokenId, uint16 pctBps) external { require(ownerOf(tokenId) == msg.sender, "not owner"); // ... existing close logic ... if (pctBps == 10_000) _burn(tokenId); } }
_renderReceipt (Base64-encoded SVG) — no IPFS, no centralized renderer, no points-of-failure beyond the contract itself. matches the rest of the HELIX promise.