@silvana-one/nft 0.1.30 → 0.2.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 (197) hide show
  1. package/dist/node/index.cjs +57 -1
  2. package/dist/node/marketplace/bb.d.ts +18 -0
  3. package/dist/node/marketplace/bb.js +58 -0
  4. package/dist/node/marketplace/bb.js.map +1 -1
  5. package/dist/tsconfig.node.tsbuildinfo +1 -0
  6. package/dist/web/src/admin/advanced.js.map +1 -0
  7. package/dist/web/src/admin/index.js.map +1 -0
  8. package/dist/web/src/contracts/admin.js.map +1 -0
  9. package/dist/web/src/contracts/collection.js.map +1 -0
  10. package/dist/web/src/contracts/index.js.map +1 -0
  11. package/dist/web/src/contracts/nft.js.map +1 -0
  12. package/dist/web/src/contracts.js.map +1 -0
  13. package/dist/web/src/index.js.map +1 -0
  14. package/dist/web/{interfaces → src/interfaces}/admin.js.map +1 -1
  15. package/dist/web/src/interfaces/approval.js.map +1 -0
  16. package/dist/web/src/interfaces/collection.js.map +1 -0
  17. package/dist/web/src/interfaces/encoding.js.map +1 -0
  18. package/dist/web/src/interfaces/events.js.map +1 -0
  19. package/dist/web/src/interfaces/index.js.map +1 -0
  20. package/dist/web/src/interfaces/ownable.js.map +1 -0
  21. package/dist/web/src/interfaces/owner.js.map +1 -0
  22. package/dist/web/src/interfaces/pausable.js.map +1 -0
  23. package/dist/web/src/interfaces/types.js.map +1 -0
  24. package/dist/web/src/interfaces/update.js.map +1 -0
  25. package/dist/web/src/marketplace/auction.js.map +1 -0
  26. package/dist/web/{marketplace → src/marketplace}/bb.d.ts +18 -0
  27. package/dist/web/{marketplace → src/marketplace}/bb.js +58 -0
  28. package/dist/web/src/marketplace/bb.js.map +1 -0
  29. package/dist/web/src/marketplace/bid.js.map +1 -0
  30. package/dist/web/src/marketplace/index.js.map +1 -0
  31. package/dist/web/src/marketplace/nft-shares.js.map +1 -0
  32. package/dist/web/src/marketplace/offer.js.map +1 -0
  33. package/dist/web/src/marketplace/types.js.map +1 -0
  34. package/dist/web/src/metadata/address.js.map +1 -0
  35. package/dist/web/src/metadata/index.js.map +1 -0
  36. package/dist/web/src/metadata/metadata.js.map +1 -0
  37. package/dist/web/src/metadata/pin.js.map +1 -0
  38. package/dist/web/src/metadata/text.js.map +1 -0
  39. package/dist/web/src/metadata/tree.js.map +1 -0
  40. package/dist/web/src/util/div.js.map +1 -0
  41. package/dist/web/src/util/index.js.map +1 -0
  42. package/dist/web/src/vk.js.map +1 -0
  43. package/dist/web/src/zkprogram-example/game.js.map +1 -0
  44. package/dist/web/src/zkprogram-example/index.js.map +1 -0
  45. package/dist/web/src/zkprogram-example/update.js.map +1 -0
  46. package/dist/web/test/auction.test.d.ts +1212 -0
  47. package/dist/web/test/auction.test.js +1248 -0
  48. package/dist/web/test/auction.test.js.map +1 -0
  49. package/dist/web/test/bb.test.d.ts +1 -0
  50. package/dist/web/test/bb.test.js +159 -0
  51. package/dist/web/test/bb.test.js.map +1 -0
  52. package/dist/web/test/contract.test.d.ts +1 -0
  53. package/dist/web/test/contract.test.js +1333 -0
  54. package/dist/web/test/contract.test.js.map +1 -0
  55. package/dist/web/test/div.test.d.ts +1 -0
  56. package/dist/web/test/div.test.js +55 -0
  57. package/dist/web/test/div.test.js.map +1 -0
  58. package/dist/web/test/helpers/config.d.ts +4 -0
  59. package/dist/web/test/helpers/config.js +103 -0
  60. package/dist/web/test/helpers/config.js.map +1 -0
  61. package/dist/web/test/helpers/metadata.d.ts +30 -0
  62. package/dist/web/test/helpers/metadata.js +155 -0
  63. package/dist/web/test/helpers/metadata.js.map +1 -0
  64. package/dist/web/test/helpers/utils.d.ts +16 -0
  65. package/dist/web/test/helpers/utils.js +36 -0
  66. package/dist/web/test/helpers/utils.js.map +1 -0
  67. package/dist/web/test/helpers/validators.d.ts +15 -0
  68. package/dist/web/test/helpers/validators.js +46 -0
  69. package/dist/web/test/helpers/validators.js.map +1 -0
  70. package/dist/web/test/metadata.test.d.ts +1 -0
  71. package/dist/web/test/metadata.test.js +66 -0
  72. package/dist/web/test/metadata.test.js.map +1 -0
  73. package/dist/web/test/mint.test.d.ts +1 -0
  74. package/dist/web/test/mint.test.js +262 -0
  75. package/dist/web/test/mint.test.js.map +1 -0
  76. package/dist/web/test/pack.test.d.ts +1 -0
  77. package/dist/web/test/pack.test.js +141 -0
  78. package/dist/web/test/pack.test.js.map +1 -0
  79. package/dist/web/test/zkprogram.test.d.ts +1 -0
  80. package/dist/web/test/zkprogram.test.js +728 -0
  81. package/dist/web/test/zkprogram.test.js.map +1 -0
  82. package/dist/web/tsconfig.web.tsbuildinfo +1 -0
  83. package/package.json +88 -14
  84. package/src/marketplace/bb.ts +51 -0
  85. package/dist/tsconfig.tsbuildinfo +0 -1
  86. package/dist/tsconfig.web.tsbuildinfo +0 -1
  87. package/dist/web/admin/advanced.js.map +0 -1
  88. package/dist/web/admin/index.js.map +0 -1
  89. package/dist/web/contracts/admin.js.map +0 -1
  90. package/dist/web/contracts/collection.js.map +0 -1
  91. package/dist/web/contracts/index.js.map +0 -1
  92. package/dist/web/contracts/nft.js.map +0 -1
  93. package/dist/web/contracts.js.map +0 -1
  94. package/dist/web/index.js.map +0 -1
  95. package/dist/web/interfaces/approval.js.map +0 -1
  96. package/dist/web/interfaces/collection.js.map +0 -1
  97. package/dist/web/interfaces/encoding.js.map +0 -1
  98. package/dist/web/interfaces/events.js.map +0 -1
  99. package/dist/web/interfaces/index.js.map +0 -1
  100. package/dist/web/interfaces/ownable.js.map +0 -1
  101. package/dist/web/interfaces/owner.js.map +0 -1
  102. package/dist/web/interfaces/pausable.js.map +0 -1
  103. package/dist/web/interfaces/types.js.map +0 -1
  104. package/dist/web/interfaces/update.js.map +0 -1
  105. package/dist/web/marketplace/auction.js.map +0 -1
  106. package/dist/web/marketplace/bb.js.map +0 -1
  107. package/dist/web/marketplace/bid.js.map +0 -1
  108. package/dist/web/marketplace/index.js.map +0 -1
  109. package/dist/web/marketplace/nft-shares.js.map +0 -1
  110. package/dist/web/marketplace/offer.js.map +0 -1
  111. package/dist/web/marketplace/types.js.map +0 -1
  112. package/dist/web/metadata/address.js.map +0 -1
  113. package/dist/web/metadata/index.js.map +0 -1
  114. package/dist/web/metadata/metadata.js.map +0 -1
  115. package/dist/web/metadata/pin.js.map +0 -1
  116. package/dist/web/metadata/text.js.map +0 -1
  117. package/dist/web/metadata/tree.js.map +0 -1
  118. package/dist/web/util/div.js.map +0 -1
  119. package/dist/web/util/index.js.map +0 -1
  120. package/dist/web/vk.js.map +0 -1
  121. package/dist/web/zkprogram-example/game.js.map +0 -1
  122. package/dist/web/zkprogram-example/index.js.map +0 -1
  123. package/dist/web/zkprogram-example/update.js.map +0 -1
  124. /package/dist/web/{admin → src/admin}/advanced.d.ts +0 -0
  125. /package/dist/web/{admin → src/admin}/advanced.js +0 -0
  126. /package/dist/web/{admin → src/admin}/index.d.ts +0 -0
  127. /package/dist/web/{admin → src/admin}/index.js +0 -0
  128. /package/dist/web/{contracts → src/contracts}/admin.d.ts +0 -0
  129. /package/dist/web/{contracts → src/contracts}/admin.js +0 -0
  130. /package/dist/web/{contracts → src/contracts}/collection.d.ts +0 -0
  131. /package/dist/web/{contracts → src/contracts}/collection.js +0 -0
  132. /package/dist/web/{contracts → src/contracts}/index.d.ts +0 -0
  133. /package/dist/web/{contracts → src/contracts}/index.js +0 -0
  134. /package/dist/web/{contracts → src/contracts}/nft.d.ts +0 -0
  135. /package/dist/web/{contracts → src/contracts}/nft.js +0 -0
  136. /package/dist/web/{contracts.d.ts → src/contracts.d.ts} +0 -0
  137. /package/dist/web/{contracts.js → src/contracts.js} +0 -0
  138. /package/dist/web/{index.d.ts → src/index.d.ts} +0 -0
  139. /package/dist/web/{index.js → src/index.js} +0 -0
  140. /package/dist/web/{interfaces → src/interfaces}/admin.d.ts +0 -0
  141. /package/dist/web/{interfaces → src/interfaces}/admin.js +0 -0
  142. /package/dist/web/{interfaces → src/interfaces}/approval.d.ts +0 -0
  143. /package/dist/web/{interfaces → src/interfaces}/approval.js +0 -0
  144. /package/dist/web/{interfaces → src/interfaces}/collection.d.ts +0 -0
  145. /package/dist/web/{interfaces → src/interfaces}/collection.js +0 -0
  146. /package/dist/web/{interfaces → src/interfaces}/encoding.d.ts +0 -0
  147. /package/dist/web/{interfaces → src/interfaces}/encoding.js +0 -0
  148. /package/dist/web/{interfaces → src/interfaces}/events.d.ts +0 -0
  149. /package/dist/web/{interfaces → src/interfaces}/events.js +0 -0
  150. /package/dist/web/{interfaces → src/interfaces}/index.d.ts +0 -0
  151. /package/dist/web/{interfaces → src/interfaces}/index.js +0 -0
  152. /package/dist/web/{interfaces → src/interfaces}/ownable.d.ts +0 -0
  153. /package/dist/web/{interfaces → src/interfaces}/ownable.js +0 -0
  154. /package/dist/web/{interfaces → src/interfaces}/owner.d.ts +0 -0
  155. /package/dist/web/{interfaces → src/interfaces}/owner.js +0 -0
  156. /package/dist/web/{interfaces → src/interfaces}/pausable.d.ts +0 -0
  157. /package/dist/web/{interfaces → src/interfaces}/pausable.js +0 -0
  158. /package/dist/web/{interfaces → src/interfaces}/types.d.ts +0 -0
  159. /package/dist/web/{interfaces → src/interfaces}/types.js +0 -0
  160. /package/dist/web/{interfaces → src/interfaces}/update.d.ts +0 -0
  161. /package/dist/web/{interfaces → src/interfaces}/update.js +0 -0
  162. /package/dist/web/{marketplace → src/marketplace}/auction.d.ts +0 -0
  163. /package/dist/web/{marketplace → src/marketplace}/auction.js +0 -0
  164. /package/dist/web/{marketplace → src/marketplace}/bid.d.ts +0 -0
  165. /package/dist/web/{marketplace → src/marketplace}/bid.js +0 -0
  166. /package/dist/web/{marketplace → src/marketplace}/index.d.ts +0 -0
  167. /package/dist/web/{marketplace → src/marketplace}/index.js +0 -0
  168. /package/dist/web/{marketplace → src/marketplace}/nft-shares.d.ts +0 -0
  169. /package/dist/web/{marketplace → src/marketplace}/nft-shares.js +0 -0
  170. /package/dist/web/{marketplace → src/marketplace}/offer.d.ts +0 -0
  171. /package/dist/web/{marketplace → src/marketplace}/offer.js +0 -0
  172. /package/dist/web/{marketplace → src/marketplace}/types.d.ts +0 -0
  173. /package/dist/web/{marketplace → src/marketplace}/types.js +0 -0
  174. /package/dist/web/{metadata → src/metadata}/address.d.ts +0 -0
  175. /package/dist/web/{metadata → src/metadata}/address.js +0 -0
  176. /package/dist/web/{metadata → src/metadata}/index.d.ts +0 -0
  177. /package/dist/web/{metadata → src/metadata}/index.js +0 -0
  178. /package/dist/web/{metadata → src/metadata}/metadata.d.ts +0 -0
  179. /package/dist/web/{metadata → src/metadata}/metadata.js +0 -0
  180. /package/dist/web/{metadata → src/metadata}/pin.d.ts +0 -0
  181. /package/dist/web/{metadata → src/metadata}/pin.js +0 -0
  182. /package/dist/web/{metadata → src/metadata}/text.d.ts +0 -0
  183. /package/dist/web/{metadata → src/metadata}/text.js +0 -0
  184. /package/dist/web/{metadata → src/metadata}/tree.d.ts +0 -0
  185. /package/dist/web/{metadata → src/metadata}/tree.js +0 -0
  186. /package/dist/web/{util → src/util}/div.d.ts +0 -0
  187. /package/dist/web/{util → src/util}/div.js +0 -0
  188. /package/dist/web/{util → src/util}/index.d.ts +0 -0
  189. /package/dist/web/{util → src/util}/index.js +0 -0
  190. /package/dist/web/{vk.d.ts → src/vk.d.ts} +0 -0
  191. /package/dist/web/{vk.js → src/vk.js} +0 -0
  192. /package/dist/web/{zkprogram-example → src/zkprogram-example}/game.d.ts +0 -0
  193. /package/dist/web/{zkprogram-example → src/zkprogram-example}/game.js +0 -0
  194. /package/dist/web/{zkprogram-example → src/zkprogram-example}/index.d.ts +0 -0
  195. /package/dist/web/{zkprogram-example → src/zkprogram-example}/index.js +0 -0
  196. /package/dist/web/{zkprogram-example → src/zkprogram-example}/update.d.ts +0 -0
  197. /package/dist/web/{zkprogram-example → src/zkprogram-example}/update.js +0 -0
