# VotingEscrow



## Overview [#overview]

`VotingEscrow` holds locked SPECTRA and represents each position as an ERC-721 NFT. The Base veSPECTRA contract is [0x6a89228055C7C28430692E342F149f37462B478B](https://basescan.org/address/0x6a89228055C7C28430692E342F149f37462B478B). A wallet can own multiple lock NFTs; contract calls identify an individual lock by its `uint256` token ID.

Use the [locking guide](/docs/guides/locking-spectra-for-vespectra) for the app flow. The methods below cover common lock integrations. [RewardsDistributor](/docs/technical-reference/contract-functions/rewardsdistributor) handles recorded rebases, while [Voter](/docs/technical-reference/contract-functions/voter) handles pool voting and voting-reward claims.

## Create a lock [#create-a-lock]

```solidity
function token() external view returns (address)
function createLock(uint256 _value, uint256 _lockDuration) external returns (uint256)
function createLockFor(uint256 _value, uint256 _lockDuration, address _to) external returns (uint256)
```

Approve the escrow to transfer `_value` units of `token()` before creating a lock. Amounts use the ERC-20 token's smallest units. `_lockDuration` is a duration in seconds, not an absolute timestamp. The contract rounds `block.timestamp + _lockDuration` down to a week boundary; the result must be in the future and no more than four years ahead.

`createLock` mints the NFT to the caller. `createLockFor` mints it to `_to` while taking the SPECTRA from the caller. Both return the new NFT token ID.

## Add tokens or extend a lock [#add-tokens-or-extend-a-lock]

```solidity
function depositFor(uint256 _tokenId, uint256 _value) external
function increaseAmount(uint256 _tokenId, uint256 _value) external
function increaseUnlockTime(uint256 _tokenId, uint256 _lockDuration) external
```

`depositFor` lets a caller add their SPECTRA to an existing, unexpired or permanent lock without changing its owner or unlock time. Adding to a managed NFT through `depositFor` is restricted to its distributor; an NFT deposited into a managed lock cannot receive a direct deposit.

`increaseAmount` requires the NFT owner or an approved operator. Both deposit methods require an ERC-20 allowance and a positive amount.

`increaseUnlockTime` requires the owner or an approved operator and an unexpired, nonpermanent normal lock. The new unlock time is calculated from the current timestamp and rounded down to a week boundary. It must exceed the existing unlock time and remain within the four-year maximum.

## Read ownership, lock state, and voting power [#read-ownership-lock-state-and-voting-power]

```solidity
function ownerOf(uint256 _tokenId) external view returns (address)
function balanceOf(address _owner) external view returns (uint256)
function ownerToNFTokenIdList(address _owner, uint256 _index) external view returns (uint256)
function locked(uint256 _tokenId) external view returns (LockedBalance memory)
function balanceOfNFT(uint256 _tokenId) external view returns (uint256)
function balanceOfNFTAt(uint256 _tokenId, uint256 _t) external view returns (uint256)
function totalSupply() external view returns (uint256)
function totalSupplyAt(uint256 _timestamp) external view returns (uint256)
```

The `LockedBalance` tuple contains `int128 amount`, `uint256 end`, and `bool isPermanent`. Timed locks lose voting power as their unlock time approaches; permanent locks do not decay.

`balanceOf(address)` counts owned NFTs. It is not the wallet's voting power. Enumerate those NFTs with `ownerToNFTokenIdList` using indices from zero to `balanceOf(owner) - 1`, or use the [voting-power aggregator](/docs/technical-reference/contract-functions/votingescrowaggregator) for a weighted total across registered escrows.

`balanceOfNFT` returns the current voting power of one NFT and returns zero in the block of an ownership change. `balanceOfNFTAt` reads its voting power at a Unix timestamp. The `totalSupply` methods return aggregate voting power, rather than an NFT count or the amount of SPECTRA locked.

## Withdraw an expired lock [#withdraw-an-expired-lock]

```solidity
function withdraw(uint256 _tokenId) external
```

Requires the NFT owner or an approved operator, a normal lock whose unlock time has passed, and no active pool-voting flag. A permanent lock cannot be withdrawn directly. Withdrawal transfers the locked SPECTRA to the caller and burns the NFT; claim its recorded rebases and voting rewards before burning the position.

## Permanent locks [#permanent-locks]

```solidity
function lockPermanent(uint256 _tokenId) external
function unlockPermanent(uint256 _tokenId) external
```

The owner or an approved operator can make an unexpired normal lock permanent. Its voting power then stops decaying. `unlockPermanent` starts a new timed lock with an unlock time four years from the call, rounded down to a week boundary; it does not make the SPECTRA immediately withdrawable. Unlocking also removes delegation and requires the NFT to have no active pool-voting flag.
