DeploySEALED

■█◆□!▫%%■#■^@▓~*░▒█▒^^●%^○%░█▫▓▓~○●!@█□#$●▓~░?□╱^!~○◆~!?╲█▒□▫~▓*

Transaction ID

Timestamp

Oct 10, 2025, 08:27:17 AM UTC
4mo ago

Block Height

128,977,341

Computation

0

Execution Fee

0.00000899 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
Bag
1codeString
import NonFungibleToken from 0x1d7e57aa55817448 import FungibleToken from 0xf233dcee88fe0abe import FlowToken from 0x1654653399040a61 import MetadataViews from 0x1d7e57aa55817448 import ViewResolver from 0x1d7e57aa55817448 import Base64Util from 0xa45ead1cf1ca9eda import BagRegistry from 0xc649103ebbc38926 import Background from 0xc649103ebbc38926 import Body from 0xc649103ebbc38926 import Cloth from 0xc649103ebbc38926 import Weapon from 0xc649103ebbc38926 import Glove from 0xc649103ebbc38926 import Ring from 0xc649103ebbc38926 import Helmet from 0xc649103ebbc38926 import Rarity from 0xc649103ebbc38926 access(all) contract Bag: NonFungibleToken, ViewResolver { /* --- Events --- */ access(all) event ContractInitialized() access(all) event NFTWithdrawn(id: UInt64, from: Address?) access(all) event NFTDeposited(id: UInt64, to: Address?) access(all) event NFTMinted(id: UInt64, svg: String, mintedFor: Address) access(all) event PriceUpdated(oldPrice: UFix64, newPrice: UFix64) /* --- Storage Paths --- */ access(all) let CollectionStoragePath: StoragePath access(all) let CollectionPublicPath: PublicPath access(all) let CollectionPrivatePath: PrivatePath access(all) let AdminStoragePath: StoragePath /* --- Contract State --- */ access(all) var totalSupply: UInt64 access(all) let maxSupply: UInt64 access(all) var mintPrice: UFix64 access(all) let reservedSupply: UInt64 access(all) var reservedMinted: UInt64 access(all) var traitsDetails: {UInt64: Bag.TraitsDetails} access(all) var bagRarityScores: {UInt64: UInt64} access(all) var uniqueTraitsCombinations: [String] access(all) let registryAddress: Address access(self) let owner: Address // Price update configuration access(all) var priceUpdateInterval: UInt64 access(all) let priceIncreaseAmount: UFix64 access(all) struct TraitsDetails { access(all) var background: String access(all) var body: String access(all) var cloth: String access(all) var glove: String access(all) var helmet: String access(all) var ring: String access(all) var weapon: String init(background: String, body: String, cloth: String, glove: String, helmet: String, ring: String, weapon: String) { self.background = background self.body = body self.cloth = cloth self.glove = glove self.helmet = helmet self.ring = ring self.weapon = weapon } } access(all) resource NFT: NonFungibleToken.NFT { access(all) let id: UInt64 access(all) let svg: String access(all) var winCount: UInt64 access(all) var rarityScore: UInt64 init(id: UInt64, svg: String, rarityScore: UInt64) { self.id = id self.svg = svg self.rarityScore = rarityScore self.winCount = 0 } access(all) view fun getSVG(): String { return self.svg } access(all) view fun getWinCount(): UInt64 { return self.winCount } access(all) view fun getRarityScore(): UInt64 { return self.rarityScore } access(all) fun updateRarityScore(newScore: UInt64) { self.rarityScore = newScore } access(all) fun incrementWinCount() { self.winCount = self.winCount + 1 } access(all) view fun getViews(): [Type] { return [ Type<MetadataViews.Display>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.Traits>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.NFTView>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>() ] } access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type<MetadataViews.Display>(): return MetadataViews.Display( name: "Bag # ".concat(self.id.toString()), description: "Bag (OCB) is a fully on-chain GameFi asset built on the Flow blockchain, powered by Flow VRF randomness to ensure complete fairness. Each Bag contains 7 unique traits, forming a one-of-a-kind warrior identity. Bag is not just about art — every mint amount is staked into Flow nodes, generating real yield that flows back to holders. It's identity, utility, and rewards, all packed into a single Bag.", thumbnail: MetadataViews.HTTPFile(url: self.getSVG()) ) case Type<MetadataViews.ExternalURL>(): return MetadataViews.ExternalURL("https://onchainbag.xyz") case Type<MetadataViews.Traits>(): return Bag.resolveNFTTraits(nftId: self.id) case Type<MetadataViews.Royalties>(): let owner = getAccount(Bag.owner) let cut = MetadataViews.Royalty( receiver: owner.capabilities.get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver), cut: 0.05, // 5% royalty description: "Creator Royalty" ) var royalties: [MetadataViews.Royalty] = [cut] return MetadataViews.Royalties(royalties) case Type<MetadataViews.NFTView>(): let display = self.resolveView(Type<MetadataViews.Display>())! as! MetadataViews.Display let externalURL = self.resolveView(Type<MetadataViews.ExternalURL>())! as! MetadataViews.ExternalURL let collectionData = Bag.resolveContractView( resourceType: self.getType(), viewType: Type<MetadataViews.NFTCollectionData>() )! as! MetadataViews.NFTCollectionData let collectionDisplay = Bag.resolveContractView( resourceType: self.getType(), viewType: Type<MetadataViews.NFTCollectionDisplay>() )! as! MetadataViews.NFTCollectionDisplay let royalties = self.resolveView(Type<MetadataViews.Royalties>())! as! MetadataViews.Royalties let traits = self.resolveView(Type<MetadataViews.Traits>())! as! MetadataViews.Traits return MetadataViews.NFTView( id: self.id, uuid: self.uuid, display: display, externalURL: externalURL, collectionData: collectionData, collectionDisplay: collectionDisplay, royalties: royalties, traits: traits ) case Type<MetadataViews.NFTCollectionData>(): return Bag.resolveContractView(resourceType: Type<@NFT>(), viewType: Type<MetadataViews.NFTCollectionData>()) case Type<MetadataViews.NFTCollectionDisplay>(): return Bag.resolveContractView(resourceType: Type<@NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>()) default: return nil } } access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <- Bag.createEmptyCollection(nftType: Type<@Bag.NFT>()) } } access(all) resource interface CollectionPublic { access(all) view fun getLength(): Int access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? access(all) fun forEachID(_ f: fun (UInt64): Bool): Void access(all) fun deposit(token: @{NonFungibleToken.NFT}) access(all) fun borrowBagNFT(id: UInt64): &Bag.NFT? { post { (result == nil) || (result?.id == id): "Cannot borrow Bag reference: The ID of the returned reference is incorrect" } } } access(all) resource Collection: CollectionPublic, NonFungibleToken.Collection { access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} init() { self.ownedNFTs <- {} } access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { pre { self.ownedNFTs.containsKey(withdrawID): "NFT with ID ".concat(withdrawID.toString()).concat(" not found in collection") } let owner = self.owner?.address! let token <- self.ownedNFTs.remove(key: withdrawID)! let id = token.id // Call BagRegistry to unregister holder if let ownerAddress = self.owner?.address { let registryRef = getAccount(Bag.registryAddress).contracts.borrow<&BagRegistry>(name: "BagRegistry") let _ = registryRef?.onNFTWithdrawn(bagId: id, from: owner)! } emit NFTWithdrawn(id: id, from: owner) return <- token } access(all) view fun getSupportedNFTTypes(): {Type: Bool} { return {Type<@Bag.NFT>(): true} } access(all) view fun isSupportedNFTType(type: Type): Bool { return self.getSupportedNFTTypes()[type] ?? false } access(all) fun deposit(token: @{NonFungibleToken.NFT}) { pre { self.isSupportedNFTType(type: token.getType()): "Unsupported NFT type: ".concat(token.getType().identifier) self.ownedNFTs[token.id] == nil: "NFT with ID ".concat(token.id.toString()).concat(" already exists in collection") } let token <- token as! @Bag.NFT let id = token.id let existingToken <- self.ownedNFTs[id] <- token emit NFTDeposited(id: id, to: self.owner?.address) destroy existingToken // Call BagRegistry to register holder if let ownerAddress = self.owner?.address { let registryRef = getAccount(Bag.registryAddress).contracts.borrow<&BagRegistry>(name: "BagRegistry") let _ = registryRef?.onNFTDeposited(bagId: id, to: ownerAddress)! } } access(all) view fun getCollectionSize(): Int { return self.ownedNFTs.length } access(all) fun forEachNFTId(_ callback: fun (UInt64): Bool) { self.ownedNFTs.forEachKey(callback) } access(all) view fun getIDs(): [UInt64] { return self.ownedNFTs.keys } access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { return &self.ownedNFTs[id] } access(all) fun borrowBagNFT(id: UInt64): &Bag.NFT? { if self.ownedNFTs[id] != nil { let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! let bagNFT = ref as! &Bag.NFT return bagNFT }else{ return nil } } access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection}{ return <- Bag.createEmptyCollection(nftType: Type<@Bag.NFT>()) } } access(all) resource Admin { access(all) fun mintReservedNFT(recipient: Address): @Bag.NFT { pre { Bag.reservedMinted < Bag.reservedSupply : "No reserved NFTs available. Reserved supply: ".concat(Bag.reservedSupply.toString()) recipient == Bag.owner: "Only contract owner can mint reserved NFTs" } Bag.totalSupply = Bag.totalSupply + 1 Bag.reservedMinted = Bag.reservedMinted + 1 let id = Bag.totalSupply let svg = Bag.createSVG() let rarityScore = Bag.bagRarityScores[id] ?? panic("Rarity score not found for NFT ID: ".concat(id.toString())) var newNFT <- create NFT(id: id, svg: svg, rarityScore: rarityScore) emit NFTMinted(id: newNFT.id, svg: newNFT.svg, mintedFor: recipient) return <- newNFT } } access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { return <- create Collection() } access(all) fun borrowNFT(ownerAddress: Address, nftId: UInt64): &Bag.NFT { let collectionRef = getAccount(ownerAddress) .capabilities .borrow<&Bag.Collection>(Bag.CollectionPublicPath) ?? panic("Cannot borrow collection reference from address: ".concat(ownerAddress.toString())) let nftRef = collectionRef.borrowBagNFT(id: nftId) ?? panic("NFT not found with ID: ".concat(nftId.toString())) return nftRef } access(self) fun getRandomBackground(): String { let index = Bag.generateRandomIndex(upperBound: Background.backgrounds.length) return Background.backgrounds[index] } access(self) fun getRandomBody(): String { let index = Bag.generateRandomIndex(upperBound: Body.body.length) return Body.body[index] } access(self) fun getRandomCloth(): String { let index = Bag.generateRandomIndex(upperBound: Cloth.cloths.length) return Cloth.cloths[index] } access(self) fun getRandomWeapon(): String { let index = Bag.generateRandomIndex(upperBound: Weapon.weapons.length) return Weapon.weapons[index] } access(self) fun getRandomGlove(): String { let index = Bag.generateRandomIndex(upperBound: Glove.gloves.length) return Glove.gloves[index] } access(self) fun getRandomRing(): String { let index = Bag.generateRandomIndex(upperBound: Ring.rings.length) return Ring.rings[index] } access(self) fun getRandomHelmet(): String { let index = Bag.generateRandomIndex(upperBound: Helmet.helmets.length) return Helmet.helmets[index] } access(self) fun generateRandomIndex(upperBound: Int): Int { assert(upperBound > 0, message: "Upper bound must be greater than 0") let randomValue: UInt64 = revertibleRandom<UInt64>() return Int(randomValue % UInt64(upperBound)) } access(self) fun getRarityScoreValue(rarity: String): UInt64 { switch rarity { case "Common": return 1 case "Rare": return 2 case "Epic": return 3 case "Legendary": return 5 default: panic("Unknown rarity type: ".concat(rarity)) } } access(self) fun calculateRarityScore(itemName: String): UInt64 { let rarity = Rarity.rarity[itemName] ?? "Common" return Bag.getRarityScoreValue(rarity: rarity) } access(self) fun generateSVG(): String { var totalRarityScore: UInt64 = 0 let bagId = Bag.totalSupply var svgContent = "<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'>" svgContent = svgContent.concat("<style>.bag { fill: white; font-family: serif; font-size: 14px; font-weight: bold} .base { fill: white; font-family: serif; font-size: 14px; } .title { fill: #ddd; font-family: Bookman; font-size: 10px; text-anchor: middle; }</style>") svgContent = svgContent.concat("<rect width='100%' height='100%' fill='black' />") svgContent = svgContent.concat("<text x='175' y='340' class='title'>build-on-flow</text>") svgContent = svgContent.concat("<text x='10' y='20' class='bag'>bag #".concat(bagId.toString()).concat("</text>")) var background: String = "" var body: String = "" var cloth: String = "" var weapon: String = "" var glove: String = "" var ring: String = "" var helmet: String = "" var traitsKey: String = "" var attempts = 0 let maxAttempts = 100 while attempts < maxAttempts { attempts = attempts + 1 background = Bag.getRandomBackground() body = Bag.getRandomBody() cloth = Bag.getRandomCloth() weapon = Bag.getRandomWeapon() glove = Bag.getRandomGlove() ring = Bag.getRandomRing() helmet = Bag.getRandomHelmet() traitsKey = Bag.generateTraitsKey( background: background, body: body, cloth: cloth, weapon: weapon, glove: glove, ring: ring, helmet: helmet ) if !self.uniqueTraitsCombinations.contains(traitsKey) { break } if attempts == maxAttempts { panic("Failed to generate unique traits combination after ".concat(maxAttempts.toString()).concat(" attempts")) } } self.uniqueTraitsCombinations.append(traitsKey) // Add traits to SVG let traits = [background, body, cloth, weapon, glove, ring, helmet] var yPosition = 60 for trait in traits { svgContent = svgContent.concat("<text x='10' y='".concat(yPosition.toString()).concat("' class='base'>").concat(trait).concat("</text>")) yPosition = yPosition + 20 totalRarityScore = totalRarityScore + self.calculateRarityScore(itemName: trait) } // Store traits details self.traitsDetails[bagId] = Bag.TraitsDetails(background:background, body:body, cloth:cloth, glove:glove, helmet:helmet, ring:ring, weapon:weapon) self.bagRarityScores[bagId] = totalRarityScore // Add rarity score svgContent = svgContent.concat("<text x='10' y='250' class='base'>rarity score: ".concat(totalRarityScore.toString()).concat("</text>")) svgContent = svgContent.concat("</svg>") return svgContent } access(self) fun generateTraitsKey(background: String, body: String, cloth: String, weapon: String, glove: String, ring: String, helmet: String): String { return background.concat("|") .concat(body).concat("|") .concat(cloth).concat("|") .concat(weapon).concat("|") .concat(glove).concat("|") .concat(ring).concat("|") .concat(helmet) } access(self) fun createSVG(): String { let svgImage = Bag.generateSVG() let base64SVG = Base64Util.encode(svgImage) return "data:image/svg+xml;base64,".concat(base64SVG) } access(all) fun mintNFT(user:Address, payment: @FlowToken.Vault): @Bag.NFT { pre { self.totalSupply < (self.maxSupply - self.reservedSupply): "Maximum supply reached. Total supply: ".concat(self.totalSupply.toString()) payment.balance >= self.mintPrice: "Insufficient payment. Required: ".concat(self.mintPrice.toString()).concat(", Provided: ").concat(payment.balance.toString()) self.getCollectionLength(user: user) < 20 : "Maximum 20 Bag per account" } let contractReceiver = self.account.capabilities .borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) ?? panic("Contract Flow token receiver capability not found") contractReceiver.deposit(from: <- payment) Bag.totalSupply = Bag.totalSupply + 1 let id = Bag.totalSupply let svg = Bag.createSVG() let rarityScore = self.bagRarityScores[id] ?? panic("Rarity score not found for NFT ID: ".concat(id.toString())) var newNFT <- create NFT(id: id, svg: svg, rarityScore: rarityScore) emit NFTMinted(id: newNFT.id, svg: newNFT.svg, mintedFor:user) if(Bag.totalSupply % Bag.priceUpdateInterval == 0 && Bag.totalSupply > 0){ self.updatePrice() } return <- newNFT } access(self) fun updatePrice() { let oldPrice = Bag.mintPrice Bag.mintPrice = Bag.mintPrice + Bag.priceIncreaseAmount emit PriceUpdated(oldPrice: oldPrice, newPrice: Bag.mintPrice) } access(all) fun resolveNFTTraits(nftId: UInt64): MetadataViews.Traits? { if let traits = self.traitsDetails[nftId] { let metadata: {String: AnyStruct} = { "Background": traits.background, "Body": traits.body, "Cloth": traits.cloth, "Glove": traits.glove, "Helmet": traits.helmet, "Ring": traits.ring, "Weapon": traits.weapon } return MetadataViews.dictToTraits(dict: metadata, excludedNames: []) } return nil } access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Royalties>() ] } access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { switch viewType { case Type<MetadataViews.NFTCollectionData>(): return MetadataViews.NFTCollectionData( storagePath: Bag.CollectionStoragePath, publicPath: Bag.CollectionPublicPath, publicCollection: Type<&Collection>(), publicLinkedType: Type<&Collection>(), createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} { return <-Bag.createEmptyCollection(nftType: Type<@Bag.NFT>()) }) ) case Type<MetadataViews.NFTCollectionDisplay>(): let media = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "https://white-worldwide-unicorn-392.mypinata.cloud/ipfs/bafybeigeadg24nqk5vuxjrcuv7w5p6ujxtzv3rilpigwh3c5wbmk2utyva" ), mediaType: "image/png" ) let mediaBanner = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "https://white-worldwide-unicorn-392.mypinata.cloud/ipfs/bafybeihoztqdrbkkdmb3jmo7yrx4kdcamq7hr5ohsuq3ks3kmpfo3yrwxm" ), mediaType: "image/png" ) return MetadataViews.NFTCollectionDisplay( name: "Bag Collection", description: "Own a Bag & get the chance to win weekly rewards and more", externalURL: MetadataViews.ExternalURL("https://onchainbag.xyz"), squareImage: media, bannerImage: mediaBanner, socials: {"twitter": MetadataViews.ExternalURL("https://x.com/onchainbag")} ) case Type<MetadataViews.Royalties>(): return MetadataViews.Royalties([]) default: return nil } } /* --- View Functions --- */ access(all) view fun getFlowBalance(): UFix64 { let vaultRef = self.account.capabilities .borrow<&FlowToken.Vault>(/public/flowTokenBalance) ?? panic("Flow token vault not found at /public/flowTokenBalance") return vaultRef.balance } access(all) view fun getMintPrice(): UFix64 { return self.mintPrice } access(all) view fun getTotalSupply(): UInt64 { return self.totalSupply } access(all) view fun getMaxSupply(): UInt64 { return self.maxSupply } access(all) view fun getReservedSupply(): UInt64 { return self.reservedSupply } access(all) view fun getReservedMinted(): UInt64 { return self.reservedMinted } access(all) view fun getTraitsDetails(nftId: UInt64): Bag.TraitsDetails? { return self.traitsDetails[nftId] } access(all) view fun getBagRarityScore(nftId: UInt64): UInt64? { return self.bagRarityScores[nftId] } access(all) view fun hasCollection(user: Address): Bool { let account = getAccount(user) return account.capabilities.get<&Bag.Collection>(Bag.CollectionPublicPath).check() } access(all) view fun getCollectionRef(user:Address): &Bag.Collection{ return getAccount(user).capabilities.get<&Bag.Collection>(Bag.CollectionPublicPath).borrow()?? panic("Cannot borrow collection reference") } access(all) view fun getCollectionLength(user: Address): Int { pre { self.hasCollection(user: user): "User does not have a Bag collection" } return self.getCollectionRef(user:user).getIDs().length } access(all) view fun getCollectionNFTIds(user: Address): [UInt64] { pre { self.hasCollection(user: user): "User does not have a Bag collection" } return self.getCollectionRef(user:user).getIDs() } access(all) fun getNFTWinCount(ownerAddress: Address, nftId: UInt64): UInt64 { let nft = Bag.borrowNFT(ownerAddress: ownerAddress, nftId: nftId) return nft.winCount } access(all) fun getNFTRarityScore(ownerAddress: Address, nftId: UInt64): UInt64 { let nft = Bag.borrowNFT(ownerAddress: ownerAddress, nftId: nftId) return nft.rarityScore } init(owner: Address, mintPrice:UFix64, reserveSupply:UInt64, registryAddress:Address) { self.totalSupply = 0 self.maxSupply = 7777 self.mintPrice = mintPrice self.traitsDetails = {} self.bagRarityScores = {} self.uniqueTraitsCombinations = [] self.owner = owner self.registryAddress = registryAddress self.reservedSupply = reserveSupply self.reservedMinted = 0 // After each interval, mint price will increase by 10.0 Flow // Giving benefit to early holders self.priceUpdateInterval = 555 self.priceIncreaseAmount = 10.0 self.CollectionStoragePath = /storage/BagCollections self.CollectionPublicPath = /public/BagCollectionPublics self.CollectionPrivatePath = /private/BagCollectionProviders self.AdminStoragePath = /storage/BagAdmins let collection <- create Collection() self.account.storage.save(<- collection, to: self.CollectionStoragePath) let admin <- create Admin() self.account.storage.save(<- admin, to: self.AdminStoragePath) let collectionCapability = self.account.capabilities.storage .issue<&Bag.Collection>(Bag.CollectionStoragePath) self.account.capabilities.publish(collectionCapability, at: self.CollectionPublicPath) emit ContractInitialized() } }
2arg0Address
3arg1UFix64
60.00000000
4arg2UInt64
100
5arg3Address

Cadence Script

1transaction(name: String, code: String ,arg0:Address,arg1:UFix64,arg2:UInt64,arg3:Address) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 ,arg0,arg1,arg2,arg3)
4		}
5	}