Skip to content

collection - [mainnet]

This defines an object-based Collection. A collection acts as a set organizer for a group of tokens. This includes aspects such as a general description, project URI, name, and may contain other useful generalizations across this set of tokens.

Being built upon objects enables collections to be relatively flexible. As core primitives it supports:

  • Common fields: name, uri, description, creator
  • MutatorRef leaving mutability configuration to a higher level component
  • Addressed by a global identifier of creator’s address and collection name, thus collections cannot be deleted as a restriction of the object model.
  • Optional support for collection-wide royalties
  • Optional support for tracking of supply with events on mint or burn

TODO:

  • Consider supporting changing the name of the collection with the MutatorRef. This would require adding the field original_name.
  • Consider supporting changing the aspects of supply with the MutatorRef.
  • Add aggregator support when added to framework
use 0x1::aggregator_v2;
use 0x1::error;
use 0x1::event;
use 0x1::features;
use 0x1::object;
use 0x1::option;
use 0x1::signer;
use 0x1::string;
use 0x4::royalty;

Constants

const MAX_U64: u64 = 18446744073709551615;

The URI is over the maximum length

const EURI_TOO_LONG: u64 = 4;
const MAX_URI_LENGTH: u64 = 512;

Tried upgrading collection to concurrent, but collection is already concurrent

const EALREADY_CONCURRENT: u64 = 8;

The collection does not exist

const ECOLLECTION_DOES_NOT_EXIST: u64 = 1;

The collection name is over the maximum length

const ECOLLECTION_NAME_TOO_LONG: u64 = 3;

The collection owner feature is not supported

const ECOLLECTION_OWNER_NOT_SUPPORTED: u64 = 11;

The collection has reached its supply and no more tokens can be minted, unless some are burned

const ECOLLECTION_SUPPLY_EXCEEDED: u64 = 2;

Concurrent feature flag is not yet enabled, so the function cannot be performed

const ECONCURRENT_NOT_ENABLED: u64 = 7;

The description is over the maximum length

const EDESCRIPTION_TOO_LONG: u64 = 5;

The new max supply cannot be less than the current supply

const EINVALID_MAX_SUPPLY: u64 = 9;

The max supply must be positive

const EMAX_SUPPLY_CANNOT_BE_ZERO: u64 = 6;

The collection does not have a max supply

const ENO_MAX_SUPPLY_IN_COLLECTION: u64 = 10;
const MAX_COLLECTION_NAME_LENGTH: u64 = 128;
const MAX_DESCRIPTION_LENGTH: u64 = 2048;

Structs

MutatorRef

This enables mutating description and URI by higher level services.

struct MutatorRef has drop, store
Fields
self: address

MutationEvent

Contains the mutated fields name. This makes the life of indexers easier, so that they can directly understand the behavior in a writeset.

struct MutationEvent has drop, store
Fields
mutated_field_name: string::String

Mutation

Contains the mutated fields name. This makes the life of indexers easier, so that they can directly understand the behavior in a writeset.

#[event]
struct Mutation has drop, store
Fields
mutated_field_name: string::String
collection: object::Object<collection::Collection>
old_value: string::String
new_value: string::String

BurnEvent

struct BurnEvent has drop, store
Fields
index: u64
token: address

MintEvent

struct MintEvent has drop, store
Fields
index: u64
token: address

Burn

#[event]
struct Burn has drop, store
Fields
collection: address
index: u64
token: address
previous_owner: address

Mint

#[event]
struct Mint has drop, store
Fields
collection: address
index: aggregator_v2::AggregatorSnapshot<u64>
token: address

ConcurrentBurnEvent

#[event]
#[deprecated]
struct ConcurrentBurnEvent has drop, store
Fields
collection_addr: address
index: u64
token: address

ConcurrentMintEvent

#[event]
#[deprecated]
struct ConcurrentMintEvent has drop, store
Fields
collection_addr: address
index: aggregator_v2::AggregatorSnapshot<u64>
token: address

SetMaxSupply

#[event]
struct SetMaxSupply has drop, store
Fields
collection: object::Object<collection::Collection>
old_max_supply: u64
new_max_supply: u64

Resources

Collection

Represents the common fields for a collection.

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct Collection has key
Fields
creator: address
The creator of this collection.
description: string::String
A brief description of the collection.
name: string::String
An optional categorization of similar token.
uri: string::String
The Uniform Resource Identifier (uri) pointing to the JSON file stored in off-chain storage; the URL length will likely need a maximum any suggestions?
mutation_events: event::EventHandle<collection::MutationEvent>
Emitted upon any mutation of the collection.

