Shutdowns
A shutdown wraps the lifecycle for retiring a collection. Holders vote
their CollectionToken balance into a quorum gate; once quorum is reached
an admin executes by liquidating all remaining locker NFTs into a
SudoSwap pool, after which voters claim their proportional share of the
ETH yielded.
The full lifecycle has six FE-relevant entry-points; admin-only flows are flagged.
start
Anyone holding CollectionToken can start a shutdown if the collection
hasn’t been opted out via preventShutdown.
Prerequisites
_collectionis registered.CollectionShutdown.shutdownPrevented(_collection) == false.- No active shutdown in progress
(
ShutdownProcessAlreadyStartedotherwise). - Caller holds at least 1 wei of the relevant
CollectionToken(UserHoldsNoTokensotherwise). - Shutdown contract is not paused.
Code
import { collectionShutdownAbi } from "@nftx/contracts";
import { NFTX_CONTRACTS } from "@nftx/config";
await walletClient.writeContract({
address: NFTX_CONTRACTS[base.id].collectionShutdown,
abi: collectionShutdownAbi,
functionName: "start",
args: [collection],
});Subgraph
CollectionShutdownStarted(collection) →
CollectionShutdown { startedAt, startedBy, quorum, claimAvailable: 0 }.
vote / voteAndClaim
Locks the caller’s CollectionToken balance into the shutdown’s vote tally.
voteAndClaim is a gas-saving combo that votes and (if execute-ready)
also claims in one tx.
Prerequisites
- Shutdown is started but not executed.
- Caller has approved the shutdown contract for the entire balance they
want to vote, or has approved
type(uint256).maxonce.
Code
import { collectionShutdownAbi, collectionTokenAbi } from "@nftx/contracts";
await walletClient.writeContract({
address: collectionToken,
abi: collectionTokenAbi,
functionName: "approve",
args: [shutdown, balance],
});
await walletClient.writeContract({
address: shutdown,
abi: collectionShutdownAbi,
functionName: "vote",
args: [collection],
});Subgraph
CollectionShutdownVote(collection, voter, vote) →
CollectionShutdownVote { votes: + }. If quorum is reached:
CollectionShutdownQuorumReached(collection) →
CollectionShutdown.quorumReachedAt = now.
reclaimVote
Withdraws an outstanding shutdown vote and returns the locked
CollectionToken balance.
Prerequisites
- Caller has previously voted (
shutdownVoters > 0). - Quorum hasn’t been passed in a way that disallows reclaiming —
ShutdownQuorumHasPassedreverts when reclaim would push the tally below quorum but the shutdown has already been executed.
Code
await walletClient.writeContract({
address: shutdown,
abi: collectionShutdownAbi,
functionName: "reclaimVote",
args: [collection],
});Subgraph
CollectionShutdownVoteReclaim(collection, voter, vote) →
CollectionShutdownVote { votes: - }. If reclaim drops the tally below
quorum, quorumReachedAt is unset.
execute (admin)
Liquidates every remaining locker NFT for the collection into a SudoSwap
linear-curve pair and starts the claim window. Admin-only, gated by
ShutdownNotReachedQuorum.
Prerequisites
quorumReachedAt != 0.- All listing rows for the collection have been resolved
(
ListingsExistotherwise). tokenIdsis the complete locker inventory; the contract verifies it viaLocker.collectionTokenbalance bookkeeping.
Code
await walletClient.writeContract({
address: shutdown,
abi: collectionShutdownAbi,
functionName: "execute",
args: [collection, allLockerTokenIds],
});Subgraph
CollectionShutdownExecuted(collection, sweeperPool, tokenIds) →
CollectionShutdown { executedAt, executedBy, liquidationPool, liquidationPoolTokens }.
Subsequent CollectionShutdownTokenLiquidated events bump claimAvailable
as the SudoSwap pair drains.
claim
Once claimAvailable > 0, voters claim a proportional share of the
realised ETH. Either the holder calls it themselves, or anyone calls it on
behalf of _claimant (e.g. a keeper).
Prerequisites
- Shutdown is executed.
_claimanthas shutdown votes recorded.- The caller can pay the gas; the eth itself is paid to
_claimant.
Code
await walletClient.writeContract({
address: shutdown,
abi: collectionShutdownAbi,
functionName: "claim",
args: [collection, claimant],
});Subgraph
CollectionShutdownClaim(collection, claimant, tokenAmount, ethAmount) →
new CollectionShutdownClaim row.
cancel
Cancels an in-progress shutdown when the collection’s CollectionToken
supply has grown back above the quorum threshold (e.g. new mints arrived
post-vote).
Prerequisites
- Shutdown started but not executed.
CollectionToken.totalSupply >= quorum * SHUTDOWN_QUORUM_PERCENT / 100(InsufficientTotalSupplyToCancelotherwise).
Code
await walletClient.writeContract({
address: shutdown,
abi: collectionShutdownAbi,
functionName: "cancel",
args: [collection],
});Subgraph
CollectionShutdownCancelled(collection) →
CollectionShutdown row’s tracker fields reset; voters keep their balances
(reclaim individually).
preventShutdown (admin)
Locks a collection out of the shutdown lifecycle entirely (used for strategic collections that should never be retired).
Code
await walletClient.writeContract({
address: shutdown,
abi: collectionShutdownAbi,
functionName: "preventShutdown",
args: [collection, true],
});Subgraph
CollectionShutdownPrevention(collection, prevented) →
Collection.shutdownPrevented updates.
Combined view: a holder’s shutdown dashboard
query MyShutdownDashboard($chainId: Int!, $voter: String!) {
CollectionShutdown(
where: {
chainId: { _eq: $chainId }
votes: { voter: { _eq: $voter } }
}
order_by: { startedAt: desc }
) {
id
collection { collection name }
quorum
quorumReachedAt
executedAt
claimAvailable
votes(where: { voter: { _eq: $voter } }) { votes createdAt }
claims(where: { claimant: { _eq: $voter } }) {
tokensBurnt
ethReceived
claimedAt
}
}
}Failure modes (across the lifecycle)
| Error | When |
|---|---|
ShutdownPrevented | start while preventShutdown(_, true) |
ShutdownProcessAlreadyStarted / ShutdownProccessNotStarted | mismatched lifecycle |
ShutdownNotReachedQuorum | execute before quorum |
ShutdownQuorumHasPassed | reclaimVote after execute |
ShutdownNotExecuted | claim before execute |
NoTokensAvailableToClaim | claimant has no votes / already claimed |
NotAllTokensSold | claim before SudoSwap pair has drained |
ListingsExist | execute while liquid/dutch listings remain |
UserHoldsNoTokens | start from a wallet with zero CT |
InsufficientTotalSupplyToCancel | cancel when supply is still below the cancel threshold |
CollectionNotShutdownCompatible(_collection) | start for an opted-out collection (e.g. very large supply) |