@flowtyio/flow-contracts 0.1.0-beta.9 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/contracts/FungibleTokenSwitchboard.cdc +360 -0
- package/contracts/MetadataViews.cdc +79 -6
- package/contracts/NonFungibleToken.cdc +17 -10
- package/contracts/capability-cache/CapabilityCache.cdc +97 -0
- package/contracts/dapper/TopShot.cdc +323 -259
- package/contracts/dapper/TopShotLocking.cdc +41 -15
- package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
- package/contracts/dapper/offers/OffersV2.cdc +40 -56
- package/contracts/dapper/offers/Resolver.cdc +20 -13
- package/contracts/emerald-city/FLOAT.cdc +259 -254
- package/contracts/evm/CrossVMNFT.cdc +50 -0
- package/contracts/evm/EVM.cdc +851 -0
- package/contracts/evm/FlowEVMBridgeConfig.cdc +454 -0
- package/contracts/evm/FlowEVMBridgeHandlerInterfaces.cdc +163 -0
- package/contracts/evm/FlowEVMBridgeHandlers.cdc +230 -0
- package/contracts/evm/FlowEVMBridgeUtils.cdc +1303 -0
- package/contracts/evm/IBridgePermissions.cdc +19 -0
- package/contracts/evm/ICrossVM.cdc +12 -0
- package/contracts/evm/ICrossVMAsset.cdc +19 -0
- package/contracts/evm/Serialize.cdc +141 -0
- package/contracts/evm/SerializeMetadata.cdc +221 -0
- package/contracts/example/ExampleNFT.cdc +2 -2
- package/contracts/find/FindViews.cdc +357 -353
- package/contracts/flow-utils/ScopedFTProviders.cdc +5 -2
- package/contracts/flow-utils/ScopedNFTProviders.cdc +6 -2
- package/contracts/flowty-drops/ContractManager.cdc +73 -0
- package/contracts/flowty-drops/DropFactory.cdc +75 -0
- package/contracts/flowty-drops/DropTypes.cdc +282 -0
- package/contracts/flowty-drops/FlowtyActiveCheckers.cdc +113 -0
- package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
- package/contracts/flowty-drops/FlowtyDrops.cdc +461 -0
- package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
- package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
- package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
- package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +57 -0
- package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
- package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
- package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
- package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
- package/contracts/flowty-drops/nft/NFTMetadata.cdc +140 -0
- package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +42 -0
- package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +54 -0
- package/contracts/flowty-drops/nft/UniversalCollection.cdc +29 -0
- package/contracts/fungible-token-router/FungibleTokenRouter.cdc +103 -0
- package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
- package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
- package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
- package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
- package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
- package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
- package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
- package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
- package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
- package/contracts/lost-and-found/LostAndFound.cdc +21 -17
- package/contracts/tokens/USDCFlow.cdc +232 -0
- package/flow.json +278 -7
- package/package.json +1 -1
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +0 -10
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// This contract defines a simple interface which can be implemented by any resource to prevent it from being
|
|
2
|
+
/// onboarded to the Flow-EVM bridge
|
|
3
|
+
///
|
|
4
|
+
/// NOTE: This is suggested only for cases where your asset (NFT/FT) incorporates non-standard logic that would
|
|
5
|
+
/// break your project if not handles properly
|
|
6
|
+
/// e.g. assets are reclaimed after a certain period of time, NFTs share IDs, etc.
|
|
7
|
+
///
|
|
8
|
+
access(all)
|
|
9
|
+
contract interface IBridgePermissions {
|
|
10
|
+
/// Contract-level method enabling implementing contracts to identify whether they allow bridging for their
|
|
11
|
+
/// project's assets. Implementers may consider adding a hook which would later enable an update to this value
|
|
12
|
+
/// should either the project be updated or the bridge be updated to handle the asset's non-standard logic which
|
|
13
|
+
/// would otherwise prevent them from supporting VM bridging at the outset.
|
|
14
|
+
///
|
|
15
|
+
access(all)
|
|
16
|
+
view fun allowsBridging(): Bool {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "EVM"
|
|
2
|
+
|
|
3
|
+
/// Contract interface denoting a cross-VM implementation, exposing methods to query EVM-associated addresses
|
|
4
|
+
///
|
|
5
|
+
access(all)
|
|
6
|
+
contract interface ICrossVM {
|
|
7
|
+
|
|
8
|
+
/// Retrieves the corresponding EVM contract address, assuming a 1:1 relationship between VM implementations
|
|
9
|
+
///
|
|
10
|
+
access(all)
|
|
11
|
+
view fun getEVMContractAddress(): EVM.EVMAddress
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "EVM"
|
|
2
|
+
|
|
3
|
+
import "ICrossVM"
|
|
4
|
+
|
|
5
|
+
/// A simple contract interface for a Cadence contract that represents an asset bridged from Flow EVM such as an ERC20
|
|
6
|
+
/// or ERC721 token.
|
|
7
|
+
///
|
|
8
|
+
access(all) contract interface ICrossVMAsset : ICrossVM {
|
|
9
|
+
/// Returns the name of the asset
|
|
10
|
+
access(all) view fun getName(): String
|
|
11
|
+
/// Returns the symbol of the asset
|
|
12
|
+
access(all) view fun getSymbol(): String
|
|
13
|
+
|
|
14
|
+
access(all) resource interface AssetInfo {
|
|
15
|
+
access(all) view fun getName(): String
|
|
16
|
+
access(all) view fun getSymbol(): String
|
|
17
|
+
access(all) view fun getEVMContractAddress(): EVM.EVMAddress
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import "ViewResolver"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
import "NonFungibleToken"
|
|
4
|
+
|
|
5
|
+
/// This contract is a utility for serializing primitive types, arrays, and common metadata mapping formats to JSON
|
|
6
|
+
/// compatible strings. Also included are interfaces enabling custom serialization for structs and resources.
|
|
7
|
+
///
|
|
8
|
+
/// Special thanks to @austinkline for the idea and initial implementation.
|
|
9
|
+
///
|
|
10
|
+
access(all)
|
|
11
|
+
contract Serialize {
|
|
12
|
+
|
|
13
|
+
/// Method that returns a serialized representation of the given value or nil if the value is not serializable
|
|
14
|
+
///
|
|
15
|
+
access(all)
|
|
16
|
+
fun tryToJSONString(_ value: AnyStruct): String? {
|
|
17
|
+
// Recursively serialize array & return
|
|
18
|
+
if value.getType().isSubtype(of: Type<[AnyStruct]>()) {
|
|
19
|
+
return self.arrayToJSONString(value as! [AnyStruct])
|
|
20
|
+
}
|
|
21
|
+
// Recursively serialize map & return
|
|
22
|
+
if value.getType().isSubtype(of: Type<{String: AnyStruct}>()) {
|
|
23
|
+
return self.dictToJSONString(dict: value as! {String: AnyStruct}, excludedNames: nil)
|
|
24
|
+
}
|
|
25
|
+
// Handle primitive types & optionals
|
|
26
|
+
switch value.getType() {
|
|
27
|
+
case Type<Never?>():
|
|
28
|
+
return "\"nil\""
|
|
29
|
+
case Type<String>():
|
|
30
|
+
return "\"".concat(value as! String).concat("\"")
|
|
31
|
+
case Type<String?>():
|
|
32
|
+
return "\"".concat(value as? String ?? "nil").concat("\"")
|
|
33
|
+
case Type<Character>():
|
|
34
|
+
return "\"".concat((value as! Character).toString()).concat("\"")
|
|
35
|
+
case Type<Bool>():
|
|
36
|
+
return "\"".concat(value as! Bool ? "true" : "false").concat("\"")
|
|
37
|
+
case Type<Address>():
|
|
38
|
+
return "\"".concat((value as! Address).toString()).concat("\"")
|
|
39
|
+
case Type<Address?>():
|
|
40
|
+
return "\"".concat((value as? Address)?.toString() ?? "nil").concat("\"")
|
|
41
|
+
case Type<Int8>():
|
|
42
|
+
return "\"".concat((value as! Int8).toString()).concat("\"")
|
|
43
|
+
case Type<Int16>():
|
|
44
|
+
return "\"".concat((value as! Int16).toString()).concat("\"")
|
|
45
|
+
case Type<Int32>():
|
|
46
|
+
return "\"".concat((value as! Int32).toString()).concat("\"")
|
|
47
|
+
case Type<Int64>():
|
|
48
|
+
return "\"".concat((value as! Int64).toString()).concat("\"")
|
|
49
|
+
case Type<Int128>():
|
|
50
|
+
return "\"".concat((value as! Int128).toString()).concat("\"")
|
|
51
|
+
case Type<Int256>():
|
|
52
|
+
return "\"".concat((value as! Int256).toString()).concat("\"")
|
|
53
|
+
case Type<Int>():
|
|
54
|
+
return "\"".concat((value as! Int).toString()).concat("\"")
|
|
55
|
+
case Type<UInt8>():
|
|
56
|
+
return "\"".concat((value as! UInt8).toString()).concat("\"")
|
|
57
|
+
case Type<UInt16>():
|
|
58
|
+
return "\"".concat((value as! UInt16).toString()).concat("\"")
|
|
59
|
+
case Type<UInt32>():
|
|
60
|
+
return "\"".concat((value as! UInt32).toString()).concat("\"")
|
|
61
|
+
case Type<UInt64>():
|
|
62
|
+
return "\"".concat((value as! UInt64).toString()).concat("\"")
|
|
63
|
+
case Type<UInt128>():
|
|
64
|
+
return "\"".concat((value as! UInt128).toString()).concat("\"")
|
|
65
|
+
case Type<UInt256>():
|
|
66
|
+
return "\"".concat((value as! UInt256).toString()).concat("\"")
|
|
67
|
+
case Type<UInt>():
|
|
68
|
+
return "\"".concat((value as! UInt).toString()).concat("\"")
|
|
69
|
+
case Type<Word8>():
|
|
70
|
+
return "\"".concat((value as! Word8).toString()).concat("\"")
|
|
71
|
+
case Type<Word16>():
|
|
72
|
+
return "\"".concat((value as! Word16).toString()).concat("\"")
|
|
73
|
+
case Type<Word32>():
|
|
74
|
+
return "\"".concat((value as! Word32).toString()).concat("\"")
|
|
75
|
+
case Type<Word64>():
|
|
76
|
+
return "\"".concat((value as! Word64).toString()).concat("\"")
|
|
77
|
+
case Type<Word128>():
|
|
78
|
+
return "\"".concat((value as! Word128).toString()).concat("\"")
|
|
79
|
+
case Type<Word256>():
|
|
80
|
+
return "\"".concat((value as! Word256).toString()).concat("\"")
|
|
81
|
+
case Type<UFix64>():
|
|
82
|
+
return "\"".concat((value as! UFix64).toString()).concat("\"")
|
|
83
|
+
default:
|
|
84
|
+
return nil
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Returns a serialized representation of the given array or nil if the value is not serializable
|
|
89
|
+
///
|
|
90
|
+
access(all)
|
|
91
|
+
fun arrayToJSONString(_ arr: [AnyStruct]): String? {
|
|
92
|
+
var serializedArr = "["
|
|
93
|
+
let arrLength = arr.length
|
|
94
|
+
for i, element in arr {
|
|
95
|
+
let serializedElement = self.tryToJSONString(element)
|
|
96
|
+
if serializedElement == nil {
|
|
97
|
+
if i == arrLength - 1 && serializedArr.length > 1 && serializedArr[serializedArr.length - 2] == "," {
|
|
98
|
+
// Remove trailing comma as this element could not be serialized
|
|
99
|
+
serializedArr = serializedArr.slice(from: 0, upTo: serializedArr.length - 2)
|
|
100
|
+
}
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
serializedArr = serializedArr.concat(serializedElement!)
|
|
104
|
+
// Add a comma if there are more elements to serialize
|
|
105
|
+
if i < arr.length - 1 {
|
|
106
|
+
serializedArr = serializedArr.concat(", ")
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return serializedArr.concat("]")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Returns a serialized representation of the given String-indexed mapping or nil if the value is not serializable.
|
|
113
|
+
/// The interface here is largely the same as as the `MetadataViews.dictToTraits` method, though here
|
|
114
|
+
/// a JSON-compatible String is returned instead of a `Traits` array.
|
|
115
|
+
///
|
|
116
|
+
access(all)
|
|
117
|
+
fun dictToJSONString(dict: {String: AnyStruct}, excludedNames: [String]?): String? {
|
|
118
|
+
if excludedNames != nil {
|
|
119
|
+
for k in excludedNames! {
|
|
120
|
+
dict.remove(key: k)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
var serializedDict = "{"
|
|
124
|
+
let dictLength = dict.length
|
|
125
|
+
for i, key in dict.keys {
|
|
126
|
+
let serializedValue = self.tryToJSONString(dict[key]!)
|
|
127
|
+
if serializedValue == nil {
|
|
128
|
+
if i == dictLength - 1 && serializedDict.length > 1 && serializedDict[serializedDict.length - 2] == "," {
|
|
129
|
+
// Remove trailing comma as this element could not be serialized
|
|
130
|
+
serializedDict = serializedDict.slice(from: 0, upTo: serializedDict.length - 2)
|
|
131
|
+
}
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
serializedDict = serializedDict.concat(self.tryToJSONString(key)!).concat(": ").concat(serializedValue!)
|
|
135
|
+
if i < dict.length - 1 {
|
|
136
|
+
serializedDict = serializedDict.concat(", ")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return serializedDict.concat("}")
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import "ViewResolver"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
import "NonFungibleToken"
|
|
4
|
+
import "FungibleTokenMetadataViews"
|
|
5
|
+
|
|
6
|
+
import "Serialize"
|
|
7
|
+
|
|
8
|
+
/// This contract defines methods for serializing NFT metadata as a JSON compatible string, according to the common
|
|
9
|
+
/// OpenSea metadata format. NFTs and metadata views can be serialized by reference via contract methods.
|
|
10
|
+
///
|
|
11
|
+
access(all) contract SerializeMetadata {
|
|
12
|
+
|
|
13
|
+
/// Serializes the metadata (as a JSON compatible String) for a given NFT according to formats expected by EVM
|
|
14
|
+
/// platforms like OpenSea. If you are a project owner seeking to expose custom traits on bridged NFTs and your
|
|
15
|
+
/// Trait.value is not natively serializable, you can implement a custom serialization method with the
|
|
16
|
+
/// `{SerializableStruct}` interface's `serialize` method.
|
|
17
|
+
///
|
|
18
|
+
/// Reference: https://docs.opensea.io/docs/metadata-standards
|
|
19
|
+
///
|
|
20
|
+
///
|
|
21
|
+
/// @returns: A JSON compatible data URL string containing the serialized display & collection display views as:
|
|
22
|
+
/// `data:application/json;utf8,{
|
|
23
|
+
/// \"name\": \"<display.name>\",
|
|
24
|
+
/// \"description\": \"<display.description>\",
|
|
25
|
+
/// \"image\": \"<display.thumbnail.uri()>\",
|
|
26
|
+
/// \"external_url\": \"<nftCollectionDisplay.externalURL.url>\",
|
|
27
|
+
/// \"attributes\": [{\"trait_type\": \"<trait.name>\", \"value\": \"<trait.value>\"}, {...}]
|
|
28
|
+
/// }`
|
|
29
|
+
access(all)
|
|
30
|
+
fun serializeNFTMetadataAsURI(_ nft: &{NonFungibleToken.NFT}): String {
|
|
31
|
+
// Serialize the display values from the NFT's Display & NFTCollectionDisplay views
|
|
32
|
+
let nftDisplay = nft.resolveView(Type<MetadataViews.Display>()) as! MetadataViews.Display?
|
|
33
|
+
let collectionDisplay = nft.resolveView(Type<MetadataViews.NFTCollectionDisplay>()) as! MetadataViews.NFTCollectionDisplay?
|
|
34
|
+
let display = self.serializeFromDisplays(nftDisplay: nftDisplay, collectionDisplay: collectionDisplay)
|
|
35
|
+
|
|
36
|
+
// Get the Traits view from the NFT, returning early if no traits are found
|
|
37
|
+
let traits = nft.resolveView(Type<MetadataViews.Traits>()) as! MetadataViews.Traits?
|
|
38
|
+
let attributes = self.serializeNFTTraitsAsAttributes(traits ?? MetadataViews.Traits([]))
|
|
39
|
+
|
|
40
|
+
// Return an empty string if nothing is serializable
|
|
41
|
+
if display == nil && attributes == nil {
|
|
42
|
+
return ""
|
|
43
|
+
}
|
|
44
|
+
// Init the data format prefix & concatenate the serialized display & attributes
|
|
45
|
+
var serializedMetadata = "data:application/json;utf8,{"
|
|
46
|
+
if display != nil {
|
|
47
|
+
serializedMetadata = serializedMetadata.concat(display!)
|
|
48
|
+
}
|
|
49
|
+
if display != nil && attributes != nil {
|
|
50
|
+
serializedMetadata = serializedMetadata.concat(", ")
|
|
51
|
+
}
|
|
52
|
+
if attributes != nil {
|
|
53
|
+
serializedMetadata = serializedMetadata.concat(attributes)
|
|
54
|
+
}
|
|
55
|
+
return serializedMetadata.concat("}")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Serializes the display & collection display views of a given NFT as a JSON compatible string. If nftDisplay is
|
|
59
|
+
/// present, the value is returned as token-level metadata. If nftDisplay is nil and collectionDisplay is present,
|
|
60
|
+
/// the value is returned as contract-level metadata. If both values are nil, nil is returned.
|
|
61
|
+
///
|
|
62
|
+
/// @param nftDisplay: The NFT's Display view from which values `name`, `description`, and `thumbnail` are serialized
|
|
63
|
+
/// @param collectionDisplay: The NFT's NFTCollectionDisplay view from which the `externalURL` is serialized
|
|
64
|
+
///
|
|
65
|
+
/// @returns: A JSON compatible string containing the serialized display & collection display views as either:
|
|
66
|
+
/// \"name\": \"<nftDisplay.name>\", \"description\": \"<nftDisplay.description>\", \"image\": \"<nftDisplay.thumbnail.uri()>\", \"external_url\": \"<collectionDisplay.externalURL.url>\",
|
|
67
|
+
/// \"name\": \"<collectionDisplay.name>\", \"description\": \"<collectionDisplay.description>\", \"image\": \"<collectionDisplay.squareImage.file.uri()>\", \"external_link\": \"<collectionDisplay.externalURL.url>\",
|
|
68
|
+
///
|
|
69
|
+
access(all)
|
|
70
|
+
fun serializeFromDisplays(nftDisplay: MetadataViews.Display?, collectionDisplay: MetadataViews.NFTCollectionDisplay?): String? {
|
|
71
|
+
// Return early if both values are nil
|
|
72
|
+
if nftDisplay == nil && collectionDisplay == nil {
|
|
73
|
+
return nil
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Initialize JSON fields
|
|
77
|
+
let name = "\"name\": "
|
|
78
|
+
let description = "\"description\": "
|
|
79
|
+
let image = "\"image\": "
|
|
80
|
+
let externalURL = "\"external_url\": "
|
|
81
|
+
let externalLink = "\"external_link\": "
|
|
82
|
+
var serializedResult = ""
|
|
83
|
+
|
|
84
|
+
// Append results from the token-level Display view to the serialized JSON compatible string
|
|
85
|
+
if nftDisplay != nil {
|
|
86
|
+
serializedResult = serializedResult
|
|
87
|
+
.concat(name).concat(Serialize.tryToJSONString(nftDisplay!.name)!).concat(", ")
|
|
88
|
+
.concat(description).concat(Serialize.tryToJSONString(nftDisplay!.description)!).concat(", ")
|
|
89
|
+
.concat(image).concat(Serialize.tryToJSONString(nftDisplay!.thumbnail.uri())!)
|
|
90
|
+
// Append the `externa_url` value from NFTCollectionDisplay view if present
|
|
91
|
+
if collectionDisplay != nil {
|
|
92
|
+
return serializedResult.concat(", ")
|
|
93
|
+
.concat(externalURL).concat(Serialize.tryToJSONString(collectionDisplay!.externalURL.url)!)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if collectionDisplay == nil {
|
|
98
|
+
return serializedResult
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Without token-level view, serialize as contract-level metadata
|
|
102
|
+
return serializedResult
|
|
103
|
+
.concat(name).concat(Serialize.tryToJSONString(collectionDisplay!.name)!).concat(", ")
|
|
104
|
+
.concat(description).concat(Serialize.tryToJSONString(collectionDisplay!.description)!).concat(", ")
|
|
105
|
+
.concat(image).concat(Serialize.tryToJSONString(collectionDisplay!.squareImage.file.uri())!).concat(", ")
|
|
106
|
+
.concat(externalLink).concat(Serialize.tryToJSONString(collectionDisplay!.externalURL.url)!)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Serializes given Traits view as a JSON compatible string. If a given Trait is not serializable, it is skipped
|
|
110
|
+
/// and not included in the serialized result.
|
|
111
|
+
///
|
|
112
|
+
/// @param traits: The Traits view to be serialized
|
|
113
|
+
///
|
|
114
|
+
/// @returns: A JSON compatible string containing the serialized traits as:
|
|
115
|
+
/// `\"attributes\": [{\"trait_type\": \"<trait.name>\", \"value\": \"<trait.value>\"}, {...}]`
|
|
116
|
+
///
|
|
117
|
+
access(all)
|
|
118
|
+
fun serializeNFTTraitsAsAttributes(_ traits: MetadataViews.Traits): String {
|
|
119
|
+
// Serialize each trait as an attribute, building the serialized JSON compatible string
|
|
120
|
+
var serializedResult = "\"attributes\": ["
|
|
121
|
+
let traitsLength = traits.traits.length
|
|
122
|
+
for i, trait in traits.traits {
|
|
123
|
+
let value = Serialize.tryToJSONString(trait.value)
|
|
124
|
+
if value == nil {
|
|
125
|
+
// Remove trailing comma if last trait is not serializable
|
|
126
|
+
if i == traitsLength - 1 && serializedResult[serializedResult.length - 1] == "," {
|
|
127
|
+
serializedResult = serializedResult.slice(from: 0, upTo: serializedResult.length - 1)
|
|
128
|
+
}
|
|
129
|
+
continue
|
|
130
|
+
}
|
|
131
|
+
serializedResult = serializedResult.concat("{")
|
|
132
|
+
.concat("\"trait_type\": ").concat(Serialize.tryToJSONString(trait.name)!)
|
|
133
|
+
.concat(", \"value\": ").concat(value!)
|
|
134
|
+
.concat("}")
|
|
135
|
+
if i < traits!.traits.length - 1 {
|
|
136
|
+
serializedResult = serializedResult.concat(",")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return serializedResult.concat("]")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Serializes the FTDisplay view of a given fungible token as a JSON compatible data URL. The value is returned as
|
|
143
|
+
/// contract-level metadata.
|
|
144
|
+
///
|
|
145
|
+
/// @param ftDisplay: The tokens's FTDisplay view from which values `name`, `symbol`, `description`, and
|
|
146
|
+
/// `externaURL` are serialized
|
|
147
|
+
///
|
|
148
|
+
/// @returns: A JSON compatible data URL string containing the serialized view as:
|
|
149
|
+
/// `data:application/json;utf8,{
|
|
150
|
+
/// \"name\": \"<ftDisplay.name>\",
|
|
151
|
+
/// \"symbol\": \"<ftDisplay.symbol>\",
|
|
152
|
+
/// \"description\": \"<ftDisplay.description>\",
|
|
153
|
+
/// \"external_link\": \"<ftDisplay.externalURL.url>\",
|
|
154
|
+
/// }`
|
|
155
|
+
access(all)
|
|
156
|
+
fun serializeFTDisplay(_ ftDisplay: FungibleTokenMetadataViews.FTDisplay): String {
|
|
157
|
+
let name = "\"name\": "
|
|
158
|
+
let symbol = "\"symbol\": "
|
|
159
|
+
let description = "\"description\": "
|
|
160
|
+
let externalLink = "\"external_link\": "
|
|
161
|
+
|
|
162
|
+
return "data:application/json;utf8,{"
|
|
163
|
+
.concat(name).concat(Serialize.tryToJSONString(ftDisplay.name)!).concat(", ")
|
|
164
|
+
.concat(symbol).concat(Serialize.tryToJSONString(ftDisplay.symbol)!).concat(", ")
|
|
165
|
+
.concat(description).concat(Serialize.tryToJSONString(ftDisplay.description)!).concat(", ")
|
|
166
|
+
.concat(externalLink).concat(Serialize.tryToJSONString(ftDisplay.externalURL.url)!)
|
|
167
|
+
.concat("}")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Derives a symbol for use as an ERC20 or ERC721 symbol from a given string, presumably a Cadence contract name.
|
|
171
|
+
/// Derivation is a process of slicing the first 4 characters of the string and converting them to uppercase.
|
|
172
|
+
///
|
|
173
|
+
/// @param fromString: The string from which to derive a symbol
|
|
174
|
+
///
|
|
175
|
+
/// @returns: A derived symbol for use as an ERC20 or ERC721 symbol based on the provided string, presumably a
|
|
176
|
+
/// Cadence contract name
|
|
177
|
+
///
|
|
178
|
+
access(all) view fun deriveSymbol(fromString: String): String {
|
|
179
|
+
let defaultLen = 4
|
|
180
|
+
let len = fromString.length < defaultLen ? fromString.length : defaultLen
|
|
181
|
+
return self.toUpperAlphaNumerical(fromString, upTo: len)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// Returns the uppercase alphanumeric version of a given string. If upTo is nil or exceeds the length of the string,
|
|
185
|
+
/// the entire string is converted to uppercase.
|
|
186
|
+
///
|
|
187
|
+
/// @param str: The string to convert to uppercase
|
|
188
|
+
/// @param upTo: The maximum number of characters to convert to uppercase
|
|
189
|
+
///
|
|
190
|
+
/// @returns: The uppercase version of the given string
|
|
191
|
+
///
|
|
192
|
+
access(all) view fun toUpperAlphaNumerical(_ str: String, upTo: Int?): String {
|
|
193
|
+
let len = upTo ?? str.length
|
|
194
|
+
var upper: String = ""
|
|
195
|
+
for char in str {
|
|
196
|
+
if upper.length == len {
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
let bytes = char.utf8
|
|
200
|
+
if bytes.length != 1 {
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
let byte = bytes[0]
|
|
204
|
+
if byte >= 97 && byte <= 122 {
|
|
205
|
+
// Convert lower case to upper case
|
|
206
|
+
let upperChar = String.fromUTF8([byte - UInt8(32)])!
|
|
207
|
+
upper = upper.concat(upperChar)
|
|
208
|
+
} else if byte >= 65 && byte <= 90 {
|
|
209
|
+
// Keep upper case
|
|
210
|
+
upper = upper.concat(char.toString())
|
|
211
|
+
} else if byte >= 48 && byte <= 57 {
|
|
212
|
+
// Keep numbers
|
|
213
|
+
upper = upper.concat(String.fromCharacters([char]))
|
|
214
|
+
} else {
|
|
215
|
+
// Skip non-alphanumeric characters
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return upper
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -164,7 +164,7 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
164
164
|
|
|
165
165
|
// dictionary of NFT conforming tokens
|
|
166
166
|
// NFT is a resource type with an `UInt64` ID field
|
|
167
|
-
access(
|
|
167
|
+
access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
|
|
168
168
|
|
|
169
169
|
init () {
|
|
170
170
|
self.ownedNFTs <- {}
|
|
@@ -206,7 +206,7 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// withdraw removes an NFT from the collection and moves it to the caller
|
|
209
|
-
access(NonFungibleToken.Withdraw
|
|
209
|
+
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
|
|
210
210
|
let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
|
|
211
211
|
|
|
212
212
|
emit Withdraw(id: token.id, from: self.owner?.address)
|