npm package: @niftory/solidity-contracts
Description: Niftory Solidity Contracts
Latest version: 0.0.2

# Niftory Smart Contracts for EVM Compatible Blockchains

This repository hosts all smart contracts (and related scripts, tests, etc.) for Niftory, an NFT management platform. Niftory NFTs are ERC1155 compatible.

Note: This README is autogenerated. Please modify docs from within the `docs` folder and run `yarn build` to regenerate.

## Usage

In order to use the contract, just extend one of the contracts from the `impl` folder. Currently, there is only `NiftoryStandardERC1155`.

Typed APIs, generated with typechain, can be accessed via `src/bindings`. [`NiftoryStandardERC1155Interface` from here](https://github.com/Niftory/niftory-solidity/blob/main/src/bindings/contracts/NiftoryStandardERC1155.ts) is the best place to see the full accessible API in one place.

## Overview

Each uniquely identifiable ERC1155 token maps to a unique `set` (via `setId`) and `template` (via `templateId`) pair. The IERC1155 token `id` (e.g. for the `uri` function) can be expressed as `(setId << 64) + templateId`, as `setId` and `templateId` are both 64 bits each.

A contract can have multiple `set`s, and each `set` can have multiple `templates`. Indexing for both starts at `1` (so there is no `set` with `setId=0` or `template` with `templateId=0` in any set).

Each `template` maps to exactly one `uri`, which can be modified by an Admin or SetAdmin (see next section). `template`s also keep track of how many NFTs have been minted for that template and how many are allowed to minted (`maxMint`). A `template` with a `maxMint` of 0 allows unlimited minting. A `maxMint` may be set later, however.

`set`s can be locked to prevent more templates from being added, and `templates` can have metadata modification and minting locked separately.

`set`s allow Admins to delegate control to a collection of `template`s to a SetAdmin, allowing the SetAdmin to lock and mint from `set`s it owns.

## Roles and Actions

This contract uses a very simple RBAC style management with three different roles, as below.

### Admin

An Admin is the superuser of the contract. An Admin can

- Assign (or revoke) more Admins
- Assign (or revoke) SetAdmins
- Create & lock sets
- Add templates to any set
- Mint from any mintable template
- Change the metadata for any template
- Modify the maximum mint amount for any template
- Lock metadata and minting for any template
- Modify the global default URI

### Set Admin

A SetAdmin is an adminstrator of `set`s it has been given power over, either from an Admin or another previous SetAdmin. A SetAdmin can

- Assign (or revoke) SetAdmins from the sets it owns
- Lock sets it owns
- Add templates to sets it owns
- Mint from mintable templates of sets it owns
- Change the metadata of templates of sets it owns
- Modify the maximum mint amount for templates of sets it owns
- Lock metadata and minting for templates of sets it owns

### Collector

`Collector` is another way of saying "no role" and can be anyone with an account on the blockchain. A `Collector` can

- Hold and transfer tokens
- Approve another account to manage tokens
- Burn tokens

All the above functionality is provided by OpenZeppelin's ERC1155 implementation.

## Architecture

There are three extensions that can be used to power this token, which all assume that an ERC1155 token uses the `set`/`template` scheme mentioned above.

The main `NiftoryStandardERC1155` uses OpenZeppelin's `ERC1155Burnable.sol` as a base. There are three custom extensions used, which can be found in `contracts/extensions`

- `UriManager` manages URI's for templates and allows modification. If the URI is blank, it will use the default `_defaultUri`.
- `RoleManager` manages the Admin and SetAdmin based ownership. `RoleManager` maps an `(address, setId)` pair to a boolean value indicating whether `address` is a SetAdmin of set `setId`. The 0-th bit is used to indicate an Admin. This extension uses a custom library (`lib/AddressIndexedBitmap.sol`) in order to tightly pack the boolean role indicator
- `TemplateManager` is broken into two parts - the `SetManager` and the deriving `TemplateManager`. The `SetManager` stores `Set`s in a `setId => Set` map. A `Set` needs to keep track of how many templates it contains, and also allows locking to stop more templates from being added. `TemplateManager` manages each `Template`, keeping track of what the max mint is, how many NFTs have been minted from it, whether metadata can still be modified, and whether minting is still unlocked.

There are also two libraries in the `lib` folder - `AddressedIndexedBitmap.sol` and `TemplateBasedId.sol`. `AddressedIndexedBitmap` was already mentioned above, but `TemplateBasedId` provides a 1-1 mapping from `(uint64 setId, uint64 templateId) <=> uint256 id`), in order to adhere to `IERC1155`.

