Bionttestnet
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
balance250.37502 OCT
version1.0 Rehovot
code hashf08e18…d39975
History
live · 20s · last 0
Source · ABI · Bytecode
✓ verified
expand →
✓ 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
  }
}