Address
octzuh9c…VGcqL7
octzuh9cEUKSd5iieVN8KUe4cdistvEtb3KbcrJLYVGcqL7
State
OCT balance
2OCT
wallet balance
Type
Contract
smart contract on chain
Chain-level
View on Octrascan · devnet ↗
raw txs · nonce · pubkey
Pipoke (no profile)
this wallet has not registered a Pipoke profile
Contract
History
live · 20s · last 0Source · ABI · Bytecode✓ verifiedexpand →
Source · ABI · Bytecode
✓ verified✓ verified
contract Xcollection {
struct TokenInfo {
owner: address
minted_epoch: int
}
event Initialized(owner: address, name: string, max_supply: int, injection_cap: int)
event Minted(minter: address, token_id: int, phase: int, price: int)
event GenesisMint(minter: address, token_id: int, genesis_id: int, genesis_minted_count: int, injection_cap: int)
event Airdropped(recipient: address, token_id: int)
event Transferred(token_id: int, from: address, to: address)
event Approved(token_id: int, approved: address)
event ApprovalRevoked(token_id: int)
event OperatorSet(owner: address, operator: address, approved: int)
event Revealed(base_uri: string)
event BaseUriUpdated(base_uri: string)
event PhaseOverrideSet(phase: int)
event PhaseOverrideCleared()
event InjectionSwept(slots_swept: int, new_public_cap: int)
event ProceedsClaimed(recipient: address, amount: int)
event XpectrumWalletUpdated(new_wallet: address)
event OwnershipTransferred(old_owner: address, new_owner: address)
const PLATFORM_FEE_BPS: int = 250
const MAX_ROYALTY_BPS: int = 1000
const MAX_INJECTION: int = 222
const INJECTION_BPS: int = 2000
const MIN_PHASE_EPOCHS: int = 360
const MAX_GTD_EPOCHS: int = 259200
const MAX_FCFS_EPOCHS: int = 259200
state {
owner: address
admin: address
xpectrum_wallet: address
name: string
symbol: string
base_uri: string
unrevealed_uri: string
revealed: int
royalty_bps: int
royalty_receiver: address
max_supply: int
total_minted: int
max_per_wallet: int
minted_per_wallet: map[address]int
initialized: int
paused: int
phase_override_active: int
phase_override_value: int
gtd_price: int
gtd_start_epoch: int
gtd_end_epoch: int
gtd_cap: int
gtd_minted: int
gtd_wallet_cap: int
gtd_minted_by: map[address]int
fcfs_price: int
fcfs_start_epoch: int
fcfs_end_epoch: int
fcfs_cap: int
fcfs_minted: int
fcfs_wallet_cap: int
fcfs_minted_by: map[address]int
public_price: int
public_start_epoch: int
public_end_epoch: int
public_cap: int
public_minted: int
public_wallet_cap: int
public_minted_by: map[address]int
xlist_addr: address
genesis_contract: address
injection_cap: int
genesis_minted_count: int
used_genesis_ids: map[int]int
injection_swept: int
airdropped_count: int
balances: map[address]int
tokens: map[int]TokenInfo
approvals: map[int]address
operator_approvals: map[address]map[address]int
reserve: int
pending_proceeds: map[address]int
}
constructor() {
self.owner = caller
self.initialized = 0
self.revealed = 0
self.total_minted = 0
self.paused = 0
self.phase_override_active = 0
self.phase_override_value = 3
self.reserve = 0
self.genesis_minted_count = 0
self.injection_swept = 0
self.airdropped_count = 0
}
private fn only_initialized() {
require(self.initialized == 1, "xcollection: not initialized")
}
private fn only_owner_or_admin() {
require(caller == self.owner || caller == self.admin, "xcollection: not authorized")
}
fn init(
collection_name: string,
collection_symbol: string,
collection_unrevealed_uri: string,
collection_max_supply: int,
collection_royalty_bps: int,
collection_max_per_wallet: int,
xpectrum_addr: address,
creator: address,
xlist: address,
genesis: address,
gtd_wallet: int,
fcfs_wallet: int,
pub_wallet: int
): bool {
require(self.initialized == 0, "xcollection: already initialized")
require(caller == self.owner, "xcollection: not authorized")
require(is_address(xpectrum_addr) && xpectrum_addr != 0, "xcollection: invalid platform addr")
require(is_address(creator) && creator != 0, "xcollection: invalid creator")
require(is_address(xlist) && xlist != 0, "xcollection: invalid xlist addr")
require(is_address(genesis) && genesis != 0, "xcollection: invalid genesis addr")
require(collection_max_supply > 0, "xcollection: invalid supply")
require(
collection_royalty_bps >= 0 &&
collection_royalty_bps <= MAX_ROYALTY_BPS,
"xcollection: invalid royalty"
)
require(collection_max_per_wallet > 0, "xcollection: invalid wallet cap")
require(gtd_wallet > 0 && fcfs_wallet > 0 && pub_wallet > 0, "xcollection: wallet caps must be > 0")
let raw_injection = collection_max_supply * INJECTION_BPS / 10000
let cap = raw_injection
if raw_injection > MAX_INJECTION {
cap = MAX_INJECTION
}
self.name = collection_name
self.symbol = collection_symbol
self.unrevealed_uri = collection_unrevealed_uri
self.max_supply = collection_max_supply
self.royalty_bps = collection_royalty_bps
self.max_per_wallet = collection_max_per_wallet
self.admin = xpectrum_addr
self.xpectrum_wallet = xpectrum_addr
self.royalty_receiver = creator
self.xlist_addr = xlist
self.genesis_contract = genesis
self.injection_cap = cap
self.gtd_wallet_cap = gtd_wallet
self.fcfs_wallet_cap = fcfs_wallet
self.public_wallet_cap = pub_wallet
self.initialized = 1
self.owner = creator
emit Initialized(creator, collection_name, collection_max_supply, cap)
return true
}
view fn get_current_phase(): int {
if self.phase_override_active == 1 {
return self.phase_override_value
}
if epoch >= self.gtd_start_epoch && epoch < self.gtd_end_epoch {
return 0
}
if epoch >= self.fcfs_start_epoch && epoch < self.fcfs_end_epoch {
return 1
}
if epoch >= self.public_start_epoch && epoch < self.public_end_epoch {
return 2
}
return 3
}
fn mint(quantity: int): bool {
only_initialized()
require(self.paused == 0, "xcollection: minting paused")
require(quantity > 0, "xcollection: zero quantity")
require(quantity <= 5, "xcollection: max 5 per tx")
require(
self.total_minted + quantity <= self.max_supply,
"xcollection: exceeds max supply"
)
require(
self.minted_per_wallet[caller] + quantity <= self.max_per_wallet,
"xcollection: global wallet cap exceeded"
)
let phase = get_current_phase()
require(phase != 3, "xcollection: minting closed")
let price = 0
if phase == 0 {
let wl = call(self.xlist_addr, "is_whitelisted", 0, caller)
require(wl == 1, "xcollection: not on GTD list — use genesis_mint if you hold a genesis NFT")
require(
self.gtd_minted_by[caller] + quantity <= self.gtd_wallet_cap,
"xcollection: GTD phase wallet cap exceeded"
)
require(
self.gtd_minted + quantity <= self.gtd_cap,
"xcollection: GTD cap reached"
)
price = self.gtd_price
self.gtd_minted += quantity
self.gtd_minted_by[caller] += quantity
}
if phase == 1 {
let wl = call(self.xlist_addr, "is_whitelisted", 1, caller)
require(wl == 1, "xcollection: not on FCFS list")
require(
self.fcfs_minted_by[caller] + quantity <= self.fcfs_wallet_cap,
"xcollection: FCFS phase wallet cap exceeded"
)
require(
self.fcfs_minted + quantity <= self.fcfs_cap,
"xcollection: FCFS cap reached"
)
price = self.fcfs_price
self.fcfs_minted += quantity
self.fcfs_minted_by[caller] += quantity
}
if phase == 2 {
require(
self.public_minted_by[caller] + quantity <= self.public_wallet_cap,
"xcollection: public phase wallet cap exceeded"
)
require(
self.public_minted + quantity <= self.public_cap,
"xcollection: public cap reached"
)
price = self.public_price
self.public_minted += quantity
self.public_minted_by[caller] += quantity
}
let cost = price * quantity
require(value >= cost, "xcollection: insufficient payment")
let fee = cost * PLATFORM_FEE_BPS / 10000
let creator_proceeds = cost - fee
self.reserve += value
self.pending_proceeds[self.owner] += creator_proceeds
self.pending_proceeds[self.xpectrum_wallet] += fee
self.minted_per_wallet[caller] += quantity
self.balances[caller] += quantity
let start_id = self.total_minted
self.total_minted += quantity
let idx = 0
for idx in 0..quantity {
self.tokens[start_id + idx].owner = caller
self.tokens[start_id + idx].minted_epoch = epoch
emit Minted(caller, start_id + idx, phase, price)
emit Transferred(start_id + idx, 0, caller)
}
if value > cost {
let refund = value - cost
self.reserve -= refund
transfer(caller, refund)
}
return true
}
fn genesis_mint(genesis_id: int): bool {
only_initialized()
require(self.paused == 0, "xcollection: minting paused")
require(
self.total_minted + 1 <= self.max_supply,
"xcollection: exceeds max supply"
)
require(
self.minted_per_wallet[caller] + 1 <= self.max_per_wallet,
"xcollection: global wallet cap exceeded"
)
let phase = get_current_phase()
require(phase == 0, "xcollection: genesis mint only available during GTD phase")
require(self.injection_swept == 0, "xcollection: genesis injection already swept")
require(self.used_genesis_ids[genesis_id] == 0, "xcollection: genesis token already used")
require(
self.genesis_minted_count < self.injection_cap,
"xcollection: genesis injection full"
)
require(
self.gtd_minted_by[caller] + 1 <= self.gtd_wallet_cap,
"xcollection: GTD phase wallet cap exceeded"
)
let token_owner = call(self.genesis_contract, "owner_of", genesis_id)
require(token_owner == caller, "xcollection: caller does not own this genesis token")
self.used_genesis_ids[genesis_id] = 1
self.genesis_minted_count += 1
self.gtd_minted_by[caller] += 1
self.gtd_minted += 1
let cost = self.gtd_price
require(value >= cost, "xcollection: insufficient payment")
let fee = cost * PLATFORM_FEE_BPS / 10000
let creator_proceeds = cost - fee
self.reserve += value
self.pending_proceeds[self.owner] += creator_proceeds
self.pending_proceeds[self.xpectrum_wallet] += fee
self.minted_per_wallet[caller] += 1
self.balances[caller] += 1
let token_id = self.total_minted
self.total_minted += 1
self.tokens[token_id].owner = caller
self.tokens[token_id].minted_epoch = epoch
emit GenesisMint(caller, token_id, genesis_id, self.genesis_minted_count, self.injection_cap)
emit Transferred(token_id, 0, caller)
if value > cost {
let refund = value - cost
self.reserve -= refund
transfer(caller, refund)
}
return true
}
fn sweep_injection(): bool {
only_initialized()
only_owner_or_admin()
require(self.injection_swept == 0, "xcollection: injection already swept")
require(
epoch >= self.gtd_end_epoch || get_current_phase() > 0,
"xcollection: GTD phase not yet ended"
)
let unclaimed = self.injection_cap - self.genesis_minted_count
if unclaimed > 0 {
self.public_cap += unclaimed
}
self.injection_cap = 0
self.injection_swept = 1
emit InjectionSwept(unclaimed, self.public_cap)
return true
}
fn airdrop(
addr_0: address, addr_1: address, addr_2: address, addr_3: address,
addr_4: address, addr_5: address, addr_6: address, addr_7: address,
addr_8: address, addr_9: address, addr_10: address, addr_11: address,
addr_12: address, addr_13: address, addr_14: address, addr_15: address,
addr_16: address, addr_17: address, addr_18: address, addr_19: address,
count: int
): bool {
only_initialized()
require(caller == self.owner, "xcollection: only owner")
require(count > 0 && count <= 20, "xcollection: count must be 1-20")
require(
self.total_minted + count <= self.max_supply,
"xcollection: exceeds max supply"
)
let actual = 0
if count > 0 && is_address(addr_0) && addr_0 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_0 self.tokens[tid].minted_epoch = epoch self.balances[addr_0] += 1 emit Minted(addr_0, tid, 3, 0) emit Airdropped(addr_0, tid) emit Transferred(tid, 0, addr_0) }
if count > 1 && is_address(addr_1) && addr_1 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_1 self.tokens[tid].minted_epoch = epoch self.balances[addr_1] += 1 emit Minted(addr_1, tid, 3, 0) emit Airdropped(addr_1, tid) emit Transferred(tid, 0, addr_1) }
if count > 2 && is_address(addr_2) && addr_2 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_2 self.tokens[tid].minted_epoch = epoch self.balances[addr_2] += 1 emit Minted(addr_2, tid, 3, 0) emit Airdropped(addr_2, tid) emit Transferred(tid, 0, addr_2) }
if count > 3 && is_address(addr_3) && addr_3 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_3 self.tokens[tid].minted_epoch = epoch self.balances[addr_3] += 1 emit Minted(addr_3, tid, 3, 0) emit Airdropped(addr_3, tid) emit Transferred(tid, 0, addr_3) }
if count > 4 && is_address(addr_4) && addr_4 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_4 self.tokens[tid].minted_epoch = epoch self.balances[addr_4] += 1 emit Minted(addr_4, tid, 3, 0) emit Airdropped(addr_4, tid) emit Transferred(tid, 0, addr_4) }
if count > 5 && is_address(addr_5) && addr_5 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_5 self.tokens[tid].minted_epoch = epoch self.balances[addr_5] += 1 emit Minted(addr_5, tid, 3, 0) emit Airdropped(addr_5, tid) emit Transferred(tid, 0, addr_5) }
if count > 6 && is_address(addr_6) && addr_6 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_6 self.tokens[tid].minted_epoch = epoch self.balances[addr_6] += 1 emit Minted(addr_6, tid, 3, 0) emit Airdropped(addr_6, tid) emit Transferred(tid, 0, addr_6) }
if count > 7 && is_address(addr_7) && addr_7 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_7 self.tokens[tid].minted_epoch = epoch self.balances[addr_7] += 1 emit Minted(addr_7, tid, 3, 0) emit Airdropped(addr_7, tid) emit Transferred(tid, 0, addr_7) }
if count > 8 && is_address(addr_8) && addr_8 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_8 self.tokens[tid].minted_epoch = epoch self.balances[addr_8] += 1 emit Minted(addr_8, tid, 3, 0) emit Airdropped(addr_8, tid) emit Transferred(tid, 0, addr_8) }
if count > 9 && is_address(addr_9) && addr_9 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_9 self.tokens[tid].minted_epoch = epoch self.balances[addr_9] += 1 emit Minted(addr_9, tid, 3, 0) emit Airdropped(addr_9, tid) emit Transferred(tid, 0, addr_9) }
if count > 10 && is_address(addr_10) && addr_10 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_10 self.tokens[tid].minted_epoch = epoch self.balances[addr_10] += 1 emit Minted(addr_10, tid, 3, 0) emit Airdropped(addr_10, tid) emit Transferred(tid, 0, addr_10) }
if count > 11 && is_address(addr_11) && addr_11 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_11 self.tokens[tid].minted_epoch = epoch self.balances[addr_11] += 1 emit Minted(addr_11, tid, 3, 0) emit Airdropped(addr_11, tid) emit Transferred(tid, 0, addr_11) }
if count > 12 && is_address(addr_12) && addr_12 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_12 self.tokens[tid].minted_epoch = epoch self.balances[addr_12] += 1 emit Minted(addr_12, tid, 3, 0) emit Airdropped(addr_12, tid) emit Transferred(tid, 0, addr_12) }
if count > 13 && is_address(addr_13) && addr_13 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_13 self.tokens[tid].minted_epoch = epoch self.balances[addr_13] += 1 emit Minted(addr_13, tid, 3, 0) emit Airdropped(addr_13, tid) emit Transferred(tid, 0, addr_13) }
if count > 14 && is_address(addr_14) && addr_14 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_14 self.tokens[tid].minted_epoch = epoch self.balances[addr_14] += 1 emit Minted(addr_14, tid, 3, 0) emit Airdropped(addr_14, tid) emit Transferred(tid, 0, addr_14) }
if count > 15 && is_address(addr_15) && addr_15 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_15 self.tokens[tid].minted_epoch = epoch self.balances[addr_15] += 1 emit Minted(addr_15, tid, 3, 0) emit Airdropped(addr_15, tid) emit Transferred(tid, 0, addr_15) }
if count > 16 && is_address(addr_16) && addr_16 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_16 self.tokens[tid].minted_epoch = epoch self.balances[addr_16] += 1 emit Minted(addr_16, tid, 3, 0) emit Airdropped(addr_16, tid) emit Transferred(tid, 0, addr_16) }
if count > 17 && is_address(addr_17) && addr_17 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_17 self.tokens[tid].minted_epoch = epoch self.balances[addr_17] += 1 emit Minted(addr_17, tid, 3, 0) emit Airdropped(addr_17, tid) emit Transferred(tid, 0, addr_17) }
if count > 18 && is_address(addr_18) && addr_18 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_18 self.tokens[tid].minted_epoch = epoch self.balances[addr_18] += 1 emit Minted(addr_18, tid, 3, 0) emit Airdropped(addr_18, tid) emit Transferred(tid, 0, addr_18) }
if count > 19 && is_address(addr_19) && addr_19 != 0 { let tid = self.total_minted self.total_minted += 1 actual += 1 self.tokens[tid].owner = addr_19 self.tokens[tid].minted_epoch = epoch self.balances[addr_19] += 1 emit Minted(addr_19, tid, 3, 0) emit Airdropped(addr_19, tid) emit Transferred(tid, 0, addr_19) }
self.airdropped_count += actual
require(actual == count, "xcollection: address mismatch — all addresses up to count must be valid and non-zero")
return true
}
fn transfer(to: address, token_id: int): bool {
only_initialized()
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
require(is_address(to) && to != 0, "xcollection: invalid address")
let current_owner = self.tokens[token_id].owner
require(caller == current_owner, "xcollection: not owner")
require(to != current_owner, "xcollection: already owner")
self.tokens[token_id].owner = to
self.balances[current_owner] -= 1
self.balances[to] += 1
self.approvals[token_id] = 0
emit Transferred(token_id, current_owner, to)
return true
}
fn transfer_from(from: address, to: address, token_id: int): bool {
only_initialized()
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
require(is_address(to) && to != 0, "xcollection: invalid address")
let current_owner = self.tokens[token_id].owner
require(from == current_owner, "xcollection: from is not owner")
require(to != current_owner, "xcollection: already owner")
let approved = self.approvals[token_id]
let is_op = self.operator_approvals[from][caller]
require(
caller == current_owner ||
caller == approved ||
is_op == 1,
"xcollection: not authorized"
)
self.tokens[token_id].owner = to
self.balances[from] -= 1
self.balances[to] += 1
self.approvals[token_id] = 0
emit Transferred(token_id, from, to)
return true
}
fn approve(approved: address, token_id: int): bool {
only_initialized()
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
require(is_address(approved) && approved != 0, "xcollection: use revoke_approval to clear")
let current_owner = self.tokens[token_id].owner
let is_op = self.operator_approvals[current_owner][caller]
require(caller == current_owner || is_op == 1, "xcollection: not authorized")
self.approvals[token_id] = approved
emit Approved(token_id, approved)
return true
}
fn revoke_approval(token_id: int): bool {
only_initialized()
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
let current_owner = self.tokens[token_id].owner
let is_op = self.operator_approvals[current_owner][caller]
require(caller == current_owner || is_op == 1, "xcollection: not authorized")
self.approvals[token_id] = 0
emit ApprovalRevoked(token_id)
return true
}
fn set_operator(operator: address, approved: int): bool {
only_initialized()
require(operator != caller, "xcollection: cannot self-operate")
require(is_address(operator) && operator != 0, "xcollection: invalid operator")
require(approved == 0 || approved == 1, "xcollection: approved must be 0 or 1")
self.operator_approvals[caller][operator] = approved
emit OperatorSet(caller, operator, approved)
return true
}
fn claim_proceeds(): bool {
only_initialized()
let amount = self.pending_proceeds[caller]
require(amount > 0, "xcollection: nothing to claim")
self.pending_proceeds[caller] = 0
self.reserve -= amount
transfer(caller, amount)
emit ProceedsClaimed(caller, amount)
return true
}
fn reveal(uri: string): bool {
only_initialized()
only_owner_or_admin()
self.base_uri = uri
self.revealed = 1
emit Revealed(uri)
return true
}
fn update_base_uri(uri: string): bool {
only_initialized()
only_owner_or_admin()
self.base_uri = uri
emit BaseUriUpdated(uri)
return true
}
fn set_unrevealed_uri(uri: string): bool {
only_initialized()
only_owner_or_admin()
self.unrevealed_uri = uri
return true
}
fn update_phase_prices(gtd: int, fcfs: int, pub_price: int): bool {
only_initialized()
only_owner_or_admin()
require(gtd >= 0 && fcfs >= 0 && pub_price >= 0, "xcollection: invalid price")
self.gtd_price = gtd
self.fcfs_price = fcfs
self.public_price = pub_price
return true
}
fn update_phase_caps(gtd_cap: int, fcfs_cap: int, pub_cap: int): bool {
only_initialized()
only_owner_or_admin()
require(gtd_cap >= 0 && fcfs_cap >= 0 && pub_cap >= 0, "xcollection: invalid cap")
require(gtd_cap >= self.gtd_minted, "xcollection: cannot lower GTD cap below minted amount")
require(fcfs_cap >= self.fcfs_minted, "xcollection: cannot lower FCFS cap below minted amount")
require(pub_cap >= self.public_minted, "xcollection: cannot lower public cap below minted amount")
let active_injection = self.injection_cap
require(
gtd_cap + active_injection + fcfs_cap + pub_cap + self.airdropped_count <= self.max_supply,
"xcollection: caps plus airdrops exceed max supply"
)
self.gtd_cap = gtd_cap
self.fcfs_cap = fcfs_cap
self.public_cap = pub_cap
return true
}
fn update_phase_wallet_caps(gtd_wallet: int, fcfs_wallet: int, pub_wallet: int): bool {
only_initialized()
only_owner_or_admin()
require(gtd_wallet > 0 && fcfs_wallet > 0 && pub_wallet > 0, "xcollection: wallet caps must be > 0")
self.gtd_wallet_cap = gtd_wallet
self.fcfs_wallet_cap = fcfs_wallet
self.public_wallet_cap = pub_wallet
return true
}
fn update_phase_epochs(
gtd_start: int,
gtd_end: int,
fcfs_start: int,
fcfs_end: int,
pub_start: int,
pub_end: int
): bool {
only_initialized()
only_owner_or_admin()
let gtd_duration = gtd_end - gtd_start
let fcfs_duration = fcfs_end - fcfs_start
let pub_duration = pub_end - pub_start
require(gtd_duration >= MIN_PHASE_EPOCHS, "xcollection: GTD phase too short (min 1 hour)")
require(gtd_duration <= MAX_GTD_EPOCHS, "xcollection: GTD phase too long (max 30 days)")
require(pub_duration >= MIN_PHASE_EPOCHS, "xcollection: public phase too short (min 1 hour)")
require(fcfs_end >= fcfs_start, "xcollection: invalid FCFS range")
require(fcfs_start >= gtd_end, "xcollection: FCFS must start after GTD")
require(pub_start >= fcfs_end, "xcollection: public must start after FCFS")
if fcfs_start < fcfs_end {
require(
fcfs_duration >= MIN_PHASE_EPOCHS && fcfs_duration <= MAX_FCFS_EPOCHS,
"xcollection: FCFS phase duration invalid"
)
}
self.gtd_start_epoch = gtd_start
self.gtd_end_epoch = gtd_end
self.fcfs_start_epoch = fcfs_start
self.fcfs_end_epoch = fcfs_end
self.public_start_epoch = pub_start
self.public_end_epoch = pub_end
return true
}
fn set_phase_override(phase_num: int): bool {
only_initialized()
only_owner_or_admin()
require(phase_num >= 0 && phase_num <= 3, "xcollection: invalid phase")
self.phase_override_active = 1
self.phase_override_value = phase_num
emit PhaseOverrideSet(phase_num)
return true
}
fn clear_phase_override(): bool {
only_initialized()
only_owner_or_admin()
self.phase_override_active = 0
self.phase_override_value = 3
emit PhaseOverrideCleared()
return true
}
fn pause(): bool {
only_initialized()
only_owner_or_admin()
self.paused = 1
return true
}
fn unpause(): bool {
only_initialized()
only_owner_or_admin()
self.paused = 0
return true
}
fn set_max_per_wallet(max_val: int): bool {
only_initialized()
only_owner_or_admin()
require(max_val > 0, "xcollection: invalid cap")
self.max_per_wallet = max_val
return true
}
fn update_xpectrum_wallet(new_wallet: address): bool {
only_initialized()
require(caller == self.admin, "xcollection: only admin")
require(is_address(new_wallet) && new_wallet != 0, "xcollection: invalid address")
self.xpectrum_wallet = new_wallet
emit XpectrumWalletUpdated(new_wallet)
return true
}
fn update_admin(new_admin: address): bool {
only_initialized()
require(caller == self.admin, "xcollection: only admin")
require(is_address(new_admin) && new_admin != 0, "xcollection: invalid address")
self.admin = new_admin
return true
}
fn update_royalty_receiver(new_receiver: address): bool {
only_initialized()
require(
caller == self.royalty_receiver || caller == self.owner,
"xcollection: not authorized"
)
require(is_address(new_receiver) && new_receiver != 0, "xcollection: invalid address")
self.royalty_receiver = new_receiver
return true
}
fn transfer_ownership(new_owner: address): bool {
only_initialized()
require(caller == self.owner, "xcollection: not owner")
require(is_address(new_owner) && new_owner != 0, "xcollection: invalid address")
let old = self.owner
self.owner = new_owner
emit OwnershipTransferred(old, new_owner)
return true
}
fn update_xlist(new_xlist: address): bool {
only_initialized()
require(caller == self.admin, "xcollection: only admin")
require(is_address(new_xlist) && new_xlist != 0, "xcollection: invalid address")
self.xlist_addr = new_xlist
return true
}
view fn owner_of(token_id: int): address {
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
return self.tokens[token_id].owner
}
view fn creator_of(token_id: int): address {
return self.royalty_receiver
}
view fn royalty_of(token_id: int): int {
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
return self.royalty_bps
}
view fn token_uri(token_id: int): string {
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
if self.revealed == 1 {
let uri = self.base_uri
uri = concat(uri, to_string(token_id))
uri = concat(uri, ".json")
return uri
}
return self.unrevealed_uri
}
view fn collection_of(token_id: int): string {
return self.name
}
view fn balance_of(addr: address): int {
return self.balances[addr]
}
view fn get_approved(token_id: int): address {
return self.approvals[token_id]
}
view fn total_supply(): int {
return self.total_minted
}
view fn collection_name(): string {
return self.name
}
view fn symbol(): string {
return self.symbol
}
view fn is_approved_or_owner(token_id: int, addr: address): int {
require(addr != 0, "xcollection: zero address")
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
let current_owner = self.tokens[token_id].owner
if addr == current_owner {
return 1
}
if self.approvals[token_id] == addr {
return 1
}
if self.operator_approvals[current_owner][addr] == 1 {
return 1
}
return 0
}
view fn get_minted_by_wallet(addr: address): int {
return self.minted_per_wallet[addr]
}
view fn get_pending_proceeds(addr: address): int {
return self.pending_proceeds[addr]
}
view fn get_reserve(): int {
return self.reserve
}
view fn is_genesis_id_used(genesis_id: int): int {
return self.used_genesis_ids[genesis_id]
}
view fn get_genesis_status(): string {
let out = to_string(self.genesis_minted_count)
out = concat(out, "|")
out = concat(out, to_string(self.injection_cap))
out = concat(out, "|")
out = concat(out, to_string(self.genesis_contract))
out = concat(out, "|")
out = concat(out, to_string(self.injection_swept))
return out
}
view fn get_token_info(token_id: int): string {
require(token_id >= 0 && token_id < self.total_minted, "xcollection: token not found")
let uri = self.unrevealed_uri
if self.revealed == 1 {
uri = concat(self.base_uri, to_string(token_id))
uri = concat(uri, ".json")
}
let out = to_string(token_id)
out = concat(out, "|")
out = concat(out, to_string(self.tokens[token_id].owner))
out = concat(out, "|")
out = concat(out, to_string(self.royalty_receiver))
out = concat(out, "|")
out = concat(out, self.name)
out = concat(out, "|")
out = concat(out, to_string(self.royalty_bps))
out = concat(out, "|")
out = concat(out, to_string(self.tokens[token_id].minted_epoch))
out = concat(out, "|")
out = concat(out, uri)
return out
}
view fn get_contract_info(): string {
let out = self.name
out = concat(out, "|")
out = concat(out, self.symbol)
out = concat(out, "|")
out = concat(out, to_string(self.total_minted))
out = concat(out, "|")
out = concat(out, to_string(self.max_supply))
out = concat(out, "|")
out = concat(out, to_string(self.royalty_bps))
out = concat(out, "|")
out = concat(out, to_string(self.owner))
out = concat(out, "|")
out = concat(out, to_string(self.revealed))
return out
}
view fn get_phase_info(): string {
let out = to_string(self.gtd_price)
out = concat(out, "|")
out = concat(out, to_string(self.gtd_start_epoch))
out = concat(out, "|")
out = concat(out, to_string(self.gtd_end_epoch))
out = concat(out, "|")
out = concat(out, to_string(self.gtd_cap))
out = concat(out, "|")
out = concat(out, to_string(self.gtd_minted))
out = concat(out, "|")
out = concat(out, to_string(self.gtd_wallet_cap))
out = concat(out, "|")
out = concat(out, to_string(self.fcfs_price))
out = concat(out, "|")
out = concat(out, to_string(self.fcfs_start_epoch))
out = concat(out, "|")
out = concat(out, to_string(self.fcfs_end_epoch))
out = concat(out, "|")
out = concat(out, to_string(self.fcfs_cap))
out = concat(out, "|")
out = concat(out, to_string(self.fcfs_minted))
out = concat(out, "|")
out = concat(out, to_string(self.fcfs_wallet_cap))
out = concat(out, "|")
out = concat(out, to_string(self.public_price))
out = concat(out, "|")
out = concat(out, to_string(self.public_start_epoch))
out = concat(out, "|")
out = concat(out, to_string(self.public_end_epoch))
out = concat(out, "|")
out = concat(out, to_string(self.public_cap))
out = concat(out, "|")
out = concat(out, to_string(self.public_minted))
out = concat(out, "|")
out = concat(out, to_string(self.public_wallet_cap))
out = concat(out, "|")
out = concat(out, to_string(self.injection_cap))
out = concat(out, "|")
out = concat(out, to_string(self.genesis_minted_count))
return out
}
}