FixedSupply

Fixed supply tracker, this is useful for ensuring that a limited number of tokens are minted. and adding events and supply tracking to a collection.

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct FixedSupply has key
Fields
current_supply: u64
Total minted - total burned
max_supply: u64
total_minted: u64
burn_events: event::EventHandle<collection::BurnEvent>
Emitted upon burning a Token.
mint_events: event::EventHandle<collection::MintEvent>
Emitted upon minting an Token.

UnlimitedSupply

Unlimited supply tracker, this is useful for adding events and supply tracking to a collection.

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct UnlimitedSupply has key
Fields
current_supply: u64
total_minted: u64
burn_events: event::EventHandle<collection::BurnEvent>
Emitted upon burning a Token.
mint_events: event::EventHandle<collection::MintEvent>
Emitted upon minting an Token.

ConcurrentSupply

Supply tracker, useful for tracking amount of issued tokens. If max_value is not set to U64_MAX, this ensures that a limited number of tokens are minted.

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct ConcurrentSupply has key
Fields
current_supply: aggregator_v2::Aggregator<u64>
Total minted - total burned
total_minted: aggregator_v2::Aggregator<u64>

Functions

create_fixed_collection

Creates a fixed-sized collection, or a collection that supports a fixed amount of tokens. This is useful to create a guaranteed, limited supply on-chain digital asset. For example, a collection 1111 vicious vipers. Note, creating restrictions such as upward limits results in data structures that prevent Aptos from parallelizing mints of this collection type. Beyond that, it adds supply tracking with events.

public fun create_fixed_collection(creator: &signer, description: string::String, max_supply: u64, name: string::String, royalty: option::Option<royalty::Royalty>, uri: string::String): object::ConstructorRef
Implementation
public fun create_fixed_collection(
creator: &signer,
description: String,
max_supply: u64,
name: String,
royalty: Option<Royalty>,
uri: String,
): ConstructorRef {
assert!(max_supply != 0, error::invalid_argument(EMAX_SUPPLY_CANNOT_BE_ZERO));
let collection_seed = create_collection_seed(&name);
let constructor_ref = object::create_named_object(creator, collection_seed);
let supply = ConcurrentSupply {
current_supply: aggregator_v2::create_aggregator(max_supply),
total_minted: aggregator_v2::create_unbounded_aggregator(),
};
create_collection_internal(
creator,
constructor_ref,
description,
name,
royalty,
uri,
option::some(supply),
)
}

create_fixed_collection_as_owner

Same functionality as create_fixed_collection, but the caller is the owner of the collection. This means that the caller can transfer the collection to another address. This transfers ownership and minting permissions to the new address.

public fun create_fixed_collection_as_owner(creator: &signer, description: string::String, max_supply: u64, name: string::String, royalty: option::Option<royalty::Royalty>, uri: string::String): object::ConstructorRef
Implementation
public fun create_fixed_collection_as_owner(
creator: &signer,
description: String,
max_supply: u64,
name: String,
royalty: Option<Royalty>,
uri: String,
): ConstructorRef {
assert!(features::is_collection_owner_enabled(), error::unavailable(ECOLLECTION_OWNER_NOT_SUPPORTED));
let constructor_ref = create_fixed_collection(
creator,
description,
max_supply,
name,
royalty,
uri,
);
enable_ungated_transfer(&constructor_ref);
constructor_ref
}

create_unlimited_collection

Creates an unlimited collection. This has support for supply tracking but does not limit the supply of tokens.

public fun create_unlimited_collection(creator: &signer, description: string::String, name: string::String, royalty: option::Option<royalty::Royalty>, uri: string::String): object::ConstructorRef
Implementation
public fun create_unlimited_collection(
creator: &signer,
description: String,
name: String,
royalty: Option<Royalty>,
uri: String,
): ConstructorRef {
let collection_seed = create_collection_seed(&name);
let constructor_ref = object::create_named_object(creator, collection_seed);
let supply = ConcurrentSupply {
current_supply: aggregator_v2::create_unbounded_aggregator(),
total_minted: aggregator_v2::create_unbounded_aggregator(),
};
create_collection_internal(
creator,
constructor_ref,
description,
name,
royalty,
uri,
option::some(supply),
)
}

create_unlimited_collection_as_owner

Same functionality as create_unlimited_collection, but the caller is the owner of the collection. This means that the caller can transfer the collection to another address. This transfers ownership and minting permissions to the new address.

