@silvana-one/nft 0.1.0

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.
Files changed (236) hide show
  1. package/README.md +1400 -0
  2. package/dist/node/admin/advanced.d.ts +469 -0
  3. package/dist/node/admin/advanced.js +525 -0
  4. package/dist/node/admin/advanced.js.map +1 -0
  5. package/dist/node/admin/index.d.ts +1 -0
  6. package/dist/node/admin/index.js +2 -0
  7. package/dist/node/admin/index.js.map +1 -0
  8. package/dist/node/contracts/admin.d.ts +140 -0
  9. package/dist/node/contracts/admin.js +336 -0
  10. package/dist/node/contracts/admin.js.map +1 -0
  11. package/dist/node/contracts/collection.d.ts +551 -0
  12. package/dist/node/contracts/collection.js +1049 -0
  13. package/dist/node/contracts/collection.js.map +1 -0
  14. package/dist/node/contracts/index.d.ts +3 -0
  15. package/dist/node/contracts/index.js +4 -0
  16. package/dist/node/contracts/index.js.map +1 -0
  17. package/dist/node/contracts/nft.d.ts +76 -0
  18. package/dist/node/contracts/nft.js +329 -0
  19. package/dist/node/contracts/nft.js.map +1 -0
  20. package/dist/node/contracts.d.ts +709 -0
  21. package/dist/node/contracts.js +61 -0
  22. package/dist/node/contracts.js.map +1 -0
  23. package/dist/node/index.cjs +5032 -0
  24. package/dist/node/index.d.ts +8 -0
  25. package/dist/node/index.js +9 -0
  26. package/dist/node/index.js.map +1 -0
  27. package/dist/node/interfaces/admin.d.ts +102 -0
  28. package/dist/node/interfaces/admin.js +2 -0
  29. package/dist/node/interfaces/admin.js.map +1 -0
  30. package/dist/node/interfaces/approval.d.ts +57 -0
  31. package/dist/node/interfaces/approval.js +62 -0
  32. package/dist/node/interfaces/approval.js.map +1 -0
  33. package/dist/node/interfaces/collection.d.ts +57 -0
  34. package/dist/node/interfaces/collection.js +2 -0
  35. package/dist/node/interfaces/collection.js.map +1 -0
  36. package/dist/node/interfaces/encoding.d.ts +24 -0
  37. package/dist/node/interfaces/encoding.js +32 -0
  38. package/dist/node/interfaces/encoding.js.map +1 -0
  39. package/dist/node/interfaces/events.d.ts +833 -0
  40. package/dist/node/interfaces/events.js +106 -0
  41. package/dist/node/interfaces/events.js.map +1 -0
  42. package/dist/node/interfaces/index.d.ts +10 -0
  43. package/dist/node/interfaces/index.js +11 -0
  44. package/dist/node/interfaces/index.js.map +1 -0
  45. package/dist/node/interfaces/ownable.d.ts +94 -0
  46. package/dist/node/interfaces/ownable.js +12 -0
  47. package/dist/node/interfaces/ownable.js.map +1 -0
  48. package/dist/node/interfaces/owner.d.ts +61 -0
  49. package/dist/node/interfaces/owner.js +101 -0
  50. package/dist/node/interfaces/owner.js.map +1 -0
  51. package/dist/node/interfaces/pausable.d.ts +74 -0
  52. package/dist/node/interfaces/pausable.js +14 -0
  53. package/dist/node/interfaces/pausable.js.map +1 -0
  54. package/dist/node/interfaces/types.d.ts +2297 -0
  55. package/dist/node/interfaces/types.js +507 -0
  56. package/dist/node/interfaces/types.js.map +1 -0
  57. package/dist/node/interfaces/update.d.ts +53 -0
  58. package/dist/node/interfaces/update.js +58 -0
  59. package/dist/node/interfaces/update.js.map +1 -0
  60. package/dist/node/marketplace/auction.d.ts +775 -0
  61. package/dist/node/marketplace/auction.js +430 -0
  62. package/dist/node/marketplace/auction.js.map +1 -0
  63. package/dist/node/marketplace/bid.d.ts +254 -0
  64. package/dist/node/marketplace/bid.js +260 -0
  65. package/dist/node/marketplace/bid.js.map +1 -0
  66. package/dist/node/marketplace/index.d.ts +5 -0
  67. package/dist/node/marketplace/index.js +6 -0
  68. package/dist/node/marketplace/index.js.map +1 -0
  69. package/dist/node/marketplace/nft-shares.d.ts +1083 -0
  70. package/dist/node/marketplace/nft-shares.js +398 -0
  71. package/dist/node/marketplace/nft-shares.js.map +1 -0
  72. package/dist/node/marketplace/offer.d.ts +192 -0
  73. package/dist/node/marketplace/offer.js +132 -0
  74. package/dist/node/marketplace/offer.js.map +1 -0
  75. package/dist/node/marketplace/types.d.ts +374 -0
  76. package/dist/node/marketplace/types.js +33 -0
  77. package/dist/node/marketplace/types.js.map +1 -0
  78. package/dist/node/metadata/index.d.ts +3 -0
  79. package/dist/node/metadata/index.js +4 -0
  80. package/dist/node/metadata/index.js.map +1 -0
  81. package/dist/node/metadata/metadata.d.ts +337 -0
  82. package/dist/node/metadata/metadata.js +439 -0
  83. package/dist/node/metadata/metadata.js.map +1 -0
  84. package/dist/node/metadata/text.d.ts +44 -0
  85. package/dist/node/metadata/text.js +42 -0
  86. package/dist/node/metadata/text.js.map +1 -0
  87. package/dist/node/metadata/tree.d.ts +75 -0
  88. package/dist/node/metadata/tree.js +85 -0
  89. package/dist/node/metadata/tree.js.map +1 -0
  90. package/dist/node/vk.d.ts +42 -0
  91. package/dist/node/vk.js +45 -0
  92. package/dist/node/vk.js.map +1 -0
  93. package/dist/node/zkprogram-example/game.d.ts +76 -0
  94. package/dist/node/zkprogram-example/game.js +108 -0
  95. package/dist/node/zkprogram-example/game.js.map +1 -0
  96. package/dist/node/zkprogram-example/index.d.ts +2 -0
  97. package/dist/node/zkprogram-example/index.js +3 -0
  98. package/dist/node/zkprogram-example/index.js.map +1 -0
  99. package/dist/node/zkprogram-example/update.d.ts +76 -0
  100. package/dist/node/zkprogram-example/update.js +85 -0
  101. package/dist/node/zkprogram-example/update.js.map +1 -0
  102. package/dist/tsconfig.tsbuildinfo +1 -0
  103. package/dist/tsconfig.web.tsbuildinfo +1 -0
  104. package/dist/web/admin/advanced.d.ts +469 -0
  105. package/dist/web/admin/advanced.js +525 -0
  106. package/dist/web/admin/advanced.js.map +1 -0
  107. package/dist/web/admin/index.d.ts +1 -0
  108. package/dist/web/admin/index.js +2 -0
  109. package/dist/web/admin/index.js.map +1 -0
  110. package/dist/web/contracts/admin.d.ts +140 -0
  111. package/dist/web/contracts/admin.js +336 -0
  112. package/dist/web/contracts/admin.js.map +1 -0
  113. package/dist/web/contracts/collection.d.ts +551 -0
  114. package/dist/web/contracts/collection.js +1049 -0
  115. package/dist/web/contracts/collection.js.map +1 -0
  116. package/dist/web/contracts/index.d.ts +3 -0
  117. package/dist/web/contracts/index.js +4 -0
  118. package/dist/web/contracts/index.js.map +1 -0
  119. package/dist/web/contracts/nft.d.ts +76 -0
  120. package/dist/web/contracts/nft.js +329 -0
  121. package/dist/web/contracts/nft.js.map +1 -0
  122. package/dist/web/contracts.d.ts +709 -0
  123. package/dist/web/contracts.js +61 -0
  124. package/dist/web/contracts.js.map +1 -0
  125. package/dist/web/index.d.ts +8 -0
  126. package/dist/web/index.js +9 -0
  127. package/dist/web/index.js.map +1 -0
  128. package/dist/web/interfaces/admin.d.ts +102 -0
  129. package/dist/web/interfaces/admin.js +2 -0
  130. package/dist/web/interfaces/admin.js.map +1 -0
  131. package/dist/web/interfaces/approval.d.ts +57 -0
  132. package/dist/web/interfaces/approval.js +62 -0
  133. package/dist/web/interfaces/approval.js.map +1 -0
  134. package/dist/web/interfaces/collection.d.ts +57 -0
  135. package/dist/web/interfaces/collection.js +2 -0
  136. package/dist/web/interfaces/collection.js.map +1 -0
  137. package/dist/web/interfaces/encoding.d.ts +24 -0
  138. package/dist/web/interfaces/encoding.js +32 -0
  139. package/dist/web/interfaces/encoding.js.map +1 -0
  140. package/dist/web/interfaces/events.d.ts +833 -0
  141. package/dist/web/interfaces/events.js +106 -0
  142. package/dist/web/interfaces/events.js.map +1 -0
  143. package/dist/web/interfaces/index.d.ts +10 -0
  144. package/dist/web/interfaces/index.js +11 -0
  145. package/dist/web/interfaces/index.js.map +1 -0
  146. package/dist/web/interfaces/ownable.d.ts +94 -0
  147. package/dist/web/interfaces/ownable.js +12 -0
  148. package/dist/web/interfaces/ownable.js.map +1 -0
  149. package/dist/web/interfaces/owner.d.ts +61 -0
  150. package/dist/web/interfaces/owner.js +101 -0
  151. package/dist/web/interfaces/owner.js.map +1 -0
  152. package/dist/web/interfaces/pausable.d.ts +74 -0
  153. package/dist/web/interfaces/pausable.js +14 -0
  154. package/dist/web/interfaces/pausable.js.map +1 -0
  155. package/dist/web/interfaces/types.d.ts +2297 -0
  156. package/dist/web/interfaces/types.js +507 -0
  157. package/dist/web/interfaces/types.js.map +1 -0
  158. package/dist/web/interfaces/update.d.ts +53 -0
  159. package/dist/web/interfaces/update.js +58 -0
  160. package/dist/web/interfaces/update.js.map +1 -0
  161. package/dist/web/marketplace/auction.d.ts +775 -0
  162. package/dist/web/marketplace/auction.js +430 -0
  163. package/dist/web/marketplace/auction.js.map +1 -0
  164. package/dist/web/marketplace/bid.d.ts +254 -0
  165. package/dist/web/marketplace/bid.js +260 -0
  166. package/dist/web/marketplace/bid.js.map +1 -0
  167. package/dist/web/marketplace/index.d.ts +5 -0
  168. package/dist/web/marketplace/index.js +6 -0
  169. package/dist/web/marketplace/index.js.map +1 -0
  170. package/dist/web/marketplace/nft-shares.d.ts +1083 -0
  171. package/dist/web/marketplace/nft-shares.js +398 -0
  172. package/dist/web/marketplace/nft-shares.js.map +1 -0
  173. package/dist/web/marketplace/offer.d.ts +192 -0
  174. package/dist/web/marketplace/offer.js +132 -0
  175. package/dist/web/marketplace/offer.js.map +1 -0
  176. package/dist/web/marketplace/types.d.ts +374 -0
  177. package/dist/web/marketplace/types.js +33 -0
  178. package/dist/web/marketplace/types.js.map +1 -0
  179. package/dist/web/metadata/index.d.ts +3 -0
  180. package/dist/web/metadata/index.js +4 -0
  181. package/dist/web/metadata/index.js.map +1 -0
  182. package/dist/web/metadata/metadata.d.ts +337 -0
  183. package/dist/web/metadata/metadata.js +439 -0
  184. package/dist/web/metadata/metadata.js.map +1 -0
  185. package/dist/web/metadata/text.d.ts +44 -0
  186. package/dist/web/metadata/text.js +42 -0
  187. package/dist/web/metadata/text.js.map +1 -0
  188. package/dist/web/metadata/tree.d.ts +75 -0
  189. package/dist/web/metadata/tree.js +85 -0
  190. package/dist/web/metadata/tree.js.map +1 -0
  191. package/dist/web/vk.d.ts +42 -0
  192. package/dist/web/vk.js +45 -0
  193. package/dist/web/vk.js.map +1 -0
  194. package/dist/web/zkprogram-example/game.d.ts +76 -0
  195. package/dist/web/zkprogram-example/game.js +108 -0
  196. package/dist/web/zkprogram-example/game.js.map +1 -0
  197. package/dist/web/zkprogram-example/index.d.ts +2 -0
  198. package/dist/web/zkprogram-example/index.js +3 -0
  199. package/dist/web/zkprogram-example/index.js.map +1 -0
  200. package/dist/web/zkprogram-example/update.d.ts +76 -0
  201. package/dist/web/zkprogram-example/update.js +85 -0
  202. package/dist/web/zkprogram-example/update.js.map +1 -0
  203. package/package.json +65 -0
  204. package/src/admin/advanced.ts +601 -0
  205. package/src/admin/index.ts +1 -0
  206. package/src/contracts/admin.ts +301 -0
  207. package/src/contracts/collection.ts +1172 -0
  208. package/src/contracts/index.ts +3 -0
  209. package/src/contracts/nft.ts +344 -0
  210. package/src/contracts.ts +107 -0
  211. package/src/index.ts +8 -0
  212. package/src/interfaces/admin.ts +127 -0
  213. package/src/interfaces/approval.ts +99 -0
  214. package/src/interfaces/collection.ts +68 -0
  215. package/src/interfaces/encoding.ts +32 -0
  216. package/src/interfaces/events.ts +115 -0
  217. package/src/interfaces/index.ts +10 -0
  218. package/src/interfaces/ownable.ts +32 -0
  219. package/src/interfaces/owner.ts +143 -0
  220. package/src/interfaces/pausable.ts +41 -0
  221. package/src/interfaces/types.ts +623 -0
  222. package/src/interfaces/update.ts +104 -0
  223. package/src/marketplace/auction.ts +527 -0
  224. package/src/marketplace/bid.ts +294 -0
  225. package/src/marketplace/index.ts +5 -0
  226. package/src/marketplace/nft-shares.ts +388 -0
  227. package/src/marketplace/offer.ts +153 -0
  228. package/src/marketplace/types.ts +33 -0
  229. package/src/metadata/index.ts +3 -0
  230. package/src/metadata/metadata.ts +603 -0
  231. package/src/metadata/text.ts +60 -0
  232. package/src/metadata/tree.ts +128 -0
  233. package/src/vk.ts +64 -0
  234. package/src/zkprogram-example/game.ts +136 -0
  235. package/src/zkprogram-example/index.ts +2 -0
  236. package/src/zkprogram-example/update.ts +98 -0
