Smart Contract

CrappieCoin

A.44100f14f70e3f78.CrappieCoin

Valid From

118,782,048

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 CrappieCoin: 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): @CrappieCoin.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<&CrappieCoin.Vault>(),
500                    metadataLinkedType: Type<&CrappieCoin.Vault>(),
501                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
502                        return <-CrappieCoin.createEmptyVault(vaultType: Type<@CrappieCoin.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 CrappieCoin.speciesMetadata.speciesCode
517        }
518        
519        access(all) view fun getTicker(): String {
520            return CrappieCoin.speciesMetadata.ticker
521        }
522        
523        access(all) view fun getCommonName(): String {
524            return CrappieCoin.speciesMetadata.commonName
525        }
526        
527        access(all) view fun getScientificName(): String {
528            return CrappieCoin.speciesMetadata.scientificName
529        }
530        
531        access(all) view fun getFamily(): String {
532            return CrappieCoin.speciesMetadata.family
533        }
534        
535        access(all) view fun getTotalSupply(): UFix64 {
536            return CrappieCoin.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": CrappieCoin.account.address,
548                "dataYear": CrappieCoin.speciesMetadata.dataYear,
549                "conservationStatus": CrappieCoin.speciesMetadata.globalConservationStatus,
550                "rarityTier": CrappieCoin.speciesMetadata.rarityTier,
551                "isRegistered": CrappieCoin.isRegisteredWithFishDEX
552            }
553        }
554        
555        // Process catch verification from Fish NFT contracts
556        access(all) fun redeemCatchNFT(fishData: {String: AnyStruct}, angler: Address): @CrappieCoin.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                    CrappieCoin.fishNFTRegistry[nftId] == nil,
572                    message: "This NFT has already been redeemed for a coin"
573                )
574                // Record this NFT as redeemed
575                CrappieCoin.fishNFTRegistry[nftId] = true
576            }
577            
578            // Auto-record first catch if this is the very first mint
579            if CrappieCoin.totalSupply == 0.0 && CrappieCoin.speciesMetadata.firstCatchDate == nil {
580                CrappieCoin.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            CrappieCoin.totalSupply = CrappieCoin.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                !CrappieCoin.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            CrappieCoin.fishDEXAddress = fishDEXAddress
617            CrappieCoin.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 = CrappieCoin.fishDEXAddress
625            CrappieCoin.fishDEXAddress = newAddress
626            CrappieCoin.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                CrappieCoin.totalSupply = CrappieCoin.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 CrappieCoin.getContractViews(resourceType: nil)
649        }
650
651        access(all) fun resolveView(_ view: Type): AnyStruct? {
652            return CrappieCoin.resolveContractView(resourceType: nil, viewType: view)
653        }
654
655        access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
656            return {Type<@CrappieCoin.Vault>(): true}
657        }
658
659        access(all) view fun isSupportedVaultType(type: Type): Bool {
660            return type == Type<@CrappieCoin.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): @CrappieCoin.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! @CrappieCoin.Vault
674            self.balance = self.balance + vault.balance
675            vault.balance = 0.0
676            destroy vault
677        }
678
679        access(all) fun createEmptyVault(): @CrappieCoin.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): @CrappieCoin.Vault {
688            pre {
689                amount == 1.0: "Only 1 coin per verified catch"
690                CrappieCoin.fishNFTRegistry[fishId] == nil: "This NFT has already been used to mint a coin"
691            }
692            
693            // Record this NFT as used
694            CrappieCoin.fishNFTRegistry[fishId] = true
695            
696            // Auto-record first catch if this is the very first mint
697            if CrappieCoin.totalSupply == 0.0 && CrappieCoin.speciesMetadata.firstCatchDate == nil {
698                CrappieCoin.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            CrappieCoin.totalSupply = CrappieCoin.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 = CrappieCoin.fishDEXAddress {
716                if !CrappieCoin.isRegisteredWithFishDEX {
717                    // Get reference to FishDEX coordinator
718                    if let coordinatorRef = CrappieCoin.account.capabilities.borrow<&FishDEXCoordinator>(
719                        CrappieCoin.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            CrappieCoin.fishDEXAddress = fishDEXAddress
730            self.autoRegisterWithFishDEX()
731        }
732
733        access(all) fun mintBatch(recipients: {Address: UFix64}): @{Address: CrappieCoin.Vault} {
734            let vaults: @{Address: CrappieCoin.Vault} <- {}
735            
736            for recipient in recipients.keys {
737                let amount = recipients[recipient]!
738                CrappieCoin.totalSupply = CrappieCoin.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 = CrappieCoin.speciesMetadata.imageURL ?? ""
756            CrappieCoin.speciesMetadata.setImageURL(newURL)
757            emit MetadataUpdated(field: "imageURL", oldValue: oldURL, newValue: newURL)
758        }
759        
760        access(all) fun updateDescription(newDescription: String) {
761            let oldDescription = CrappieCoin.speciesMetadata.description
762            CrappieCoin.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 = CrappieCoin.speciesMetadata.habitat ?? ""
770            CrappieCoin.speciesMetadata.setHabitat(newHabitat)
771            emit MetadataUpdated(field: "habitat", oldValue: oldHabitat, newValue: newHabitat)
772        }
773        
774        access(all) fun updateAverageWeight(newWeight: UFix64) {
775            let oldWeight = CrappieCoin.speciesMetadata.averageWeight?.toString() ?? "0.0"
776            CrappieCoin.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 = CrappieCoin.speciesMetadata.averageLength?.toString() ?? "0.0"
782            CrappieCoin.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 = CrappieCoin.speciesMetadata.rarityTier?.toString() ?? "0"
791            CrappieCoin.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                CrappieCoin.speciesMetadata.firstCatchDate == nil: "First catch already recorded"
798            }
799            CrappieCoin.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 = CrappieCoin.speciesMetadata.globalConservationStatus ?? ""
807            CrappieCoin.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 CrappieCoin.speciesMetadata.nativeRegions {
814                if i > 0 { oldRegionsStr = oldRegionsStr.concat(",") }
815                oldRegionsStr = oldRegionsStr.concat(region)
816            }
817            oldRegionsStr = oldRegionsStr.concat("]")
818            
819            CrappieCoin.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 = CrappieCoin.speciesMetadata.seasonalPatterns ?? ""
833            CrappieCoin.speciesMetadata.setSeasonalPatterns(newPatterns)
834            emit MetadataUpdated(field: "seasonalPatterns", oldValue: oldPatterns, newValue: newPatterns)
835        }
836        
837        access(all) fun updateRecordWeight(newRecord: UFix64) {
838            let oldRecord = CrappieCoin.speciesMetadata.recordWeight?.toString() ?? "0.0"
839            CrappieCoin.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 = CrappieCoin.speciesMetadata.recordLength?.toString() ?? "0.0"
845            CrappieCoin.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 = CrappieCoin.speciesMetadata.recordWeightLocation ?? ""
852            CrappieCoin.speciesMetadata.setRecordWeightLocation(newLocation)
853            emit MetadataUpdated(field: "recordWeightLocation", oldValue: oldLocation, newValue: newLocation ?? "")
854        }
855        
856        access(all) fun updateRecordWeightDate(newDate: String?) {
857            let oldDate = CrappieCoin.speciesMetadata.recordWeightDate ?? ""
858            CrappieCoin.speciesMetadata.setRecordWeightDate(newDate)
859            emit MetadataUpdated(field: "recordWeightDate", oldValue: oldDate, newValue: newDate ?? "")
860        }
861        
862        access(all) fun updateRecordLengthLocation(newLocation: String?) {
863            let oldLocation = CrappieCoin.speciesMetadata.recordLengthLocation ?? ""
864            CrappieCoin.speciesMetadata.setRecordLengthLocation(newLocation)
865            emit MetadataUpdated(field: "recordLengthLocation", oldValue: oldLocation, newValue: newLocation ?? "")
866        }
867        
868        access(all) fun updateRecordLengthDate(newDate: String?) {
869            let oldDate = CrappieCoin.speciesMetadata.recordLengthDate ?? ""
870            CrappieCoin.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 < CrappieCoin.pendingUpdates.length: "Invalid update index"
891            }
892            let update = CrappieCoin.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 < CrappieCoin.pendingUpdates.length: "Invalid update index"
900            }
901            CrappieCoin.pendingUpdates.remove(at: index)
902        }
903        
904        access(all) fun clearAllPendingUpdates() {
905            CrappieCoin.pendingUpdates = []
906        }
907        
908        // ENHANCED ADMIN FUNCTIONS WITH VALIDATION
909        access(all) fun updateRarityTierValidated(newTier: UInt8) {
910            pre {
911                CrappieCoin.validateRating(newTier): "Invalid rarity tier (must be 1-10)"
912            }
913            let oldTier = CrappieCoin.speciesMetadata.rarityTier?.toString() ?? "0"
914            CrappieCoin.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                CrappieCoin.validateConservationStatus(newStatus): "Invalid conservation status"
921            }
922            let oldStatus = CrappieCoin.speciesMetadata.globalConservationStatus ?? ""
923            CrappieCoin.speciesMetadata.setConservationStatus(newStatus)
924            emit MetadataUpdated(field: "globalConservationStatus", oldValue: oldStatus, newValue: newStatus)
925        }
926        
927        access(all) fun updateFightRatingValidated(newRating: UInt8) {
928            pre {
929                CrappieCoin.validateRating(newRating): "Fight rating must be 1-10"
930            }
931            let oldRating = CrappieCoin.speciesMetadata.fightRating?.toString() ?? "0"
932            CrappieCoin.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 = CrappieCoin.currentMetadataYear
939            CrappieCoin.metadataHistory[currentYear] = CrappieCoin.speciesMetadata
940            emit YearlyMetadataCreated(year: currentYear)
941        }
942        
943        access(all) fun updateToNewYear(_ newYear: UInt64) {
944            pre {
945                newYear > CrappieCoin.currentMetadataYear: "New year must be greater than current year"
946            }
947            // Archive current year data
948            self.archiveCurrentYear()
949            
950            let oldYear = CrappieCoin.currentMetadataYear
951            CrappieCoin.currentMetadataYear = newYear
952            emit MetadataYearUpdated(oldYear: oldYear, newYear: newYear)
953        }
954    }
955
956    // Public functions
957    access(all) fun createEmptyVault(vaultType: Type): @CrappieCoin.Vault {
958        pre {
959            vaultType == Type<@CrappieCoin.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: Crappie 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: &CrappieCoin.Vault): UFix64 {
1297        return vaultRef.balance
1298    }
1299    
1300    access(all) view fun canWithdraw(vaultRef: &CrappieCoin.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<@CrappieCoin.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: "POMOXIS_NIGROMACULATUS",
1344            ticker: "POMNIG",
1345            scientificName: "Pomoxis nigromaculatus",
1346            family: "Centrarchidae",
1347            dataYear: 2024,
1348            
1349            // BASIC DESCRIPTIVE FIELDS
1350            commonName: "Crappie",
1351            habitat: "Freshwater lakes, reservoirs, slow-moving rivers, and ponds with vegetation",
1352            averageWeight: 1.5,
1353            averageLength: 12.0,
1354            imageURL: "https://derby.fish/images/species/Crappie.jpg",
1355            description: "Black Crappie are popular panfish known for their excellent table fare and schooling behavior. They have distinctive dark mottled patterns and are prized for their sweet, flaky meat.",
1356            firstCatchDate: nil,
1357            rarityTier: 2,
1358            
1359            // CONSERVATION & POPULATION
1360            globalConservationStatus: "Least Concern",
1361            regionalPopulations: {
1362                "Great Lakes": RegionalPopulation(
1363                    populationTrend: "Stable",
1364                    threats: ["Habitat Loss", "Water Quality", "Invasive Species"],
1365                    protectedAreas: ["Great Lakes National Marine Sanctuaries"],
1366                    estimatedPopulation: 25000000
1367                ),
1368                "Southeast US": RegionalPopulation(
1369                    populationTrend: "Stable",
1370                    threats: ["Agricultural Runoff", "Dam Construction", "Sedimentation"],
1371                    protectedAreas: ["Tennessee River System", "Various State Parks"],
1372                    estimatedPopulation: nil
1373                ),
1374                "Central US": RegionalPopulation(
1375                    populationTrend: "Stable",
1376                    threats: ["Habitat Fragmentation", "Water Level Fluctuations", "Pollution"],
1377                    protectedAreas: ["Mark Twain National Forest Lakes"],
1378                    estimatedPopulation: nil
1379                )
1380            },
1381            
1382            // BIOLOGICAL INTELLIGENCE
1383            lifespan: 10.0,
1384            diet: "Zooplankton, insects, small fish, crustaceans, worms",
1385            predators: ["Largemouth Bass", "Northern Pike", "Walleye", "Muskellunge", "Humans"],
1386            temperatureRange: "50-85°F (optimal 68-78°F)",
1387            depthRange: "5-20 feet",
1388            spawningAge: 2.0,
1389            spawningBehavior: "Spawns in shallow areas with vegetation, males build nests and guard eggs",
1390            migrationPattern: "Moves to deeper water in winter, shallow areas during spawning",
1391            waterQualityNeeds: "pH 6.5-8.5, moderate dissolved oxygen, tolerates some turbidity",
1392            
1393            // GEOGRAPHIC & HABITAT
1394            nativeRegions: ["Eastern North America", "Great Lakes", "Mississippi River Basin"],
1395            currentRange: ["Throughout US", "Southern Canada", "Introduced to Western US"],
1396            waterTypes: ["Lake", "Reservoir", "River", "Pond", "Slow-moving Streams"],
1397            invasiveStatus: "Native",
1398            
1399            // ECONOMIC & COMMERCIAL
1400            regionalCommercialValue: {
1401                "Great Lakes": 4.50,
1402                "Southeast US": 3.00,
1403                "Central US": 3.50,
1404                "Western US": 5.00
1405            },
1406            tourismValue: 7,
1407            ecosystemRole: "Secondary Consumer, Prey Species",
1408            culturalSignificance: "Popular family fishing target, important to recreational fishing culture",
1409            
1410            // ANGLING & RECREATIONAL
1411            bestBaits: ["Minnows", "Jigs", "Small spinners", "Worms", "Grubs"],
1412            fightRating: 6,
1413            culinaryRating: 9,
1414            catchDifficulty: 4,
1415            seasonalAvailability: "Best in spring and fall, active year-round in warm climates",
1416            bestTechniques: ["Vertical jigging", "Trolling", "Still fishing", "Spider rigging", "Dock fishing"],
1417            
1418            // REGULATORY
1419            regionalRegulations: {
1420                "Minnesota": RegionalRegulations(
1421                    sizeLimit: 10.0,
1422                    bagLimit: 10,
1423                    closedSeasons: [],
1424                    specialRegulations: "Check specific lake regulations",
1425                    licenseRequired: true
1426                ),
1427                "Wisconsin": RegionalRegulations(
1428                    sizeLimit: 8.0,
1429                    bagLimit: 25,
1430                    closedSeasons: [],
1431                    specialRegulations: "Varies by water body",
1432                    licenseRequired: true
1433                ),
1434                "Texas": RegionalRegulations(
1435                    sizeLimit: 10.0,
1436                    bagLimit: 25,
1437                    closedSeasons: [],
1438                    specialRegulations: "No minimum on some waters",
1439                    licenseRequired: true
1440                )
1441            },
1442            
1443            // PHYSICAL & BEHAVIORAL
1444            physicalDescription: "Silver-green body with dark mottled patterns, deep compressed body, large fins",
1445            behaviorTraits: "Schooling fish, structure-oriented, active during dawn and dusk",
1446            seasonalPatterns: "Spring spawning in shallows, summer deep water, fall feeding aggregations",
1447            
1448            // RECORDS & ACHIEVEMENTS
1449            recordWeight: 5.0,
1450            recordWeightLocation: "Privateer Lake, Louisiana",
1451            recordWeightDate: "March 15, 2006",
1452            recordLength: 19.3,
1453            recordLengthLocation: "Weiss Lake, Alabama",
1454            recordLengthDate: "April 21, 2018",
1455            
1456            // RESEARCH & SCIENTIFIC
1457            researchPriority: 6,
1458            geneticMarkers: "Studies on stock enhancement and population genetics",
1459            studyPrograms: ["State fisheries agencies", "University research programs", "Regional fisheries councils"],
1460            
1461            // FLEXIBLE METADATA
1462            additionalMetadata: {
1463                "last_updated": "2024-01-01",
1464                "data_quality": "High",
1465                "contributor": "DerbyFish Research Team",
1466                "state_fish": "None",
1467                "nickname": "Specks"
1468            }
1469        )
1470
1471        // Set storage paths using common name format
1472        self.VaultStoragePath = /storage/CrappieCoinVault
1473        self.VaultPublicPath = /public/CrappieCoinReceiver
1474        self.MinterStoragePath = /storage/CrappieCoinMinter
1475        self.MetadataAdminStoragePath = /storage/CrappieCoinMetadataAdmin
1476        self.FishDEXCoordinatorStoragePath = /storage/CrappieCoinFishDEXCoordinator
1477        self.FishDEXCoordinatorPublicPath = /public/CrappieCoinFishDEXCoordinator
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<&CrappieCoin.Vault>(self.VaultStoragePath)
1497        self.account.capabilities.publish(cap, at: self.VaultPublicPath)
1498    }
1499}