public fun create_unlimited_collection_as_owner(creator: &signer, description: string::String, name: string::String, royalty: option::Option<royalty::Royalty>, uri: string::String): object::ConstructorRef
Implementation
public fun create_unlimited_collection_as_owner(
creator: &signer,
description: String,
name: String,
royalty: Option<Royalty>,
uri: String,
): ConstructorRef {
assert!(features::is_collection_owner_enabled(), error::unavailable(ECOLLECTION_OWNER_NOT_SUPPORTED));
let constructor_ref = create_unlimited_collection(
creator,
description,
name,
royalty,
uri,
);
enable_ungated_transfer(&constructor_ref);
constructor_ref
}

create_untracked_collection

Creates an untracked collection, or a collection that supports an arbitrary amount of tokens. This is useful for mass airdrops that fully leverage Aptos parallelization. TODO: Hide this until we bring back meaningful way to enforce burns

fun create_untracked_collection(creator: &signer, description: string::String, name: string::String, royalty: option::Option<royalty::Royalty>, uri: string::String): object::ConstructorRef
Implementation
fun create_untracked_collection(
creator: &signer,
description: String,
name: String,
royalty: Option<Royalty>,
uri: String,
): ConstructorRef {
let collection_seed = create_collection_seed(&name);
let constructor_ref = object::create_named_object(creator, collection_seed);
create_collection_internal<FixedSupply>(
creator,
constructor_ref,
description,
name,
royalty,
uri,
option::none(),
)
}

create_collection_internal

fun create_collection_internal<Supply: key>(creator: &signer, constructor_ref: object::ConstructorRef, description: string::String, name: string::String, royalty: option::Option<royalty::Royalty>, uri: string::String, supply: option::Option<Supply>): object::ConstructorRef
Implementation
inline fun create_collection_internal<Supply: key>(
creator: &signer,
constructor_ref: ConstructorRef,
description: String,
name: String,
royalty: Option<Royalty>,
uri: String,
supply: Option<Supply>,
): ConstructorRef {
assert!(name.length() <= MAX_COLLECTION_NAME_LENGTH, error::out_of_range(ECOLLECTION_NAME_TOO_LONG));
assert!(uri.length() <= MAX_URI_LENGTH, error::out_of_range(EURI_TOO_LONG));
assert!(description.length() <= MAX_DESCRIPTION_LENGTH, error::out_of_range(EDESCRIPTION_TOO_LONG));
let object_signer = object::generate_signer(&constructor_ref);
let collection = Collection {
creator: signer::address_of(creator),
description,
name,
uri,
mutation_events: object::new_event_handle(&object_signer),
};
move_to(&object_signer, collection);
if (supply.is_some()) {
move_to(&object_signer, supply.destroy_some())
} else {
supply.destroy_none()
};
if (royalty.is_some()) {
royalty::init(&constructor_ref, royalty.extract())
};
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
object::disable_ungated_transfer(&transfer_ref);
constructor_ref
}

enable_ungated_transfer

fun enable_ungated_transfer(constructor_ref: &object::ConstructorRef)
Implementation
inline fun enable_ungated_transfer(constructor_ref: &ConstructorRef) {
let transfer_ref = object::generate_transfer_ref(constructor_ref);
object::enable_ungated_transfer(&transfer_ref);
}

create_collection_address

Generates the collections address based upon the creators address and the collection’s name

public fun create_collection_address(creator: &address, name: &string::String): address
Implementation
public fun create_collection_address(creator: &address, name: &String): address {
object::create_object_address(creator, create_collection_seed(name))
}

create_collection_seed

Named objects are derived from a seed, the collection’s seed is its name.

public fun create_collection_seed(name: &string::String): vector<u8>
Implementation
public fun create_collection_seed(name: &String): vector<u8> {
assert!(name.length() <= MAX_COLLECTION_NAME_LENGTH, error::out_of_range(ECOLLECTION_NAME_TOO_LONG));
*name.bytes()
}

increment_supply

Called by token on mint to increment supply if there’s an appropriate Supply struct.