@@ -0,0 +1,1172 @@
1
+ /**
2
+ * The NFT Collection Contract is responsible for managing a collection of NFTs.
3
+ * It handles minting new NFTs, transferring ownership, buying, selling,
4
+ * and interfacing with Admin Contracts for additional functionalities.
5
+ *
6
+ * @module CollectionContract
7
+ */
8
+
9
+ import {
10
+ Field,
11
+ PublicKey,
12
+ AccountUpdate,
13
+ Bool,
14
+ method,
15
+ state,
16
+ State,
17
+ DeployArgs,
18
+ Permissions,
19
+ TokenContract,
20
+ AccountUpdateForest,
21
+ VerificationKey,
22
+ UInt32,
23
+ UInt64,
24
+ SmartContract,
25
+ Mina,
26
+ Provable,
27
+ } from "o1js";
28
+ import { NFT } from "./nft.js";
29
+ import {
30
+ MintParams,
31
+ MintRequest,
32
+ TransferParams,
33
+ CollectionData,
34
+ NFTUpdateProof,
35
+ NFTStateStruct,
36
+ MintEvent,
37
+ TransferEvent,
38
+ ApproveEvent,
39
+ UpgradeVerificationKeyEvent,
40
+ LimitMintingEvent,
41
+ PauseNFTEvent,
42
+ NFTAdminBase,
43
+ NFTAdminContractConstructor,
44
+ PausableContract,
45
+ PauseEvent,
46
+ OwnableContract,
47
+ OwnershipChangeEvent,
48
+ NFTOwnerBase,
49
+ NFTOwnerContractConstructor,
50
+ UInt64Option,
51
+ NFTCollectionBase,
52
+ UpgradeVerificationKeyData,
53
+ NFTApprovalContractConstructor,
54
+ NFTApprovalBase,
55
+ NFTUpdateContractConstructor,
56
+ NFTUpdateBase,
57
+ MAX_ROYALTY_FEE,
58
+ TransferExtendedParams,
59
+ } from "../interfaces/index.js";
60
+ import { nftVerificationKeys } from "../vk.js";
61
+ export { CollectionDeployProps, CollectionFactory, CollectionErrors };
62
+
63
+ const CollectionErrors = {
64
+ wrongMasterNFTaddress:
65
+ "Master NFT address should be the same as the collection address",
66
+ transferNotAllowed:
67
+ "Transfers of tokens are not allowed, change the owner instead",
68
+ collectionPaused: "Collection is currently paused",
69
+ cannotMintMasterNFT: "Only the creator can mint the Master NFT",
70
+ cannotMint: "Admin contract did not provide permission to mint",
71
+ noPermissionToPause: "Not allowed to pause collection",
72
+ noPermissionToResume: "Not allowed to resume collection",
73
+ collectionNotPaused: "Collection is not paused",
74
+ transferApprovalRequired: "Transfer approval is required",
75
+ noPermissionToChangeName: "Not allowed to change collection name",
76
+ noPermissionToChangeBaseUri: "Not allowed to change collection base URI",
77
+ noPermissionToChangeCreator: "Not allowed to change collection creator",
78
+ noPermissionToChangeRoyalty: "Not allowed to change royalty fee",
79
+ noPermissionToChangeTransferFee: "Not allowed to change transfer fee",
80
+ noPermissionToSetAdmin: "Not allowed to set admin contract",
81
+ cannotUpgradeVerificationKey: "Cannot upgrade verification key",
82
+ upgradeContractAddressNotSet: "Upgrade contract address is not set",
83
+ adminContractAddressNotSet: "Admin contract address is not set",
84
+ onlyOwnerCanUpgradeVerificationKey: "Only owner can upgrade verification key",
85
+ invalidRoyaltyFee: "Royalty fee is too high, cannot be more than 100%",
86
+ invalidOracleAddress: "Oracle address is invalid",
87
+ };
88
+
89
+ interface CollectionDeployProps extends Exclude<DeployArgs, undefined> {
90
+ collectionName: Field;
91
+ creator: PublicKey;
92
+ admin: PublicKey;
93
+ baseURL: Field;
94
+ symbol: string;
95
+ url: string;
96
+ }
97
+
98
+ /**
99
+ * Creates a new NFT Collection Contract class.
100
+ *
101
+ * @param params - Constructor parameters including admin and upgrade contracts, and network ID.
102
+ * @returns The Collection class extending TokenContract and implementing required interfaces.
103
+ */
104
+ function CollectionFactory(params: {
105
+ adminContract: () => NFTAdminContractConstructor;
106
+ ownerContract: () => NFTOwnerContractConstructor;
107
+ approvalContract: () => NFTApprovalContractConstructor;
108
+ updateContract: () => NFTUpdateContractConstructor;
109
+ }) {
110
+ const { adminContract, ownerContract, approvalContract, updateContract } =
111
+ params;
112
+
113
+ /**
114
+ * The NFT Collection Contract manages a collection of NFTs.
115
+ * It handles minting, transferring, buying, selling, and integrates with Admin Contracts.
116
+ */
117
+ class Collection
118
+ extends TokenContract
119
+ implements OwnableContract, PausableContract, NFTCollectionBase
120
+ {
121
+ /** The name of the NFT collection. */
122
+ @state(Field) collectionName = State<Field>();
123
+ /** The public key of the creator of the collection. */
124
+ @state(PublicKey) creator = State<PublicKey>();
125
+ /** The public key of the Admin Contract. */
126
+ @state(PublicKey) admin = State<PublicKey>();
127
+ /** The base URL for the metadata of the NFTs in the collection. */
128
+ @state(Field) baseURL = State<Field>();
129
+ /**
130
+ * A packed data field containing additional collection parameters,
131
+ * such as flags and fee configurations.
132
+ */
133
+ @state(Field) packedData = State<Field>();
134
+
135
+ /**
136
+ * Deploys the NFT Collection Contract with the initial settings.
137
+ *
138
+ * @param props - Deployment properties including collection name, creator, admin, baseURL, symbol, and URL.
139
+ */
140
+ async deploy(props: CollectionDeployProps): Promise<void> {
141
+ await super.deploy(props);
142
+ this.collectionName.set(props.collectionName);
143
+ this.creator.set(props.creator);
144
+ this.admin.set(props.admin);
145
+ this.baseURL.set(props.baseURL);
146
+ // Set the collection to be paused by default
147
+ this.packedData.set(
148
+ CollectionData.new({
149
+ isPaused: true,
150
+ }).pack()
151
+ );
152
+ this.account.zkappUri.set(props.url);
153
+ this.account.tokenSymbol.set(props.symbol);
154
+ this.account.permissions.set({
155
+ ...Permissions.default(),
156
+ setVerificationKey:
157
+ Permissions.VerificationKey.proofDuringCurrentVersion(),
158
+ setPermissions: Permissions.impossible(),
159
+ access: Permissions.proof(),
160
+ send: Permissions.proof(),
161
+ setZkappUri: Permissions.none(),
162
+ setTokenSymbol: Permissions.none(),
163
+ });
164
+ }
165
+
166
+ /**
167
+ * Initializes the collection with a master NFT and initial data.
168
+ *
169
+ * @param masterNFT - The master NFT parameters.
170
+ * @param collectionData - Initial collection data including flags and configurations.
171
+ */
172
+ @method
173
+ async initialize(masterNFT: MintParams, collectionData: CollectionData) {
174
+ this.account.provedState.requireEquals(Bool(false));
175
+ collectionData.royaltyFee.assertLessThanOrEqual(
176
+ UInt32.from(MAX_ROYALTY_FEE),
177
+ CollectionErrors.invalidRoyaltyFee
178
+ );
179
+ this.packedData.set(collectionData.pack());
180
+ masterNFT.address
181
+ .equals(this.address)
182
+ .assertTrue(CollectionErrors.wrongMasterNFTaddress);
183
+ await this._mint(masterNFT);
184
+ }
185
+
186
+ /**
187
+ * Defines the events emitted by the contract.
188
+ */
189
+ events = {
190
+ mint: MintEvent,
191
+ update: PublicKey,
192
+ transfer: TransferEvent,
193
+ approve: ApproveEvent,
194
+ upgradeNFTVerificationKey: UpgradeVerificationKeyEvent,
195
+ upgradeVerificationKey: Field,
196
+ limitMinting: LimitMintingEvent,
197
+ pause: PauseEvent,
198
+ resume: PauseEvent,
199
+ pauseNFT: PauseNFTEvent,
200
+ resumeNFT: PauseNFTEvent,
201
+ ownershipChange: OwnershipChangeEvent,
202
+ setName: Field,
203
+ setBaseURL: Field,
204
+ setRoyaltyFee: UInt32,
205
+ setTransferFee: UInt64,
206
+ setAdmin: PublicKey,
207
+ };
208
+
209
+ /**
210
+ * Overrides the approveBase method to prevent transfers of tokens.
211
+ *
212
+ * @param forest - The account update forest.
213
+ */
214
+ async approveBase(forest: AccountUpdateForest) {
215
+ throw Error(CollectionErrors.transferNotAllowed);
216
+ }
217
+
218
+ /**
219
+ * Retrieves the Admin Contract instance.
220
+ *
221
+ * @returns The Admin Contract instance implementing NFTAdminBase.
222
+ */
223
+ getAdminContract(): NFTAdminBase {
224
+ const admin = this.admin.getAndRequireEquals();
225
+ const AdminContract = adminContract();
226
+ return new AdminContract(admin);
227
+ }
228
+
229
+ /**
230
+ * Retrieves the NFT Owner Contract instance.
231
+ *
232
+ * @returns The Owner Contract instance implementing NFTOwnerBase.
233
+ */
234
+ getOwnerContract(address: PublicKey): NFTOwnerBase {
235
+ const OwnerContract = ownerContract();
236
+ return new OwnerContract(address);
237
+ }
238
+
239
+ /**
240
+ * Retrieves the NFT Approval Contract instance.
241
+ *
242
+ * @returns The Approval Contract instance implementing NFTApprovalBase.
243
+ */
244
+ getApprovalContract(address: PublicKey): NFTApprovalBase {
245
+ const ApprovalContract = approvalContract();
246
+ return new ApprovalContract(address);
247
+ }
248
+
249
+ /**
250
+ * Retrieves the NFT Update Contract instance.
251
+ *
252
+ * @returns The Update Contract instance implementing NFTUpdateBase.
253
+ */
254
+ getUpdateContract(address: PublicKey): NFTUpdateBase {
255
+ const UpdateContract = updateContract();
256
+ return new UpdateContract(address);
257
+ }
258
+
259
+ /**
260
+ * Ensures that the transaction is authorized by the creator.
261
+ *
262
+ * @returns The AccountUpdate of the creator.
263
+ */
264
+ async ensureCreatorSignature(): Promise<AccountUpdate> {
265
+ const creator = this.creator.getAndRequireEquals();
266
+ const creatorUpdate = AccountUpdate.createSigned(creator);
267
+ creatorUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change
268
+ return creatorUpdate;
269
+ }
270
+
271
+ /**
272
+ * Ensures that the transaction is authorized by the NFT owner
273
+ *
274
+ * @returns The AccountUpdate of the NFT owner.
275
+ */
276
+ async ensureOwnerSignature(owner: PublicKey): Promise<AccountUpdate> {
277
+ const ownerUpdate = AccountUpdate.createSigned(owner);
278
+ ownerUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change
279
+ return ownerUpdate;
280
+ }
281
+
282
+ /**
283
+ * Ensures that the collection is not paused.
284
+ *
285
+ * @returns The packed data of the collection.
286
+ */
287
+ async ensureNotPaused(): Promise<void> {
288
+ CollectionData.isPaused(
289
+ this.packedData.getAndRequireEquals()
290
+ ).assertFalse(CollectionErrors.collectionPaused);
291
+ }
292
+
293
+ /**
294
+ * Mints a new NFT directly by the creator.
295
+ *
296
+ * This method allows the creator of the collection to mint an NFT without requiring approval
297
+ * from the admin contract. It ensures that the collection is not paused and that the caller
298
+ * is the creator of the collection. A fee of 1 MINA is deducted from the creator's balance
299
+ * to cover the cost of creating a new account.
300
+ *
301
+ * We do not constrain here the address of the NFT to allow for the Master NFT to be minted.
302
+ * The Master NFT is the NFT with the same address as the Collection contract and it holds
303
+ * the metadata for the collection. It can be minted only by the creator of the collection.
304
+ *
305
+ * @param params - The mint parameters containing details of the NFT to be minted.
306
+ */
307
+
308
+ @method async mintByCreator(params: MintParams): Promise<void> {
309
+ CollectionData.mintingIsLimited(
310
+ this.packedData.getAndRequireEquals()
311
+ ).assertFalse(CollectionErrors.cannotMint);
312
+ const creatorUpdate = await this.ensureCreatorSignature();
313
+ // Pay 1 MINA fee for a new account
314
+ creatorUpdate.balance.subInPlace(1_000_000_000);
315
+ await this._mint(params);
316
+ }
317
+
318
+ /**
319
+ * Mints a new NFT with approval.
320
+ *
321
+ * @param mintRequest - The minting request containing parameters and proofs.
322
+ */
323
+ @method async mint(mintRequest: MintRequest): Promise<void> {
324
+ CollectionData.mintingIsLimited(
325
+ this.packedData.getAndRequireEquals()
326
+ ).assertFalse(CollectionErrors.cannotMint);
327
+ const adminContract = this.getAdminContract();
328
+ // The admin contract checks that the sender is allowed to mint
329
+ const mintParams = (await adminContract.canMint(mintRequest)).assertSome(
330
+ CollectionErrors.cannotMint
331
+ );
332
+
333
+ // Prevent minting the Master NFT using this method
334
+ mintParams.address
335
+ .equals(this.address)
336
+ .assertFalse(CollectionErrors.cannotMintMasterNFT);
337
+ await this._mint(mintParams);
338
+ }
339
+
340
+ /**
341
+ * Internal method to mint an NFT.
342
+ *
343
+ * @param params - The mint parameters.
344
+ * @param collectionData - The current collection data.
345
+ * @returns The MintEvent emitted.
346
+ */
347
+ async _mint(params: MintParams): Promise<MintEvent> {
348
+ const {
349
+ name,
350
+ address,
351
+ data,
352
+ metadata,
353
+ storage,
354
+ metadataVerificationKeyHash,
355
+ expiry,
356
+ } = params;
357
+
358
+ this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, expiry);
359
+ data.version.assertEquals(UInt32.zero);
360
+ const packedData = data.pack();
361
+ const tokenId = this.deriveTokenId();
362
+
363
+ const update = AccountUpdate.createSigned(address, tokenId);
364
+ update.body.useFullCommitment = Bool(true); // Prevent memo and fee change
365
+ update.account.isNew.getAndRequireEquals().assertTrue();
366
+
367
+ // Mint 1 NFT
368
+ this.internal.mint({ address: update, amount: 1_000_000_000 });
369
+
370
+ const verificationKey: VerificationKey = Provable.witness(
371
+ VerificationKey,
372
+ () => {
373
+ // This code does NOT create a constraint on the verification key
374
+ // as this witness can be replaced during runtime
375
+ // We use devnet to get future compatibility https://github.com/o1-labs/o1js/pull/1938
376
+ // As of writing this, 'testnet' is used in the o1js codebase
377
+ const networkId =
378
+ Mina.getNetworkId() === "mainnet" ? "mainnet" : "devnet";
379
+ const verificationKey = new VerificationKey({
380
+ data: nftVerificationKeys[networkId].vk.NFT.data,
381
+ hash: Field(nftVerificationKeys[networkId].vk.NFT.hash),
382
+ });
383
+ const vkHash = NFT._verificationKey?.hash;
384
+ if (
385
+ !verificationKey ||
386
+ !verificationKey.hash ||
387
+ !verificationKey.data
388
+ )
389
+ throw Error("NFT verification key is incorrect");
390
+ if (
391
+ vkHash &&
392
+ vkHash.equals(verificationKey.hash).toBoolean() === false
393
+ )
394
+ throw Error(
395
+ "NFT verification key does not match the compiled verification key"
396
+ );
397
+ return verificationKey;
398
+ }
399
+ );
400
+
401
+ const mainnetVerificationKeyHash = Field(
402
+ nftVerificationKeys.mainnet.vk.NFT.hash
403
+ );
404
+ const devnetVerificationKeyHash = Field(
405
+ nftVerificationKeys.devnet.vk.NFT.hash
406
+ );
407
+ const isMainnet = Provable.witness(Bool, () => {
408
+ // This check does NOT create a constraint on the verification key
409
+ // as this witness can be replaced during runtime
410
+ // and is useful only for making sure that the verification key
411
+ // of the NFT will match the compiled verification key of the NFT
412
+ // at the time of the deployment of the Collection Contract
413
+ return Bool(Mina.getNetworkId() === "mainnet");
414
+ });
415
+ // We check that the verification key hash is the same as the one
416
+ // that was compiled at the time of the deployment
417
+ verificationKey.hash.assertEquals(
418
+ Provable.if(
419
+ isMainnet,
420
+ mainnetVerificationKeyHash,
421
+ devnetVerificationKeyHash
422
+ )
423
+ );
424
+ update.body.update.verificationKey = {
425
+ isSome: Bool(true),
426
+ value: verificationKey,
427
+ };
428
+ update.body.update.permissions = {
429
+ isSome: Bool(true),
430
+ value: {
431
+ ...Permissions.default(),
432
+ // NFT cannot be sent to other accounts, only owner can be changed
433
+ send: Permissions.impossible(),
434
+ // Allow the upgrade authority to set the verification key
435
+ // even when there is no protocol upgrade
436
+ setVerificationKey:
437
+ Permissions.VerificationKey.proofDuringCurrentVersion(),
438
+ setPermissions: Permissions.impossible(),
439
+ access: Permissions.proof(),
440
+ setZkappUri: Permissions.impossible(),
441
+ setTokenSymbol: Permissions.impossible(),
442
+ },
443
+ };
444
+ const initialState = new NFTStateStruct({
445
+ name,
446
+ metadata,
447
+ storage,
448
+ packedData,
449
+ metadataVerificationKeyHash,
450
+ });
451
+
452
+ update.body.update.appState = NFTStateStruct.toFields(initialState).map(
453
+ (field) => ({
454
+ isSome: Bool(true),
455
+ value: field,
456
+ })
457
+ );
458
+ const event = new MintEvent({
459
+ initialState,
460
+ address,
461
+ });
462
+ this.emitEvent("mint", event);
463
+ return event;
464
+ }
465
+
466
+ /**
467
+ * Updates the NFT with admin approval.
468
+ *
469
+ * @param proof - The proof of the NFT update.
470
+ * @param vk - The verification key.
471
+ */
472
+ @method async update(
473
+ proof: NFTUpdateProof,
474
+ vk: VerificationKey
475
+ ): Promise<void> {
476
+ await this._update(proof, vk);
477
+ }
478
+
479
+ /**
480
+ * Updates the NFT with admin approval and oracle approval.
481
+ *
482
+ * @param proof - The proof of the NFT update.
483
+ * @param vk - The verification key.
484
+ */
485
+ @method async updateWithOracle(
486
+ proof: NFTUpdateProof,
487
+ vk: VerificationKey
488
+ ): Promise<void> {
489
+ // The oracle address is optional and can be empty, NFT ZkProgram can verify the address
490
+ // as it can be different for different NFTs
491
+ const oracleAddress = proof.publicInput.oracleAddress;
492
+ oracleAddress
493
+ .equals(PublicKey.empty())
494
+ .assertFalse(CollectionErrors.invalidOracleAddress);
495
+ const oracle = this.getUpdateContract(oracleAddress);
496
+ const canUpdate = await oracle.canUpdate(
497
+ this.address,
498
+ proof.publicInput.immutableState.address,
499
+ proof.publicInput,
500
+ proof.publicOutput
501
+ );
502
+ canUpdate.assertTrue();
503
+ await this._update(proof, vk);
504
+ }
505
+
506
+ /**
507
+ * Updates the NFT with admin approval - internal method.
508
+ *
509
+ * @param proof - The proof of the NFT update.
510
+ * @param vk - The verification key.
511
+ */
512
+ async _update(proof: NFTUpdateProof, vk: VerificationKey): Promise<void> {
513
+ await this.ensureNotPaused();
514
+
515
+ const adminContract = this.getAdminContract();
516
+ const canUpdate = await adminContract.canUpdate(
517
+ proof.publicInput,
518
+ proof.publicOutput
519
+ );
520
+ canUpdate.assertTrue();
521
+
522
+ const creator = this.creator.getAndRequireEquals();
523
+ creator.assertEquals(proof.publicInput.creator);
524
+ const tokenId = this.deriveTokenId();
525
+ tokenId.assertEquals(proof.publicInput.immutableState.tokenId);
526
+
527
+ const nft = new NFT(proof.publicInput.immutableState.address, tokenId);
528
+ const metadataVerificationKeyHash = await nft.update(
529
+ proof.publicInput,
530
+ proof.publicOutput,
531
+ creator
532
+ );
533
+
534
+ // Verify the metadata update proof
535
+ metadataVerificationKeyHash.assertEquals(vk.hash);
536
+ proof.verify(vk);
537
+ this.emitEvent("update", proof.publicInput.immutableState.address);
538
+ }
539
+
540
+ /**
541
+ * Approves an address to transfer an NFT.
542
+ *
543
+ * @param nftAddress - The address of the NFT.
544
+ * @param approved - The approved public key.
545
+ */
546
+ @method async approveAddress(
547
+ nftAddress: PublicKey,
548
+ approved: PublicKey
549
+ ): Promise<void> {
550
+ await this.ensureNotPaused();
551
+ const tokenId = this.deriveTokenId();
552
+ const nft = new NFT(nftAddress, tokenId);
553
+ const owner = await nft.approveAddress(approved);
554
+ await this.ensureOwnerSignature(owner);
555
+ this.emitEvent("approve", new ApproveEvent({ nftAddress, approved }));
556
+ }
557
+
558
+ /**
559
+ * Transfers ownership of an NFT without admin approval.
560
+ *
561
+ * @param address - The address of the NFT.
562
+ * @param to - The recipient's public key.
563
+ */
564
+ @method async approveAddressByProof(
565
+ nftAddress: PublicKey,
566
+ approved: PublicKey
567
+ ): Promise<void> {
568
+ const tokenId = this.deriveTokenId();
569
+ const nft = new NFT(nftAddress, tokenId);
570
+ const owner = await nft.approveAddress(approved);
571
+ const ownerContract = this.getOwnerContract(owner);
572
+ const canApprove = await ownerContract.canApproveAddress(
573
+ this.address,
574
+ nftAddress,
575
+ approved
576
+ );
577
+ canApprove.assertTrue();
578
+ this.emitEvent("approve", new ApproveEvent({ nftAddress, approved }));
579
+ }
580
+
581
+ /**
582
+ * Transfers ownership of an NFT without admin approval.
583
+ * This method should be used by wallets for collections that do not require transfer approval
584
+ * and the owners of the NFTs which approve the transfer by signature
585
+ *
586
+ * @param address - The address of the NFT.
587
+ * @param to - The recipient's public key.
588
+ * @param price - The price of the NFT (optional).
589
+ */
590
+ @method async transferBySignature(params: TransferParams): Promise<void> {
591
+ const { address, to, price, context } = params;
592
+ const collectionData = CollectionData.unpack(
593
+ this.packedData.getAndRequireEquals()
594
+ );
595
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
596
+ collectionData.requireTransferApproval.assertFalse(
597
+ CollectionErrors.transferApprovalRequired
598
+ );
599
+
600
+ const transferEventDraft = new TransferExtendedParams({
601
+ from: PublicKey.empty(), // will be added later
602
+ to,
603
+ collection: this.address,
604
+ nft: address,
605
+ fee: UInt64Option.none(), // will be added later
606
+ price,
607
+ transferByOwner: Bool(false), // will be added later
608
+ approved: PublicKey.empty(), // will be added later
609
+ context,
610
+ });
611
+ await this._transfer({
612
+ transferEventDraft,
613
+ transferFee: collectionData.transferFee,
614
+ royaltyFee: collectionData.royaltyFee,
615
+ });
616
+ }
617
+
618
+ /**
619
+ * Transfers ownership of an NFT using a proof in case the owner is a contract
620
+ * Can be called by the owner or approved that should be a contracts
621
+ * supporting NFTApprovalBase interface
622
+ *
623
+ * @param params - The transfer parameters.
624
+ */
625
+ @method async transferByProof(params: TransferParams): Promise<void> {
626
+ const { address, from, to, price, context } = params;
627
+ const collectionData = CollectionData.unpack(
628
+ this.packedData.getAndRequireEquals()
629
+ );
630
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
631
+
632
+ const transferEventDraft = new TransferExtendedParams({
633
+ from,
634
+ to,
635
+ collection: this.address,
636
+ nft: address,
637
+ fee: UInt64Option.none(), // will be added later
638
+ price,
639
+ transferByOwner: Bool(false), // will be added later
640
+ approved: PublicKey.empty(), // will be added later
641
+ context,
642
+ });
643
+ const transferEvent = await this._transfer({
644
+ transferEventDraft,
645
+ transferFee: collectionData.transferFee,
646
+ royaltyFee: collectionData.royaltyFee,
647
+ });
648
+ const approvalContract = this.getApprovalContract(from);
649
+ // This operation is not atomic and the owner or approval contract cannot rely on the fact
650
+ // that it is being called by the Collection contract
651
+ // It is the responsibility of the owner contract to maintain the state
652
+ // that allow for escrow-like agreement between the buyer and the seller
653
+ // in case of the selling and buying of the NFT and return 'true' only if the
654
+ // payment is made or guaranteed by the deposit of the funds in the owner contract
655
+ // or the owner contract is able to verify that it is being called by the Collection contract
656
+ // by setting the flag in its state as in the Offer contract example
657
+ const canTransfer = await approvalContract.canTransfer(transferEvent);
658
+ canTransfer.assertTrue();
659
+ }
660
+
661
+ /**
662
+ * Transfers ownership of an NFT using a proof in case the owner is a contract
663
+ * Can be called by the owner or approved that should be a contracts
664
+ * supporting NFTApprovalBase interface
665
+ *
666
+ * @param params - The transfer parameters.
667
+ */
668
+ @method async approvedTransferByProof(
669
+ params: TransferParams
670
+ ): Promise<void> {
671
+ const { address, from, to, price, context } = params;
672
+ const collectionData = CollectionData.unpack(
673
+ this.packedData.getAndRequireEquals()
674
+ );
675
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
676
+
677
+ const transferEventDraft = new TransferExtendedParams({
678
+ from,
679
+ to,
680
+ collection: this.address,
681
+ nft: address,
682
+ fee: UInt64Option.none(), // will be added later
683
+ price,
684
+ transferByOwner: Bool(false), // will be added later
685
+ approved: PublicKey.empty(), // will be added later
686
+ context,
687
+ });
688
+ const transferEvent = await this._transfer({
689
+ transferEventDraft,
690
+ transferFee: collectionData.transferFee,
691
+ royaltyFee: collectionData.royaltyFee,
692
+ });
693
+
694
+ const adminContract = this.getAdminContract();
695
+ const adminApprovedTransfer = await adminContract.canTransfer(
696
+ transferEvent
697
+ );
698
+ adminApprovedTransfer.assertTrue();
699
+
700
+ const approvalContract = this.getApprovalContract(from);
701
+ // This operation is not atomic and the owner or approval contract cannot rely on the fact
702
+ // that it is being called by the Collection contract
703
+ // It is the responsibility of the owner contract to maintain the state
704
+ // that allow for escrow-like agreement between the buyer and the seller
705
+ // in case of the selling and buying of the NFT and return 'true' only if the
706
+ // payment is made or guaranteed by the deposit of the funds in the owner contract
707
+ // or the owner contract is able to verify that it is being called by the Collection contract
708
+ // by setting the flag in its state as in the Offer contract example
709
+ const canTransfer = await approvalContract.canTransfer(transferEvent);
710
+ canTransfer.assertTrue();
711
+ }
712
+
713
+ /**
714
+ * Transfers ownership of an NFT with admin approval.
715
+ *
716
+ * @param address - The address of the NFT.
717
+ * @param to - The recipient's public key.
718
+ * @param price - The price of the NFT (optional).
719
+ */
720
+ @method async approvedTransferBySignature(
721
+ params: TransferParams
722
+ ): Promise<void> {
723
+ const { address, to, price, context } = params;
724
+ const collectionData = CollectionData.unpack(
725
+ this.packedData.getAndRequireEquals()
726
+ );
727
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
728
+
729
+ const transferEventDraft = new TransferExtendedParams({
730
+ from: PublicKey.empty(), // will be added later
731
+ to,
732
+ collection: this.address,
733
+ nft: address,
734
+ fee: UInt64Option.none(), // will be added later
735
+ price,
736
+ transferByOwner: Bool(false), // will be added later
737
+ approved: PublicKey.empty(), // will be added later
738
+ context,
739
+ });
740
+ const transferEvent = await this._transfer({
741
+ transferEventDraft,
742
+ transferFee: collectionData.transferFee,
743
+ royaltyFee: collectionData.royaltyFee,
744
+ });
745
+ const adminContract = this.getAdminContract();
746
+ const canTransfer = await adminContract.canTransfer(transferEvent);
747
+ canTransfer.assertTrue();
748
+ this.emitEvent("transfer", transferEvent);
749
+ }
750
+
751
+ /**
752
+ * Internal method to transfer an NFT.
753
+ *
754
+ * @param address - The address of the NFT.
755
+ * @param to - The recipient's public key.
756
+ * @param transferFee - The transfer fee amount.
757
+ * @returns The TransferEvent emitted.
758
+ */
759
+ async _transfer(params: {
760
+ transferEventDraft: TransferExtendedParams;
761
+ transferFee: UInt64;
762
+ royaltyFee: UInt32;
763
+ }): Promise<TransferExtendedParams> {
764
+ const { transferEventDraft, transferFee, royaltyFee } = params;
765
+ const sender = this.sender.getUnconstrained();
766
+ // If the from is empty, we set the sender as the from and require signature from the sender
767
+ const isFromEmpty = transferEventDraft.from.equals(PublicKey.empty());
768
+ transferEventDraft.from = Provable.if(
769
+ isFromEmpty,
770
+ sender,
771
+ transferEventDraft.from
772
+ );
773
+
774
+ const tokenId = this.deriveTokenId();
775
+ const nft = new NFT(transferEventDraft.nft, tokenId);
776
+ const transferEvent = await nft.transfer(transferEventDraft);
777
+ const creator = this.creator.getAndRequireEquals();
778
+ // TODO: check is the owner is the creator
779
+ let fee = Provable.if(
780
+ transferEventDraft.price.isSome,
781
+ // We cannot check the price here, so we just rely on owner contract
782
+ // Malicious owner contracts can be blocked by the admin contract
783
+ // or by setting the transfer fee to a higher value reflecting the market price
784
+ transferEventDraft.price
785
+ .orElse(1_000_000_000n) // is not used, can be any value
786
+ .div(MAX_ROYALTY_FEE)
787
+ .mul(UInt64.from(royaltyFee)),
788
+ transferFee
789
+ );
790
+
791
+ const isOwnedByCreator = transferEvent.from.equals(creator);
792
+ fee = Provable.if(
793
+ isOwnedByCreator,
794
+ UInt64.zero,
795
+ // The minimum fee is the transfer fee
796
+ Provable.if(fee.lessThanOrEqual(transferFee), transferFee, fee)
797
+ );
798
+ const senderUpdate = AccountUpdate.createIf(
799
+ fee.equals(UInt64.zero).not().or(isFromEmpty),
800
+ sender
801
+ );
802
+ senderUpdate.requireSignature();
803
+ senderUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change
804
+ senderUpdate.account.balance.requireBetween(fee, UInt64.MAXINT());
805
+
806
+ senderUpdate.send({
807
+ to: this.creator.getAndRequireEquals(),
808
+ amount: fee,
809
+ });
810
+
811
+ transferEvent.fee = UInt64Option.fromValue({
812
+ value: fee,
813
+ isSome: fee.equals(UInt64.zero).not(),
814
+ });
815
+ this.emitEvent(
816
+ "transfer",
817
+ new TransferEvent({
818
+ ...transferEvent,
819
+ })
820
+ );
821
+ return transferEvent;
822
+ }
823
+
824
+ /**
825
+ * Upgrades the verification key of a specific NFT.
826
+ *
827
+ * @param address - The address of the NFT.
828
+ * @param vk - The new verification key.
829
+ */
830
+ @method
831
+ async upgradeNFTVerificationKeyBySignature(
832
+ address: PublicKey,
833
+ vk: VerificationKey
834
+ ): Promise<void> {
835
+ const sender = this.sender.getAndRequireSignature();
836
+ const data = await this._upgrade(address, vk);
837
+ data.owner
838
+ .equals(sender)
839
+ .or(data.isOwnerApprovalRequired.not())
840
+ .assertTrue(CollectionErrors.onlyOwnerCanUpgradeVerificationKey);
841
+ }
842
+
843
+ /**
844
+ * Upgrades the verification key of a specific NFT by Proof.
845
+ *
846
+ * @param address - The address of the NFT.
847
+ * @param vk - The new verification key.
848
+ */
849
+ @method
850
+ async upgradeNFTVerificationKeyByProof(
851
+ address: PublicKey,
852
+ vk: VerificationKey
853
+ ): Promise<void> {
854
+ const data = await this._upgrade(address, vk);
855
+ const ownerContract = this.getOwnerContract(data.owner);
856
+ const canUpgrade = await ownerContract.canChangeVerificationKey(
857
+ this.address,
858
+ address,
859
+ vk
860
+ );
861
+ canUpgrade.assertTrue();
862
+ }
863
+
864
+ async _upgrade(
865
+ address: PublicKey,
866
+ vk: VerificationKey
867
+ ): Promise<UpgradeVerificationKeyData> {
868
+ const tokenId = this.deriveTokenId();
869
+ const nft = new NFT(address, tokenId);
870
+ const adminContract = this.getAdminContract();
871
+ const canUpgrade = await adminContract.canChangeVerificationKey(
872
+ vk,
873
+ address,
874
+ tokenId
875
+ );
876
+ canUpgrade.assertTrue(CollectionErrors.cannotUpgradeVerificationKey);
877
+
878
+ const data = await nft.upgradeVerificationKey(vk);
879
+
880
+ const event = new UpgradeVerificationKeyEvent({
881
+ address,
882
+ tokenId,
883
+ verificationKeyHash: vk.hash,
884
+ });
885
+ this.emitEvent("upgradeNFTVerificationKey", event);
886
+ return data;
887
+ }
888
+
889
+ /**
890
+ * Upgrades the verification key of the collection contract.
891
+ *
892
+ * @param vk - The new verification key.
893
+ */
894
+ @method
895
+ async upgradeVerificationKey(vk: VerificationKey): Promise<void> {
896
+ const adminContract = this.getAdminContract();
897
+ const canUpgrade = await adminContract.canChangeVerificationKey(
898
+ vk,
899
+ this.address,
900
+ this.tokenId
901
+ );
902
+ canUpgrade.assertTrue(CollectionErrors.cannotUpgradeVerificationKey);
903
+ this.account.verificationKey.set(vk);
904
+
905
+ this.emitEvent("upgradeVerificationKey", vk.hash);
906
+ }
907
+
908
+ /**
909
+ * Limits further minting of NFTs in the collection.
910
+ */
911
+ @method
912
+ async limitMinting(): Promise<void> {
913
+ await this.ensureCreatorSignature();
914
+ const collectionData = CollectionData.unpack(
915
+ this.packedData.getAndRequireEquals()
916
+ );
917
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
918
+ collectionData.mintingIsLimited = Bool(true);
919
+ this.packedData.set(collectionData.pack());
920
+ this.emitEvent(
921
+ "limitMinting",
922
+ new LimitMintingEvent({ mintingLimited: Bool(true) })
923
+ );
924
+ }
925
+
926
+ /**
927
+ * Pauses the collection, disabling certain actions.
928
+ */
929
+ @method
930
+ async pause(): Promise<void> {
931
+ const collectionData = CollectionData.unpack(
932
+ this.packedData.getAndRequireEquals()
933
+ );
934
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
935
+ const adminContract = this.getAdminContract();
936
+ const canPause = await adminContract.canPause();
937
+ canPause.assertTrue(CollectionErrors.noPermissionToPause);
938
+ collectionData.isPaused = Bool(true);
939
+ this.packedData.set(collectionData.pack());
940
+ this.emitEvent("pause", new PauseEvent({ isPaused: Bool(true) }));
941
+ }
942
+
943
+ /**
944
+ * Resumes the collection, re-enabling actions.
945
+ */
946
+ @method
947
+ async resume(): Promise<void> {
948
+ const collectionData = CollectionData.unpack(
949
+ this.packedData.getAndRequireEquals()
950
+ );
951
+ collectionData.isPaused.assertTrue(CollectionErrors.collectionNotPaused);
952
+ const adminContract = this.getAdminContract();
953
+ const canResume = await adminContract.canResume();
954
+ canResume.assertTrue(CollectionErrors.noPermissionToResume);
955
+ collectionData.isPaused = Bool(false);
956
+ this.packedData.set(collectionData.pack());
957
+ this.emitEvent("resume", new PauseEvent({ isPaused: Bool(false) }));
958
+ }
959
+
960
+ /**
961
+ * Pauses a specific NFT, disabling its actions.
962
+ *
963
+ * @param address - The address of the NFT to pause.
964
+ */
965
+ @method
966
+ async pauseNFTBySignature(address: PublicKey): Promise<void> {
967
+ const tokenId = this.deriveTokenId();
968
+ const nft = new NFT(address, tokenId);
969
+ const owner = await nft.pause();
970
+ await this.ensureOwnerSignature(owner);
971
+ this.emitEvent(
972
+ "pauseNFT",
973
+ new PauseNFTEvent({ isPaused: Bool(true), address })
974
+ );
975
+ }
976
+
977
+ /**
978
+ * Pauses a specific NFT, disabling its actions.
979
+ *
980
+ * @param address - The address of the NFT to pause.
981
+ */
982
+ @method
983
+ async pauseNFTByProof(address: PublicKey): Promise<void> {
984
+ const tokenId = this.deriveTokenId();
985
+ const nft = new NFT(address, tokenId);
986
+ const owner = await nft.pause();
987
+ const ownerContract = this.getOwnerContract(owner);
988
+ const canPause = await ownerContract.canPause(this.address, address);
989
+ canPause.assertTrue();
990
+ this.emitEvent(
991
+ "pauseNFT",
992
+ new PauseNFTEvent({ isPaused: Bool(true), address })
993
+ );
994
+ }
995
+
996
+ /**
997
+ * Resumes a specific NFT, re-enabling its actions.
998
+ *
999
+ * @param address - The address of the NFT to resume.
1000
+ */
1001
+ @method
1002
+ async resumeNFT(address: PublicKey): Promise<void> {
1003
+ const tokenId = this.deriveTokenId();
1004
+ const nft = new NFT(address, tokenId);
1005
+ const owner = await nft.resume();
1006
+ await this.ensureOwnerSignature(owner);
1007
+ this.emitEvent(
1008
+ "resumeNFT",
1009
+ new PauseNFTEvent({ isPaused: Bool(false), address })
1010
+ );
1011
+ }
1012
+
1013
+ /**
1014
+ * Resumes a specific NFT, re-enabling its actions.
1015
+ *
1016
+ * @param address - The address of the NFT to resume.
1017
+ */
1018
+ @method
1019
+ async resumeNFTByProof(address: PublicKey): Promise<void> {
1020
+ const tokenId = this.deriveTokenId();
1021
+ const nft = new NFT(address, tokenId);
1022
+ const owner = await nft.resume();
1023
+ const ownerContract = this.getOwnerContract(owner);
1024
+ const canResume = await ownerContract.canResume(this.address, address);
1025
+ canResume.assertTrue();
1026
+ this.emitEvent(
1027
+ "resumeNFT",
1028
+ new PauseNFTEvent({ isPaused: Bool(false), address })
1029
+ );
1030
+ }
1031
+
1032
+ /**
1033
+ * Sets a new name for the collection.
1034
+ * Requires owner signature and collection to not be paused.
1035
+ * Emits a 'setName' event with the new name.
1036
+ *
1037
+ * @param name - The new name for the collection as a Field value
1038
+ * @throws {Error} If caller lacks permission to change name
1039
+ */
1040
+ @method
1041
+ async setName(name: Field): Promise<void> {
1042
+ await this.ensureNotPaused();
1043
+ const adminContract = this.getAdminContract();
1044
+ const canChangeName = await adminContract.canChangeName(name);
1045
+ canChangeName.assertTrue(CollectionErrors.noPermissionToChangeName);
1046
+ this.collectionName.set(name);
1047
+ this.emitEvent("setName", name);
1048
+ }
1049
+
1050
+ /**
1051
+ * Updates the base URL for the collection's metadata.
1052
+ * Requires owner signature and collection to not be paused.
1053
+ * Emits a 'setBaseURL' event with the new URL.
1054
+ *
1055
+ * @param baseURL - The new base URL as a Field value
1056
+ * @throws {Error} If caller lacks permission to change base URI
1057
+ */
1058
+ @method
1059
+ async setBaseURL(baseURL: Field): Promise<void> {
1060
+ await this.ensureNotPaused();
1061
+ const adminContract = this.getAdminContract();
1062
+ const canChangeBaseUri = await adminContract.canChangeBaseUri(baseURL);
1063
+ canChangeBaseUri.assertTrue(CollectionErrors.noPermissionToChangeBaseUri);
1064
+ this.baseURL.set(baseURL);
1065
+ this.emitEvent("setBaseURL", baseURL);
1066
+ }
1067
+
1068
+ /**
1069
+ * Sets a new admin address for the collection.
1070
+ * Requires owner signature and collection to not be paused.
1071
+ * Emits a 'setAdmin' event with the new admin address.
1072
+ *
1073
+ * @param admin - The public key of the new admin
1074
+ * @throws {Error} If caller lacks permission to set admin
1075
+ */
1076
+ @method
1077
+ async setAdmin(admin: PublicKey): Promise<void> {
1078
+ await this.ensureNotPaused();
1079
+ const adminContract = this.getAdminContract();
1080
+ const canSetAdmin = await adminContract.canSetAdmin(admin);
1081
+ canSetAdmin.assertTrue(CollectionErrors.noPermissionToSetAdmin);
1082
+ this.admin.set(admin);
1083
+ this.emitEvent("setAdmin", admin);
1084
+ }
1085
+
1086
+ /**
1087
+ * Updates the royalty fee for the collection.
1088
+ * Requires owner signature and collection to not be paused.
1089
+ * Emits a 'setRoyaltyFee' event with the new fee.
1090
+ *
1091
+ * @param royaltyFee - The new royalty fee as a UInt32 value
1092
+ * @throws {Error} If caller lacks permission to change royalty fee
1093
+ */
1094
+ @method
1095
+ async setRoyaltyFee(royaltyFee: UInt32): Promise<void> {
1096
+ const collectionData = CollectionData.unpack(
1097
+ this.packedData.getAndRequireEquals()
1098
+ );
1099
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
1100
+ royaltyFee.assertLessThanOrEqual(
1101
+ UInt32.from(MAX_ROYALTY_FEE),
1102
+ CollectionErrors.invalidRoyaltyFee
1103
+ );
1104
+ const adminContract = this.getAdminContract();
1105
+ const canChangeRoyalty = await adminContract.canChangeRoyalty(royaltyFee);
1106
+ canChangeRoyalty.assertTrue(CollectionErrors.noPermissionToChangeRoyalty);
1107
+ collectionData.royaltyFee = royaltyFee;
1108
+ this.packedData.set(collectionData.pack());
1109
+ this.emitEvent("setRoyaltyFee", royaltyFee);
1110
+ }
1111
+
1112
+ /**
1113
+ * Updates the transfer fee for the collection.
1114
+ * Requires owner signature and collection to not be paused.
1115
+ * Emits a 'setTransferFee' event with the new fee.
1116
+ *
1117
+ * @param transferFee - The new transfer fee as a UInt64 value
1118
+ * @throws {Error} If caller lacks permission to change transfer fee
1119
+ */
1120
+ @method
1121
+ async setTransferFee(transferFee: UInt64): Promise<void> {
1122
+ const collectionData = CollectionData.unpack(
1123
+ this.packedData.getAndRequireEquals()
1124
+ );
1125
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
1126
+ const adminContract = this.getAdminContract();
1127
+ const canChangeTransferFee = await adminContract.canChangeTransferFee(
1128
+ transferFee
1129
+ );
1130
+ canChangeTransferFee.assertTrue(
1131
+ CollectionErrors.noPermissionToChangeTransferFee
1132
+ );
1133
+ collectionData.transferFee = transferFee;
1134
+ this.packedData.set(collectionData.pack());
1135
+ this.emitEvent("setTransferFee", transferFee);
1136
+ }
1137
+
1138
+ /**
1139
+ * Transfers ownership of the collection to a new owner.
1140
+ *
1141
+ * @param to - The public key of the new owner.
1142
+ * @returns The public key of the old owner.
1143
+ */
1144
+ @method.returns(PublicKey)
1145
+ async transferOwnership(to: PublicKey): Promise<PublicKey> {
1146
+ await this.ensureCreatorSignature();
1147
+ await this.ensureNotPaused();
1148
+ const adminContract = this.getAdminContract();
1149
+ const canChangeCreator = await adminContract.canChangeCreator(to);
1150
+ canChangeCreator.assertTrue(CollectionErrors.noPermissionToChangeCreator);
1151
+ const from = this.creator.getAndRequireEquals();
1152
+ this.creator.set(to);
1153
+ this.emitEvent(
1154
+ "ownershipChange",
1155
+ new OwnershipChangeEvent({
1156
+ from,
1157
+ to,
1158
+ })
1159
+ );
1160
+ return from;
1161
+ }
1162
+
1163
+ @method.returns(NFTStateStruct)
1164
+ async getNFTState(address: PublicKey): Promise<NFTStateStruct> {
1165
+ const tokenId = this.deriveTokenId();
1166
+ const nft = new NFT(address, tokenId);
1167
+ const state = await nft.getState();
1168
+ return state;
1169
+ }
1170
+ }
1171
+ return Collection;
1172
+ }