Technical Documentation

FLOCK
EXCHANGE

A permissionless marketplace for veNFT positions. Flock Exchange lets veUP holders sell a locked position before it matures, and lets buyers acquire locked UP and voting power below its face value, all without ever handing custody of the NFT or the payment currency to the contracts until the exact moment a trade settles.

veUP Asset traded
Robinhood Chain Chain ID 4663
1% Protocol fee
UP / WETH / USDG Settlement currencies
Non-custodial Trust model
Section 01

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 — sell side Offers — buy side

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.

Only veUP is live today. A second market for veGIGA, GigaDex's equivalent vote-escrow NFT, is wired into the interface and will switch on once GigaDex's gauge system ships.
Section 02

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

┌───────────────────────────────────────────────────┐ │ FLOCK EXCHANGE │ │ Listings (ask side) + Offers (bid side) │ └───────────────────────────────────────────────────┘ │ │ ▼ ▼ VeUpMarketplace VeUpOffers × 3 createListing one deployed contract buyListing per settlement currency: cancelNftListings UP · WETH · USDG │ │ └────────────┬─────────────┘ ▼ VeUpLens reads locked amount + remaining duration from up.'s VotingEscrow contract │ ▼ up. veUP NFT (external, ERC-721)

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.

Section 03

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.

  • 01
    Fixed price: set slopeDuration to 0. The listing sells at exactly the Asking price for its entire life.
  • 02
    Dutch 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 of slopeDuration, then holds flat at Asking for fixedDuration after 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.

Section 04

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:

ParameterMeaning
budgetMaximum total amount of the offer's currency the buyer will spend across all fills.
priceMultiplierPrice per unit of locked UP, as a fixed-point fraction of 1e18. This is where the discount or premium is set.
lockedMin / lockedMaxThe range of locked UP a qualifying NFT must fall inside.
lockTimeMaxMaximum remaining lock duration accepted, in seconds. 0 accepts any duration, including permanent locks.
durationHow long the offer stays valid, capped at 60 days.
replaceOfferIdOptional. 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).

Offers are never pre-funded. Creating an offer only records intent on-chain, no currency is pulled or locked at that moment. The buyer's balance and allowance are only checked and drawn the instant a seller calls 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.

Section 05

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 typeBuyer paysSeller receivesFee recipient receives
ListingListing pricePrice × 99%Price × 1%
OfferOffer pricePrice × 99%Price × 1%
Section 06

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

ContractAddressPurpose
VeUpMarketplace0x78305A9e...Df3F17Listings — fixed price and Dutch decay
VeUpOffers (UP)0x4b24BD4D...6fF3201Standing offers settled in UP
VeUpOffers (WETH)0x52ae2127...875bdC6cStanding offers settled in WETH
VeUpOffers (USDG)0x88B777D7...b17894a2Standing offers settled in USDG
VeUpLens0x5E0f86E6...24207cB4Reads locked amount & remaining duration from up.'s VotingEscrow

Underlying assets

AssetAddressNotes
veUP (traded NFT)0x5d321dE3...4582B7B6up.'s VotingEscrow contract. External, not deployed or controlled by Ravenhood.
UP0x57C0E45c...50BB4F1up.'s governance token, 18 decimals
WETH0x0Bd7D308...41EAcAD73Robinhood Chain's canonical wrapped ETH, 18 decimals
USDG0x5fc5360D...716F1d168Global Dollar stablecoin, 6 decimals
Section 07

Security & Trust Assumptions

What is immutable

No Escrow No Upgradeability Fixed 1% Fee Rate Currency Fixed Per Offers Contract
  • 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 VeUpMarketplace and every VeUpOffers deployment. It cannot be changed by anyone.
  • A listing or offer can only ever be cancelled by the address that created it.
  • A VeUpOffers contract'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.

Section 08

FAQ

What is veUP?
veUP is up.'s vote-escrowed governance NFT. Locking UP tokens with up. mints a veUP position that carries the locked UP balance, a remaining lock duration, and voting power over up.'s gauges.
Do I need to unlock my veUP position before I can sell it?
No. Flock Exchange trades the veUP NFT itself. The buyer receives the NFT with its existing lock intact and simply becomes its new owner, they do not need to wait for the lock to expire either.
Why does a WETH or USDG listing's discount keep changing?
A veUP position's value is denominated in UP (its 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.
I see "Cancel Offer" on my own offer but no "Accept Offer", why?
An offer is a standing bid you placed. You cannot accept your own bid, only cancel it. Accepting an offer is an action for a seller who owns a qualifying veUP NFT and wants to sell into your offer.
Why does selling into an offer sometimes say the buyer is underfunded?
Offers are never pre-funded, see Section 4. If the offer creator's wallet balance or allowance has dropped below the offer's live price since it was posted, the interface flags it before you spend gas on a transaction that would revert.
What happens if a listing or offer isn't filled?
Every listing and offer expires automatically after at most 60 days. An expired listing simply cannot be bought anymore, an expired offer simply cannot be sold into anymore. Either can also be cancelled manually at any time before that.
Is veGIGA supported?
Not yet. The interface already has a market selector for veGIGA, GigaDex's equivalent vote-escrow NFT, and it will be enabled once GigaDex's gauge and voting system is live on-chain.
Where can I read the actual contract source?
Every address in Section 6 links to its verified source code on Blockscout.