Address
octFTPfF…wi4DpE
octFTPfFKYKdDorGxcUmtS7KT4Rz4sbDom2ZKpC8Pwi4DpE
State
OCT balance
250.375OCT
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 SpectrumMarketplace {
struct Listing {
seller: address
nft_contract: address
token_id: int
price: int
status: int
listed_epoch: int
}
struct Offer {
offerer: address
nft_contract: address
token_id: int
amount: int
expires_epoch: int
active: int
}
event Listed(listing_id: int, seller: address, nft_contract: address, token_id: int, price: int)
event Sale(listing_id: int, seller: address, buyer: address, nft_contract: address, token_id: int, price: int)
event ListingCancelled(listing_id: int)
event OfferMade(offer_id: int, offerer: address, nft_contract: address, token_id: int, amount: int)
event OfferAccepted(offer_id: int, seller: address, nft_contract: address, token_id: int)
event OfferCancelled(offer_id: int)
event ProceedsClaimed(recipient: address, amount: int)
const MARKETPLACE_FEE_BPS: int = 250
const MAX_ROYALTY_BPS: int = 1000
state {
owner: address
listing_count: int
offer_count: int
listings: map[int]Listing
offers: map[int]Offer
contract_token_to_listing: map[address]map[int]int
total_volume: int
fee_balance: int
reserve: int
offer_reserve: int
pending_proceeds: map[address]int
}
constructor() {
self.owner = origin
self.listing_count = 0
self.offer_count = 0
self.total_volume = 0
self.fee_balance = 0
self.reserve = 0
self.offer_reserve = 0
}
private fn only_owner() {
require(caller == self.owner, "not authorized")
}
fn list_nft(nft_contract: address, token_id: int, price: int): int {
require(is_address(nft_contract) && nft_contract != 0, "invalid nft contract")
require(price > 0, "price must be > 0")
require(token_id >= 0, "invalid token id")
let existing = self.contract_token_to_listing[nft_contract][token_id]
if existing > 0 {
let stale_id = existing - 1
let current_owner = call(nft_contract, "owner_of", token_id)
require(current_owner == caller, "token already listed by another owner")
self.listings[stale_id].status = 2
self.contract_token_to_listing[nft_contract][token_id] = 0
emit ListingCancelled(stale_id)
}
let is_approved = call(nft_contract, "is_approved_or_owner", token_id, self_addr)
require(is_approved == 1, "marketplace not approved")
let listing_id = self.listing_count
self.listings[listing_id].seller = caller
self.listings[listing_id].nft_contract = nft_contract
self.listings[listing_id].token_id = token_id
self.listings[listing_id].price = price
self.listings[listing_id].status = 1
self.listings[listing_id].listed_epoch = epoch
self.listing_count += 1
self.contract_token_to_listing[nft_contract][token_id] = listing_id + 1
emit Listed(listing_id, caller, nft_contract, token_id, price)
return listing_id
}
fn cancel_listing(listing_id: int): bool {
require(listing_id >= 0 && listing_id < self.listing_count, "listing not found")
require(self.listings[listing_id].status == 1, "listing not active")
require(self.listings[listing_id].seller == caller, "not seller")
let nft_contract = self.listings[listing_id].nft_contract
let token_id = self.listings[listing_id].token_id
self.listings[listing_id].status = 0
self.contract_token_to_listing[nft_contract][token_id] = 0
emit ListingCancelled(listing_id)
return true
}
fn buy_nft(listing_id: int): bool {
require(listing_id >= 0 && listing_id < self.listing_count, "listing not found")
require(self.listings[listing_id].status == 1, "listing not active")
let seller = self.listings[listing_id].seller
let nft_contract = self.listings[listing_id].nft_contract
let token_id = self.listings[listing_id].token_id
let price = self.listings[listing_id].price
require(caller != seller, "cannot buy own listing")
require(value >= price, "insufficient payment")
let is_approved = call(nft_contract, "is_approved_or_owner", token_id, self_addr)
require(is_approved == 1, "marketplace approval revoked")
let creator = call(nft_contract, "creator_of", token_id)
let royalty_bps = call(nft_contract, "royalty_of", token_id)
let safe_royalty = royalty_bps
if safe_royalty > MAX_ROYALTY_BPS {
safe_royalty = MAX_ROYALTY_BPS
}
let fee = price * MARKETPLACE_FEE_BPS / 10000
let royalty = price * safe_royalty / 10000
let seller_proceeds = price - fee - royalty
self.listings[listing_id].status = 0
self.contract_token_to_listing[nft_contract][token_id] = 0
self.reserve += price
self.fee_balance += fee
assert_address(seller)
assert_address(creator)
self.pending_proceeds[seller] += seller_proceeds
self.pending_proceeds[creator] += royalty
self.total_volume += price
let refund = value - price
if refund > 0 {
self.reserve -= refund
self.pending_proceeds[caller] += refund
}
call(nft_contract, "transfer_from", seller, caller, token_id)
emit Sale(listing_id, seller, caller, nft_contract, token_id, price)
return true
}
fn make_offer(nft_contract: address, token_id: int, duration_epochs: int): int {
require(is_address(nft_contract) && nft_contract != 0, "invalid nft contract")
require(token_id >= 0, "invalid token id")
require(value > 0, "offer must include payment")
require(duration_epochs > 0 && duration_epochs <= 518400, "invalid duration")
let offer_id = self.offer_count
self.offers[offer_id].offerer = caller
self.offers[offer_id].nft_contract = nft_contract
self.offers[offer_id].token_id = token_id
self.offers[offer_id].amount = value
self.offers[offer_id].expires_epoch = epoch + duration_epochs
self.offers[offer_id].active = 1
self.offer_count += 1
self.offer_reserve += value
emit OfferMade(offer_id, caller, nft_contract, token_id, value)
return offer_id
}
fn cancel_offer(offer_id: int): bool {
require(offer_id >= 0 && offer_id < self.offer_count, "offer not found")
require(self.offers[offer_id].active == 1, "offer not active")
require(self.offers[offer_id].offerer == caller || caller == self.owner, "not authorized")
let amount = self.offers[offer_id].amount
self.offers[offer_id].active = 0
self.offer_reserve -= amount
transfer(caller, amount)
emit OfferCancelled(offer_id)
return true
}
fn accept_offer(offer_id: int): bool {
require(offer_id >= 0 && offer_id < self.offer_count, "offer not found")
require(self.offers[offer_id].active == 1, "offer not active")
require(epoch < self.offers[offer_id].expires_epoch, "offer expired")
let offerer = self.offers[offer_id].offerer
let nft_contract = self.offers[offer_id].nft_contract
let token_id = self.offers[offer_id].token_id
let amount = self.offers[offer_id].amount
let current_owner = call(nft_contract, "owner_of", token_id)
require(current_owner == caller, "not token owner")
let is_approved = call(nft_contract, "is_approved_or_owner", token_id, self_addr)
require(is_approved == 1, "marketplace not approved")
let creator = call(nft_contract, "creator_of", token_id)
let royalty_bps = call(nft_contract, "royalty_of", token_id)
let safe_royalty = royalty_bps
if safe_royalty > MAX_ROYALTY_BPS {
safe_royalty = MAX_ROYALTY_BPS
}
let fee = amount * MARKETPLACE_FEE_BPS / 10000
let royalty = amount * safe_royalty / 10000
let seller_proceeds = amount - fee - royalty
self.offers[offer_id].active = 0
self.offer_reserve -= amount
self.reserve += amount
self.fee_balance += fee
assert_address(caller)
assert_address(creator)
self.pending_proceeds[caller] += seller_proceeds
self.pending_proceeds[creator] += royalty
self.total_volume += amount
if self.contract_token_to_listing[nft_contract][token_id] > 0 {
let stale_id = self.contract_token_to_listing[nft_contract][token_id] - 1
self.listings[stale_id].status = 2
self.contract_token_to_listing[nft_contract][token_id] = 0
emit ListingCancelled(stale_id)
}
call(nft_contract, "transfer_from", caller, offerer, token_id)
emit OfferAccepted(offer_id, caller, nft_contract, token_id)
return true
}
fn claim_proceeds(): bool {
let amount = self.pending_proceeds[caller]
require(amount > 0, "nothing to claim")
self.pending_proceeds[caller] = 0
self.reserve -= amount
transfer(caller, amount)
emit ProceedsClaimed(caller, amount)
return true
}
fn withdraw_fees(): bool {
only_owner()
let amount = self.fee_balance
require(amount > 0, "no fees to withdraw")
self.fee_balance = 0
self.reserve -= amount
transfer(self.owner, amount)
return true
}
view fn get_listing(listing_id: int): string {
require(listing_id >= 0 && listing_id < self.listing_count, "listing not found")
return concat(to_string(listing_id), concat("|", concat(to_string(self.listings[listing_id].seller), concat("|", concat(to_string(self.listings[listing_id].nft_contract), concat("|", concat(to_string(self.listings[listing_id].token_id), concat("|", concat(to_string(self.listings[listing_id].price), concat("|", to_string(self.listings[listing_id].status)))))))))))
}
view fn get_offer(offer_id: int): string {
require(offer_id >= 0 && offer_id < self.offer_count, "offer not found")
return concat(to_string(offer_id), concat("|", concat(to_string(self.offers[offer_id].offerer), concat("|", concat(to_string(self.offers[offer_id].nft_contract), concat("|", concat(to_string(self.offers[offer_id].token_id), concat("|", concat(to_string(self.offers[offer_id].amount), concat("|", concat(to_string(self.offers[offer_id].expires_epoch), concat("|", to_string(self.offers[offer_id].active)))))))))))))
}
view fn get_pending_proceeds(addr: address): int {
return self.pending_proceeds[addr]
}
view fn get_listing_count(): int {
return self.listing_count
}
view fn get_offer_count(): int {
return self.offer_count
}
view fn get_total_volume(): int {
return self.total_volume
}
view fn get_fee_balance(): int {
return self.fee_balance
}
view fn get_reserve(): int {
return self.reserve
}
view fn get_offer_reserve(): int {
return self.offer_reserve
}
view fn get_token_listing(nft_contract: address, token_id: int): int {
let val = self.contract_token_to_listing[nft_contract][token_id]
if val == 0 {
return -1
}
return val - 1
}
}