public(friend) fun increment_supply(collection: &object::Object<collection::Collection>, token: address): option::Option<aggregator_v2::AggregatorSnapshot<u64>>
Implementation
friend fun increment_supply(
collection: &Object<Collection>,
token: address,
): Option<AggregatorSnapshot<u64>> acquires FixedSupply, UnlimitedSupply, ConcurrentSupply {
let collection_addr = object::object_address(collection);
if (exists<ConcurrentSupply>(collection_addr)) {
let supply = &mut ConcurrentSupply[collection_addr];
assert!(
aggregator_v2::try_add(&mut supply.current_supply, 1),
error::out_of_range(ECOLLECTION_SUPPLY_EXCEEDED),
);
aggregator_v2::add(&mut supply.total_minted, 1);
event::emit(
Mint {
collection: collection_addr,
index: aggregator_v2::snapshot(&supply.total_minted),
token,
},
);
option::some(aggregator_v2::snapshot(&supply.total_minted))
} else if (exists<FixedSupply>(collection_addr)) {
let supply = &mut FixedSupply[collection_addr];
supply.current_supply += 1;
supply.total_minted += 1;
assert!(
supply.current_supply <= supply.max_supply,
error::out_of_range(ECOLLECTION_SUPPLY_EXCEEDED),
);
if (std::features::module_event_migration_enabled()) {
event::emit(
Mint {
collection: collection_addr,
index: aggregator_v2::create_snapshot(supply.total_minted),
token,
},
);
} else {
event::emit_event(&mut supply.mint_events,
MintEvent {
index: supply.total_minted,
token,
},
);
};
option::some(aggregator_v2::create_snapshot<u64>(supply.total_minted))
} else if (exists<UnlimitedSupply>(collection_addr)) {
let supply = &mut UnlimitedSupply[collection_addr];
supply.current_supply += 1;
supply.total_minted += 1;
if (std::features::module_event_migration_enabled()) {
event::emit(
Mint {
collection: collection_addr,
index: aggregator_v2::create_snapshot(supply.total_minted),
token,
},
);
} else {
event::emit_event(
&mut supply.mint_events,
MintEvent {
index: supply.total_minted,
token,
},
);
};
option::some(aggregator_v2::create_snapshot<u64>(supply.total_minted))
} else {
option::none()
}
}

decrement_supply

Called by token on burn to decrement supply if there’s an appropriate Supply struct.

public(friend) fun decrement_supply(collection: &object::Object<collection::Collection>, token: address, index: option::Option<u64>, previous_owner: address)
Implementation
friend fun decrement_supply(
collection: &Object<Collection>,
token: address,
index: Option<u64>,
previous_owner: address,
) acquires FixedSupply, UnlimitedSupply, ConcurrentSupply {
let collection_addr = object::object_address(collection);
if (exists<ConcurrentSupply>(collection_addr)) {
let supply = &mut ConcurrentSupply[collection_addr];
aggregator_v2::sub(&mut supply.current_supply, 1);
event::emit(
Burn {
collection: collection_addr,
index: *index.borrow(),
token,
previous_owner,
},
);
} else if (exists<FixedSupply>(collection_addr)) {
let supply = &mut FixedSupply[collection_addr];
supply.current_supply -= 1;
if (std::features::module_event_migration_enabled()) {
event::emit(
Burn {
collection: collection_addr,
index: *index.borrow(),
token,
previous_owner,
},
);
} else {
event::emit_event(
&mut supply.burn_events,
BurnEvent {
index: *index.borrow(),
token,
},
);
};
} else if (exists<UnlimitedSupply>(collection_addr)) {
let supply = &mut UnlimitedSupply[collection_addr];
supply.current_supply -= 1;
if (std::features::module_event_migration_enabled()) {
event::emit(
Burn {
collection: collection_addr,
index: *index.borrow(),
token,
previous_owner,
},
);
} else {
event::emit_event(
&mut supply.burn_events,
BurnEvent {
index: *index.borrow(),
token,
},
);
};
}
}

generate_mutator_ref

Creates a MutatorRef, which gates the ability to mutate any fields that support mutation.

public fun generate_mutator_ref(ref: &object::ConstructorRef): collection::MutatorRef
Implementation
public fun generate_mutator_ref(ref: &ConstructorRef): MutatorRef {
let object = object::object_from_constructor_ref<Collection>(ref);
MutatorRef { self: object::object_address(&object) }
}

upgrade_to_concurrent

