Introduction
Flock Exchange is Ravenhood's secondary marketplace for veUP, the vote-escrowed governance NFT of up. (UPDex) on Robinhood Chain. Locking UP with up. mints a veUP position: an ERC-721 that carries a locked UP balance, a remaining lock duration, and voting power over up.'s gauges. That position is normally illiquid until its lock expires. Flock Exchange gives it a market.
The exchange is built as two independent order books that mirror each other's role, the same split used by established veNFT marketplaces on other vote-escrow ecosystems:
Listings are ask orders. A holder puts one specific veUP NFT up for sale at a price they choose. Offers are standing bid orders. A buyer posts a budget and a target discount that applies to any qualifying veUP NFT, not one specific token, so a seller can match into an existing offer instantly instead of waiting for a buyer to find their listing.
Every contract is permissionless and immutable. There is no admin function that can seize a listed NFT, redirect a buyer's payment, or freeze trading. The only privileged action anywhere in the system is choosing where the flat 1% protocol fee is paid, covered in Section 7.
How It Works
Custody
Flock Exchange never takes custody of anything. Creating a listing or an offer only ever grants an approval, never a transfer. A seller listing a veUP NFT grants setApprovalForAll on the veUP contract to VeUpMarketplace. A buyer creating an offer does not move any funds at all at creation time, a currency allowance is only checked and pulled the moment a sale actually executes. Nothing changes hands until the single transaction that completes a trade.
Two order books, one purpose
Settlement currencies
The interface supports three settlement currencies: UP, WETH, and USDG. Offers are currency-locked at the contract level, a separate VeUpOffers instance is deployed for each currency because the currency is set once, immutably, in its constructor. Listings are more flexible on-chain, createListing accepts any ERC-20 as its currency parameter, but the interface only prices and displays the same three currencies so every listing stays tradeable end to end through the UI.
Whichever currency is chosen is what actually moves. Because a veUP position's value is denominated in UP, a listing or offer priced in WETH or USDG will drift in real-time discount terms as the UP market price moves, since locked is a UP amount but payment is settled in a different asset. Pricing in UP keeps the discount fixed regardless of UP's own price action.
Listings
A listing is a single veUP NFT offered for sale by its owner through VeUpMarketplace.createListing, in a chosen currency, at a price the seller sets.
Fixed vs. Dutch decay
Every listing has a floor price, the Asking price, and can optionally decay down to it from an elevated starting price over a chosen window before holding flat. Passing a decay window of zero seconds makes it a plain fixed-price listing.
- 01Fixed price: set
slopeDurationto 0. The listing sells at exactly the Asking price for its entire life. - 02Dutch decay: the listing opens at
Asking × (1 + slopeMax)and glides down along a cubic ease-out curve to exactly the Asking price by the end ofslopeDuration, then holds flat at Asking forfixedDurationafter that. Buyers watching a decaying listing see the price fall fastest early and flatten out as it nears the floor.
Combined slopeDuration + fixedDuration cannot exceed 60 days, after which the listing simply expires and can no longer be bought.
Buying
buyListing(listingId) checks three things before moving anything: the listing has not already sold, it has not expired, and the seller has not invalidated it since it was created. If all three pass, the buyer's currency splits into seller proceeds (minus the 1% fee) and the fee itself, and the NFT transfers straight from seller to buyer in the same transaction.
Cancelling
There is no per-listing cancel call. Instead, every listing carries a snapshot of a per-seller, per-NFT nonce at creation time. Calling cancelNftListings(collection, nftId) bumps that nonce, which instantly invalidates every existing listing for that specific veUP token in one transaction, no matter how many were created. Creating a new listing for the same NFT does the same thing implicitly, since it also bumps the nonce, so relisting automatically retires the old listing without a separate cancel step.
Offers
An offer is a standing "want to buy" order. Unlike a listing, it is not tied to a specific veUP NFT, it is a budget and a price formula that any qualifying position can be sold into.
Creating an offer
createOffer(budget, priceMultiplier, lockedMin, lockedMax, lockTimeMax, duration, replaceOfferId) takes:
| Parameter | Meaning |
|---|---|
budget | Maximum total amount of the offer's currency the buyer will spend across all fills. |
priceMultiplier | Price per unit of locked UP, as a fixed-point fraction of 1e18. This is where the discount or premium is set. |
lockedMin / lockedMax | The range of locked UP a qualifying NFT must fall inside. |
lockTimeMax | Maximum remaining lock duration accepted, in seconds. 0 accepts any duration, including permanent locks. |
duration | How long the offer stays valid, capped at 60 days. |
replaceOfferId | Optional. If set, that offer is cancelled and this new one created in the same transaction, used to edit an existing offer without two separate calls. |
The price for any given NFT is always locked × priceMultiplier / 1e18, read live through offerPrice(offerId, nftId).
sellNFT. A published offer can be technically valid on paper but temporarily unfillable if the buyer's wallet balance or allowance has since dropped below the live price, the interface checks this before letting a seller submit, to avoid a transaction that would simply revert.
Partial fills
One sellNFT call only spends what that one NFT costs. The remaining budget stays open afterward for the next matching NFT, so a single offer can fill against multiple sellers over its lifetime until the budget is exhausted or it expires.
Accepting an offer
Any holder of a qualifying veUP NFT can call sellNFT(offerId, nftId) once they have approved the matching currency's VeUpOffers contract on their NFT. The call verifies the offer has not expired, the position's locked amount and lock duration fall inside the offer's range, and the resulting price does not exceed the offer's remaining budget, then settles atomically: currency moves from the buyer to the seller and the fee recipient, and the NFT moves from seller to buyer.
Cancelling
removeOffer(offerId), callable only by the offer's creator, sets its budget to zero, immediately closing it to any further fills.
Fees
Flock Exchange charges a flat 1% protocol fee (FEE = 0.01 ether, a hardcoded constant) on every completed trade, on both Listings and Offers. There are no other fees anywhere in the system.
The fee is taken out of the seller's proceeds. A buyer always pays exactly the price shown in the interface, price minus fee is sent to the seller, and the fee itself is sent to the fee recipient in the same transaction.
| Trade type | Buyer pays | Seller receives | Fee recipient receives |
|---|---|---|---|
| Listing | Listing price | Price × 99% | Price × 1% |
| Offer | Offer price | Price × 99% | Price × 1% |
Contract Addresses
All contracts are deployed and verified on Robinhood Chain mainnet, chain ID 4663. Every address below links to its verified source on Blockscout.
Flock Exchange contracts
| Contract | Address | Purpose |
|---|---|---|
| VeUpMarketplace | 0x78305A9e...Df3F17 | Listings — fixed price and Dutch decay |
| VeUpOffers (UP) | 0x4b24BD4D...6fF3201 | Standing offers settled in UP |
| VeUpOffers (WETH) | 0x52ae2127...875bdC6c | Standing offers settled in WETH |
| VeUpOffers (USDG) | 0x88B777D7...b17894a2 | Standing offers settled in USDG |
| VeUpLens | 0x5E0f86E6...24207cB4 | Reads locked amount & remaining duration from up.'s VotingEscrow |
Underlying assets
| Asset | Address | Notes |
|---|---|---|
| veUP (traded NFT) | 0x5d321dE3...4582B7B6 | up.'s VotingEscrow contract. External, not deployed or controlled by Ravenhood. |
| UP | 0x57C0E45c...50BB4F1 | up.'s governance token, 18 decimals |
| WETH | 0x0Bd7D308...41EAcAD73 | Robinhood Chain's canonical wrapped ETH, 18 decimals |
| USDG | 0x5fc5360D...716F1d168 | Global Dollar stablecoin, 6 decimals |
Security & Trust Assumptions
What is immutable
- ✓No contract ever holds a user's veUP NFT or currency balance, only an approval. There is nothing to seize or freeze.
- ✓The 1% fee rate is a hardcoded constant on both
VeUpMarketplaceand everyVeUpOffersdeployment. It cannot be changed by anyone. - ✓A listing or offer can only ever be cancelled by the address that created it.
- ✓A
VeUpOfferscontract's settlement currency is set once, in its constructor, and cannot be changed.
What the fee owner controls
VeUpMarketplace.feeRecipient and each VeUpOffers.feeReceiver determine only where the 1% fee is paid, they have no other privilege in either contract. Right now both are set to a single address, 0x097ba31b7ACfFd75B909fc7BEf2e55424d2dAcdc, not a multisig. A compromised key could redirect future fee revenue, but it cannot touch a listed NFT, an offer's budget, or reach into any trader's wallet, since none of those are ever held by the contracts in the first place.
External dependency
The veUP NFT itself is entirely outside Ravenhood's control, it is up.'s own contract. Flock Exchange only ever reads its locked balance and remaining duration through VeUpLens, and calls its standard transferFrom once a trade has cleared every check. Any issue in up.'s own VotingEscrow contract is outside Flock Exchange's scope.
FAQ
locked balance). If the listing or offer is priced in a different currency, the effective discount moves with the live UP market price. Pricing in UP keeps the discount fixed.