Skip to content

Commit

Permalink
optimize serializeNFTTraitsAsAttributes & add single trait serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
sisyphusSmiling committed Oct 23, 2024
1 parent 6f9f862 commit 56cc52e
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 24 deletions.
4 changes: 2 additions & 2 deletions cadence/contracts/utils/Serialize.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ contract Serialize {
access(all)
fun arrayToJSONString(_ arr: [AnyStruct]): String? {
let parts: [String]= []
for i, element in arr {
for element in arr {
let serializedElement = self.tryToJSONString(element)
if serializedElement == nil {
continue
Expand All @@ -112,7 +112,7 @@ contract Serialize {
}
}
let parts: [String] = []
for i, key in dict.keys {
for key in dict.keys {
let serializedValue = self.tryToJSONString(dict[key]!)
if serializedValue == nil {
continue
Expand Down
48 changes: 28 additions & 20 deletions cadence/contracts/utils/SerializeMetadata.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -112,36 +112,44 @@ access(all) contract SerializeMetadata {
///
/// @param traits: The Traits view to be serialized
///
/// @returns: A JSON compatible string containing the serialized traits as:
/// @returns: A JSON compatible string containing the serialized traits as follows
/// (display_type omitted if trait.displayType == nil):
/// `\"attributes\": [{\"trait_type\": \"<trait.name>\", \"display_type\": \"<trait.displayType>\", \"value\": \"<trait.value>\"}, {...}]`
///
access(all)
fun serializeNFTTraitsAsAttributes(_ traits: MetadataViews.Traits): String {
// Serialize each trait as an attribute, building the serialized JSON compatible string
var serializedResult = "\"attributes\": ["
let parts: [String] = []
let traitsLength = traits.traits.length
for i, trait in traits.traits {
let value = Serialize.tryToJSONString(trait.value)
if value == nil {
// Remove trailing comma if last trait is not serializable
if i == traitsLength - 1 && serializedResult[serializedResult.length - 1] == "," {
serializedResult = serializedResult.slice(from: 0, upTo: serializedResult.length - 1)
}
let attribute = self.serializeNFTTraitAsAttribute(trait)
if attribute == nil {
continue
}
serializedResult = serializedResult.concat("{")
.concat("\"trait_type\": ").concat(Serialize.tryToJSONString(trait.name)!)
if trait.displayType != nil {
serializedResult = serializedResult.concat(", \"display_type\": ")
.concat(Serialize.tryToJSONString(trait.displayType)!)
}
serializedResult = serializedResult.concat(", \"value\": ").concat(value!)
.concat("}")
if i < traits!.traits.length - 1 {
serializedResult = serializedResult.concat(",")
}
parts.append(attribute!)
}
// Join all serialized attributes with a comma separator, wrapping the result in square brackets under the
// `attributes` key
return "\"attributes\": [".concat(String.join(parts, separator: ", ")).concat("]")
}

/// Serializes a given Trait as an attribute in a JSON compatible format. If the trait's value is not serializable,
/// nil is returned.
/// The format of the serialized trait is as follows (display_type omitted if trait.displayType == nil):
/// `{"trait_type": "<trait.name>", "display_type": "<trait.displayType>", "value": "<trait.value>"}`
access(all)
fun serializeNFTTraitAsAttribute(_ trait: MetadataViews.Trait): String? {
let value = Serialize.tryToJSONString(trait.value)
if value == nil {
return nil
}
return serializedResult.concat("]")
let parts: [String] = ["{"]
parts.appendAll( [ "\"trait_type\": ", Serialize.tryToJSONString(trait.name)! ] )
if trait.displayType != nil {
parts.appendAll( [ ", \"display_type\": ", Serialize.tryToJSONString(trait.displayType)! ] )
}
parts.appendAll( [ ", \"value\": ", value! , "}" ] )
return String.join(parts, separator: "")
}

/// Serializes the FTDisplay view of a given fungible token as a JSON compatible data URL. The value is returned as
Expand Down
5 changes: 3 additions & 2 deletions cadence/tests/serialize_metadata_tests.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ fun testSerializeNFTSucceeds() {
mintedBlockHeight = heightResult.returnValue! as! UInt64
let heightString = mintedBlockHeight.toString()

// Cadence dictionaries are not ordered by insertion order, so we need to check for both possible orderings
let expectedPrefix = "data:application/json;utf8,{\"name\": \"ExampleNFT\", \"description\": \"Example NFT Collection\", \"image\": \"https://flow.com/examplenft.jpg\", \"external_url\": \"https://example-nft.onflow.org\", "
let altSuffix1 = "\"attributes\": [{\"trait_type\": \"mintedBlock\", \"value\": \"".concat(heightString).concat("\"},{\"trait_type\": \"foo\", \"value\": \"nil\"}]}")
let altSuffix2 = "\"attributes\": [{\"trait_type\": \"foo\", \"value\": \"nil\"}]}, {\"trait_type\": \"mintedBlock\", \"value\": \"".concat(heightString).concat("\"}")
let altSuffix1 = "\"attributes\": [{\"trait_type\": \"mintedBlock\", \"value\": \"".concat(heightString).concat("\"}, {\"trait_type\": \"foo\", \"value\": \"nil\"}]}")
let altSuffix2 = "\"attributes\": [{\"trait_type\": \"foo\", \"value\": \"nil\"}, {\"trait_type\": \"mintedBlock\", \"value\": \"".concat(heightString).concat("\"}]}")

let idsResult = executeScript(
"../scripts/nft/get_ids.cdc",
Expand Down

0 comments on commit 56cc52e

Please sign in to comment.