public fun upgrade_to_concurrent(ref: &object::ExtendRef)
Implementation
public fun upgrade_to_concurrent(
ref: &ExtendRef,
) acquires FixedSupply, UnlimitedSupply {
let metadata_object_address = object::address_from_extend_ref(ref);
let metadata_object_signer = object::generate_signer_for_extending(ref);
let (supply, current_supply, total_minted, burn_events, mint_events) = if (exists<FixedSupply>(
metadata_object_address
)) {
let FixedSupply {
current_supply,
max_supply,
total_minted,
burn_events,
mint_events,
} = move_from<FixedSupply>(metadata_object_address);
let supply = ConcurrentSupply {
current_supply: aggregator_v2::create_aggregator(max_supply),
total_minted: aggregator_v2::create_unbounded_aggregator(),
};
(supply, current_supply, total_minted, burn_events, mint_events)
} else if (exists<UnlimitedSupply>(metadata_object_address)) {
let UnlimitedSupply {
current_supply,
total_minted,
burn_events,
mint_events,
} = move_from<UnlimitedSupply>(metadata_object_address);
let supply = ConcurrentSupply {
current_supply: aggregator_v2::create_unbounded_aggregator(),
total_minted: aggregator_v2::create_unbounded_aggregator(),
};
(supply, current_supply, total_minted, burn_events, mint_events)
} else {
// untracked collection is already concurrent, and other variants too.
abort error::invalid_argument(EALREADY_CONCURRENT)
};
// update current state:
aggregator_v2::add(&mut supply.current_supply, current_supply);
aggregator_v2::add(&mut supply.total_minted, total_minted);
move_to(&metadata_object_signer, supply);
event::destroy_handle(burn_events);
event::destroy_handle(mint_events);
}

check_collection_exists

fun check_collection_exists(addr: address)
Implementation
inline fun check_collection_exists(addr: address) {
assert!(
exists<Collection>(addr),
error::not_found(ECOLLECTION_DOES_NOT_EXIST),
);
}

borrow

fun borrow<T: key>(collection: &object::Object<T>): &collection::Collection
Implementation
inline fun borrow<T: key>(collection: &Object<T>): &Collection {
let collection_address = object::object_address(collection);
check_collection_exists(collection_address);
&Collection[collection_address]
}

count

Provides the count of the current selection if supply tracking is used

Note: Calling this method from transaction that also mints/burns, prevents it from being parallelized.

#[view]
public fun count<T: key>(collection: object::Object<T>): option::Option<u64>
Implementation
public fun count<T: key>(
collection: Object<T>
): Option<u64> acquires FixedSupply, UnlimitedSupply, ConcurrentSupply {
let collection_address = object::object_address(&collection);
check_collection_exists(collection_address);
if (exists<ConcurrentSupply>(collection_address)) {
let supply = &ConcurrentSupply[collection_address];
option::some(aggregator_v2::read(&supply.current_supply))
} else if (exists<FixedSupply>(collection_address)) {
let supply = &FixedSupply[collection_address];
option::some(supply.current_supply)
} else if (exists<UnlimitedSupply>(collection_address)) {
let supply = &UnlimitedSupply[collection_address];
option::some(supply.current_supply)
} else {
option::none()
}
}

creator

#[view]
public fun creator<T: key>(collection: object::Object<T>): address
Implementation
public fun creator<T: key>(collection: Object<T>): address acquires Collection {
borrow(&collection).creator
}

description

#[view]
public fun description<T: key>(collection: object::Object<T>): string::String
Implementation
public fun description<T: key>(collection: Object<T>): String acquires Collection {
borrow(&collection).description
}

name

#[view]
public fun name<T: key>(collection: object::Object<T>): string::String
Implementation
public fun name<T: key>(collection: Object<T>): String acquires Collection {
borrow(&collection).name
}

uri

#[view]
public fun uri<T: key>(collection: object::Object<T>): string::String
Implementation
public fun uri<T: key>(collection: Object<T>): String acquires Collection {
borrow(&collection).uri
}

borrow_mut

fun borrow_mut(mutator_ref: &collection::MutatorRef): &mut collection::Collection
Implementation
inline fun borrow_mut(mutator_ref: &MutatorRef): &mut Collection {
check_collection_exists(mutator_ref.self);
&mut Collection[mutator_ref.self]
}

set_name

Callers of this function must be aware that changing the name will change the calculated collection’s address when calling create_collection_address. Once the collection has been created, the collection address should be saved for reference and create_collection_address should not be used to derive the collection’s address.

After changing the collection’s name, to create tokens - only call functions that accept the collection object as an argument.

public fun set_name(mutator_ref: &collection::MutatorRef, name: string::String)
Implementation
public fun set_name(mutator_ref: &MutatorRef, name: String) acquires Collection {
assert!(name.length() <= MAX_COLLECTION_NAME_LENGTH, error::out_of_range(ECOLLECTION_NAME_TOO_LONG));
let collection = borrow_mut(mutator_ref);
event::emit(Mutation {
mutated_field_name: string::utf8(b"name") ,
collection: object::address_to_object(mutator_ref.self),
old_value: collection.name,
new_value: name,
});
collection.name = name;
}