## Development

- `yarn` - install all node dependencies
- `yarn clean` - remove all hardhat generated files
- `yarn compile` - compile Solidity files and generate docs
- `yarn build` - clean and compile
- `yarn test` - build and run tests (and show gas usage information)
- `yarn coverage` - generate coverage reports

# Solidity API

## AddressIndexedBitmap

A bitmap that can be efficiently indexed by an (address, uint64)
pair.

_Best if used as a decorator for a mapping of (uint256 => uint256)_

### getBit

```solidity
function getBit(mapping(uint256 => uint256) bitmap, address addr, uint64 index) internal view returns (bool)
```

get the bit at position `index` for `addr` in `bitmap`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| bitmap | mapping(uint256 &#x3D;&gt; uint256) | the underlying bitmap - target of decorator |
| addr | address | the address to index into the bitmap |
| index | uint64 | the bit to get |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | the bit at position `index` for `addr` in `bitmap` |

### setBit

```solidity
function setBit(mapping(uint256 => uint256) bitmap, address addr, uint64 index) internal
```

set the bit at position `index` for `addr` in `bitmap`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| bitmap | mapping(uint256 &#x3D;&gt; uint256) | the underlying bitmap - target of decorator |
| addr | address | the address to index into the bitmap |
| index | uint64 | the bit to set |

### clearBit

```solidity
function clearBit(mapping(uint256 => uint256) bitmap, address addr, uint64 index) internal
```

clear the bit at position `index` for `addr` in `bitmap`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| bitmap | mapping(uint256 &#x3D;&gt; uint256) | the underlying bitmap - target of decorator |
| addr | address | the address to index into the bitmap |
| index | uint64 | the bit to clear |

### _wordIndex

```solidity
function _wordIndex(address addr, uint64 index) private pure returns (uint256)
```

get the word index for `addr` and `index`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addr | address | the address part of the bitmap key |
| index | uint64 | the index part of the bitmap key |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint256 | essentially `addr` << 64 + `index`/256 |

## NiftoryStandardERC1155

This is an implementation of ERC1155. It uses `ERC1155Burnable` from
OpenZeppelin as a base. This contract uses three custom extensions.
`TemplateManager` provides data structures to organize our contract into
`Set`s and `Template`s. A contract can have multiple `Set`s, and each `Set`
can have multiple `Template`s. Each unique token 1-1 maps with a `Template`.
`UriManager` manages the URIs for each `Template`, or provides a default
if a particular token doesn't have a URI set.
`RoleManager` helps provide gated access to mutating functions to Admins,
who have full control over all functions, and Set Admins, who have access
to mutate `Set`s that they have been privileged for.

### AdminsAssigned

```solidity
event AdminsAssigned(uint64 setId, address[] admins)
```

### AdminsRevoked

```solidity
event AdminsRevoked(uint64 setId, address[] admins)
```

### SetsAdded

```solidity
event SetsAdded(uint64[] setIds)
```

### SetsLocked

```solidity
event SetsLocked(uint64[] setIds)
```

### TemplatesAdded

```solidity
event TemplatesAdded(uint64 setId, uint64[] templateIds)
```

### TemplatesMetadataLocked

```solidity
event TemplatesMetadataLocked(uint64 setId, uint64[] templateIds)
```

### SetMetadataLocked

```solidity
event SetMetadataLocked(uint64 setId)
```

### TemplatesMaxMintModified

```solidity
event TemplatesMaxMintModified(uint64 setId, uint64[] templateIds, uint64[] newMaxes)
```

### TemplatesMintingLocked

```solidity
event TemplatesMintingLocked(uint64 setId, uint64[] templateIds)
```

### SetMintingLocked

```solidity
event SetMintingLocked(uint64 setId)
```

### constructor

```solidity
constructor(string defaultUri) public
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| defaultUri | string | default URI of tokens without their own URI |

### uri

```solidity
function uri(uint256 tokenId) public view returns (string)
```

_See {IERC1155MetadataURI-uri}.

This implementation returns the same URI for *all* token types. It relies
on the token type ID substitution mechanism
https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].

Clients calling this function must replace the `\{id\}` substring with the
actual token type ID._

### modifyDefaultUri

```solidity
function modifyDefaultUri(string newDefaultUri) external
```

modify the default URI if the caller `isAdmin`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| newDefaultUri | string | new default URI |

### assignAdmins

```solidity
function assignAdmins(address[] addrs) external
```

assign all `addrs` as a new Admins, only if caller `isAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addrs | address[] | the addresses to assign as Admins |

### revokeAdmins

```solidity
function revokeAdmins(address[] addrs) external
```

revoke all `addrs`'s Admin roles, only if caller `isAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addrs | address[] | the addresses to revoke as Admins |

### assignSetAdmins

```solidity
function assignSetAdmins(uint64 setId, address[] addrs) external
```

assign all `addrs` as new SetAdmins for the set with id `setId`,
only if caller `isSetAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to assign new SetAdmins to |
| addrs | address[] | the addresses to assign as SetAdmins |

### revokeSetAdmins

```solidity
function revokeSetAdmins(uint64 setId, address[] addrs) external
```

revoke all `addrs`'s SetAdmin roles for the set with id `setId`,
only if caller `isSetAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to revoke SetAdmins from |
| addrs | address[] | the addresses to revoke as SetAdmins |

### addSets

```solidity
function addSets(uint64 numNewSets) external
```

add new sets if caller `isAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| numNewSets | uint64 | number of sets to add |

### _lockSet

```solidity
function _lockSet(uint64 setId) internal
```

lock a set

_overriden to check if caller is `Set` Admin of `setId`_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

### lockSets

```solidity
function lockSets(uint64[] setIds) external
```

lock multiple sets if caller `isAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setIds | uint64[] | set IDs |

### _modifyUri

```solidity
function _modifyUri(uint64 setId, uint64 templateId, string newUri) internal
```

modify the URI for a token

_overriden to check `ifTemplateMetadataUnlocked`_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |
| newUri | string | new URI |

### addTemplates

```solidity
function addTemplates(uint64 setId, uint64 numNewTemplates, uint64[] maxMints, string[] uris) external
```

add templates to a set and add their URIs if caller is `Set`
admin

_new templates are given an ID based on the number of templates in
set `setId`_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| numNewTemplates | uint64 | number of new templates to add |
| maxMints | uint64[] | maximum number of NFTs that can be minted from each template |
| uris | string[] | URIs for each template |

### modifyMetadataForTemplates

```solidity
function modifyMetadataForTemplates(uint64 setId, uint64[] templateIds, string[] newUris) external
```

modify the URIs for a given list of templates within a set, only
if caller is `Set` Admin of `Set` `setId`.

_requires `templateIds.length == newUris.length` as they map 1-1_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | list of template IDs |
| newUris | string[] | list of new URIs |

### lockMetadataForTemplates

```solidity
function lockMetadataForTemplates(uint64 setId, uint64[] templateIds) external
```

lock metadata for multiple `Template`s within a set, only if
caller is `Set` Admin of `Set` `setId`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |

### lockMetadataForSet

```solidity
function lockMetadataForSet(uint64 setId) external
```

lock metadata for all `Template`s within a set, only if caller
is `Set` Admin of `Set` `setId`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

### modifyMaxMints

```solidity
function modifyMaxMints(uint64 setId, uint64[] templateIds, uint64[] newMaxes) external
```

modify the maximum number of NFTs that can be minted from
multiple `Template`s, if not already set, only if caller is `Set` Admin
of `Set` `setId`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |
| newMaxes | uint64[] | new maximum number of NFTs that can be minted from |

### mintFromTemplates

```solidity
function mintFromTemplates(address to, uint64 setId, uint64[] templateIds, uint64[] amounts) external
```

bulk mint NFTs for multiple `Template`s, if the `Template`s are
not locked and no max mints will be surpassed, only if caller is `Set`
Admin of `Set` `setId`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| to | address | address to mint to |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |
| amounts | uint64[] | number of NFTs to register |

### lockMintingForTemplates

```solidity
function lockMintingForTemplates(uint64 setId, uint64[] templateIds) external
```

lock minting for multiple `Template`s within a set, only if
caller is `Set` Admin of `Set` `setId`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |

### lockMintingForSet

```solidity
function lockMintingForSet(uint64 setId) external
```

lock minting for all `Template`s within a set, only if caller
is `Set` Admin of `Set` `setId`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

## RoleManager

A helper that manages roles for an NFT implementation. Roles include
Admin and SetAdmin. An Admin manages an entire contract, while a
SetAdmin can manage specific sets.

_implemented using `AddressIndexedBitmap`. `bitmap[0]` respresents
whether an address is an Admin, while `bitmap[setId]` (where `setId` >= 1)
represents whether an address is a SetAdmin for the set with id `setId`._

### _ADMIN_BIT

```solidity
uint64 _ADMIN_BIT
```

_the special `ADMIN_BIT` refers to the 0'th bit in the bitmap_

### _roles

```solidity
mapping(uint256 => uint256) _roles
```

_the underlying bitmap from (address, setId) => bool_

### isAdmin

```solidity
function isAdmin(address addr) public view returns (bool)
```

check whether `addr` is an Admin

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addr | address | the address to check |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if `addr` is an Admin |

### isSetAdmin

```solidity
function isSetAdmin(uint64 setId, address addr) public view returns (bool)
```

check whether `addr` is a SetAdmin for the set with id `setId`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to check |
| addr | address | the address to check |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if `addr` is a SetAdmin for the set with id `setId` |

### _assignAdmin

```solidity
function _assignAdmin(address addr) internal virtual
```

assign `addr` as a new Admin

_this should not be exposed externally. Override this just to add
restrictions. Use `_assignAdmins` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addr | address | the address to assign as an Admin |

### _assignAdmins

```solidity
function _assignAdmins(address[] addrs) internal
```

assign all `addrs` as a new Admins, only if caller `isAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addrs | address[] | the addresses to assign as Admins |

### _revokeAdmin

```solidity
function _revokeAdmin(address addr) internal virtual
```

revoke `addr`'s Admin role

_this should not be exposed externally. Override this just to add
restrictions. Use `_revokeAdmins` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addr | address | the address to revoke as Admin |

### _revokeAdmins

```solidity
function _revokeAdmins(address[] addrs) internal
```

revoke all `addrs`'s Admin roles, only if caller `isAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| addrs | address[] | the addresses to revoke as Admins |

### _assignSetAdmin

```solidity
function _assignSetAdmin(uint64 setId, address addr) internal virtual
```

assign `addr` as a new SetAdmin for the set with id `setId`

_this should not be exposed externally. Override this just to add
restrictions. Use `_assignSetAdmins` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to assign a new SetAdmin |
| addr | address | the address to assign as a SetAdmin |

### _assignSetAdmins

```solidity
function _assignSetAdmins(uint64 setId, address[] addrs) internal
```

assign all `addrs` as new SetAdmins for the set with id `setId`,
only if caller `isSetAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to assign new SetAdmins to |
| addrs | address[] | the addresses to assign as SetAdmins |

### _revokeSetAdmin

```solidity
function _revokeSetAdmin(uint64 setId, address addr) internal virtual
```

revoke `addr`'s SetAdmin role for the set with id `setId`

_this should not be exposed externally. Override this just to add
restrictions. Use `_revokeSetAdmins` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to revoke a SetAdmin from |
| addr | address | the address to revoke as a SetAdmin |

### _revokeSetAdmins

```solidity
function _revokeSetAdmins(uint64 setId, address[] addrs) internal
```

revoke all `addrs`'s SetAdmin roles for the set with id `setId`,
only if caller `isSetAdmin`.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to revoke SetAdmins from |
| addrs | address[] | the addresses to revoke as SetAdmins |

### ifAdmin

```solidity
modifier ifAdmin()
```

check whether `msg.sender` is an Admin, revert if not

### ifSetAdmin

```solidity
modifier ifSetAdmin(uint64 setId)
```

check whether `msg.sender` is a SetAdmin for the set with id
`setId`, revert if not

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | the set ID to check |

## TemplateBasedId

ERC1155 tokens use a single uint256 to represent a token ID.
This library provides a way to encode a uint64 set ID and a uint64 template
ID into a single uint256 id so that it can be combined into a key that fits
in a single EVM slot. Useful for functions like `IERC1155#uri`

_This can be used as a decorator for the uint256 tokenId decoding_

### encodeAsTokenId

```solidity
function encodeAsTokenId(uint64 setId, uint64 templateId) internal pure returns (uint256)
```

bijectively encode `setId` and `templateId` into a single
uint256 `id`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint256 | encoded ID |

### decodeSetId

```solidity
function decodeSetId(uint256 tokenId) internal pure returns (uint64)
```

decode `tokenId` into a uint64 set ID

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| tokenId | uint256 | encoded ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint64 | set ID |

### decodeTemplateId

```solidity
function decodeTemplateId(uint256 tokenId) internal pure returns (uint64)
```

decode `tokenId` into a uint64 template ID

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| tokenId | uint256 | encoded ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint64 | template ID |

## SetManager

A generic NFT extension that allows NFTs to be organized within
`Set`s. Each `Set` has a unique ID within an NFT contract and can contain
multiple `Template`s (which has its own contract below this one).

_The NFT contract should inherit from `TemplateManager` and not this
`SetManager` contract._

### _numSets

```solidity
uint64 _numSets
```

_number of sets this contract has - used to determine valid set IDs_

### _sets

```solidity
mapping(uint64 => struct SetManager.Set) _sets
```

_mapping from set ID to its corresponding `Set` struct_

### Set

```solidity
struct Set {
  uint64 numTemplates;
  bool locked;
}
```

### numSets

```solidity
function numSets() public view returns (uint64)
```

number of sets this contract has

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint64 | number of sets |

### setExists

```solidity
function setExists(uint64 setId) public view returns (bool)
```

check if a set exists. Valid set IDs are between [1, numSets].

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if set exists |

### set

```solidity
function set(uint64 setId) public view returns (struct SetManager.Set)
```

get `Set` with id `setId`

_only a copy - cannot modify `Set` in storage_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | struct SetManager.Set | a copy of the `Set` struct with id `setId` |

### numTemplatesInSet

```solidity
function numTemplatesInSet(uint64 setId) public view returns (uint64)
```

number of templates in a set

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint64 | number of templates |

### isSetLocked

```solidity
function isSetLocked(uint64 setId) public view returns (bool)
```

check if a set is locked

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if set is locked |

### _addSets

```solidity
function _addSets(uint64 numNewSets) internal
```

add new sets

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| numNewSets | uint64 | number of sets to add |

### _incrementNumTemplates

```solidity
function _incrementNumTemplates(uint64 setId, uint64 amount) internal
```

increment the number of templates in a set

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| amount | uint64 | amount to increment by |

### _lockSet

```solidity
function _lockSet(uint64 setId) internal virtual
```

lock a set

_this should not be exposed externally. Override this just to add
restrictions. Use `_lockSets` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

### _lockSets

```solidity
function _lockSets(uint64[] setIds) internal
```

lock multiple sets

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setIds | uint64[] | set IDs |

### ifSetExists

```solidity
modifier ifSetExists(uint64 setId)
```

check if set exists, otherwise revert

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

### ifSetUnlocked

```solidity
modifier ifSetUnlocked(uint64 setId)
```

check if set is unlocked, otherwise revert

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

## TemplateManager

A `Set` can contain multiple `Template`s. Each `Template` should
have a 1-1 mapping with a unique NFT token. A `Template` can be used to
manage NFT minting (allowing max mints and lcoking) and manage NFT metadata,
probably alongside `UriManager`.

_The final NFT contract should inherit from `TemplateManager`_

### _templates

```solidity
mapping(uint256 => struct TemplateManager.Template) _templates
```

_mapping from a template to a `Template` struct. The key of the
mapping is essentially determined by `setId` << 64 + `templateId`, which
allows us to keep the keys compact yet keep uniqueness._

### Template

```solidity
struct Template {
  uint64 max;
  uint64 minted;
  bool metadataLocked;
  bool mintingLocked;
}
```

### templateExists

```solidity
function templateExists(uint64 setId, uint64 templateId) public view returns (bool)
```

check if a template exists. Valid template IDs are between
[1, numTemplatesInSet].

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if template exists |

### template

```solidity
function template(uint64 setId, uint64 templateId) public view returns (struct TemplateManager.Template)
```

get `Template` with id `templateId` in set `setId`

_only a copy - cannot modify `Template` in storage_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | struct TemplateManager.Template | a copy of the `Template` struct with id `templateId` in set `setId` |

### maxMintOfTemplate

```solidity
function maxMintOfTemplate(uint64 setId, uint64 templateId) public view returns (uint64)
```

get the maximum mint allowed for a `Template`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint64 | maximum mint allowed |

### numMintedFromTemplate

```solidity
function numMintedFromTemplate(uint64 setId, uint64 templateId) public view returns (uint64)
```

get the number of NFTs minted for a `Template`

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | uint64 | number of NFTs minted |

### isTemplateMetadataLocked

```solidity
function isTemplateMetadataLocked(uint64 setId, uint64 templateId) public view returns (bool)
```

check if metadata for a `Template` is locked

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if metadata is locked |

### isTemplateMintingLocked

```solidity
function isTemplateMintingLocked(uint64 setId, uint64 templateId) public view returns (bool)
```

check if minting for a `Template` is locked

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | bool | true if minting is locked |

### _addTemplates

```solidity
function _addTemplates(uint64 setId, uint64 numNewTemplates, uint64[] maxMints) internal
```

add templates to a set

_new templates are given an ID based on the number of templates in
set `setId`_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| numNewTemplates | uint64 | number of new templates to add |
| maxMints | uint64[] | maximum number of NFTs that can be minted from each template |

### _modifyMaxMint

```solidity
function _modifyMaxMint(uint64 setId, uint64 templateId, uint64 newMax) internal virtual
```

modify the maximum number of NFTs that can be minted from a
`Template`, if not already set.

_this should not be exposed externally. Override this just to add
restrictions. Use `_modifyMaxMints` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |
| newMax | uint64 | new maximum number of NFTs that can be minted from a `Template` |

### _modifyMaxMints

```solidity
function _modifyMaxMints(uint64 setId, uint64[] templateIds, uint64[] newMaxes) internal
```

modify the maximum number of NFTs that can be minted from
multiple `Template`s, if not already set.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |
| newMaxes | uint64[] | new maximum number of NFTs that can be minted from |

### _registerMint

```solidity
function _registerMint(uint64 setId, uint64 templateId, uint64 amount) internal virtual
```

register mints for a `Template`, if the `Template` is not locked
and the max mint won't be surpassed

_this should not be exposed externally. Override this just to add
restrictions. Use `_registerMints` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |
| amount | uint64 | number of NFTs to register |

### _registerMints

```solidity
function _registerMints(uint64 setId, uint64[] templateIds, uint64[] amounts) internal
```

register mints for multiple `Template`s, if the `Template`s are
not locked and no max mints will be surpassed

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |
| amounts | uint64[] | number of NFTs to register |

### _lockMinting

```solidity
function _lockMinting(uint64 setId, uint64 templateId) internal virtual
```

lock minting for a `Template`

_this should not be exposed externally. Override this just to add
restrictions. Use `_lockMintingForTemplates` or `_lockMintingForSet`
instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

### _lockMintingForTemplates

```solidity
function _lockMintingForTemplates(uint64 setId, uint64[] templateIds) internal
```

lock minting for multiple `Template`s within a set

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |

### _lockMintingForSet

```solidity
function _lockMintingForSet(uint64 setId) internal
```

lock minting for all `Template`s within a set

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

### _lockMetadata

```solidity
function _lockMetadata(uint64 setId, uint64 templateId) internal virtual
```

lock metadata for a `Template`

_this should not be exposed externally. Override this just to add
restrictions. Use `_lockMetadataForTemplates` or `_lockMetadataForSet`
instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

### _lockMetadataForTemplates

```solidity
function _lockMetadataForTemplates(uint64 setId, uint64[] templateIds) internal
```

lock metadata for multiple `Template`s within a set

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | template IDs |

### _lockMetadataForSet

```solidity
function _lockMetadataForSet(uint64 setId) internal
```

lock metadata for all `Template`s within a set

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |

### ifTemplateExists

```solidity
modifier ifTemplateExists(uint64 setId, uint64 templateId)
```

check if a `Template` exists, and revert if it doesn't

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

### ifTemplateMetadataUnlocked

```solidity
modifier ifTemplateMetadataUnlocked(uint64 setId, uint64 templateId)
```

check if the metadata for a `Template` is still modifiable, and
revert if it isn't

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

### ifTemplateCanMint

```solidity
modifier ifTemplateCanMint(uint64 setId, uint64 templateId, uint64 amount)
```

check if a `Template` is still able to register mints

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |
| amount | uint64 |  |

## UriManager

There is one metadata URI associated with each (set, template) pair.
`UriManager` manages a mapping from the (set, template) pair to a string URI.
The URI should default to `_defaultUri` if no URI is set for the
(set, template) pair. This contract also allows modification of URIs.

_Privileging for mutating functions should be handled by an implementing
contract via modifiers._

### _defaultUri

```solidity
string _defaultUri
```

_default URI that's used if a token's URI is not set_

### _uris

```solidity
mapping(uint256 => string) _uris
```

_mapping from (set << 64 + template) pair to its URI_

### uri

```solidity
function uri(uint64 setId, uint64 templateId) public view returns (string)
```

get the URI for a token, or use the default if none.

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |

#### Return Values

| Name | Type | Description |
| ---- | ---- | ----------- |
| [0] | string | URI |

### _modifyDefaultUri

```solidity
function _modifyDefaultUri(string newDefaultUri) internal
```

modify the default URI

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| newDefaultUri | string | new default URI |

### _modifyUri

```solidity
function _modifyUri(uint64 setId, uint64 templateId, string newUri) internal virtual
```

modify the URI for a token

_this should not be exposed externally. Override this just to add
restrictions. Use `_modifyUris` instead._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateId | uint64 | template ID |
| newUri | string | new URI |

### _modifyUris

```solidity
function _modifyUris(uint64 setId, uint64[] templateIds, string[] newUris) internal
```

modify the URIs for a given list of templates within a set

_requires `templateIds.length == newUris.length` as they map 1-1_

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| setId | uint64 | set ID |
| templateIds | uint64[] | list of template IDs |
| newUris | string[] | list of new URIs |