Smart Contract

BluefinTunaCoin

A.44100f14f70e3f78.BluefinTunaCoin

Valid From

118,782,276

Deployed

6d ago
Feb 22, 2026, 03:35:46 PM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import MetadataViews from 0x1d7e57aa55817448
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5access(all) contract BluefinTunaCoin: FungibleToken {
6
7    // FISHDEX INTEGRATION - Cross-contract coordination interface
8    access(all) resource interface SpeciesCoinPublic {
9        access(all) view fun getSpeciesCode(): String
10        access(all) view fun getTicker(): String
11        access(all) view fun getCommonName(): String
12        access(all) view fun getScientificName(): String
13        access(all) view fun getFamily(): String
14        access(all) view fun getTotalSupply(): UFix64
15        access(all) view fun getBasicRegistryInfo(): {String: AnyStruct}
16        access(all) fun redeemCatchNFT(fishData: {String: AnyStruct}, angler: Address): @BluefinTunaCoin.Vault
17    }
18
19    // FISHDEX COORDINATION - Registry management
20    access(all) var fishDEXAddress: Address?
21    access(all) var isRegisteredWithFishDEX: Bool
22
23    // Regional data structures for location-specific information
24    access(all) struct RegionalRegulations {
25        access(all) var sizeLimit: UFix64?         // Minimum legal size in inches
26        access(all) var bagLimit: UInt8?           // Daily catch limit  
27        access(all) var closedSeasons: [String]    // Protected breeding periods
28        access(all) var specialRegulations: String // Additional rules/restrictions
29        access(all) var licenseRequired: Bool      // Fishing license requirement
30        
31        init(sizeLimit: UFix64?, bagLimit: UInt8?, closedSeasons: [String], specialRegulations: String, licenseRequired: Bool) {
32            self.sizeLimit = sizeLimit
33            self.bagLimit = bagLimit
34            self.closedSeasons = closedSeasons
35            self.specialRegulations = specialRegulations
36            self.licenseRequired = licenseRequired
37        }
38    }
39
40    access(all) struct RegionalPopulation {
41        access(all) var populationTrend: String?   // "Increasing", "Stable", "Declining", "Critical"
42        access(all) var threats: [String]          // Region-specific threats
43        access(all) var protectedAreas: [String]   // Local protected areas
44        access(all) var estimatedPopulation: UInt64? // If known
45        
46        init(populationTrend: String?, threats: [String], protectedAreas: [String], estimatedPopulation: UInt64?) {
47            self.populationTrend = populationTrend
48            self.threats = threats
49            self.protectedAreas = protectedAreas
50            self.estimatedPopulation = estimatedPopulation
51        }
52    }
53
54    // Events
55    access(all) event TokensMinted(amount: UFix64, to: Address?)
56    access(all) event TokensBurned(amount: UFix64, from: Address?)
57    access(all) event CatchVerified(fishId: UInt64, angler: Address, amount: UFix64)
58    access(all) event MetadataUpdated(field: String, oldValue: String, newValue: String)
59    access(all) event FirstCatchRecorded(timestamp: UInt64, angler: Address)
60    access(all) event YearlyMetadataCreated(year: UInt64)
61    access(all) event MetadataYearUpdated(oldYear: UInt64, newYear: UInt64)
62    access(all) event NFTAlreadyMinted(fishId: UInt64, angler: Address)
63    
64    // FISHDEX INTEGRATION EVENTS
65    access(all) event FishDEXRegistrationAttempted(fishDEXAddress: Address, speciesCode: String)
66    access(all) event FishDEXRegistrationCompleted(fishDEXAddress: Address, speciesCode: String)
67    access(all) event FishDEXAddressUpdated(oldAddress: Address?, newAddress: Address)
68    access(all) event CatchProcessedFromNFT(fishNFTId: UInt64?, angler: Address, amount: UFix64)
69
70    // Total supply
71    access(all) var totalSupply: UFix64
72
73    // Track which NFTs have been used for minting
74    access(all) var fishNFTRegistry: {UInt64: Bool}
75
76    // Temporal metadata system - Track yearly updates
77    access(all) var currentMetadataYear: UInt64
78    access(all) var metadataHistory: {UInt64: SpeciesMetadata}
79    access(all) var speciesMetadata: SpeciesMetadata // Current year metadata (pointer to latest)
80    
81    // Regional context for location-specific operations
82    access(all) var defaultRegion: String
83
84    // Storage paths
85    access(all) let VaultStoragePath: StoragePath
86    access(all) let VaultPublicPath: PublicPath
87    access(all) let MinterStoragePath: StoragePath
88    access(all) let MetadataAdminStoragePath: StoragePath
89    access(all) let FishDEXCoordinatorStoragePath: StoragePath
90    access(all) let FishDEXCoordinatorPublicPath: PublicPath
91
92    // Species metadata - HYBRID: Core fields immutable, descriptive fields mutable + REGIONAL + TEMPORAL
93    access(all) struct SpeciesMetadata {
94        // IMMUTABLE - Core identity fields that should never change
95        access(all) let commonName: String         // e.g., "Example Fish"
96        access(all) let speciesCode: String        // e.g., "EXAMPLE_FISH"
97        access(all) let ticker: String             // e.g., "EXFISH"
98        access(all) let scientificName: String     // e.g., "Example fish"
99        access(all) let family: String             // e.g., "Example family"
100        access(all) let dataYear: UInt64           // Year this metadata represents
101        
102        // MUTABLE - Descriptive fields that can be updated (NULLABLE WHERE APPROPRIATE)
103        access(all) var habitat: String?           // e.g., "Freshwater" - nullable if unknown
104        access(all) var averageWeight: UFix64?     // in pounds - nullable if unknown
105        access(all) var averageLength: UFix64?     // in inches - nullable if unknown
106        access(all) var imageURL: String?          // species reference image - nullable
107        access(all) var description: String        // species description
108        access(all) var firstCatchDate: UInt64?    // timestamp of first verified catch
109        access(all) var rarityTier: UInt8?         // 1=Common, 2=Uncommon, 3=Rare, 4=Epic, 5=Legendary
110        
111        // GLOBAL CONSERVATION & POPULATION INTELLIGENCE
112        access(all) var globalConservationStatus: String? // IUCN status: "Least Concern", "Threatened", etc.
113        access(all) var regionalPopulations: {String: RegionalPopulation} // Region-specific population data
114        
115        // BIOLOGICAL INTELLIGENCE (NULLABLE WHERE DATA MAY BE MISSING)
116        access(all) var lifespan: UFix64?          // Maximum age in years
117        access(all) var diet: String?              // Primary food sources
118        access(all) var predators: [String]        // Natural predators (can be empty)
119        access(all) var temperatureRange: String?  // Preferred water temps
120        access(all) var depthRange: String?        // Habitat depth range
121        access(all) var spawningAge: UFix64?       // Sexual maturity age in years
122        access(all) var spawningBehavior: String?  // Detailed spawning patterns
123        access(all) var migrationPattern: String?  // Migration behavior
124        access(all) var waterQualityNeeds: String? // pH, oxygen, salinity requirements
125        
126        // GEOGRAPHIC & HABITAT INTELLIGENCE
127        access(all) var nativeRegions: [String]    // ["North America", "Great Lakes", "Mississippi River"]
128        access(all) var currentRange: [String]     // Current distribution (may differ from native)
129        access(all) var waterTypes: [String]       // ["River", "Lake", "Stream", "Reservoir"]
130        access(all) var invasiveStatus: String?    // "Native", "Introduced", "Invasive", "Hybrid"
131        
132        // ECONOMIC & COMMERCIAL INTELLIGENCE (REGIONAL)
133        access(all) var regionalCommercialValue: {String: UFix64} // Market price per pound by region
134        access(all) var tourismValue: UInt8?       // Tourism draw rating 1-10
135        access(all) var ecosystemRole: String?     // "Apex Predator", "Baitfish", "Bottom Feeder"
136        access(all) var culturalSignificance: String? // Historical/cultural importance
137        
138        // ANGLING & RECREATIONAL INTELLIGENCE
139        access(all) var bestBaits: [String]        // Most effective baits
140        access(all) var fightRating: UInt8?        // Fight intensity 1-10
141        access(all) var culinaryRating: UInt8?     // Eating quality 1-10
142        access(all) var catchDifficulty: UInt8?    // How hard to catch 1-10
143        access(all) var seasonalAvailability: String? // When most catchable
144        access(all) var bestTechniques: [String]   // Preferred fishing methods
145        
146        // REGIONAL REGULATORY INTELLIGENCE
147        access(all) var regionalRegulations: {String: RegionalRegulations} // Region-specific rules
148        
149        // PHYSICAL & BEHAVIORAL CHARACTERISTICS
150        access(all) var physicalDescription: String? // Colors, patterns, distinguishing features
151        access(all) var behaviorTraits: String?     // Feeding habits, aggression, schooling
152        access(all) var seasonalPatterns: String?   // Activity throughout the year
153        
154        // RECORDS & ACHIEVEMENTS
155        access(all) var recordWeight: UFix64?       // World record weight in pounds
156        access(all) var recordWeightLocation: String? // Where weight record was caught
157        access(all) var recordWeightDate: String?   // When weight record was set
158        access(all) var recordLength: UFix64?       // World record length in inches
159        access(all) var recordLengthLocation: String? // Where length record was caught
160        access(all) var recordLengthDate: String?   // When length record was set
161        
162        // RESEARCH & SCIENTIFIC INTELLIGENCE
163        access(all) var researchPriority: UInt8?   // Scientific research importance 1-10
164        access(all) var geneticMarkers: String?    // DNA/genetic information
165        access(all) var studyPrograms: [String]    // Active research programs
166        
167        // FLEXIBLE METADATA SYSTEM
168        access(all) var additionalMetadata: {String: String} // Custom key-value pairs for future expansion
169
170        // BASIC FIELD SETTERS
171        access(all) fun setHabitat(_ newHabitat: String) { self.habitat = newHabitat }
172        access(all) fun setAverageWeight(_ newWeight: UFix64) { self.averageWeight = newWeight }
173        access(all) fun setAverageLength(_ newLength: UFix64) { self.averageLength = newLength }
174        access(all) fun setImageURL(_ newURL: String) { self.imageURL = newURL }
175        access(all) fun setDescription(_ newDescription: String) { self.description = newDescription }
176        access(all) fun setFirstCatchDate(_ newDate: UInt64?) { self.firstCatchDate = newDate }
177        access(all) fun setRarityTier(_ newTier: UInt8) { self.rarityTier = newTier }
178        
179        // CONSERVATION & POPULATION SETTERS
180        access(all) fun setConservationStatus(_ newStatus: String) { self.globalConservationStatus = newStatus }
181        access(all) fun setRegionalPopulationTrend(_ region: String, _ newTrend: String?) { 
182            if let existing = self.regionalPopulations[region] {
183                self.regionalPopulations[region] = RegionalPopulation(
184                    populationTrend: newTrend,
185                    threats: existing.threats,
186                    protectedAreas: existing.protectedAreas,
187                    estimatedPopulation: existing.estimatedPopulation
188                )
189            }
190        }
191        access(all) fun setRegionalThreats(_ region: String, _ newThreats: [String]) { 
192            if let existing = self.regionalPopulations[region] {
193                self.regionalPopulations[region] = RegionalPopulation(
194                    populationTrend: existing.populationTrend,
195                    threats: newThreats,
196                    protectedAreas: existing.protectedAreas,
197                    estimatedPopulation: existing.estimatedPopulation
198                )
199            }
200        }
201        access(all) fun setRegionalProtectedAreas(_ region: String, _ newAreas: [String]) { 
202            if let existing = self.regionalPopulations[region] {
203                self.regionalPopulations[region] = RegionalPopulation(
204                    populationTrend: existing.populationTrend,
205                    threats: existing.threats,
206                    protectedAreas: newAreas,
207                    estimatedPopulation: existing.estimatedPopulation
208                )
209            }
210        }
211        
212        // BIOLOGICAL SETTERS
213        access(all) fun setLifespan(_ newLifespan: UFix64) { self.lifespan = newLifespan }
214        access(all) fun setDiet(_ newDiet: String) { self.diet = newDiet }
215        access(all) fun setPredators(_ newPredators: [String]) { self.predators = newPredators }
216        access(all) fun setTemperatureRange(_ newRange: String) { self.temperatureRange = newRange }
217        access(all) fun setDepthRange(_ newRange: String) { self.depthRange = newRange }
218        access(all) fun setSpawningAge(_ newAge: UFix64) { self.spawningAge = newAge }
219        access(all) fun setSpawningBehavior(_ newBehavior: String) { self.spawningBehavior = newBehavior }
220        access(all) fun setMigrationPattern(_ newPattern: String) { self.migrationPattern = newPattern }
221        access(all) fun setWaterQualityNeeds(_ newNeeds: String) { self.waterQualityNeeds = newNeeds }
222        
223        // GEOGRAPHIC & HABITAT SETTERS
224        access(all) fun setNativeRegions(_ newRegions: [String]) { self.nativeRegions = newRegions }
225        access(all) fun setCurrentRange(_ newRange: [String]) { self.currentRange = newRange }
226        access(all) fun setWaterTypes(_ newTypes: [String]) { self.waterTypes = newTypes }
227        access(all) fun setInvasiveStatus(_ newStatus: String) { self.invasiveStatus = newStatus }
228        
229        // ECONOMIC & COMMERCIAL SETTERS
230        access(all) fun setRegionalCommercialValue(_ region: String, _ newValue: UFix64) { self.regionalCommercialValue[region] = newValue }
231        access(all) fun setTourismValue(_ newValue: UInt8?) { self.tourismValue = newValue }
232        access(all) fun setEcosystemRole(_ newRole: String?) { self.ecosystemRole = newRole }
233        access(all) fun setCulturalSignificance(_ newSignificance: String?) { self.culturalSignificance = newSignificance }
234        
235        // ANGLING & RECREATIONAL SETTERS
236        access(all) fun setBestBaits(_ newBaits: [String]) { self.bestBaits = newBaits }
237        access(all) fun setFightRating(_ newRating: UInt8?) { self.fightRating = newRating }
238        access(all) fun setCulinaryRating(_ newRating: UInt8?) { self.culinaryRating = newRating }
239        access(all) fun setCatchDifficulty(_ newDifficulty: UInt8?) { self.catchDifficulty = newDifficulty }
240        access(all) fun setSeasonalAvailability(_ newAvailability: String?) { self.seasonalAvailability = newAvailability }
241        access(all) fun setBestTechniques(_ newTechniques: [String]) { self.bestTechniques = newTechniques }
242        
243        // REGULATORY SETTERS
244        access(all) fun setRegionalSizeLimit(_ region: String, _ newLimit: UFix64?) { 
245            if let existing = self.regionalRegulations[region] {
246                self.regionalRegulations[region] = RegionalRegulations(
247                    sizeLimit: newLimit,
248                    bagLimit: existing.bagLimit,
249                    closedSeasons: existing.closedSeasons,
250                    specialRegulations: existing.specialRegulations,
251                    licenseRequired: existing.licenseRequired
252                )
253            }
254        }
255        access(all) fun setRegionalBagLimit(_ region: String, _ newLimit: UInt8?) { 
256            if let existing = self.regionalRegulations[region] {
257                self.regionalRegulations[region] = RegionalRegulations(
258                    sizeLimit: existing.sizeLimit,
259                    bagLimit: newLimit,
260                    closedSeasons: existing.closedSeasons,
261                    specialRegulations: existing.specialRegulations,
262                    licenseRequired: existing.licenseRequired
263                )
264            }
265        }
266        access(all) fun setRegionalClosedSeasons(_ region: String, _ newSeasons: [String]) { 
267            if let existing = self.regionalRegulations[region] {
268                self.regionalRegulations[region] = RegionalRegulations(
269                    sizeLimit: existing.sizeLimit,
270                    bagLimit: existing.bagLimit,
271                    closedSeasons: newSeasons,
272                    specialRegulations: existing.specialRegulations,
273                    licenseRequired: existing.licenseRequired
274                )
275            }
276        }
277        access(all) fun setRegionalSpecialRegulations(_ region: String, _ newRegulations: String) { 
278            if let existing = self.regionalRegulations[region] {
279                self.regionalRegulations[region] = RegionalRegulations(
280                    sizeLimit: existing.sizeLimit,
281                    bagLimit: existing.bagLimit,
282                    closedSeasons: existing.closedSeasons,
283                    specialRegulations: newRegulations,
284                    licenseRequired: existing.licenseRequired
285                )
286            }
287        }
288        
289        // PHYSICAL & BEHAVIORAL SETTERS
290        access(all) fun setPhysicalDescription(_ newDescription: String) { self.physicalDescription = newDescription }
291        access(all) fun setBehaviorTraits(_ newTraits: String) { self.behaviorTraits = newTraits }
292        access(all) fun setSeasonalPatterns(_ newPatterns: String) { self.seasonalPatterns = newPatterns }
293        
294        // RECORDS & ACHIEVEMENTS SETTERS
295        access(all) fun setRecordWeight(_ newRecord: UFix64?) { self.recordWeight = newRecord }
296        access(all) fun setRecordWeightLocation(_ newLocation: String?) { self.recordWeightLocation = newLocation }
297        access(all) fun setRecordWeightDate(_ newDate: String?) { self.recordWeightDate = newDate }
298        access(all) fun setRecordLength(_ newRecord: UFix64?) { self.recordLength = newRecord }
299        access(all) fun setRecordLengthLocation(_ newLocation: String?) { self.recordLengthLocation = newLocation }
300        access(all) fun setRecordLengthDate(_ newDate: String?) { self.recordLengthDate = newDate }
301        
302        // RESEARCH & SCIENTIFIC SETTERS
303        access(all) fun setResearchPriority(_ newPriority: UInt8) { self.researchPriority = newPriority }
304        access(all) fun setGeneticMarkers(_ newMarkers: String) { self.geneticMarkers = newMarkers }
305        access(all) fun setStudyPrograms(_ newPrograms: [String]) { self.studyPrograms = newPrograms }
306        
307        // FLEXIBLE METADATA SETTERS
308        access(all) fun setAdditionalMetadata(_ newMetadata: {String: String}) { self.additionalMetadata = newMetadata }
309        access(all) fun updateMetadataField(_ key: String, _ value: String) { self.additionalMetadata[key] = value }
310
311        init(
312            // IMMUTABLE CORE FIELDS
313            speciesCode: String,
314            ticker: String,
315            scientificName: String,
316            family: String,
317            dataYear: UInt64,
318            
319            // BASIC DESCRIPTIVE FIELDS
320            commonName: String,
321            habitat: String?,
322            averageWeight: UFix64?,
323            averageLength: UFix64?,
324            imageURL: String?,
325            description: String,
326            firstCatchDate: UInt64?,
327            rarityTier: UInt8?,
328            
329            // CONSERVATION & POPULATION
330            globalConservationStatus: String?,
331            regionalPopulations: {String: RegionalPopulation},
332            
333            // BIOLOGICAL INTELLIGENCE
334            lifespan: UFix64?,
335            diet: String?,
336            predators: [String],
337            temperatureRange: String?,
338            depthRange: String?,
339            spawningAge: UFix64?,
340            spawningBehavior: String?,
341            migrationPattern: String?,
342            waterQualityNeeds: String?,
343            
344            // GEOGRAPHIC & HABITAT
345            nativeRegions: [String],
346            currentRange: [String],
347            waterTypes: [String],
348            invasiveStatus: String?,
349            
350            // ECONOMIC & COMMERCIAL
351            regionalCommercialValue: {String: UFix64},
352            tourismValue: UInt8?,
353            ecosystemRole: String?,
354            culturalSignificance: String?,
355            
356            // ANGLING & RECREATIONAL
357            bestBaits: [String],
358            fightRating: UInt8?,
359            culinaryRating: UInt8?,
360            catchDifficulty: UInt8?,
361            seasonalAvailability: String?,
362            bestTechniques: [String],
363            
364            // REGULATORY
365            regionalRegulations: {String: RegionalRegulations},
366            
367            // PHYSICAL & BEHAVIORAL
368            physicalDescription: String?,
369            behaviorTraits: String?,
370            seasonalPatterns: String?,
371            
372            // RECORDS & ACHIEVEMENTS
373            recordWeight: UFix64?,
374            recordWeightLocation: String?,
375            recordWeightDate: String?,
376            recordLength: UFix64?,
377            recordLengthLocation: String?,
378            recordLengthDate: String?,
379            
380            // RESEARCH & SCIENTIFIC
381            researchPriority: UInt8?,
382            geneticMarkers: String?,
383            studyPrograms: [String],
384            
385            // FLEXIBLE METADATA
386            additionalMetadata: {String: String}
387        ) {
388            // Set immutable fields
389            self.speciesCode = speciesCode
390            self.ticker = ticker
391            self.scientificName = scientificName
392            self.family = family
393            self.dataYear = dataYear
394            
395            // Set basic descriptive fields
396            self.commonName = commonName
397            self.habitat = habitat
398            self.averageWeight = averageWeight
399            self.averageLength = averageLength
400            self.imageURL = imageURL
401            self.description = description
402            self.firstCatchDate = firstCatchDate
403            self.rarityTier = rarityTier
404            
405            // Set conservation & population fields
406            self.globalConservationStatus = globalConservationStatus
407            self.regionalPopulations = regionalPopulations
408            
409            // Set biological intelligence fields
410            self.lifespan = lifespan
411            self.diet = diet
412            self.predators = predators
413            self.temperatureRange = temperatureRange
414            self.depthRange = depthRange
415            self.spawningAge = spawningAge
416            self.spawningBehavior = spawningBehavior
417            self.migrationPattern = migrationPattern
418            self.waterQualityNeeds = waterQualityNeeds
419            
420            // Set geographic & habitat fields
421            self.nativeRegions = nativeRegions
422            self.currentRange = currentRange
423            self.waterTypes = waterTypes
424            self.invasiveStatus = invasiveStatus
425            
426            // Set economic & commercial fields
427            self.regionalCommercialValue = regionalCommercialValue
428            self.tourismValue = tourismValue
429            self.ecosystemRole = ecosystemRole
430            self.culturalSignificance = culturalSignificance
431            
432            // Set angling & recreational fields
433            self.bestBaits = bestBaits
434            self.fightRating = fightRating
435            self.culinaryRating = culinaryRating
436            self.catchDifficulty = catchDifficulty
437            self.seasonalAvailability = seasonalAvailability
438            self.bestTechniques = bestTechniques
439            
440            // Set regulatory fields
441            self.regionalRegulations = regionalRegulations
442            
443            // Set physical & behavioral fields
444            self.physicalDescription = physicalDescription
445            self.behaviorTraits = behaviorTraits
446            self.seasonalPatterns = seasonalPatterns
447            
448            // Set records & achievements fields
449            self.recordWeight = recordWeight
450            self.recordWeightLocation = recordWeightLocation
451            self.recordWeightDate = recordWeightDate
452            self.recordLength = recordLength
453            self.recordLengthLocation = recordLengthLocation
454            self.recordLengthDate = recordLengthDate
455            
456            // Set research & scientific fields
457            self.researchPriority = researchPriority
458            self.geneticMarkers = geneticMarkers
459            self.studyPrograms = studyPrograms
460            
461            // Set flexible metadata
462            self.additionalMetadata = additionalMetadata
463        }
464    }
465
466    // Contract Views
467    access(all) view fun getContractViews(resourceType: Type?): [Type] {
468        return [
469            Type<FungibleTokenMetadataViews.FTView>(),
470            Type<FungibleTokenMetadataViews.FTDisplay>(),
471            Type<FungibleTokenMetadataViews.FTVaultData>(),
472            Type<FungibleTokenMetadataViews.TotalSupply>()
473        ]
474    }
475
476    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
477        switch viewType {
478            case Type<FungibleTokenMetadataViews.FTDisplay>():
479                let media = MetadataViews.Media(
480                    file: MetadataViews.HTTPFile(url: self.speciesMetadata.imageURL ?? ""),
481                    mediaType: "image/jpeg"
482                )
483                return FungibleTokenMetadataViews.FTDisplay(
484                    name: self.speciesMetadata.commonName.concat(" Coin"),
485                    symbol: self.speciesMetadata.ticker,
486                    description: self.speciesMetadata.description,
487                    externalURL: MetadataViews.ExternalURL("https://derby.fish/species/".concat(self.speciesMetadata.speciesCode.toLower())),
488                    logos: MetadataViews.Medias([media]),
489                    socials: {
490                        "website": MetadataViews.ExternalURL("https://derby.fish"),
491                        "twitter": MetadataViews.ExternalURL("https://twitter.com/derbyfish")
492                    }
493                )
494            case Type<FungibleTokenMetadataViews.FTVaultData>():
495                return FungibleTokenMetadataViews.FTVaultData(
496                    storagePath: self.VaultStoragePath,
497                    receiverPath: self.VaultPublicPath,
498                    metadataPath: self.VaultPublicPath,
499                    receiverLinkedType: Type<&BluefinTunaCoin.Vault>(),
500                    metadataLinkedType: Type<&BluefinTunaCoin.Vault>(),
501                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
502                        return <-BluefinTunaCoin.createEmptyVault(vaultType: Type<@BluefinTunaCoin.Vault>())
503                    })
504                )
505            case Type<FungibleTokenMetadataViews.TotalSupply>():
506                return FungibleTokenMetadataViews.TotalSupply(totalSupply: self.totalSupply)
507        }
508        return nil
509    }
510
511    // FISHDEX COORDINATOR RESOURCE - Handles cross-contract integration
512    access(all) resource FishDEXCoordinator: SpeciesCoinPublic {
513        
514        // Interface implementation for FishDEX registry
515        access(all) view fun getSpeciesCode(): String {
516            return BluefinTunaCoin.speciesMetadata.speciesCode
517        }
518        
519        access(all) view fun getTicker(): String {
520            return BluefinTunaCoin.speciesMetadata.ticker
521        }
522        
523        access(all) view fun getCommonName(): String {
524            return BluefinTunaCoin.speciesMetadata.commonName
525        }
526        
527        access(all) view fun getScientificName(): String {
528            return BluefinTunaCoin.speciesMetadata.scientificName
529        }
530        
531        access(all) view fun getFamily(): String {
532            return BluefinTunaCoin.speciesMetadata.family
533        }
534        
535        access(all) view fun getTotalSupply(): UFix64 {
536            return BluefinTunaCoin.totalSupply
537        }
538        
539        access(all) view fun getBasicRegistryInfo(): {String: AnyStruct} {
540            return {
541                "speciesCode": self.getSpeciesCode(),
542                "ticker": self.getTicker(),
543                "commonName": self.getCommonName(),
544                "scientificName": self.getScientificName(),
545                "family": self.getFamily(),
546                "totalSupply": self.getTotalSupply(),
547                "contractAddress": BluefinTunaCoin.account.address,
548                "dataYear": BluefinTunaCoin.speciesMetadata.dataYear,
549                "conservationStatus": BluefinTunaCoin.speciesMetadata.globalConservationStatus,
550                "rarityTier": BluefinTunaCoin.speciesMetadata.rarityTier,
551                "isRegistered": BluefinTunaCoin.isRegisteredWithFishDEX
552            }
553        }
554        
555        // Process catch verification from Fish NFT contracts
556        access(all) fun redeemCatchNFT(fishData: {String: AnyStruct}, angler: Address): @BluefinTunaCoin.Vault {
557            // Validate fish data matches this species
558            if let fishSpeciesCode = fishData["speciesCode"] as? String {
559                assert(
560                    fishSpeciesCode == self.getSpeciesCode(),
561                    message: "Fish species code does not match this species coin"
562                )
563            }
564            
565            // Extract fish NFT ID if available
566            let fishNFTId = fishData["nftId"] as? UInt64
567            
568            // Validate NFT hasn't been redeemed before
569            if let nftId = fishNFTId {
570                assert(
571                    BluefinTunaCoin.fishNFTRegistry[nftId] == nil,
572                    message: "This NFT has already been redeemed for a coin"
573                )
574                // Record this NFT as redeemed
575                BluefinTunaCoin.fishNFTRegistry[nftId] = true
576            }
577            
578            // Auto-record first catch if this is the very first mint
579            if BluefinTunaCoin.totalSupply == 0.0 && BluefinTunaCoin.speciesMetadata.firstCatchDate == nil {
580                BluefinTunaCoin.speciesMetadata.setFirstCatchDate(UInt64(getCurrentBlock().timestamp))
581                emit FirstCatchRecorded(timestamp: UInt64(getCurrentBlock().timestamp), angler: angler)
582            }
583            
584            // Mint 1 coin for verified catch
585            let amount: UFix64 = 1.0
586            BluefinTunaCoin.totalSupply = BluefinTunaCoin.totalSupply + amount
587            
588            // Create vault with the minted amount (not empty!)
589            let vault <- create Vault(balance: amount)
590            
591            // Emit events
592            emit TokensMinted(amount: amount, to: angler)
593            emit CatchProcessedFromNFT(fishNFTId: fishNFTId, angler: angler, amount: amount)
594            
595            if let nftId = fishNFTId {
596                emit CatchVerified(fishId: nftId, angler: angler, amount: amount)
597            }
598            
599            return <- vault
600        }
601        
602        // Register this species with FishDEX
603        access(all) fun registerWithFishDEX(fishDEXAddress: Address) {
604            pre {
605                !BluefinTunaCoin.isRegisteredWithFishDEX: "Already registered with FishDEX"
606            }
607            
608            emit FishDEXRegistrationAttempted(fishDEXAddress: fishDEXAddress, speciesCode: self.getSpeciesCode())
609            
610            // Get reference to FishDEX contract
611            let fishDEXAccount = getAccount(fishDEXAddress)
612            
613            // Call FishDEX registration function
614            // Note: This will be completed when FishDEX contract is implemented
615            // For now, just update our state
616            BluefinTunaCoin.fishDEXAddress = fishDEXAddress
617            BluefinTunaCoin.isRegisteredWithFishDEX = true
618            
619            emit FishDEXRegistrationCompleted(fishDEXAddress: fishDEXAddress, speciesCode: self.getSpeciesCode())
620        }
621        
622        // Update FishDEX address (admin only via proper access)
623        access(all) fun updateFishDEXAddress(newAddress: Address) {
624            let oldAddress = BluefinTunaCoin.fishDEXAddress
625            BluefinTunaCoin.fishDEXAddress = newAddress
626            BluefinTunaCoin.isRegisteredWithFishDEX = false // Reset registration status
627            emit FishDEXAddressUpdated(oldAddress: oldAddress, newAddress: newAddress)
628        }
629    }
630
631    // Vault Resource
632    access(all) resource Vault: FungibleToken.Vault {
633        access(all) var balance: UFix64
634
635        init(balance: UFix64) {
636            self.balance = balance
637        }
638
639        access(contract) fun burnCallback() {
640            if self.balance > 0.0 {
641                BluefinTunaCoin.totalSupply = BluefinTunaCoin.totalSupply - self.balance
642                emit TokensBurned(amount: self.balance, from: self.owner?.address)
643            }
644            self.balance = 0.0
645        }
646
647        access(all) view fun getViews(): [Type] {
648            return BluefinTunaCoin.getContractViews(resourceType: nil)
649        }
650
651        access(all) fun resolveView(_ view: Type): AnyStruct? {
652            return BluefinTunaCoin.resolveContractView(resourceType: nil, viewType: view)
653        }
654
655        access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
656            return {Type<@BluefinTunaCoin.Vault>(): true}
657        }
658
659        access(all) view fun isSupportedVaultType(type: Type): Bool {
660            return type == Type<@BluefinTunaCoin.Vault>()
661        }
662
663        access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
664            return amount <= self.balance
665        }
666
667        access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @BluefinTunaCoin.Vault {
668            self.balance = self.balance - amount
669            return <-create Vault(balance: amount)
670        }
671
672        access(all) fun deposit(from: @{FungibleToken.Vault}) {
673            let vault <- from as! @BluefinTunaCoin.Vault
674            self.balance = self.balance + vault.balance
675            vault.balance = 0.0
676            destroy vault
677        }
678
679        access(all) fun createEmptyVault(): @BluefinTunaCoin.Vault {
680            return <-create Vault(balance: 0.0)
681        }
682    }
683
684    // Minter Resource - Admin only
685    access(all) resource Minter {
686        
687        access(all) fun mintForCatch(amount: UFix64, fishId: UInt64, angler: Address): @BluefinTunaCoin.Vault {
688            pre {
689                amount == 1.0: "Only 1 coin per verified catch"
690                BluefinTunaCoin.fishNFTRegistry[fishId] == nil: "This NFT has already been used to mint a coin"
691            }
692            
693            // Record this NFT as used
694            BluefinTunaCoin.fishNFTRegistry[fishId] = true
695            
696            // Auto-record first catch if this is the very first mint
697            if BluefinTunaCoin.totalSupply == 0.0 && BluefinTunaCoin.speciesMetadata.firstCatchDate == nil {
698                BluefinTunaCoin.speciesMetadata.setFirstCatchDate(UInt64(getCurrentBlock().timestamp))
699                emit FirstCatchRecorded(timestamp: UInt64(getCurrentBlock().timestamp), angler: angler)
700                
701                // Auto-register with FishDEX after first mint if FishDEX address is set
702                self.autoRegisterWithFishDEX()
703            }
704            
705            BluefinTunaCoin.totalSupply = BluefinTunaCoin.totalSupply + amount
706            
707            emit TokensMinted(amount: amount, to: angler)
708            emit CatchVerified(fishId: fishId, angler: angler, amount: amount)
709            
710            return <-create Vault(balance: amount)
711        }
712        
713        // Auto-registration helper function
714        access(all) fun autoRegisterWithFishDEX() {
715            if let fishDEXAddr = BluefinTunaCoin.fishDEXAddress {
716                if !BluefinTunaCoin.isRegisteredWithFishDEX {
717                    // Get reference to FishDEX coordinator
718                    if let coordinatorRef = BluefinTunaCoin.account.capabilities.borrow<&FishDEXCoordinator>(
719                        BluefinTunaCoin.FishDEXCoordinatorPublicPath
720                    ) {
721                        coordinatorRef.registerWithFishDEX(fishDEXAddress: fishDEXAddr)
722                    }
723                }
724            }
725        }
726        
727        // Manual FishDEX registration (admin function)
728        access(all) fun registerWithFishDEXManually(fishDEXAddress: Address) {
729            BluefinTunaCoin.fishDEXAddress = fishDEXAddress
730            self.autoRegisterWithFishDEX()
731        }
732
733        access(all) fun mintBatch(recipients: {Address: UFix64}): @{Address: BluefinTunaCoin.Vault} {
734            let vaults: @{Address: BluefinTunaCoin.Vault} <- {}
735            
736            for recipient in recipients.keys {
737                let amount = recipients[recipient]!
738                BluefinTunaCoin.totalSupply = BluefinTunaCoin.totalSupply + amount
739                
740                let vault <- create Vault(balance: amount)
741                let oldVault <- vaults[recipient] <- vault
742                destroy oldVault
743                
744                emit TokensMinted(amount: amount, to: recipient)
745            }
746            
747            return <-vaults
748        }
749    }
750
751    // MetadataAdmin Resource - Admin only for updating mutable metadata fields
752    access(all) resource MetadataAdmin {
753        
754        access(all) fun updateImageURL(newURL: String) {
755            let oldURL = BluefinTunaCoin.speciesMetadata.imageURL ?? ""
756            BluefinTunaCoin.speciesMetadata.setImageURL(newURL)
757            emit MetadataUpdated(field: "imageURL", oldValue: oldURL, newValue: newURL)
758        }
759        
760        access(all) fun updateDescription(newDescription: String) {
761            let oldDescription = BluefinTunaCoin.speciesMetadata.description
762            BluefinTunaCoin.speciesMetadata.setDescription(newDescription)
763            emit MetadataUpdated(field: "description", oldValue: oldDescription, newValue: newDescription)
764        }
765        
766
767            
768        access(all) fun updateHabitat(newHabitat: String) {
769            let oldHabitat = BluefinTunaCoin.speciesMetadata.habitat ?? ""
770            BluefinTunaCoin.speciesMetadata.setHabitat(newHabitat)
771            emit MetadataUpdated(field: "habitat", oldValue: oldHabitat, newValue: newHabitat)
772        }
773        
774        access(all) fun updateAverageWeight(newWeight: UFix64) {
775            let oldWeight = BluefinTunaCoin.speciesMetadata.averageWeight?.toString() ?? "0.0"
776            BluefinTunaCoin.speciesMetadata.setAverageWeight(newWeight)
777            emit MetadataUpdated(field: "averageWeight", oldValue: oldWeight, newValue: newWeight.toString())
778        }
779        
780        access(all) fun updateAverageLength(newLength: UFix64) {
781            let oldLength = BluefinTunaCoin.speciesMetadata.averageLength?.toString() ?? "0.0"
782            BluefinTunaCoin.speciesMetadata.setAverageLength(newLength)
783            emit MetadataUpdated(field: "averageLength", oldValue: oldLength, newValue: newLength.toString())
784        }
785        
786        access(all) fun updateRarityTier(newTier: UInt8) {
787            pre {
788                newTier >= 1 && newTier <= 5: "Invalid rarity tier (must be 1-5)"
789            }
790            let oldTier = BluefinTunaCoin.speciesMetadata.rarityTier?.toString() ?? "0"
791            BluefinTunaCoin.speciesMetadata.setRarityTier(newTier)
792            emit MetadataUpdated(field: "rarityTier", oldValue: oldTier, newValue: newTier.toString())
793        }
794        
795        access(all) fun manuallySetFirstCatch(timestamp: UInt64, angler: Address) {
796            pre {
797                BluefinTunaCoin.speciesMetadata.firstCatchDate == nil: "First catch already recorded"
798            }
799            BluefinTunaCoin.speciesMetadata.setFirstCatchDate(timestamp)
800            emit FirstCatchRecorded(timestamp: timestamp, angler: angler)
801            emit MetadataUpdated(field: "firstCatchDate", oldValue: "", newValue: timestamp.toString())
802        }
803        
804        // FishDEX-specific metadata updates
805        access(all) fun updateConservationStatus(newStatus: String) {
806            let oldStatus = BluefinTunaCoin.speciesMetadata.globalConservationStatus ?? ""
807            BluefinTunaCoin.speciesMetadata.setConservationStatus(newStatus)
808            emit MetadataUpdated(field: "globalConservationStatus", oldValue: oldStatus, newValue: newStatus)
809        }
810        
811        access(all) fun updateNativeRegions(newRegions: [String]) {
812            var oldRegionsStr = "["
813            for i, region in BluefinTunaCoin.speciesMetadata.nativeRegions {
814                if i > 0 { oldRegionsStr = oldRegionsStr.concat(",") }
815                oldRegionsStr = oldRegionsStr.concat(region)
816            }
817            oldRegionsStr = oldRegionsStr.concat("]")
818            
819            BluefinTunaCoin.speciesMetadata.setNativeRegions(newRegions)
820            
821            var newRegionsStr = "["
822            for i, region in newRegions {
823                if i > 0 { newRegionsStr = newRegionsStr.concat(",") }
824                newRegionsStr = newRegionsStr.concat(region)
825            }
826            newRegionsStr = newRegionsStr.concat("]")
827            
828            emit MetadataUpdated(field: "nativeRegions", oldValue: oldRegionsStr, newValue: newRegionsStr)
829        }
830        
831        access(all) fun updateSeasonalPatterns(newPatterns: String) {
832            let oldPatterns = BluefinTunaCoin.speciesMetadata.seasonalPatterns ?? ""
833            BluefinTunaCoin.speciesMetadata.setSeasonalPatterns(newPatterns)
834            emit MetadataUpdated(field: "seasonalPatterns", oldValue: oldPatterns, newValue: newPatterns)
835        }
836        
837        access(all) fun updateRecordWeight(newRecord: UFix64) {
838            let oldRecord = BluefinTunaCoin.speciesMetadata.recordWeight?.toString() ?? "0.0"
839            BluefinTunaCoin.speciesMetadata.setRecordWeight(newRecord)
840            emit MetadataUpdated(field: "recordWeight", oldValue: oldRecord, newValue: newRecord.toString())
841        }
842        
843        access(all) fun updateRecordLength(newRecord: UFix64) {
844            let oldRecord = BluefinTunaCoin.speciesMetadata.recordLength?.toString() ?? "0.0"
845            BluefinTunaCoin.speciesMetadata.setRecordLength(newRecord)
846            emit MetadataUpdated(field: "recordLength", oldValue: oldRecord, newValue: newRecord.toString())
847        }
848        
849        // MISSING: Record location and date admin functions
850        access(all) fun updateRecordWeightLocation(newLocation: String?) {
851            let oldLocation = BluefinTunaCoin.speciesMetadata.recordWeightLocation ?? ""
852            BluefinTunaCoin.speciesMetadata.setRecordWeightLocation(newLocation)
853            emit MetadataUpdated(field: "recordWeightLocation", oldValue: oldLocation, newValue: newLocation ?? "")
854        }
855        
856        access(all) fun updateRecordWeightDate(newDate: String?) {
857            let oldDate = BluefinTunaCoin.speciesMetadata.recordWeightDate ?? ""
858            BluefinTunaCoin.speciesMetadata.setRecordWeightDate(newDate)
859            emit MetadataUpdated(field: "recordWeightDate", oldValue: oldDate, newValue: newDate ?? "")
860        }
861        
862        access(all) fun updateRecordLengthLocation(newLocation: String?) {
863            let oldLocation = BluefinTunaCoin.speciesMetadata.recordLengthLocation ?? ""
864            BluefinTunaCoin.speciesMetadata.setRecordLengthLocation(newLocation)
865            emit MetadataUpdated(field: "recordLengthLocation", oldValue: oldLocation, newValue: newLocation ?? "")
866        }
867        
868        access(all) fun updateRecordLengthDate(newDate: String?) {
869            let oldDate = BluefinTunaCoin.speciesMetadata.recordLengthDate ?? ""
870            BluefinTunaCoin.speciesMetadata.setRecordLengthDate(newDate)
871            emit MetadataUpdated(field: "recordLengthDate", oldValue: oldDate, newValue: newDate ?? "")
872        }
873        
874        // MISSING: Convenience function to update complete records
875        access(all) fun updateCompleteWeightRecord(weight: UFix64?, location: String?, date: String?) {
876            if let w = weight { self.updateRecordWeight(newRecord: w) }
877            self.updateRecordWeightLocation(newLocation: location)
878            self.updateRecordWeightDate(newDate: date)
879        }
880        
881        access(all) fun updateCompleteLengthRecord(length: UFix64?, location: String?, date: String?) {
882            if let l = length { self.updateRecordLength(newRecord: l) }
883            self.updateRecordLengthLocation(newLocation: location)
884            self.updateRecordLengthDate(newDate: date)
885        }
886        
887        // MISSING: Community data curation approval
888        access(all) fun approvePendingUpdate(index: Int) {
889            pre {
890                index >= 0 && index < BluefinTunaCoin.pendingUpdates.length: "Invalid update index"
891            }
892            let update = BluefinTunaCoin.pendingUpdates.remove(at: index)
893            // Apply the update based on field type
894            emit MetadataUpdated(field: update.field, oldValue: "pending", newValue: update.newValue)
895        }
896        
897        access(all) fun rejectPendingUpdate(index: Int) {
898            pre {
899                index >= 0 && index < BluefinTunaCoin.pendingUpdates.length: "Invalid update index"
900            }
901            BluefinTunaCoin.pendingUpdates.remove(at: index)
902        }
903        
904        access(all) fun clearAllPendingUpdates() {
905            BluefinTunaCoin.pendingUpdates = []
906        }
907        
908        // ENHANCED ADMIN FUNCTIONS WITH VALIDATION
909        access(all) fun updateRarityTierValidated(newTier: UInt8) {
910            pre {
911                BluefinTunaCoin.validateRating(newTier): "Invalid rarity tier (must be 1-10)"
912            }
913            let oldTier = BluefinTunaCoin.speciesMetadata.rarityTier?.toString() ?? "0"
914            BluefinTunaCoin.speciesMetadata.setRarityTier(newTier)
915            emit MetadataUpdated(field: "rarityTier", oldValue: oldTier, newValue: newTier.toString())
916        }
917        
918        access(all) fun updateConservationStatusValidated(newStatus: String) {
919            pre {
920                BluefinTunaCoin.validateConservationStatus(newStatus): "Invalid conservation status"
921            }
922            let oldStatus = BluefinTunaCoin.speciesMetadata.globalConservationStatus ?? ""
923            BluefinTunaCoin.speciesMetadata.setConservationStatus(newStatus)
924            emit MetadataUpdated(field: "globalConservationStatus", oldValue: oldStatus, newValue: newStatus)
925        }
926        
927        access(all) fun updateFightRatingValidated(newRating: UInt8) {
928            pre {
929                BluefinTunaCoin.validateRating(newRating): "Fight rating must be 1-10"
930            }
931            let oldRating = BluefinTunaCoin.speciesMetadata.fightRating?.toString() ?? "0"
932            BluefinTunaCoin.speciesMetadata.setFightRating(newRating)
933            emit MetadataUpdated(field: "fightRating", oldValue: oldRating, newValue: newRating.toString())
934        }
935        
936        // TEMPORAL ADMIN FUNCTIONS
937        access(all) fun archiveCurrentYear() {
938            let currentYear = BluefinTunaCoin.currentMetadataYear
939            BluefinTunaCoin.metadataHistory[currentYear] = BluefinTunaCoin.speciesMetadata
940            emit YearlyMetadataCreated(year: currentYear)
941        }
942        
943        access(all) fun updateToNewYear(_ newYear: UInt64) {
944            pre {
945                newYear > BluefinTunaCoin.currentMetadataYear: "New year must be greater than current year"
946            }
947            // Archive current year data
948            self.archiveCurrentYear()
949            
950            let oldYear = BluefinTunaCoin.currentMetadataYear
951            BluefinTunaCoin.currentMetadataYear = newYear
952            emit MetadataYearUpdated(oldYear: oldYear, newYear: newYear)
953        }
954    }
955
956    // Public functions
957    access(all) fun createEmptyVault(vaultType: Type): @BluefinTunaCoin.Vault {
958        pre {
959            vaultType == Type<@BluefinTunaCoin.Vault>(): "Vault type mismatch"
960        }
961        return <-create Vault(balance: 0.0)
962    }
963
964    access(all) view fun getSpeciesMetadata(): SpeciesMetadata {
965        return self.speciesMetadata
966    }
967
968    // TEMPORAL METADATA MANAGEMENT - Track changes over time
969    access(all) view fun getMetadataForYear(_ year: UInt64): SpeciesMetadata? {
970        return self.metadataHistory[year]
971    }
972    
973    access(all) view fun getAvailableYears(): [UInt64] {
974        return self.metadataHistory.keys
975    }
976    
977    access(all) fun createYearlyMetadata(_ year: UInt64) {
978        pre {
979            self.metadataHistory[year] == nil: "Metadata for this year already exists"
980        }
981        // Create a copy of current metadata for the new year
982        let newMetadata = self.speciesMetadata
983        self.metadataHistory[year] = newMetadata
984        emit YearlyMetadataCreated(year: year)
985    }
986
987    // REGIONAL DATA MANAGEMENT - Handle location-specific information
988    access(all) fun addRegionalPopulation(_ region: String, _ data: RegionalPopulation) {
989        self.speciesMetadata.regionalPopulations[region] = data
990    }
991    
992    access(all) fun addRegionalRegulation(_ region: String, _ data: RegionalRegulations) {
993        self.speciesMetadata.regionalRegulations[region] = data
994    }
995    
996    access(all) view fun getRegionalPrice(_ region: String): UFix64? {
997        return self.speciesMetadata.regionalCommercialValue[region]
998    }
999
1000    // SIMPLE SPECIES-LEVEL ANALYTICS - Building blocks for FishDEX
1001    access(all) view fun isEndangered(): Bool {
1002        if let status = self.speciesMetadata.globalConservationStatus {
1003            return status == "Endangered" || status == "Critical" || status == "Critically Endangered"
1004        }
1005        return false
1006    }
1007    
1008    access(all) view fun getConservationTier(): UInt8 {
1009        // Simple 1-5 scale based on conservation status (for trading algorithms)
1010        if let status = self.speciesMetadata.globalConservationStatus {
1011            switch status {
1012                case "Least Concern": return 1
1013                case "Near Threatened": return 2  
1014                case "Vulnerable": return 3
1015                case "Endangered": return 4
1016                case "Critically Endangered": return 5
1017                default: return 3
1018            }
1019        }
1020        return 3
1021    }
1022    
1023    // FISH NFT INTEGRATION HOOKS - Ready for when Fish NFTs are implemented
1024    access(all) view fun getSpeciesInfo(): {String: AnyStruct} {
1025        // Standard interface Fish NFTs can call to get species data
1026        return {
1027            "speciesCode": self.speciesMetadata.speciesCode,
1028            "ticker": self.speciesMetadata.ticker,
1029            "commonName": self.speciesMetadata.commonName,
1030            "scientificName": self.speciesMetadata.scientificName,
1031            "conservationTier": self.getConservationTier(),
1032            "totalSupply": self.totalSupply
1033        }
1034    }
1035    
1036    access(all) fun recordCatchForSpecies(fishNFTId: UInt64, catchData: {String: AnyStruct}) {
1037        // Called by Fish NFT contract when a catch is verified
1038        // This will trigger species coin minting
1039        let angler = catchData["angler"] as? Address ?? self.account.address
1040        emit CatchVerified(fishId: fishNFTId, angler: angler, amount: 1.0)
1041    }
1042    
1043    access(all) view fun getCatchCount(): UInt64 {
1044        // Total verified catches = total supply (1 coin per catch)
1045        return UInt64(self.totalSupply)
1046    }
1047
1048    // BAITCOIN EXCHANGE INTEGRATION - Dual-token economy
1049    access(all) var baitExchangeRate: UFix64?  // Species coin → BaitCoin conversion rate
1050    
1051    access(all) view fun getBaitExchangeRate(): UFix64? {
1052        return self.baitExchangeRate
1053    }
1054    
1055    access(all) fun updateBaitExchangeRate(_ rate: UFix64) {
1056        // Only admin can set exchange rates
1057        let oldRate = self.baitExchangeRate?.toString() ?? "0.0"
1058        self.baitExchangeRate = rate
1059        emit MetadataUpdated(field: "baitExchangeRate", oldValue: oldRate, newValue: rate.toString())
1060    }
1061
1062    // INPUT VALIDATION - Prevent bad data
1063    access(all) view fun validateRating(_ rating: UInt8): Bool {
1064        return rating >= 1 && rating <= 10
1065    }
1066    
1067    access(all) view fun validateConservationStatus(_ status: String): Bool {
1068        let validStatuses = ["Least Concern", "Near Threatened", "Vulnerable", "Endangered", "Critically Endangered", "Extinct", "Stable", "Threatened", "Critical"]
1069        return validStatuses.contains(status)
1070    }
1071
1072    // COMMUNITY DATA CURATION - Simplified for future growth
1073    access(all) struct DataUpdate {
1074        access(all) let field: String
1075        access(all) let newValue: String
1076        access(all) let contributor: Address
1077        access(all) let source: String
1078        access(all) let timestamp: UFix64
1079        
1080        init(field: String, newValue: String, contributor: Address, source: String) {
1081            self.field = field
1082            self.newValue = newValue
1083            self.contributor = contributor
1084            self.source = source
1085            self.timestamp = getCurrentBlock().timestamp
1086        }
1087    }
1088    
1089    access(all) var pendingUpdates: [DataUpdate]
1090    
1091    access(all) fun submitDataUpdate(field: String, value: String, source: String) {
1092        let update = DataUpdate(
1093            field: field,
1094            newValue: value,
1095            contributor: self.account.address,
1096            source: source
1097        )
1098        self.pendingUpdates.append(update)
1099    }
1100
1101    // BATCH OPERATIONS - Scientific data import
1102    access(all) fun updateMetadataBatch(updates: {String: String}) {
1103        // Admin can update multiple fields at once
1104        for field in updates.keys {
1105            let value = updates[field]!
1106            // Apply validation and update metadata
1107            emit MetadataUpdated(field: field, oldValue: "batch_old", newValue: value)
1108        }
1109    }
1110    
1111    access(all) fun addMultipleRegions(
1112        populations: {String: RegionalPopulation}, 
1113        regulations: {String: RegionalRegulations}
1114    ) {
1115        // Add population data for multiple regions
1116        for region in populations.keys {
1117            self.speciesMetadata.regionalPopulations[region] = populations[region]!
1118        }
1119        
1120        // Add regulatory data for multiple regions  
1121        for region in regulations.keys {
1122            self.speciesMetadata.regionalRegulations[region] = regulations[region]!
1123        }
1124    }
1125
1126    // FISHDEX QUERY HELPERS - Trading decision support
1127    access(all) view fun getRegionsWithData(): [String] {
1128        return self.speciesMetadata.regionalPopulations.keys
1129    }
1130    
1131    access(all) view fun hasCompleteMetadata(): Bool {
1132        // Check if core fields are populated
1133        return self.speciesMetadata.habitat != nil &&
1134               self.speciesMetadata.averageWeight != nil &&
1135               self.speciesMetadata.globalConservationStatus != nil &&
1136               self.speciesMetadata.regionalPopulations.length > 0
1137    }
1138    
1139    access(all) view fun getDataCompleteness(): UInt8 {
1140        // Return 1-10 score of how complete the species data is
1141        var score: UInt8 = 0
1142        
1143        // Core biological data (3 points)
1144        if self.speciesMetadata.habitat != nil { score = score + 1 }
1145        if self.speciesMetadata.averageWeight != nil { score = score + 1 }
1146        if self.speciesMetadata.lifespan != nil { score = score + 1 }
1147        
1148        // Conservation data (2 points)
1149        if self.speciesMetadata.globalConservationStatus != nil { score = score + 1 }
1150        if self.speciesMetadata.regionalPopulations.length > 0 { score = score + 1 }
1151        
1152        // Regional data (2 points)  
1153        if self.speciesMetadata.regionalRegulations.length > 0 { score = score + 1 }
1154        if self.speciesMetadata.regionalCommercialValue.length > 0 { score = score + 1 }
1155        
1156        // Records & research (3 points)
1157        if self.speciesMetadata.recordWeight != nil { score = score + 1 }
1158        if self.speciesMetadata.studyPrograms.length > 0 { score = score + 1 }
1159        if self.speciesMetadata.firstCatchDate != nil { score = score + 1 }
1160        
1161        return score
1162    }
1163
1164    // MISSING: Public query functions for practical interaction
1165    access(all) view fun getBasicInfo(): {String: AnyStruct} {
1166        return {
1167            "speciesCode": self.speciesMetadata.speciesCode,
1168            "ticker": self.speciesMetadata.ticker,
1169            "commonName": self.speciesMetadata.commonName,
1170            "scientificName": self.speciesMetadata.scientificName,
1171            "family": self.speciesMetadata.family,
1172            "totalSupply": self.totalSupply,
1173            "description": self.speciesMetadata.description
1174        }
1175    }
1176    
1177    access(all) view fun getRegionalInfo(region: String): {String: AnyStruct?} {
1178        return {
1179            "population": self.speciesMetadata.regionalPopulations[region],
1180            "regulations": self.speciesMetadata.regionalRegulations[region],
1181            "commercialValue": self.speciesMetadata.regionalCommercialValue[region]
1182        }
1183    }
1184    
1185    access(all) view fun getRecordInfo(): {String: AnyStruct?} {
1186        return {
1187            "recordWeight": self.speciesMetadata.recordWeight,
1188            "recordWeightLocation": self.speciesMetadata.recordWeightLocation,
1189            "recordWeightDate": self.speciesMetadata.recordWeightDate,
1190            "recordLength": self.speciesMetadata.recordLength,
1191            "recordLengthLocation": self.speciesMetadata.recordLengthLocation,
1192            "recordLengthDate": self.speciesMetadata.recordLengthDate
1193        }
1194    }
1195    
1196    access(all) view fun getAnglingInfo(): {String: AnyStruct?} {
1197        return {
1198            "bestBaits": self.speciesMetadata.bestBaits,
1199            "bestTechniques": self.speciesMetadata.bestTechniques,
1200            "fightRating": self.speciesMetadata.fightRating,
1201            "culinaryRating": self.speciesMetadata.culinaryRating,
1202            "catchDifficulty": self.speciesMetadata.catchDifficulty,
1203            "seasonalAvailability": self.speciesMetadata.seasonalAvailability
1204        }
1205    }
1206    
1207    access(all) view fun getConservationInfo(): {String: AnyStruct?} {
1208        return {
1209            "conservationStatus": self.speciesMetadata.globalConservationStatus,
1210            "conservationTier": self.getConservationTier(),
1211            "isEndangered": self.isEndangered(),
1212            "nativeRegions": self.speciesMetadata.nativeRegions,
1213            "currentRange": self.speciesMetadata.currentRange,
1214            "invasiveStatus": self.speciesMetadata.invasiveStatus
1215        }
1216    }
1217    
1218    access(all) view fun getBiologicalInfo(): {String: AnyStruct?} {
1219        return {
1220            "lifespan": self.speciesMetadata.lifespan,
1221            "diet": self.speciesMetadata.diet,
1222            "predators": self.speciesMetadata.predators,
1223            "temperatureRange": self.speciesMetadata.temperatureRange,
1224            "depthRange": self.speciesMetadata.depthRange,
1225            "spawningAge": self.speciesMetadata.spawningAge,
1226            "spawningBehavior": self.speciesMetadata.spawningBehavior,
1227            "migrationPattern": self.speciesMetadata.migrationPattern
1228        }
1229    }
1230    
1231    access(all) view fun getPendingUpdates(): [DataUpdate] {
1232        return self.pendingUpdates
1233    }
1234    
1235    access(all) view fun getPendingUpdateCount(): Int {
1236        return self.pendingUpdates.length
1237    }
1238    
1239    // MISSING: Temporal query functions
1240    access(all) view fun getCurrentYear(): UInt64 {
1241        return self.currentMetadataYear
1242    }
1243    
1244    access(all) view fun hasHistoricalData(year: UInt64): Bool {
1245        return self.metadataHistory[year] != nil
1246    }
1247    
1248    access(all) view fun getYearlyDataSummary(): {UInt64: String} {
1249        let summary: {UInt64: String} = {}
1250        for year in self.metadataHistory.keys {
1251            let metadata = self.metadataHistory[year]!
1252            summary[year] = metadata.commonName.concat(" - ").concat(metadata.description.slice(from: 0, upTo: 50))
1253        }
1254        return summary
1255    }
1256
1257    // FISHDEX INTEGRATION - Public query functions
1258    access(all) view fun getFishDEXAddress(): Address? {
1259        return self.fishDEXAddress
1260    }
1261    
1262    access(all) view fun getFishDEXRegistrationStatus(): Bool {
1263        return self.isRegisteredWithFishDEX
1264    }
1265    
1266    access(all) view fun getRegistryInfo(): {String: AnyStruct} {
1267        return {
1268            "speciesCode": self.speciesMetadata.speciesCode,
1269            "ticker": self.speciesMetadata.ticker,
1270            "commonName": self.speciesMetadata.commonName,
1271            "scientificName": self.speciesMetadata.scientificName,
1272            "family": self.speciesMetadata.family,
1273            "totalSupply": self.totalSupply,
1274            "contractAddress": self.account.address,
1275            "fishDEXAddress": self.fishDEXAddress,
1276            "isRegistered": self.isRegisteredWithFishDEX,
1277            "dataYear": self.speciesMetadata.dataYear
1278        }
1279    }
1280    
1281    // Create public capability for FishDEX coordinator (called during account setup)
1282    access(all) fun createFishDEXCoordinatorCapability(): Capability<&FishDEXCoordinator> {
1283        return self.account.capabilities.storage.issue<&FishDEXCoordinator>(self.FishDEXCoordinatorStoragePath)
1284    }
1285
1286    // Essential token interaction functions
1287    access(all) view fun getTotalSupply(): UFix64 {
1288        return self.totalSupply
1289    }
1290    
1291    // NOTE: BluefinTuna coins represent permanent historical catch records
1292    // No burning functions provided - total supply always equals total verified catches
1293    // Only natural vault destruction (via burnCallback) affects individual balances
1294    
1295    // MISSING: Token utility functions
1296    access(all) view fun getVaultBalance(vaultRef: &BluefinTunaCoin.Vault): UFix64 {
1297        return vaultRef.balance
1298    }
1299    
1300    access(all) view fun canWithdraw(vaultRef: &BluefinTunaCoin.Vault, amount: UFix64): Bool {
1301        return vaultRef.isAvailableToWithdraw(amount: amount)
1302    }
1303    
1304    // MISSING: Supply analytics for trading
1305    access(all) view fun getSupplyMetrics(): {String: UFix64} {
1306        return {
1307            "totalSupply": self.totalSupply,
1308            "totalCatches": self.totalSupply, // 1 coin per catch
1309            "circulatingSupply": self.totalSupply // Assuming all minted coins are in circulation
1310        }
1311    }
1312    
1313    // MISSING: Token validation
1314    access(all) view fun isValidVaultType(vaultType: Type): Bool {
1315        return vaultType == Type<@BluefinTunaCoin.Vault>()
1316    }
1317
1318    // Contract initialization
1319    init() {
1320        self.totalSupply = 0.0
1321        
1322        // Initialize NFT tracking
1323        self.fishNFTRegistry = {}
1324        
1325        // Initialize temporal metadata system
1326        self.currentMetadataYear = 2024
1327        self.metadataHistory = {}
1328        self.defaultRegion = "Global"
1329        
1330        // Initialize BaitCoin exchange rate (nil until set by admin)
1331        self.baitExchangeRate = nil
1332        
1333        // Initialize community curation system
1334        self.pendingUpdates = []
1335        
1336        // Initialize FishDEX integration
1337        self.fishDEXAddress = nil
1338        self.isRegisteredWithFishDEX = false
1339        
1340        // Set comprehensive species metadata for Example Fish
1341        self.speciesMetadata = SpeciesMetadata(
1342            // IMMUTABLE CORE FIELDS
1343            speciesCode: "THUNNUS_THYNNUS",
1344            ticker: "THUTHY",
1345            scientificName: "Thunnus thynnus",
1346            family: "Scombridae",
1347            dataYear: 2024,
1348            
1349            // BASIC DESCRIPTIVE FIELDS
1350            commonName: "Bluefin Tuna",
1351            habitat: "Open ocean, temperate and tropical waters",
1352            averageWeight: 550.0,
1353            averageLength: 78.0,
1354            imageURL: "https://derby.fish/images/species/BluefinTuna.jpg",
1355            description: "Bluefin Tuna are the giants of the BluefinTuna family, capable of reaching enormous sizes and incredible speeds. They are among the most valuable fish in the world, prized for their rich, fatty meat in sushi and sashimi markets. These powerful predators are warm-blooded and can regulate their body temperature.",
1356            firstCatchDate: nil,
1357            rarityTier: 5,
1358            
1359            // CONSERVATION & POPULATION
1360            globalConservationStatus: "Critically Endangered",
1361            regionalPopulations: {
1362                "North Atlantic": RegionalPopulation(
1363                    populationTrend: "Declining",
1364                    threats: ["Overfishing", "Climate Change", "Illegal Fishing"],
1365                    protectedAreas: ["Marine Protected Areas", "Fisheries Closure Areas"],
1366                    estimatedPopulation: nil
1367                ),
1368                "Mediterranean": RegionalPopulation(
1369                    populationTrend: "Critically Declining",
1370                    threats: ["Overfishing", "Ranching Operations", "Habitat Loss"],
1371                    protectedAreas: ["Mediterranean MPAs", "Spawning Sanctuaries"],
1372                    estimatedPopulation: nil
1373                ),
1374                "Pacific": RegionalPopulation(
1375                    populationTrend: "Stable",
1376                    threats: ["Commercial Fishing", "Longline Bycatch", "Climate Change"],
1377                    protectedAreas: ["Pacific Remote Islands Marine National Monument"],
1378                    estimatedPopulation: nil
1379                )
1380            },
1381            
1382            // BIOLOGICAL INTELLIGENCE
1383            lifespan: 40.0,
1384            diet: "Mackerel, herring, sardines, squid, crustaceans, flying fish",
1385            predators: ["Large Sharks", "Killer Whales", "Humans"],
1386            temperatureRange: "50-82°F (can thermoregulate)",
1387            depthRange: "0-3000 feet (highly migratory)",
1388            spawningAge: 8.0,
1389            spawningBehavior: "Spawns in warm waters of Gulf of Mexico and Mediterranean, pelagic eggs",
1390            migrationPattern: "Highly migratory across ocean basins, follows temperature gradients and food sources",
1391            waterQualityNeeds: "Clean, well-oxygenated saltwater, prefers deep blue water",
1392            
1393            // GEOGRAPHIC & HABITAT
1394            nativeRegions: ["North Atlantic", "Mediterranean Sea", "North Pacific"],
1395            currentRange: ["Atlantic Ocean", "Mediterranean Sea", "Pacific Ocean"],
1396            waterTypes: ["Open Ocean", "Pelagic", "Continental Shelf", "Deep Water"],
1397            invasiveStatus: "Native",
1398            
1399            // ECONOMIC & COMMERCIAL
1400            regionalCommercialValue: {
1401                "Japan": 200.00,
1402                "Mediterranean": 150.00,
1403                "US East Coast": 180.00,
1404                "Atlantic Canada": 120.00
1405            },
1406            tourismValue: 10,
1407            ecosystemRole: "Apex Predator, Key Pelagic Species",
1408            culturalSignificance: "Central to Japanese cuisine culture, historically important to Mediterranean fishing communities",
1409            
1410            // ANGLING & RECREATIONAL
1411            bestBaits: ["Live mackerel", "Chunked bait", "Large lures", "Butterfly jigs", "Cedar plugs"],
1412            fightRating: 10,
1413            culinaryRating: 10,
1414            catchDifficulty: 10,
1415            seasonalAvailability: "Seasonal migrations, best summer-fall in temperate waters",
1416            bestTechniques: ["Chunking", "Trolling", "Live bait fishing", "Butterfly jigging", "Kite fishing"],
1417            
1418            // REGULATORY
1419            regionalRegulations: {
1420                "US Atlantic": RegionalRegulations(
1421                    sizeLimit: 73.0,
1422                    bagLimit: 1,
1423                    closedSeasons: ["January 1 - June 1"],
1424                    specialRegulations: "Highly regulated quota system, HMS permit required",
1425                    licenseRequired: true
1426                ),
1427                "Mediterranean": RegionalRegulations(
1428                    sizeLimit: 30.0,
1429                    bagLimit: nil,
1430                    closedSeasons: ["November 16 - December 15"],
1431                    specialRegulations: "ICCAT quotas, catch documentation required",
1432                    licenseRequired: true
1433                ),
1434                "Pacific": RegionalRegulations(
1435                    sizeLimit: nil,
1436                    bagLimit: 2,
1437                    closedSeasons: [],
1438                    specialRegulations: "Varies by region, some areas closed to fishing",
1439                    licenseRequired: true
1440                )
1441            },
1442            
1443            // PHYSICAL & BEHAVIORAL
1444            physicalDescription: "Massive, torpedo-shaped body with metallic blue-black back, silver sides, powerful crescent tail, large head with big eyes",
1445            behaviorTraits: "Warm-blooded, schooling behavior, incredibly fast (up to 43 mph), deep diving capability",
1446            seasonalPatterns: "Complex migrations across ocean basins, spawning in specific warm-water areas",
1447            
1448            // RECORDS & ACHIEVEMENTS
1449            recordWeight: 1496.0,
1450            recordWeightLocation: "Nova Scotia, Canada",
1451            recordWeightDate: "October 26, 1979",
1452            recordLength: 144.0,
1453            recordLengthLocation: "Prince Edward Island, Canada",
1454            recordLengthDate: "September 1988",
1455            
1456            // RESEARCH & SCIENTIFIC
1457            researchPriority: 10,
1458            geneticMarkers: "Extensive genetic studies for stock assessment and population structure",
1459            studyPrograms: ["ICCAT Research", "NOAA Bluefin BluefinTuna Research", "Stanford BluefinTuna Research Program"],
1460            
1461            // FLEXIBLE METADATA
1462            additionalMetadata: {
1463                "last_updated": "2024-01-01",
1464                "data_quality": "High",
1465                "contributor": "DerbyFish Research Team",
1466                "max_speed": "43 mph",
1467                "nickname": "Giant Bluefin"
1468            }
1469        )
1470
1471        // Set storage paths using common name format
1472        self.VaultStoragePath = /storage/BluefinTunaCoinVault
1473        self.VaultPublicPath = /public/BluefinTunaCoinReceiver
1474        self.MinterStoragePath = /storage/BluefinTunaCoinMinter
1475        self.MetadataAdminStoragePath = /storage/BluefinTunaCoinMetadataAdmin
1476        self.FishDEXCoordinatorStoragePath = /storage/BluefinTunaCoinFishDEXCoordinator
1477        self.FishDEXCoordinatorPublicPath = /public/BluefinTunaCoinFishDEXCoordinator
1478
1479        // Create and store admin resources
1480        let minter <- create Minter()
1481        self.account.storage.save(<-minter, to: self.MinterStoragePath)
1482        
1483        let metadataAdmin <- create MetadataAdmin()
1484        self.account.storage.save(<-metadataAdmin, to: self.MetadataAdminStoragePath)
1485        
1486        // Create and store FishDEX coordinator
1487        let fishDEXCoordinator <- create FishDEXCoordinator()
1488        self.account.storage.save(<-fishDEXCoordinator, to: self.FishDEXCoordinatorStoragePath)
1489        
1490        // Create public capability for FishDEX coordinator
1491        let coordinatorCapability = self.account.capabilities.storage.issue<&FishDEXCoordinator>(self.FishDEXCoordinatorStoragePath)
1492        self.account.capabilities.publish(coordinatorCapability, at: self.FishDEXCoordinatorPublicPath)
1493
1494        let vault <- create Vault(balance: self.totalSupply)
1495        self.account.storage.save(<-vault, to: self.VaultStoragePath)
1496        let cap = self.account.capabilities.storage.issue<&BluefinTunaCoin.Vault>(self.VaultStoragePath)
1497        self.account.capabilities.publish(cap, at: self.VaultPublicPath)
1498    }
1499}