@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.
- package/README.md +1400 -0
- package/dist/node/admin/advanced.d.ts +469 -0
- package/dist/node/admin/advanced.js +525 -0
- package/dist/node/admin/advanced.js.map +1 -0
- package/dist/node/admin/index.d.ts +1 -0
- package/dist/node/admin/index.js +2 -0
- package/dist/node/admin/index.js.map +1 -0
- package/dist/node/contracts/admin.d.ts +140 -0
- package/dist/node/contracts/admin.js +336 -0
- package/dist/node/contracts/admin.js.map +1 -0
- package/dist/node/contracts/collection.d.ts +551 -0
- package/dist/node/contracts/collection.js +1049 -0
- package/dist/node/contracts/collection.js.map +1 -0
- package/dist/node/contracts/index.d.ts +3 -0
- package/dist/node/contracts/index.js +4 -0
- package/dist/node/contracts/index.js.map +1 -0
- package/dist/node/contracts/nft.d.ts +76 -0
- package/dist/node/contracts/nft.js +329 -0
- package/dist/node/contracts/nft.js.map +1 -0
- package/dist/node/contracts.d.ts +709 -0
- package/dist/node/contracts.js +61 -0
- package/dist/node/contracts.js.map +1 -0
- package/dist/node/index.cjs +5032 -0
- package/dist/node/index.d.ts +8 -0
- package/dist/node/index.js +9 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/interfaces/admin.d.ts +102 -0
- package/dist/node/interfaces/admin.js +2 -0
- package/dist/node/interfaces/admin.js.map +1 -0
- package/dist/node/interfaces/approval.d.ts +57 -0
- package/dist/node/interfaces/approval.js +62 -0
- package/dist/node/interfaces/approval.js.map +1 -0
- package/dist/node/interfaces/collection.d.ts +57 -0
- package/dist/node/interfaces/collection.js +2 -0
- package/dist/node/interfaces/collection.js.map +1 -0
- package/dist/node/interfaces/encoding.d.ts +24 -0
- package/dist/node/interfaces/encoding.js +32 -0
- package/dist/node/interfaces/encoding.js.map +1 -0
- package/dist/node/interfaces/events.d.ts +833 -0
- package/dist/node/interfaces/events.js +106 -0
- package/dist/node/interfaces/events.js.map +1 -0
- package/dist/node/interfaces/index.d.ts +10 -0
- package/dist/node/interfaces/index.js +11 -0
- package/dist/node/interfaces/index.js.map +1 -0
- package/dist/node/interfaces/ownable.d.ts +94 -0
- package/dist/node/interfaces/ownable.js +12 -0
- package/dist/node/interfaces/ownable.js.map +1 -0
- package/dist/node/interfaces/owner.d.ts +61 -0
- package/dist/node/interfaces/owner.js +101 -0
- package/dist/node/interfaces/owner.js.map +1 -0
- package/dist/node/interfaces/pausable.d.ts +74 -0
- package/dist/node/interfaces/pausable.js +14 -0
- package/dist/node/interfaces/pausable.js.map +1 -0
- package/dist/node/interfaces/types.d.ts +2297 -0
- package/dist/node/interfaces/types.js +507 -0
- package/dist/node/interfaces/types.js.map +1 -0
- package/dist/node/interfaces/update.d.ts +53 -0
- package/dist/node/interfaces/update.js +58 -0
- package/dist/node/interfaces/update.js.map +1 -0
- package/dist/node/marketplace/auction.d.ts +775 -0
- package/dist/node/marketplace/auction.js +430 -0
- package/dist/node/marketplace/auction.js.map +1 -0
- package/dist/node/marketplace/bid.d.ts +254 -0
- package/dist/node/marketplace/bid.js +260 -0
- package/dist/node/marketplace/bid.js.map +1 -0
- package/dist/node/marketplace/index.d.ts +5 -0
- package/dist/node/marketplace/index.js +6 -0
- package/dist/node/marketplace/index.js.map +1 -0
- package/dist/node/marketplace/nft-shares.d.ts +1083 -0
- package/dist/node/marketplace/nft-shares.js +398 -0
- package/dist/node/marketplace/nft-shares.js.map +1 -0
- package/dist/node/marketplace/offer.d.ts +192 -0
- package/dist/node/marketplace/offer.js +132 -0
- package/dist/node/marketplace/offer.js.map +1 -0
- package/dist/node/marketplace/types.d.ts +374 -0
- package/dist/node/marketplace/types.js +33 -0
- package/dist/node/marketplace/types.js.map +1 -0
- package/dist/node/metadata/index.d.ts +3 -0
- package/dist/node/metadata/index.js +4 -0
- package/dist/node/metadata/index.js.map +1 -0
- package/dist/node/metadata/metadata.d.ts +337 -0
- package/dist/node/metadata/metadata.js +439 -0
- package/dist/node/metadata/metadata.js.map +1 -0
- package/dist/node/metadata/text.d.ts +44 -0
- package/dist/node/metadata/text.js +42 -0
- package/dist/node/metadata/text.js.map +1 -0
- package/dist/node/metadata/tree.d.ts +75 -0
- package/dist/node/metadata/tree.js +85 -0
- package/dist/node/metadata/tree.js.map +1 -0
- package/dist/node/vk.d.ts +42 -0
- package/dist/node/vk.js +45 -0
- package/dist/node/vk.js.map +1 -0
- package/dist/node/zkprogram-example/game.d.ts +76 -0
- package/dist/node/zkprogram-example/game.js +108 -0
- package/dist/node/zkprogram-example/game.js.map +1 -0
- package/dist/node/zkprogram-example/index.d.ts +2 -0
- package/dist/node/zkprogram-example/index.js +3 -0
- package/dist/node/zkprogram-example/index.js.map +1 -0
- package/dist/node/zkprogram-example/update.d.ts +76 -0
- package/dist/node/zkprogram-example/update.js +85 -0
- package/dist/node/zkprogram-example/update.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/tsconfig.web.tsbuildinfo +1 -0
- package/dist/web/admin/advanced.d.ts +469 -0
- package/dist/web/admin/advanced.js +525 -0
- package/dist/web/admin/advanced.js.map +1 -0
- package/dist/web/admin/index.d.ts +1 -0
- package/dist/web/admin/index.js +2 -0
- package/dist/web/admin/index.js.map +1 -0
- package/dist/web/contracts/admin.d.ts +140 -0
- package/dist/web/contracts/admin.js +336 -0
- package/dist/web/contracts/admin.js.map +1 -0
- package/dist/web/contracts/collection.d.ts +551 -0
- package/dist/web/contracts/collection.js +1049 -0
- package/dist/web/contracts/collection.js.map +1 -0
- package/dist/web/contracts/index.d.ts +3 -0
- package/dist/web/contracts/index.js +4 -0
- package/dist/web/contracts/index.js.map +1 -0
- package/dist/web/contracts/nft.d.ts +76 -0
- package/dist/web/contracts/nft.js +329 -0
- package/dist/web/contracts/nft.js.map +1 -0
- package/dist/web/contracts.d.ts +709 -0
- package/dist/web/contracts.js +61 -0
- package/dist/web/contracts.js.map +1 -0
- package/dist/web/index.d.ts +8 -0
- package/dist/web/index.js +9 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/interfaces/admin.d.ts +102 -0
- package/dist/web/interfaces/admin.js +2 -0
- package/dist/web/interfaces/admin.js.map +1 -0
- package/dist/web/interfaces/approval.d.ts +57 -0
- package/dist/web/interfaces/approval.js +62 -0
- package/dist/web/interfaces/approval.js.map +1 -0
- package/dist/web/interfaces/collection.d.ts +57 -0
- package/dist/web/interfaces/collection.js +2 -0
- package/dist/web/interfaces/collection.js.map +1 -0
- package/dist/web/interfaces/encoding.d.ts +24 -0
- package/dist/web/interfaces/encoding.js +32 -0
- package/dist/web/interfaces/encoding.js.map +1 -0
- package/dist/web/interfaces/events.d.ts +833 -0
- package/dist/web/interfaces/events.js +106 -0
- package/dist/web/interfaces/events.js.map +1 -0
- package/dist/web/interfaces/index.d.ts +10 -0
- package/dist/web/interfaces/index.js +11 -0
- package/dist/web/interfaces/index.js.map +1 -0
- package/dist/web/interfaces/ownable.d.ts +94 -0
- package/dist/web/interfaces/ownable.js +12 -0
- package/dist/web/interfaces/ownable.js.map +1 -0
- package/dist/web/interfaces/owner.d.ts +61 -0
- package/dist/web/interfaces/owner.js +101 -0
- package/dist/web/interfaces/owner.js.map +1 -0
- package/dist/web/interfaces/pausable.d.ts +74 -0
- package/dist/web/interfaces/pausable.js +14 -0
- package/dist/web/interfaces/pausable.js.map +1 -0
- package/dist/web/interfaces/types.d.ts +2297 -0
- package/dist/web/interfaces/types.js +507 -0
- package/dist/web/interfaces/types.js.map +1 -0
- package/dist/web/interfaces/update.d.ts +53 -0
- package/dist/web/interfaces/update.js +58 -0
- package/dist/web/interfaces/update.js.map +1 -0
- package/dist/web/marketplace/auction.d.ts +775 -0
- package/dist/web/marketplace/auction.js +430 -0
- package/dist/web/marketplace/auction.js.map +1 -0
- package/dist/web/marketplace/bid.d.ts +254 -0
- package/dist/web/marketplace/bid.js +260 -0
- package/dist/web/marketplace/bid.js.map +1 -0
- package/dist/web/marketplace/index.d.ts +5 -0
- package/dist/web/marketplace/index.js +6 -0
- package/dist/web/marketplace/index.js.map +1 -0
- package/dist/web/marketplace/nft-shares.d.ts +1083 -0
- package/dist/web/marketplace/nft-shares.js +398 -0
- package/dist/web/marketplace/nft-shares.js.map +1 -0
- package/dist/web/marketplace/offer.d.ts +192 -0
- package/dist/web/marketplace/offer.js +132 -0
- package/dist/web/marketplace/offer.js.map +1 -0
- package/dist/web/marketplace/types.d.ts +374 -0
- package/dist/web/marketplace/types.js +33 -0
- package/dist/web/marketplace/types.js.map +1 -0
- package/dist/web/metadata/index.d.ts +3 -0
- package/dist/web/metadata/index.js +4 -0
- package/dist/web/metadata/index.js.map +1 -0
- package/dist/web/metadata/metadata.d.ts +337 -0
- package/dist/web/metadata/metadata.js +439 -0
- package/dist/web/metadata/metadata.js.map +1 -0
- package/dist/web/metadata/text.d.ts +44 -0
- package/dist/web/metadata/text.js +42 -0
- package/dist/web/metadata/text.js.map +1 -0
- package/dist/web/metadata/tree.d.ts +75 -0
- package/dist/web/metadata/tree.js +85 -0
- package/dist/web/metadata/tree.js.map +1 -0
- package/dist/web/vk.d.ts +42 -0
- package/dist/web/vk.js +45 -0
- package/dist/web/vk.js.map +1 -0
- package/dist/web/zkprogram-example/game.d.ts +76 -0
- package/dist/web/zkprogram-example/game.js +108 -0
- package/dist/web/zkprogram-example/game.js.map +1 -0
- package/dist/web/zkprogram-example/index.d.ts +2 -0
- package/dist/web/zkprogram-example/index.js +3 -0
- package/dist/web/zkprogram-example/index.js.map +1 -0
- package/dist/web/zkprogram-example/update.d.ts +76 -0
- package/dist/web/zkprogram-example/update.js +85 -0
- package/dist/web/zkprogram-example/update.js.map +1 -0
- package/package.json +65 -0
- package/src/admin/advanced.ts +601 -0
- package/src/admin/index.ts +1 -0
- package/src/contracts/admin.ts +301 -0
- package/src/contracts/collection.ts +1172 -0
- package/src/contracts/index.ts +3 -0
- package/src/contracts/nft.ts +344 -0
- package/src/contracts.ts +107 -0
- package/src/index.ts +8 -0
- package/src/interfaces/admin.ts +127 -0
- package/src/interfaces/approval.ts +99 -0
- package/src/interfaces/collection.ts +68 -0
- package/src/interfaces/encoding.ts +32 -0
- package/src/interfaces/events.ts +115 -0
- package/src/interfaces/index.ts +10 -0
- package/src/interfaces/ownable.ts +32 -0
- package/src/interfaces/owner.ts +143 -0
- package/src/interfaces/pausable.ts +41 -0
- package/src/interfaces/types.ts +623 -0
- package/src/interfaces/update.ts +104 -0
- package/src/marketplace/auction.ts +527 -0
- package/src/marketplace/bid.ts +294 -0
- package/src/marketplace/index.ts +5 -0
- package/src/marketplace/nft-shares.ts +388 -0
- package/src/marketplace/offer.ts +153 -0
- package/src/marketplace/types.ts +33 -0
- package/src/metadata/index.ts +3 -0
- package/src/metadata/metadata.ts +603 -0
- package/src/metadata/text.ts +60 -0
- package/src/metadata/tree.ts +128 -0
- package/src/vk.ts +64 -0
- package/src/zkprogram-example/game.ts +136 -0
- package/src/zkprogram-example/index.ts +2 -0
- 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
|
+
}
|