Smart Contract
Resolver
A.b8ea91944fd51c43.Resolver
1import NonFungibleToken from 0x1d7e57aa55817448
2import MetadataViews from 0x1d7e57aa55817448
3import ViewResolver from 0x1d7e57aa55817448
4import TopShot from 0x0b2a3299cc857e29
5
6// Resolver
7//
8// Contract holds the Offer exchange resolution rules.
9//
10// When an Offer is created a ResolverType is included. The ResolverType is also
11// passed into checkOfferResolver() from the Offers contract on exchange validation
12access(all) contract Resolver {
13 // Current list of supported resolution rules.
14 access(all) enum ResolverType: UInt8 {
15 access(all) case NFT
16 access(all) case TopShotEdition
17 access(all) case MetadataViewsEditions
18 access(all) case EditionIdAndSerialNumberTraits
19 }
20
21 // Public resource interface that defines a method signature for checkOfferResolver
22 // which is used within the Resolver resource for offer acceptance validation
23 access(all) resource interface ResolverPublic {
24 access(all) fun checkOfferResolver(
25 item: &{NonFungibleToken.NFT},
26 offerParamsString: {String:String},
27 offerParamsUInt64: {String:UInt64},
28 offerParamsUFix64: {String:UFix64}
29 ): Bool
30 }
31
32
33 // Resolver resource holds the Offer exchange resolution rules.
34 access(all) resource OfferResolver: ResolverPublic {
35 // checkOfferResolver
36 // Holds the validation rules for resolver each type of supported ResolverType
37 // Function returns TRUE if the provided nft item passes the criteria for exchange
38 access(all) fun checkOfferResolver(
39 item: &{NonFungibleToken.NFT},
40 offerParamsString: {String:String},
41 offerParamsUInt64: {String:UInt64},
42 offerParamsUFix64: {String:UFix64}
43 ): Bool {
44 pre {
45 offerParamsString.containsKey("resolver"): "offerParamsString must contain key 'resolver'"
46 }
47 switch offerParamsString["resolver"]! {
48 case ResolverType.NFT.rawValue.toString():
49 return Resolver.resolveNFTType(item, offerParamsString)
50 case ResolverType.TopShotEdition.rawValue.toString():
51 return Resolver.resolveTopShotEditionType(item, offerParamsString)
52 case ResolverType.MetadataViewsEditions.rawValue.toString():
53 return Resolver.resolveMetadataViewsEditionsType(item, offerParamsString)
54 case ResolverType.EditionIdAndSerialNumberTraits.rawValue.toString():
55 return Resolver.resolveEditionIdAndSerialNumberTraitsType(item, offerParamsString)
56 default:
57 panic("Invalid Resolver on Offer: ".concat(offerParamsString["resolver"]!))
58 }
59 }
60 }
61
62 // Resolves an offer for NFT type
63 access(contract) fun resolveNFTType(_ item: &{NonFungibleToken.NFT}, _ offerParamsString: {String:String}): Bool {
64 assert(item.id.toString() == offerParamsString["nftId"], message: "item NFT does not have specified ID")
65 return true
66 }
67
68 // Resolves an offer for TopShotEdition type
69 access(contract) fun resolveTopShotEditionType(_ item: &{NonFungibleToken.NFT}, _ offerParamsString: {String:String}): Bool {
70 let view = item.resolveView(Type<TopShot.TopShotMomentMetadataView>())
71 ?? panic("NFT does not use TopShot.TopShotMomentMetadataView")
72 let metadata = view as! TopShot.TopShotMomentMetadataView
73
74 // Always validate set and play first
75 let setMatch = offerParamsString["setId"] == metadata.setID.toString()
76 let playMatch = offerParamsString["playId"] == metadata.playID.toString()
77
78 if !setMatch || !playMatch {
79 return false
80 }
81
82 // if present, check if subeditionId matches the NFT's subeditionId
83 if let offerSubeditionId = offerParamsString["subeditionId"] {
84 let subeditionID = TopShot.getMomentsSubedition(nftID: item.id) ?? 0
85 return subeditionID.toString() == offerSubeditionId
86 }
87
88 // if subeditionId is not present, return true (any subedition is accepted, not just base edition)
89 return true
90 }
91
92 // Resolves an offer for MetadataViewsEditions type
93 access(contract) fun resolveMetadataViewsEditionsType(_ item: &{NonFungibleToken.NFT}, _ offerParamsString: {String:String}): Bool {
94 let view = item.resolveView(Type<MetadataViews.Editions>())
95 ?? panic("NFT does not use MetadataViews.Editions")
96 let editions = view as! [MetadataViews.Edition]
97 for edition in editions {
98 if edition.name == offerParamsString["editionName"] {
99 return true
100 }
101 }
102 panic("no matching edition name for NFT")
103 }
104
105 // Resolves an offer for EditionIdAndSerialNumberTraits type
106 access(contract) fun resolveEditionIdAndSerialNumberTraitsType(_ item: &{NonFungibleToken.NFT}, _ offerParamsString: {String:String}): Bool {
107 pre {
108 offerParamsString.containsKey("editionId"): "offerParamsString must contain key 'editionId'"
109 }
110
111 let view = item.resolveView(Type<MetadataViews.Traits>())
112 ?? panic("NFT does not use MetadataViews.Traits")
113 let traits = view as! MetadataViews.Traits
114
115 let shouldValidateSerial = offerParamsString.containsKey("serialNumber")
116 var hasValidEditionId = false
117 var hasValidSerialNumber = false
118
119 for trait in traits.traits {
120 if trait.name.toLower() == "editionid" {
121 if let nftEditionIdTrait = trait.value as! UInt64? {
122 hasValidEditionId = nftEditionIdTrait.toString() == offerParamsString["editionId"]
123 }
124 }
125 if shouldValidateSerial && trait.name.toLower() == "serialnumber" {
126 if let nftSerialNumber = trait.value as! UInt64? {
127 hasValidSerialNumber = nftSerialNumber.toString() == offerParamsString["serialNumber"]
128 }
129 }
130 // Return early if the required traits are found and match
131 if hasValidEditionId && (!shouldValidateSerial || hasValidSerialNumber) {
132 return true
133 }
134 }
135 return false
136 }
137
138 // Public function to create a new OfferResolver resource
139 access(all) fun createResolver(): @OfferResolver {
140 return <-create OfferResolver()
141 }
142}
143