set_description

public fun set_description(mutator_ref: &collection::MutatorRef, description: string::String)
Implementation
public fun set_description(mutator_ref: &MutatorRef, description: String) acquires Collection {
assert!(description.length() <= MAX_DESCRIPTION_LENGTH, error::out_of_range(EDESCRIPTION_TOO_LONG));
let collection = borrow_mut(mutator_ref);
if (std::features::module_event_migration_enabled()) {
event::emit(Mutation {
mutated_field_name: string::utf8(b"description"),
collection: object::address_to_object(mutator_ref.self),
old_value: collection.description,
new_value: description,
});
} else {
event::emit_event(
&mut collection.mutation_events,
MutationEvent { mutated_field_name: string::utf8(b"description") },
);
};
collection.description = description;
}

set_uri

public fun set_uri(mutator_ref: &collection::MutatorRef, uri: string::String)
Implementation
public fun set_uri(mutator_ref: &MutatorRef, uri: String) acquires Collection {
assert!(uri.length() <= MAX_URI_LENGTH, error::out_of_range(EURI_TOO_LONG));
let collection = borrow_mut(mutator_ref);
if (std::features::module_event_migration_enabled()) {
event::emit(Mutation {
mutated_field_name: string::utf8(b"uri"),
collection: object::address_to_object(mutator_ref.self),
old_value: collection.uri,
new_value: uri,
});
} else {
event::emit_event(
&mut collection.mutation_events,
MutationEvent { mutated_field_name: string::utf8(b"uri") },
);
};
collection.uri = uri;
}

set_max_supply

public fun set_max_supply(mutator_ref: &collection::MutatorRef, max_supply: u64)
Implementation
public fun set_max_supply(mutator_ref: &MutatorRef, max_supply: u64) acquires ConcurrentSupply, FixedSupply {
let collection = object::address_to_object<Collection>(mutator_ref.self);
let collection_address = object::object_address(&collection);
let old_max_supply;
if (exists<ConcurrentSupply>(collection_address)) {
let supply = &mut ConcurrentSupply[collection_address];
let current_supply = aggregator_v2::read(&supply.current_supply);
assert!(
max_supply >= current_supply,
error::out_of_range(EINVALID_MAX_SUPPLY),
);
old_max_supply = aggregator_v2::max_value(&supply.current_supply);
supply.current_supply = aggregator_v2::create_aggregator(max_supply);
aggregator_v2::add(&mut supply.current_supply, current_supply);
} else if (exists<FixedSupply>(collection_address)) {
let supply = &mut FixedSupply[collection_address];
assert!(
max_supply >= supply.current_supply,
error::out_of_range(EINVALID_MAX_SUPPLY),
);
old_max_supply = supply.max_supply;
supply.max_supply = max_supply;
} else {
abort error::invalid_argument(ENO_MAX_SUPPLY_IN_COLLECTION)
};
event::emit(SetMaxSupply { collection, old_max_supply, new_max_supply: max_supply });
}

Specification

increment_supply

public(friend) fun increment_supply(collection: &object::Object<collection::Collection>, token: address): option::Option<aggregator_v2::AggregatorSnapshot<u64>>
pragma aborts_if_is_partial;
let collection_addr = object::object_address(collection);
let supply = global<ConcurrentSupply>(collection_addr);
let post supply_post = global<ConcurrentSupply>(collection_addr);
aborts_if exists<ConcurrentSupply>(collection_addr) &&
aggregator_v2::spec_get_value(supply.current_supply) + 1
> aggregator_v2::spec_get_max_value(supply.current_supply);
aborts_if exists<ConcurrentSupply>(collection_addr) &&
aggregator_v2::spec_get_value(supply.total_minted) + 1
> aggregator_v2::spec_get_max_value(supply.total_minted);
ensures
aggregator_v2::spec_get_max_value(supply.current_supply)
== aggregator_v2::spec_get_max_value(supply_post.current_supply);
ensures exists<ConcurrentSupply>(collection_addr) &&
aggregator_v2::spec_get_value(supply.current_supply) + 1
<= aggregator_v2::spec_get_max_value(supply.current_supply) ==>
aggregator_v2::spec_get_value(supply.current_supply) + 1
== aggregator_v2::spec_get_value(supply_post.current_supply);