@@ -0,0 +1,1333 @@
1
+ import { afterEach, beforeEach, describe, it } from "node:test";
2
+ import assert from "node:assert";
3
+ import { Mina, Field, AccountUpdate, UInt32, Bool, Cache, UInt64, fetchLastBlock, PublicKey, Signature, Poseidon, verify, Nullifier, } from "o1js";
4
+ import { fetchMinaAccount, initBlockchain, accountBalanceMina, Memory, serializeIndexedMap, sendTx, pinJSON, } from "@silvana-one/mina-utils";
5
+ import { TEST_ACCOUNTS } from "./helpers/config.js";
6
+ import { NFT, NFTAdmin, NFTAdvancedAdmin, CollectionData, fieldFromString, NFTData, NFTState, NFTUpdateProof, NFTStateStruct, MintParams, NFTProgram, nftVerificationKeys, Metadata, BidFactory, NFTAddress, Bid, UInt64Option, OfferFactory, AdminData, NonFungibleTokenContractsFactory, TransferParams, NFTTransactionContext, } from "../src/index.js";
7
+ import { VerificationKeyUpgradeAuthority, ChainId, UpgradeDatabaseState, ValidatorsList, ValidatorsState, ValidatorsVoting, UpgradeAuthorityDatabase, ValidatorsDecision, ValidatorDecisionType, ValidatorsDecisionState, ValidatorsVotingProof, VerificationKeyUpgradeData, PublicKeyOption, } from "@silvana-one/upgradable";
8
+ import { processArguments } from "./helpers/utils.js";
9
+ import { checkValidatorsList } from "./helpers/validators.js";
10
+ import { randomMetadata } from "./helpers/metadata.js";
11
+ import { Whitelist, Storage, OffChainList } from "@silvana-one/storage";
12
+ let { chain, useAdvancedAdmin, approveTransfer, noLog } = processArguments();
13
+ const networkId = chain === "mainnet" ? "mainnet" : "devnet";
14
+ const expectedTxStatus = chain === "zeko" ? "pending" : "included";
15
+ const vk = nftVerificationKeys[networkId].vk;
16
+ const { TestPublicKey } = Mina;
17
+ let nftContractVk;
18
+ let nftProgramVk;
19
+ let collectionVk;
20
+ let adminVk;
21
+ let AdvancedAdminVk;
22
+ let upgradeAuthorityVk;
23
+ let validatorsVotingVk;
24
+ let offerVk;
25
+ const cache = Cache.FileSystem("./cache");
26
+ const zkNFTKey = TestPublicKey.random();
27
+ const zkCollectionKey = TestPublicKey.random();
28
+ const zkAdminKey = TestPublicKey.random();
29
+ const zkBidKey = TestPublicKey.random();
30
+ const zkOfferKey = TestPublicKey.random();
31
+ const upgradeAuthority = TestPublicKey.random();
32
+ const upgradeAuthorityContract = new VerificationKeyUpgradeAuthority(upgradeAuthority);
33
+ // https://github.com/o1-labs/o1js/issues/1317 - issue with #private in SmartContract
34
+ // is being fixed by using unknown as ...
35
+ const { Collection, Approval } = NonFungibleTokenContractsFactory({
36
+ approvalFactory: OfferFactory,
37
+ adminContract: useAdvancedAdmin
38
+ ? NFTAdvancedAdmin
39
+ : NFTAdmin,
40
+ });
41
+ const NonFungibleTokenBidContract = BidFactory({
42
+ collectionContract: () => Collection,
43
+ });
44
+ const NonFungibleTokenOfferContract = Approval;
45
+ const collectionContract = new Collection(zkCollectionKey);
46
+ const tokenId = collectionContract.deriveTokenId();
47
+ const nftContract = new NFT(zkNFTKey, tokenId);
48
+ const adminContract = useAdvancedAdmin
49
+ ? new NFTAdvancedAdmin(zkAdminKey)
50
+ : new NFTAdmin(zkAdminKey);
51
+ const bidContract = new NonFungibleTokenBidContract(zkBidKey);
52
+ const offerContract = new NonFungibleTokenOfferContract(zkOfferKey);
53
+ const NUMBER_OF_USERS = 6;
54
+ let admin;
55
+ let faucet;
56
+ let users = [];
57
+ const whitelistedUsers = TEST_ACCOUNTS.slice(NUMBER_OF_USERS)
58
+ .map((account) => TestPublicKey.fromBase58(account.privateKey))
59
+ .slice(0, NUMBER_OF_USERS);
60
+ const validators = [
61
+ TestPublicKey.random(),
62
+ TestPublicKey.random(),
63
+ TestPublicKey.random(),
64
+ ];
65
+ const creator = whitelistedUsers[0];
66
+ let owner = creator;
67
+ const price = UInt64.from(10_000_000_000);
68
+ let collectionName;
69
+ const nftParams = [];
70
+ describe(`NFT contracts tests: ${chain} ${useAdvancedAdmin ? "advanced " : ""}${approveTransfer ? "approve " : ""}${noLog ? "noLog" : ""}`, () => {
71
+ const originalConsoleLog = console.log;
72
+ if (noLog) {
73
+ beforeEach(() => {
74
+ console.log = () => { };
75
+ });
76
+ afterEach(() => {
77
+ console.log = originalConsoleLog;
78
+ });
79
+ }
80
+ it("should initialize a blockchain", async () => {
81
+ if (chain === "devnet" || chain === "zeko" || chain === "mainnet") {
82
+ await initBlockchain(chain);
83
+ admin = TestPublicKey.fromBase58(TEST_ACCOUNTS[0].privateKey);
84
+ users = TEST_ACCOUNTS.slice(1).map((account) => TestPublicKey.fromBase58(account.privateKey));
85
+ }
86
+ else if (chain === "local") {
87
+ const { keys } = await initBlockchain(chain, NUMBER_OF_USERS + 2);
88
+ faucet = TestPublicKey(keys[0].key);
89
+ admin = TestPublicKey(keys[1].key);
90
+ users = keys.slice(2);
91
+ }
92
+ else if (chain === "lightnet") {
93
+ const { keys } = await initBlockchain(chain, NUMBER_OF_USERS + 2);
94
+ faucet = TestPublicKey(keys[0].key);
95
+ admin = TestPublicKey(keys[1].key);
96
+ users = keys.slice(2);
97
+ }
98
+ owner = creator;
99
+ assert(users.length >= NUMBER_OF_USERS);
100
+ console.log("chain:", chain);
101
+ console.log("networkId:", Mina.getNetworkId());
102
+ console.log("Collection contract address:", zkCollectionKey.toBase58());
103
+ console.log("Admin contract address:", zkAdminKey.toBase58());
104
+ console.log("NFT contract address:", zkNFTKey.toBase58());
105
+ console.log("Upgrade authority contract address:", upgradeAuthority.toBase58());
106
+ console.log("AdvancedAdmin:", useAdvancedAdmin);
107
+ console.log("Bid contract address:", zkBidKey.toBase58());
108
+ console.log("Offer contract address:", zkOfferKey.toBase58());
109
+ if (chain === "local" || chain === "lightnet") {
110
+ await fetchMinaAccount({ publicKey: faucet, force: true });
111
+ let nonce = Number(Mina.getAccount(faucet).nonce.toBigint());
112
+ let txs = [];
113
+ for (const user of whitelistedUsers) {
114
+ await fetchMinaAccount({ publicKey: user, force: true });
115
+ const balance = await accountBalanceMina(user);
116
+ if (balance > 30) {
117
+ continue;
118
+ }
119
+ const transaction = await Mina.transaction({
120
+ sender: users[0],
121
+ fee: 100_000_000,
122
+ memo: "topup",
123
+ nonce: nonce++,
124
+ }, async () => {
125
+ const senderUpdate = AccountUpdate.createSigned(users[0]);
126
+ if (balance === 0)
127
+ senderUpdate.balance.subInPlace(1000000000);
128
+ senderUpdate.send({ to: user, amount: 100_000_000_000 });
129
+ });
130
+ txs.push(await sendTx({
131
+ tx: transaction.sign([users[0].key]),
132
+ description: "topup",
133
+ wait: false,
134
+ }));
135
+ }
136
+ for (const tx of txs) {
137
+ if (tx?.status === "pending") {
138
+ const txIncluded = await tx.safeWait();
139
+ if (txIncluded.status !== expectedTxStatus) {
140
+ throw new Error("Transaction not included");
141
+ }
142
+ else {
143
+ console.log("Topup tx included:", txIncluded.hash);
144
+ }
145
+ }
146
+ else
147
+ throw new Error("Topup transaction not pending");
148
+ }
149
+ console.log("Topup done");
150
+ }
151
+ console.log("Creator", creator.toBase58(), "balance:", await accountBalanceMina(creator));
152
+ console.log("Admin ", admin.toBase58(), "balance:", await accountBalanceMina(admin));
153
+ for (let i = 0; i < NUMBER_OF_USERS; i++) {
154
+ console.log(`User ${i} `, users[i].toBase58(), "balance:", await accountBalanceMina(users[i]));
155
+ }
156
+ for (let i = 0; i < NUMBER_OF_USERS; i++) {
157
+ console.log(`Whitelisted User ${i} `, whitelistedUsers[i].toBase58(), "balance:", await accountBalanceMina(whitelistedUsers[i]));
158
+ }
159
+ Memory.info("before compiling");
160
+ });
161
+ it("should analyze contracts methods", async () => {
162
+ console.log("Analyzing contracts methods...");
163
+ console.time("methods analyzed");
164
+ const methods = [
165
+ {
166
+ name: "NFT",
167
+ result: await NFT.analyzeMethods(),
168
+ skip: false,
169
+ },
170
+ {
171
+ name: "Admin",
172
+ result: await NFTAdmin.analyzeMethods(),
173
+ skip: false,
174
+ },
175
+ {
176
+ name: "AdvancedAdmin",
177
+ result: await NFTAdvancedAdmin.analyzeMethods(),
178
+ skip: false,
179
+ },
180
+ {
181
+ name: "UpgradeAuthority",
182
+ result: await VerificationKeyUpgradeAuthority.analyzeMethods(),
183
+ skip: false,
184
+ },
185
+ {
186
+ name: "Collection",
187
+ result: await Collection.analyzeMethods(),
188
+ skip: false,
189
+ },
190
+ {
191
+ name: "NFTProgram",
192
+ result: await NFTProgram.analyzeMethods(),
193
+ skip: true,
194
+ },
195
+ {
196
+ name: "Offer Contract",
197
+ result: await NonFungibleTokenOfferContract.analyzeMethods(),
198
+ skip: false,
199
+ },
200
+ {
201
+ name: "Bid Contract",
202
+ result: await NonFungibleTokenBidContract.analyzeMethods(),
203
+ skip: false,
204
+ },
205
+ ];
206
+ console.timeEnd("methods analyzed");
207
+ const maxRows = 2 ** 16;
208
+ for (const contract of methods) {
209
+ // calculate the size of the contract - the sum or rows for each method
210
+ const size = Object.values(contract.result).reduce((acc, method) => acc + method.rows, 0);
211
+ // calculate percentage rounded to 0 decimal places
212
+ const percentage = Math.round(((size * 100) / maxRows) * 100) / 100;
213
+ console.log(`${contract.name} rows: ${size} (${percentage}% of max ${maxRows} rows)`);
214
+ if (contract.skip !== true)
215
+ for (const method in contract.result) {
216
+ console.log("\t", method, `rows:`, contract.result[method].rows);
217
+ }
218
+ }
219
+ });
220
+ it("should compile NFT Contract", async () => {
221
+ console.log("compiling...");
222
+ console.time("compiled NFTContract");
223
+ const { verificationKey } = await NFT.compile({ cache });
224
+ nftContractVk = verificationKey;
225
+ console.timeEnd("compiled NFTContract");
226
+ assert.strictEqual(nftContractVk.hash.toJSON(), vk.NFT.hash);
227
+ assert.strictEqual(nftContractVk.data, vk.NFT.data);
228
+ });
229
+ it("should compile Admin", { skip: useAdvancedAdmin }, async () => {
230
+ console.time("compiled Admin");
231
+ const { verificationKey } = await NFTAdmin.compile({ cache });
232
+ adminVk = verificationKey;
233
+ console.timeEnd("compiled Admin");
234
+ console.log("Admin vk hash:", adminVk.hash.toJSON());
235
+ });
236
+ it("should compile AdvancedAdmin", { skip: !useAdvancedAdmin }, async () => {
237
+ console.time("compiled AdvancedAdmin");
238
+ const { verificationKey } = await NFTAdvancedAdmin.compile({
239
+ cache,
240
+ });
241
+ AdvancedAdminVk = verificationKey;
242
+ console.timeEnd("compiled AdvancedAdmin");
243
+ console.log("AdvancedAdmin vk hash:", AdvancedAdminVk.hash.toJSON());
244
+ });
245
+ it("should compile UpgradeAuthority", { skip: !useAdvancedAdmin }, async () => {
246
+ console.time("compiled UpgradeAuthority");
247
+ const { verificationKey } = await VerificationKeyUpgradeAuthority.compile({
248
+ cache,
249
+ });
250
+ upgradeAuthorityVk = verificationKey;
251
+ console.timeEnd("compiled UpgradeAuthority");
252
+ console.log("UpgradeAuthority vk hash:", upgradeAuthorityVk.hash.toJSON());
253
+ });
254
+ it("should compile ValidatorsVoting", { skip: !useAdvancedAdmin }, async () => {
255
+ console.time("compiled ValidatorsVoting");
256
+ const { verificationKey } = await ValidatorsVoting.compile({ cache });
257
+ validatorsVotingVk = verificationKey;
258
+ console.timeEnd("compiled ValidatorsVoting");
259
+ console.log("ValidatorsVoting vk hash:", validatorsVotingVk.hash.toJSON());
260
+ });
261
+ it("should compile Collection", async () => {
262
+ console.time("compiled Collection");
263
+ const { verificationKey } = await Collection.compile({ cache });
264
+ collectionVk = verificationKey;
265
+ console.timeEnd("compiled Collection");
266
+ console.log("Collection vk hash:", collectionVk.hash.toJSON());
267
+ });
268
+ it("should compile nft ZkProgram", async () => {
269
+ console.time("compiled NFTProgram");
270
+ nftProgramVk = (await NFTProgram.compile({ cache })).verificationKey;
271
+ console.timeEnd("compiled NFTProgram");
272
+ console.log("NFTProgram vk hash:", nftProgramVk.hash.toJSON());
273
+ });
274
+ it("should compile Offer Contract", async () => {
275
+ console.time("compiled Offer Contract");
276
+ const { verificationKey } = await NonFungibleTokenOfferContract.compile({
277
+ cache,
278
+ });
279
+ offerVk = verificationKey;
280
+ console.timeEnd("compiled Offer Contract");
281
+ console.log("Offer Contract vk hash:", offerVk.hash.toJSON());
282
+ });
283
+ it("should compile Bid Contract", async () => {
284
+ console.time("compiled Bid Contract");
285
+ const { verificationKey } = await NonFungibleTokenBidContract.compile({
286
+ cache,
287
+ });
288
+ const bidVk = verificationKey;
289
+ console.timeEnd("compiled Bid Contract");
290
+ console.log("Bid Contract vk hash:", bidVk.hash.toJSON());
291
+ });
292
+ it("should deploy an UpgradeAuthority", { skip: !useAdvancedAdmin }, async () => {
293
+ Memory.info("before deploy");
294
+ console.time("deployed UpgradeAuthority");
295
+ const validatorsList = new ValidatorsList();
296
+ const validatorsCount = 2; // majority is 2 validators out of 3
297
+ const list = [
298
+ { key: validators[0], authorizedToVote: true },
299
+ { key: TestPublicKey.random(), authorizedToVote: false },
300
+ { key: validators[1], authorizedToVote: true },
301
+ { key: validators[2], authorizedToVote: true },
302
+ { key: TestPublicKey.random(), authorizedToVote: false },
303
+ ];
304
+ for (let i = 0; i < list.length; i++) {
305
+ const key = Poseidon.hashPacked(PublicKey, list[i].key);
306
+ validatorsList.set(key, Field(Bool(list[i].authorizedToVote).value));
307
+ }
308
+ const data = {
309
+ validators: list.map((v) => ({
310
+ publicKey: v.key.toBase58(),
311
+ authorizedToVote: v.authorizedToVote,
312
+ })),
313
+ validatorsCount,
314
+ root: validatorsList.root.toJSON(),
315
+ map: serializeIndexedMap(validatorsList),
316
+ };
317
+ const ipfs = await pinJSON({
318
+ data,
319
+ name: "upgrade-authority-list",
320
+ });
321
+ if (!ipfs) {
322
+ throw new Error("List IPFS hash is undefined");
323
+ }
324
+ const validatorState = new ValidatorsState({
325
+ chainId: ChainId[chain === "devnet" ? "mina:devnet" : "zeko:devnet"],
326
+ root: validatorsList.root,
327
+ count: UInt32.from(validatorsCount),
328
+ });
329
+ await fetchMinaAccount({ publicKey: admin, force: true });
330
+ const tx = await Mina.transaction({
331
+ sender: admin,
332
+ fee: 100_000_000,
333
+ memo: `Deploy UpgradeAuthority`,
334
+ }, async () => {
335
+ AccountUpdate.fundNewAccount(admin, 1);
336
+ // deploy() and initialize() create 2 account updates for the same publicKey, it is intended
337
+ await upgradeAuthorityContract.deploy();
338
+ await upgradeAuthorityContract.initialize(validatorState, Storage.fromString(ipfs), validatorsVotingVk.hash);
339
+ });
340
+ await tx.prove();
341
+ assert.strictEqual((await sendTx({
342
+ tx: tx.sign([admin.key, upgradeAuthority.key]),
343
+ description: "deploy UpgradeAuthority",
344
+ }))?.status, expectedTxStatus);
345
+ console.timeEnd("deployed UpgradeAuthority");
346
+ });
347
+ it("should deploy a Collection", async () => {
348
+ console.time("deployed Collection");
349
+ const { metadataRoot, ipfsHash, serializedMap, name, privateMetadata } = await randomMetadata({
350
+ includePrivateTraits: false,
351
+ includeBanner: true,
352
+ });
353
+ if (!ipfsHash) {
354
+ throw new Error("IPFS hash is undefined");
355
+ }
356
+ collectionName = name;
357
+ const slot = chain === "local"
358
+ ? Mina.currentSlot()
359
+ : chain === "zeko"
360
+ ? UInt32.zero
361
+ : (await fetchLastBlock()).globalSlotSinceGenesis;
362
+ const expiry = slot.add(UInt32.from(100000));
363
+ const masterNFT = new MintParams({
364
+ name: fieldFromString(name),
365
+ address: zkCollectionKey,
366
+ tokenId,
367
+ data: NFTData.new({
368
+ owner: creator,
369
+ }),
370
+ fee: UInt64.zero,
371
+ metadata: metadataRoot,
372
+ storage: Storage.fromString(ipfsHash),
373
+ metadataVerificationKeyHash: Field(0),
374
+ expiry,
375
+ });
376
+ await fetchMinaAccount({ publicKey: creator, force: true });
377
+ const whitelist = useAdvancedAdmin
378
+ ? (await Whitelist.create({
379
+ list: whitelistedUsers.map((user) => ({
380
+ address: user,
381
+ amount: 50_000_000_000,
382
+ })),
383
+ })).whitelist
384
+ : undefined;
385
+ const tx = await Mina.transaction({
386
+ sender: creator,
387
+ fee: 100_000_000,
388
+ memo: `Deploy Collection ${name}`.substring(0, 30),
389
+ }, async () => {
390
+ AccountUpdate.fundNewAccount(creator, 3);
391
+ if (adminContract instanceof NFTAdvancedAdmin) {
392
+ if (!whitelist) {
393
+ throw new Error("Whitelist is undefined");
394
+ }
395
+ await adminContract.deploy({
396
+ admin: creator,
397
+ upgradeAuthority,
398
+ whitelist,
399
+ uri: `AdvancedAdminContract`,
400
+ adminData: AdminData.new(),
401
+ });
402
+ }
403
+ else if (adminContract instanceof NFTAdmin) {
404
+ await adminContract.deploy({
405
+ admin: creator,
406
+ uri: `AdminContract`,
407
+ });
408
+ }
409
+ else {
410
+ throw new Error("Admin contract is not supported");
411
+ }
412
+ // deploy() and initialize() create 2 account updates for the same publicKey, it is intended
413
+ await collectionContract.deploy({
414
+ creator,
415
+ collectionName: fieldFromString(name),
416
+ baseURL: fieldFromString("ipfs"),
417
+ admin: zkAdminKey,
418
+ symbol: "NFT",
419
+ url: `https://${chain}.minanft.io`,
420
+ });
421
+ await collectionContract.initialize(masterNFT, CollectionData.new({
422
+ requireTransferApproval: approveTransfer,
423
+ royaltyFee: 10, // 10%
424
+ transferFee: 1_000_000_000, // 1 MINA
425
+ }));
426
+ });
427
+ await tx.prove();
428
+ assert.strictEqual((await sendTx({
429
+ tx: tx.sign([
430
+ creator.key,
431
+ zkCollectionKey.key,
432
+ zkAdminKey.key,
433
+ upgradeAuthority.key,
434
+ ]),
435
+ description: "deploy Collection",
436
+ }))?.status, expectedTxStatus);
437
+ console.timeEnd("deployed Collection");
438
+ });
439
+ it("should mint NFT", async () => {
440
+ Memory.info("before mint");
441
+ console.time("minted NFT");
442
+ const { name, ipfsHash, metadataRoot, privateMetadata } = await randomMetadata();
443
+ if (!ipfsHash) {
444
+ throw new Error("IPFS hash is undefined");
445
+ }
446
+ nftParams.push({
447
+ name,
448
+ address: zkNFTKey,
449
+ collection: zkCollectionKey,
450
+ privateMetadata,
451
+ });
452
+ await fetchMinaAccount({ publicKey: creator, force: true });
453
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
454
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
455
+ owner = creator;
456
+ const slot = chain === "local"
457
+ ? Mina.currentSlot()
458
+ : chain === "zeko"
459
+ ? UInt32.zero
460
+ : (await fetchLastBlock()).globalSlotSinceGenesis;
461
+ const expiry = slot.add(UInt32.from(100000));
462
+ const tx = await Mina.transaction({
463
+ sender: creator,
464
+ fee: 100_000_000,
465
+ memo: `Mint NFT ${name}`.substring(0, 30),
466
+ }, async () => {
467
+ await collectionContract.mintByCreator({
468
+ name: fieldFromString(name),
469
+ address: zkNFTKey,
470
+ tokenId,
471
+ metadata: metadataRoot,
472
+ data: NFTData.new({
473
+ canChangeMetadata: true,
474
+ canPause: true,
475
+ owner,
476
+ }),
477
+ metadataVerificationKeyHash: nftProgramVk.hash,
478
+ expiry,
479
+ fee: UInt64.from(10_000_000_000),
480
+ storage: Storage.fromString(ipfsHash),
481
+ });
482
+ });
483
+ await tx.prove();
484
+ assert.strictEqual((await sendTx({
485
+ tx: tx.sign([creator.key, zkNFTKey.key]),
486
+ description: "mint",
487
+ }))?.status, expectedTxStatus);
488
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
489
+ const nft = new NFT(zkNFTKey, tokenId);
490
+ const dataCheck = NFTData.unpack(nft.packedData.get());
491
+ console.log("owner", owner.toBase58());
492
+ console.log("ownerCheck", dataCheck.owner.toBase58());
493
+ console.log("approvalCheck", dataCheck.approved.toBase58());
494
+ console.log("creator", creator.toBase58());
495
+ assert.strictEqual(dataCheck.owner.equals(creator).toBoolean(), true);
496
+ assert.strictEqual(dataCheck.approved.equals(PublicKey.empty()).toBoolean(), true);
497
+ console.timeEnd("minted NFT");
498
+ owner = creator;
499
+ });
500
+ it("should offer NFT for sale", async () => {
501
+ Memory.info("before offer");
502
+ console.time("offered NFT");
503
+ const seller = owner;
504
+ await fetchMinaAccount({ publicKey: seller, force: true });
505
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
506
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
507
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
508
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
509
+ p.collection.equals(zkCollectionKey).toBoolean());
510
+ if (!nft) {
511
+ throw new Error("NFT not found");
512
+ }
513
+ const { name } = nft;
514
+ const tx = await Mina.transaction({
515
+ sender: seller,
516
+ fee: 100_000_000,
517
+ memo: `Offer NFT ${name}`.substring(0, 30),
518
+ }, async () => {
519
+ AccountUpdate.fundNewAccount(seller, 1);
520
+ await collectionContract.approveAddress(zkNFTKey, zkOfferKey);
521
+ await offerContract.deploy({
522
+ collection: zkCollectionKey,
523
+ nft: zkNFTKey,
524
+ owner: seller,
525
+ price,
526
+ });
527
+ });
528
+ await tx.prove();
529
+ assert.strictEqual((await sendTx({
530
+ tx: tx.sign([seller.key, zkOfferKey.key]),
531
+ description: "offer",
532
+ }))?.status, expectedTxStatus);
533
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
534
+ const zkNFT = new NFT(zkNFTKey, tokenId);
535
+ const dataCheck = NFTData.unpack(zkNFT.packedData.get());
536
+ console.log("owner", owner.toBase58());
537
+ console.log("ownerCheck", dataCheck.owner.toBase58());
538
+ console.log("approvalCheck", dataCheck.approved.toBase58());
539
+ console.log("creator", creator.toBase58());
540
+ assert.strictEqual(dataCheck.owner.equals(creator).toBoolean(), true);
541
+ assert.strictEqual(dataCheck.approved.equals(zkOfferKey).toBoolean(), true);
542
+ console.timeEnd("offered NFT");
543
+ });
544
+ it("should buy NFT", async () => {
545
+ Memory.info("before buy");
546
+ console.time("bought NFT");
547
+ const buyer = whitelistedUsers[1];
548
+ await fetchMinaAccount({ publicKey: buyer, force: true });
549
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
550
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
551
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
552
+ await fetchMinaAccount({ publicKey: zkOfferKey, force: true });
553
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
554
+ p.collection.equals(zkCollectionKey).toBoolean());
555
+ if (!nft) {
556
+ throw new Error("NFT not found");
557
+ }
558
+ const { name } = nft;
559
+ const tx = await Mina.transaction({
560
+ sender: buyer,
561
+ fee: 100_000_000,
562
+ memo: `Buy NFT ${name}`.substring(0, 30),
563
+ }, async () => {
564
+ await offerContract.buy(buyer);
565
+ });
566
+ await tx.prove();
567
+ assert.strictEqual((await sendTx({
568
+ tx: tx.sign([buyer.key]),
569
+ description: "buy",
570
+ }))?.status, expectedTxStatus);
571
+ console.timeEnd("bought NFT");
572
+ owner = buyer;
573
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
574
+ const zkNFT = new NFT(zkNFTKey, tokenId);
575
+ const dataCheck = NFTData.unpack(zkNFT.packedData.get());
576
+ console.log("owner", owner.toBase58());
577
+ console.log("ownerCheck", dataCheck.owner.toBase58());
578
+ console.log("approvalCheck", dataCheck.approved.toBase58());
579
+ console.log("creator", creator.toBase58());
580
+ assert.strictEqual(dataCheck.owner.equals(owner).toBoolean(), true);
581
+ assert.strictEqual(dataCheck.approved.equals(PublicKey.empty()).toBoolean(), true);
582
+ });
583
+ it("should bid NFT", async () => {
584
+ Memory.info("before bid");
585
+ console.time("bid NFT");
586
+ const buyer = whitelistedUsers[1];
587
+ console.log("Bid contract address:", zkBidKey.toBase58());
588
+ console.log("Buyer address:", buyer.toBase58());
589
+ await fetchMinaAccount({ publicKey: buyer, force: true });
590
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
591
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
592
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
593
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
594
+ p.collection.equals(zkCollectionKey).toBoolean());
595
+ if (!nft) {
596
+ throw new Error("NFT not found");
597
+ }
598
+ const { name } = nft;
599
+ const nftAddress = {
600
+ collection: zkCollectionKey,
601
+ nft: zkNFTKey,
602
+ };
603
+ const bidsData = [
604
+ {
605
+ collection: zkCollectionKey,
606
+ nft: zkNFTKey,
607
+ price: UInt64.from(3_000_000_000),
608
+ points: UInt64.from(100),
609
+ },
610
+ ];
611
+ const key = Poseidon.hashPacked(NFTAddress, nftAddress);
612
+ const price = UInt64.from(3_000_000_000);
613
+ const points = UInt64.from(100);
614
+ const bids = await OffChainList.create({
615
+ list: bidsData.map((bid) => ({
616
+ key: Poseidon.hashPacked(NFTAddress, new NFTAddress({ collection: bid.collection, nft: bid.nft })),
617
+ value: new Bid({ price: bid.price, points: bid.points }).pack(),
618
+ })),
619
+ name: "bids",
620
+ data: bidsData.map((bid) => ({
621
+ collection: bid.collection.toBase58(),
622
+ nft: bid.nft.toBase58(),
623
+ price: Number(bid.price.toBigInt()) / 1_000_000_000,
624
+ points: Number(bid.points.toBigInt()),
625
+ })),
626
+ pin: false,
627
+ });
628
+ const whitelist = await Whitelist.create({
629
+ list: [{ address: owner, amount: price }],
630
+ name: "whitelist",
631
+ json: bids.json,
632
+ pin: true,
633
+ });
634
+ const storage = whitelist.whitelist.list.storage;
635
+ const tx = await Mina.transaction({
636
+ sender: buyer,
637
+ fee: 100_000_000,
638
+ memo: `Bid NFT ${name}`.substring(0, 30),
639
+ }, async () => {
640
+ //AccountUpdate.fundNewAccount(buyer, 1);
641
+ await bidContract.deploy({
642
+ bids: bids.list.root,
643
+ whitelist: whitelist.whitelist.list.root,
644
+ storage,
645
+ });
646
+ await bidContract.initialize(UInt64.from(5_000_000_000), UInt64.from(1000));
647
+ });
648
+ await tx.prove();
649
+ assert.strictEqual((await sendTx({
650
+ tx: tx.sign([buyer.key, zkBidKey.key]),
651
+ description: "bid",
652
+ }))?.status, expectedTxStatus);
653
+ console.timeEnd("bid NFT");
654
+ });
655
+ it("should accept bid on NFT", async () => {
656
+ Memory.info("before accepting bid");
657
+ console.time("accept bid");
658
+ const seller = owner;
659
+ console.log("Seller address:", seller.toBase58());
660
+ await fetchMinaAccount({ publicKey: seller, force: true });
661
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
662
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
663
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
664
+ await fetchMinaAccount({ publicKey: zkBidKey, force: true });
665
+ const requireTransferApproval = CollectionData.unpack(collectionContract.packedData.get()).requireTransferApproval.toBoolean();
666
+ console.log("requireTransferApproval", requireTransferApproval);
667
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
668
+ p.collection.equals(zkCollectionKey).toBoolean());
669
+ if (!nft) {
670
+ throw new Error("NFT not found");
671
+ }
672
+ const { name } = nft;
673
+ const price = UInt64.from(2_000_000_000); // 1 MINA less than the bid
674
+ const nftAddress = {
675
+ collection: zkCollectionKey,
676
+ nft: zkNFTKey,
677
+ };
678
+ const tx = await Mina.transaction({
679
+ sender: seller,
680
+ fee: 100_000_000,
681
+ memo: `Accept bid on NFT ${name}`.substring(0, 30),
682
+ }, async () => {
683
+ if (requireTransferApproval)
684
+ await bidContract.approvedSell(nftAddress, price);
685
+ else
686
+ await bidContract.sell(nftAddress, price);
687
+ });
688
+ await tx.prove();
689
+ assert.strictEqual((await sendTx({
690
+ tx: tx.sign([seller.key]),
691
+ description: "accept bid",
692
+ }))?.status, expectedTxStatus);
693
+ console.timeEnd("accept bid");
694
+ owner = whitelistedUsers[1];
695
+ });
696
+ it("should update NFT metadata", { skip: useAdvancedAdmin }, async () => {
697
+ Memory.info("before update");
698
+ console.time("updated NFT");
699
+ await fetchMinaAccount({ publicKey: owner, force: true });
700
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
701
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
702
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
703
+ const nftAccount = Mina.getAccount(zkNFTKey, tokenId);
704
+ const nftStateStruct = NFTStateStruct.fromAccount(nftAccount);
705
+ assert.strictEqual(nftAccount.zkapp?.verificationKey?.hash.toJSON(), nftContractVk.hash.toJSON());
706
+ const nftState = NFTState.fromNFTState({
707
+ nftState: nftStateStruct,
708
+ creator: creator,
709
+ address: zkNFTKey,
710
+ tokenId,
711
+ });
712
+ const index = nftParams.findIndex((p) => p.address.equals(zkNFTKey).toBoolean() &&
713
+ p.collection.equals(zkCollectionKey).toBoolean());
714
+ if (index === -1) {
715
+ throw new Error("NFT not found");
716
+ }
717
+ const nft = nftParams[index];
718
+ const { name } = nft;
719
+ const metadata = Metadata.fromJSON({
720
+ json: JSON.parse(nft.privateMetadata),
721
+ checkRoot: true,
722
+ });
723
+ assert.strictEqual(nftState.metadata.toJSON(), metadata.map.root.toJSON());
724
+ if (nftState.metadata.toJSON() !== metadata.map.root.toJSON()) {
725
+ throw new Error("NFT metadata is not the same as the one in the collection");
726
+ }
727
+ console.time("proved update 1");
728
+ const map1 = metadata.map.clone();
729
+ let { key: key1, value: value1 } = metadata.addTrait({
730
+ key: "New Key 1",
731
+ value: "New Value 1",
732
+ type: "string",
733
+ });
734
+ const signature1 = Signature.create(owner.key, [
735
+ ...NFTState.toFields(nftState),
736
+ key1,
737
+ value1.hash(),
738
+ ]);
739
+ const update1 = await NFTProgram.insertMetadata(nftState, map1, key1, value1.hash(), signature1);
740
+ console.timeEnd("proved update 1");
741
+ assert.strictEqual(update1.auxiliaryOutput.root.toJSON(), metadata.map.root.toJSON());
742
+ console.time("proved update 2");
743
+ const map2 = metadata.map.clone();
744
+ const { key: key2, value: value2 } = metadata.addTrait({
745
+ key: "New Key 2",
746
+ value: "New Value 2",
747
+ type: "string",
748
+ });
749
+ const signature2 = Signature.create(owner.key, [
750
+ ...NFTState.toFields(update1.proof.publicOutput),
751
+ key2,
752
+ value2.hash(),
753
+ ]);
754
+ const update2 = await NFTProgram.insertMetadata(update1.proof.publicOutput, map2, key2, value2.hash(), signature2);
755
+ console.timeEnd("proved update 2");
756
+ assert.strictEqual(update2.auxiliaryOutput.root.toJSON(), metadata.map.root.toJSON());
757
+ console.time("merged proofs");
758
+ const mergedProof = await NFTProgram.merge(nftState, update1.proof, update2.proof);
759
+ const dynamicProof = NFTUpdateProof.fromProof(mergedProof.proof);
760
+ console.timeEnd("merged proofs");
761
+ const tx = await Mina.transaction({
762
+ sender: owner,
763
+ fee: 100_000_000,
764
+ memo: `Update NFT ${name}`.substring(0, 30),
765
+ }, async () => {
766
+ await collectionContract.update(dynamicProof, nftProgramVk);
767
+ });
768
+ await tx.prove();
769
+ const txSigned = tx.sign([owner.key]);
770
+ assert.strictEqual((await sendTx({
771
+ tx: txSigned,
772
+ description: "update",
773
+ }))?.status, expectedTxStatus);
774
+ console.timeEnd("updated NFT");
775
+ nftParams[index].privateMetadata = JSON.stringify(metadata.toJSON(true), null, 2);
776
+ });
777
+ it("should transfer NFT", async () => {
778
+ Memory.info("before transfer");
779
+ console.time("transferred NFT");
780
+ await fetchMinaAccount({ publicKey: owner, force: true });
781
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
782
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
783
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
784
+ const requireTransferApproval = CollectionData.unpack(collectionContract.packedData.get()).requireTransferApproval.toBoolean();
785
+ console.log("requireTransferApproval", requireTransferApproval);
786
+ const to = whitelistedUsers[2];
787
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
788
+ p.collection.equals(zkCollectionKey).toBoolean());
789
+ if (!nft) {
790
+ throw new Error("NFT not found");
791
+ }
792
+ const { name } = nft;
793
+ const tx = await Mina.transaction({
794
+ sender: owner,
795
+ fee: 100_000_000,
796
+ memo: `Transfer NFT ${name}`.substring(0, 30),
797
+ }, async () => {
798
+ if (requireTransferApproval) {
799
+ await collectionContract.approvedTransferBySignature(new TransferParams({
800
+ address: zkNFTKey,
801
+ from: owner,
802
+ to,
803
+ price: UInt64Option.none(),
804
+ context: NFTTransactionContext.empty(),
805
+ }));
806
+ }
807
+ else {
808
+ await collectionContract.transferBySignature(new TransferParams({
809
+ address: zkNFTKey,
810
+ from: owner,
811
+ to,
812
+ price: UInt64Option.none(),
813
+ context: NFTTransactionContext.empty(),
814
+ }));
815
+ }
816
+ });
817
+ await tx.prove();
818
+ assert.strictEqual((await sendTx({
819
+ tx: tx.sign([owner.key]),
820
+ description: "transfer",
821
+ }))?.status, expectedTxStatus);
822
+ console.timeEnd("transferred NFT");
823
+ owner = to;
824
+ });
825
+ it("should pause NFT", async () => {
826
+ Memory.info("before pause");
827
+ console.time("paused NFT");
828
+ await fetchMinaAccount({ publicKey: owner, force: true });
829
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
830
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
831
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
832
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
833
+ p.collection.equals(zkCollectionKey).toBoolean());
834
+ if (!nft) {
835
+ throw new Error("NFT not found");
836
+ }
837
+ const { name } = nft;
838
+ const tx = await Mina.transaction({
839
+ sender: owner,
840
+ fee: 100_000_000,
841
+ memo: `Pause NFT ${name}`.substring(0, 30),
842
+ }, async () => {
843
+ await collectionContract.pauseNFTBySignature(zkNFTKey);
844
+ });
845
+ await tx.prove();
846
+ assert.strictEqual((await sendTx({
847
+ tx: tx.sign([owner.key]),
848
+ description: "pause NFT",
849
+ }))?.status, expectedTxStatus);
850
+ console.timeEnd("paused NFT");
851
+ });
852
+ it("should fail to transfer paused NFT", async () => {
853
+ Memory.info("before transfer");
854
+ console.time("tried to transfer paused NFT");
855
+ await fetchMinaAccount({ publicKey: owner, force: true });
856
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
857
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
858
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
859
+ const requireTransferApproval = CollectionData.unpack(collectionContract.packedData.get()).requireTransferApproval.toBoolean();
860
+ console.log("requireTransferApproval", requireTransferApproval);
861
+ const to = whitelistedUsers[3];
862
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
863
+ p.collection.equals(zkCollectionKey).toBoolean());
864
+ if (!nft) {
865
+ throw new Error("NFT not found");
866
+ }
867
+ const { name } = nft;
868
+ let transferred = true;
869
+ try {
870
+ const tx = await Mina.transaction({
871
+ sender: owner,
872
+ fee: 100_000_000,
873
+ memo: `Transfer NFT ${name}`.substring(0, 30),
874
+ }, async () => {
875
+ if (requireTransferApproval) {
876
+ await collectionContract.approvedTransferBySignature(new TransferParams({
877
+ address: zkNFTKey,
878
+ from: owner,
879
+ to,
880
+ price: UInt64Option.none(),
881
+ context: NFTTransactionContext.empty(),
882
+ }));
883
+ }
884
+ else {
885
+ await collectionContract.transferBySignature(new TransferParams({
886
+ address: zkNFTKey,
887
+ from: owner,
888
+ to,
889
+ price: UInt64Option.none(),
890
+ context: NFTTransactionContext.empty(),
891
+ }));
892
+ }
893
+ });
894
+ await tx.prove();
895
+ await sendTx({
896
+ tx: tx.sign([owner.key]),
897
+ description: "tried to transfer paused NFT",
898
+ });
899
+ }
900
+ catch (e) {
901
+ console.log("error during attempt to transfer paused NFT:", e?.message ?? "");
902
+ transferred = false;
903
+ }
904
+ assert.strictEqual(transferred, false);
905
+ console.timeEnd("tried to transfer paused NFT");
906
+ });
907
+ it("should resume NFT", async () => {
908
+ Memory.info("before resume");
909
+ console.time("resumed NFT");
910
+ await fetchMinaAccount({ publicKey: owner, force: true });
911
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
912
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
913
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
914
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
915
+ p.collection.equals(zkCollectionKey).toBoolean());
916
+ if (!nft) {
917
+ throw new Error("NFT not found");
918
+ }
919
+ const { name } = nft;
920
+ const tx = await Mina.transaction({
921
+ sender: owner,
922
+ fee: 100_000_000,
923
+ memo: `Resume NFT ${name}`.substring(0, 30),
924
+ }, async () => {
925
+ await collectionContract.resumeNFT(zkNFTKey);
926
+ });
927
+ await tx.prove();
928
+ assert.strictEqual((await sendTx({
929
+ tx: tx.sign([owner.key]),
930
+ description: "resume NFT",
931
+ }))?.status, expectedTxStatus);
932
+ console.timeEnd("resumed NFT");
933
+ });
934
+ it("should transfer NFT", async () => {
935
+ Memory.info("before transfer");
936
+ console.time("transferred NFT");
937
+ await fetchMinaAccount({ publicKey: owner, force: true });
938
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
939
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
940
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
941
+ const requireTransferApproval = CollectionData.unpack(collectionContract.packedData.get()).requireTransferApproval.toBoolean();
942
+ console.log("requireTransferApproval", requireTransferApproval);
943
+ const to = whitelistedUsers[3];
944
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
945
+ p.collection.equals(zkCollectionKey).toBoolean());
946
+ if (!nft) {
947
+ throw new Error("NFT not found");
948
+ }
949
+ const { name } = nft;
950
+ const tx = await Mina.transaction({
951
+ sender: owner,
952
+ fee: 100_000_000,
953
+ memo: `Transfer NFT ${name}`.substring(0, 30),
954
+ }, async () => {
955
+ if (requireTransferApproval) {
956
+ await collectionContract.approvedTransferBySignature(new TransferParams({
957
+ address: zkNFTKey,
958
+ from: owner,
959
+ to,
960
+ price: UInt64Option.none(),
961
+ context: NFTTransactionContext.empty(),
962
+ }));
963
+ }
964
+ else {
965
+ await collectionContract.transferBySignature(new TransferParams({
966
+ address: zkNFTKey,
967
+ from: owner,
968
+ to,
969
+ price: UInt64Option.none(),
970
+ context: NFTTransactionContext.empty(),
971
+ }));
972
+ }
973
+ });
974
+ await tx.prove();
975
+ assert.strictEqual((await sendTx({
976
+ tx: tx.sign([owner.key]),
977
+ description: "transfer",
978
+ }))?.status, expectedTxStatus);
979
+ console.timeEnd("transferred NFT");
980
+ owner = to;
981
+ });
982
+ it("should pause Collection", async () => {
983
+ Memory.info("before pause");
984
+ console.time("paused Collection");
985
+ await fetchMinaAccount({ publicKey: creator, force: true });
986
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
987
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
988
+ const tx = await Mina.transaction({
989
+ sender: creator,
990
+ fee: 100_000_000,
991
+ memo: `Pause Collection ${collectionName}`.substring(0, 30),
992
+ }, async () => {
993
+ await collectionContract.pause();
994
+ });
995
+ await tx.prove();
996
+ assert.strictEqual((await sendTx({
997
+ tx: tx.sign([creator.key]),
998
+ description: "pause Collection",
999
+ }))?.status, expectedTxStatus);
1000
+ console.timeEnd("paused Collection");
1001
+ });
1002
+ it("should fail to transfer NFT on paused Collection", async () => {
1003
+ Memory.info("before transfer");
1004
+ console.time("tried to transfer NFT on paused Collection");
1005
+ await fetchMinaAccount({ publicKey: owner, force: true });
1006
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
1007
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
1008
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
1009
+ const requireTransferApproval = CollectionData.unpack(collectionContract.packedData.get()).requireTransferApproval.toBoolean();
1010
+ console.log("requireTransferApproval", requireTransferApproval);
1011
+ const to = whitelistedUsers[4];
1012
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
1013
+ p.collection.equals(zkCollectionKey).toBoolean());
1014
+ if (!nft) {
1015
+ throw new Error("NFT not found");
1016
+ }
1017
+ const { name } = nft;
1018
+ let transferred = true;
1019
+ try {
1020
+ const tx = await Mina.transaction({
1021
+ sender: owner,
1022
+ fee: 100_000_000,
1023
+ memo: `Transfer NFT ${name}`.substring(0, 30),
1024
+ }, async () => {
1025
+ if (requireTransferApproval) {
1026
+ await collectionContract.approvedTransferBySignature(new TransferParams({
1027
+ address: zkNFTKey,
1028
+ from: owner,
1029
+ to,
1030
+ price: UInt64Option.none(),
1031
+ context: NFTTransactionContext.empty(),
1032
+ }));
1033
+ }
1034
+ else {
1035
+ await collectionContract.transferBySignature(new TransferParams({
1036
+ address: zkNFTKey,
1037
+ from: owner,
1038
+ to,
1039
+ price: UInt64Option.none(),
1040
+ context: NFTTransactionContext.empty(),
1041
+ }));
1042
+ }
1043
+ });
1044
+ await tx.prove();
1045
+ await sendTx({
1046
+ tx: tx.sign([owner.key]),
1047
+ description: "tried to transfer NFT on paused Collection",
1048
+ });
1049
+ }
1050
+ catch (e) {
1051
+ console.log("error during attempt to transfer NFT on paused Collection:", e?.message ?? "");
1052
+ transferred = false;
1053
+ }
1054
+ assert.strictEqual(transferred, false);
1055
+ console.timeEnd("tried to transfer NFT on paused Collection");
1056
+ });
1057
+ it("should resume Collection", async () => {
1058
+ Memory.info("before resume");
1059
+ console.time("resumed Collection");
1060
+ await fetchMinaAccount({ publicKey: creator, force: true });
1061
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
1062
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
1063
+ const tx = await Mina.transaction({
1064
+ sender: creator,
1065
+ fee: 100_000_000,
1066
+ memo: `Resume Collection ${collectionName}`.substring(0, 30),
1067
+ }, async () => {
1068
+ await collectionContract.resume();
1069
+ });
1070
+ await tx.prove();
1071
+ assert.strictEqual((await sendTx({
1072
+ tx: tx.sign([creator.key]),
1073
+ description: "resume Collection",
1074
+ }))?.status, expectedTxStatus);
1075
+ console.timeEnd("resumed Collection");
1076
+ });
1077
+ it("should set a UpgradeAuthority database", { skip: !useAdvancedAdmin }, async () => {
1078
+ console.time("Set UpgradeAuthority");
1079
+ await fetchMinaAccount({ publicKey: admin, force: true });
1080
+ await fetchMinaAccount({ publicKey: upgradeAuthority, force: true });
1081
+ const events = await upgradeAuthorityContract.fetchEvents();
1082
+ const lastEvent = events
1083
+ .filter((e) => e.type === "validatorsList")
1084
+ .reverse()[0];
1085
+ if (!lastEvent) {
1086
+ throw new Error("No validatorsList event found");
1087
+ }
1088
+ const eventData = lastEvent.event.data;
1089
+ const storage = eventData.storage;
1090
+ console.log("storage", storage.toString());
1091
+ const { map, data } = await checkValidatorsList({
1092
+ storage,
1093
+ });
1094
+ const validatorState = new ValidatorsState({
1095
+ chainId: ChainId[chain === "devnet" ? "mina:devnet" : "zeko:devnet"],
1096
+ root: map.root,
1097
+ count: UInt32.from(data.validatorsCount),
1098
+ });
1099
+ const key1 = new VerificationKeyUpgradeData({
1100
+ address: collectionContract.address,
1101
+ tokenId: collectionContract.tokenId,
1102
+ previousVerificationKeyHash: collectionVk.hash,
1103
+ newVerificationKeyHash: collectionVk.hash,
1104
+ });
1105
+ const key2 = new VerificationKeyUpgradeData({
1106
+ address: nftContract.address,
1107
+ tokenId: nftContract.tokenId,
1108
+ previousVerificationKeyHash: nftContractVk.hash,
1109
+ newVerificationKeyHash: nftContractVk.hash,
1110
+ });
1111
+ const key3 = new VerificationKeyUpgradeData({
1112
+ address: adminContract.address,
1113
+ tokenId: adminContract.tokenId,
1114
+ previousVerificationKeyHash: useAdvancedAdmin
1115
+ ? AdvancedAdminVk.hash
1116
+ : adminVk.hash,
1117
+ newVerificationKeyHash: useAdvancedAdmin
1118
+ ? AdvancedAdminVk.hash
1119
+ : adminVk.hash,
1120
+ });
1121
+ const db = new UpgradeAuthorityDatabase();
1122
+ db.set(key1.hash(), key1.newVerificationKeyHash);
1123
+ db.set(key2.hash(), key2.newVerificationKeyHash);
1124
+ db.set(key3.hash(), key3.newVerificationKeyHash);
1125
+ const ipfs = await pinJSON({
1126
+ data: { "indexed-map": { map: serializeIndexedMap(db) } },
1127
+ name: "upgrade-authority-database",
1128
+ });
1129
+ if (!ipfs) {
1130
+ throw new Error("UpgradeAuthority database IPFS hash is undefined");
1131
+ }
1132
+ const decision = new ValidatorsDecision({
1133
+ message: fieldFromString("Set UpgradeAuthority"),
1134
+ decisionType: ValidatorDecisionType["updateDatabase"],
1135
+ contractAddress: upgradeAuthority,
1136
+ chainId: ChainId[chain === "devnet" ? "mina:devnet" : "zeko:devnet"],
1137
+ validators: validatorState,
1138
+ upgradeDatabase: new UpgradeDatabaseState({
1139
+ root: db.root,
1140
+ storage: Storage.fromString(ipfs),
1141
+ nextUpgradeAuthority: PublicKeyOption.none(),
1142
+ version: UInt32.from(1),
1143
+ validFrom: UInt32.zero,
1144
+ }),
1145
+ updateValidatorsList: ValidatorsState.empty(),
1146
+ expiry: UInt32.MAXINT(),
1147
+ });
1148
+ let state = ValidatorsDecisionState.startVoting(decision);
1149
+ const proofs = [];
1150
+ console.log("voting...");
1151
+ console.time("voted");
1152
+ const voted = new ValidatorsList();
1153
+ const startProof = await ValidatorsVoting.startVoting(state, decision);
1154
+ proofs.push(startProof.proof);
1155
+ for (let i = 0; i < validators.length; i++) {
1156
+ const signature = Signature.create(validators[i].key, ValidatorsDecision.toFields(decision));
1157
+ const nullifier = Nullifier.fromJSON(decision.createJsonNullifier({
1158
+ network: "testnet",
1159
+ privateKey: validators[i].key,
1160
+ }));
1161
+ // state = ValidatorsDecisionState.vote(
1162
+ // state,
1163
+ // decision,
1164
+ // validators[i],
1165
+ // map.clone(),
1166
+ // signature
1167
+ // );
1168
+ /*
1169
+ state: ValidatorsDecisionState,
1170
+ decision: ValidatorsDecision,
1171
+ nullifier: Nullifier,
1172
+ validatorsList: ValidatorsList,
1173
+ votedList: ValidatorsList,
1174
+ yes: Bool,
1175
+ no: Bool,
1176
+ abstain: Bool,
1177
+ signature: Signature
1178
+ */
1179
+ const step = await ValidatorsVoting.vote(state, decision, nullifier, map.clone(), voted.clone(), Bool(true), Bool(false), Bool(false), signature);
1180
+ voted.insert(nullifier.key(), Field(1));
1181
+ state = step.proof.publicOutput;
1182
+ proofs.push(step.proof);
1183
+ }
1184
+ let proof = proofs[0];
1185
+ console.timeEnd("voted");
1186
+ console.log("merging vote proofs...");
1187
+ console.time("merged vote proofs");
1188
+ for (let i = 1; i < proofs.length; i++) {
1189
+ const mergedProof = await ValidatorsVoting.merge(proofs[i - 1].publicInput, proofs[i - 1], proofs[i]);
1190
+ proof = mergedProof.proof;
1191
+ const ok = await verify(mergedProof.proof, validatorsVotingVk);
1192
+ if (!ok) {
1193
+ throw new Error("calculateValidatorsProof: Proof is not valid");
1194
+ }
1195
+ }
1196
+ const dynamicProof = ValidatorsVotingProof.fromProof(proof);
1197
+ console.timeEnd("merged vote proofs");
1198
+ await fetchMinaAccount({ publicKey: admin, force: true });
1199
+ await fetchMinaAccount({ publicKey: upgradeAuthority, force: true });
1200
+ const tx = await Mina.transaction({
1201
+ sender: admin,
1202
+ fee: 100_000_000,
1203
+ memo: `Set UpgradeAuthority`,
1204
+ }, async () => {
1205
+ await upgradeAuthorityContract.updateDatabase(dynamicProof, validatorsVotingVk, validatorState);
1206
+ });
1207
+ await tx.prove();
1208
+ assert.strictEqual((await sendTx({
1209
+ tx: tx.sign([admin.key]),
1210
+ description: "Set UpgradeAuthority",
1211
+ }))?.status, expectedTxStatus);
1212
+ console.timeEnd("Set UpgradeAuthority");
1213
+ });
1214
+ it("should upgrade NFT verification key", async () => {
1215
+ Memory.info("before NFT upgrade");
1216
+ console.time("upgraded NFT");
1217
+ await fetchMinaAccount({ publicKey: owner, force: true });
1218
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
1219
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
1220
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
1221
+ await fetchMinaAccount({ publicKey: upgradeAuthority, force: true });
1222
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
1223
+ p.collection.equals(zkCollectionKey).toBoolean());
1224
+ if (!nft) {
1225
+ throw new Error("NFT not found");
1226
+ }
1227
+ const { name } = nft;
1228
+ const tx = await Mina.transaction({
1229
+ sender: owner,
1230
+ fee: 100_000_000,
1231
+ memo: `Upgrade NFT ${name}`.substring(0, 30),
1232
+ }, async () => {
1233
+ await collectionContract.upgradeNFTVerificationKeyBySignature(nft.address, nftContractVk);
1234
+ });
1235
+ await tx.prove();
1236
+ assert.strictEqual((await sendTx({
1237
+ tx: tx.sign([owner.key, creator.key]),
1238
+ description: "upgrade NFT vk",
1239
+ }))?.status, expectedTxStatus);
1240
+ console.timeEnd("upgraded NFT");
1241
+ });
1242
+ it("should upgrade Collection verification key", async () => {
1243
+ Memory.info("before Collection upgrade");
1244
+ console.time("upgraded Collection");
1245
+ await fetchMinaAccount({ publicKey: creator, force: true });
1246
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
1247
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
1248
+ await fetchMinaAccount({ publicKey: upgradeAuthority, force: true });
1249
+ const tx = await Mina.transaction({
1250
+ sender: creator,
1251
+ fee: 100_000_000,
1252
+ memo: `Upgrade Collection ${collectionName}`.substring(0, 30),
1253
+ }, async () => {
1254
+ await collectionContract.upgradeVerificationKey(collectionVk);
1255
+ });
1256
+ await tx.prove();
1257
+ assert.strictEqual((await sendTx({
1258
+ tx: tx.sign([creator.key]),
1259
+ description: "upgrade Collection vk",
1260
+ }))?.status, expectedTxStatus);
1261
+ console.timeEnd("upgraded Collection");
1262
+ });
1263
+ it("should upgrade AdminContract verification key", async () => {
1264
+ Memory.info("before AdminContract upgrade");
1265
+ console.time("upgraded AdminContract");
1266
+ await fetchMinaAccount({ publicKey: creator, force: true });
1267
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
1268
+ await fetchMinaAccount({ publicKey: upgradeAuthority, force: true });
1269
+ console.log("useAdvancedAdmin", useAdvancedAdmin, useAdvancedAdmin ? AdvancedAdminVk.hash.toJSON() : adminVk.hash.toJSON());
1270
+ const tx = await Mina.transaction({
1271
+ sender: creator,
1272
+ fee: 100_000_000,
1273
+ memo: `Upgrade AdminContract`.substring(0, 30),
1274
+ }, async () => {
1275
+ await adminContract.upgradeVerificationKey(useAdvancedAdmin ? AdvancedAdminVk : adminVk);
1276
+ });
1277
+ await tx.prove();
1278
+ assert.strictEqual((await sendTx({
1279
+ tx: tx.sign([creator.key]),
1280
+ description: "upgrade AdminContract vk",
1281
+ }))?.status, expectedTxStatus);
1282
+ console.timeEnd("upgraded AdminContract");
1283
+ });
1284
+ it("should transfer NFT", async () => {
1285
+ Memory.info("before transfer");
1286
+ console.time("transferred NFT");
1287
+ await fetchMinaAccount({ publicKey: owner, force: true });
1288
+ await fetchMinaAccount({ publicKey: zkCollectionKey, force: true });
1289
+ await fetchMinaAccount({ publicKey: zkAdminKey, force: true });
1290
+ await fetchMinaAccount({ publicKey: zkNFTKey, tokenId, force: true });
1291
+ const requireTransferApproval = CollectionData.unpack(collectionContract.packedData.get()).requireTransferApproval.toBoolean();
1292
+ console.log("requireTransferApproval", requireTransferApproval);
1293
+ const to = whitelistedUsers[4];
1294
+ const nft = nftParams.find((p) => p.address.equals(zkNFTKey).toBoolean() &&
1295
+ p.collection.equals(zkCollectionKey).toBoolean());
1296
+ if (!nft) {
1297
+ throw new Error("NFT not found");
1298
+ }
1299
+ const { name } = nft;
1300
+ const tx = await Mina.transaction({
1301
+ sender: owner,
1302
+ fee: 100_000_000,
1303
+ memo: `Transfer NFT ${name}`.substring(0, 30),
1304
+ }, async () => {
1305
+ if (requireTransferApproval) {
1306
+ await collectionContract.approvedTransferBySignature(new TransferParams({
1307
+ address: zkNFTKey,
1308
+ from: owner,
1309
+ to,
1310
+ price: UInt64Option.none(),
1311
+ context: NFTTransactionContext.empty(),
1312
+ }));
1313
+ }
1314
+ else {
1315
+ await collectionContract.transferBySignature(new TransferParams({
1316
+ address: zkNFTKey,
1317
+ from: owner,
1318
+ to,
1319
+ price: UInt64Option.none(),
1320
+ context: NFTTransactionContext.empty(),
1321
+ }));
1322
+ }
1323
+ });
1324
+ await tx.prove();
1325
+ assert.strictEqual((await sendTx({
1326
+ tx: tx.sign([owner.key]),
1327
+ description: "transfer",
1328
+ }))?.status, expectedTxStatus);
1329
+ console.timeEnd("transferred NFT");
1330
+ owner = to;
1331
+ });
1332
+ });
1333
+ //# sourceMappingURL=contract.test.js.map