UNPKG

@silvana-one/nft

Version:
437 lines 17.1 kB
import { __decorate, __metadata } from "tslib"; import { Bool, method, Permissions, PublicKey, SmartContract, State, state, VerificationKey, UInt64, Field, AccountUpdate, UInt32, UInt8, Struct, assert, } from "o1js"; import { MintRequest, NFTState, MintParamsOption, PauseEvent, OwnershipChangeEvent, TransferEvent, } from "../interfaces/index.js"; export { NFTAdmin, NFTAdminAllowFlags }; /** * Contains flags for the admin contract related to the NFT collection permissions. */ class NFTAdminAllowFlags extends Struct({ allowChangeRoyalty: Bool, allowChangeTransferFee: Bool, allowPauseCollection: Bool, }) { /** * Packs the NFTAdminAllowFlags into a UInt8 representation for efficient storage. * @returns The packed UInt8 instance. */ pack() { return UInt8.from(Field.fromBits([ this.allowChangeRoyalty, this.allowChangeTransferFee, this.allowPauseCollection, ])); } /** * Unpacks a UInt8 instance into a NFTAdminAllowFlags instance. * @param packed The packed UInt8 instance. * @returns A new NFTAdminAllowFlags instance. */ static unpack(packed) { const bits = packed.value.toBits(3); const allowChangeRoyalty = bits[0]; const allowChangeTransferFee = bits[1]; const allowPauseCollection = bits[2]; return new NFTAdminAllowFlags({ allowChangeRoyalty, allowChangeTransferFee, allowPauseCollection, }); } } /** * The **NFTAdmin** contract serves as the foundational administrative layer for NFT collections on the Mina Protocol. * It provides essential functionalities such as contract upgrades, pausing and resuming operations, and ownership management. * This contract can be extended by custom admin contracts to implement specific administrative logic, * ensuring flexibility while maintaining a standardized interface. */ class NFTAdmin extends SmartContract { constructor() { super(...arguments); /** * The public key of the contract's administrator. * This account has the authority to perform administrative actions such as pausing the contract or upgrading the verification key. */ this.admin = State(); /** * The public key of the contract's pending administrator. */ this.pendingAdmin = State(); /** * A boolean flag indicating whether the contract is currently paused. * When `true`, certain operations are disabled. */ this.isPaused = State(); /** * A boolean flag indicating whether the contract has the ability to be paused. * This allows for disabling the pause functionality if desired. */ this.canBePaused = State(); /** * A boolean flags indicating whether the collection is allowed to change the royalty fee, transfer fee and pause the collection. */ this.flags = State(); /** * Contract events emitted during various operations. */ this.events = { /** Emitted when the verification key is upgraded. */ upgradeVerificationKey: Field, /** Emitted when the contract is paused. */ pause: PauseEvent, /** Emitted when the contract is resumed. */ resume: PauseEvent, /** Emitted when ownership of the contract changes. */ ownershipTransfer: OwnershipChangeEvent, /** Emitted when ownership of the contract is accepted. */ ownershipAccepted: OwnershipChangeEvent, }; } /** * Deploys the contract with initial settings. * @param props - Deployment properties including admin, upgradeAuthority, uri, canPause, and isPaused. */ async deploy(props) { await super.deploy(props); const isPaused = props.isPaused ?? Bool(false); const canBePaused = props.canBePaused ?? Bool(true); assert(isPaused.equals(Bool(false)).or(canBePaused.equals(Bool(true))), "Cannot deploy paused contract that cannot be resumed"); this.admin.set(props.admin); this.pendingAdmin.set(PublicKey.empty()); this.isPaused.set(isPaused); this.canBePaused.set(canBePaused); this.flags.set(new NFTAdminAllowFlags({ allowChangeRoyalty: props.allowChangeRoyalty ?? Bool(false), allowChangeTransferFee: props.allowChangeTransferFee ?? Bool(false), allowPauseCollection: props.allowPauseCollection ?? Bool(true), }).pack()); this.account.zkappUri.set(props.uri); this.account.permissions.set({ ...Permissions.default(), // Allow the upgrade authority to set the verification key // even when there is no protocol upgrade setVerificationKey: Permissions.VerificationKey.proofDuringCurrentVersion(), setPermissions: Permissions.impossible(), access: Permissions.proof(), send: Permissions.proof(), setZkappUri: Permissions.proof(), setTokenSymbol: Permissions.proof(), }); } /** * Ensures that the transaction is authorized by the contract owner. * @returns A signed `AccountUpdate` from the admin. */ async ensureOwnerSignature() { const admin = this.admin.getAndRequireEquals(); const adminUpdate = AccountUpdate.createSigned(admin); adminUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change return adminUpdate; } /** * Upgrades the contract's verification key after validating with the upgrade authority. * @param vk - The new verification key to upgrade to. */ async upgradeVerificationKey(vk) { await this.ensureOwnerSignature(); // Set the new verification key this.account.verificationKey.set(vk); // Emit the upgrade event this.emitEvent("upgradeVerificationKey", vk.hash); } /** * Determines whether minting is allowed for the given request. * Returns mint parameters if allowed, or none if not allowed. * @param mintRequest - The minting request details. * @returns A `MintParamsOption` indicating if minting is permitted. */ async canMint(mintRequest) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); // Only the creator can mint by default return MintParamsOption.none(); } /** * Checks whether the NFT state can be updated. * Typically returns true if the contract is not paused. * @param input - The current state of the NFT. * @param output - The desired new state of the NFT. * @returns A `Bool` indicating whether the update is allowed. */ async canUpdate(input, output) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); return Bool(true); } /** * Determines whether a transfer between the specified addresses is permitted. * @param transferEvent - The transfer event details. * @returns A `Bool` indicating whether the transfer is allowed. */ async canTransfer(transferEvent) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); return Bool(true); } /** * Pauses the contract, disabling certain administrative actions. * Can only be called by the admin if `canPause` is `true`. */ async pause() { await this.ensureOwnerSignature(); this.canBePaused.getAndRequireEquals().assertTrue(); this.isPaused.set(Bool(true)); this.emitEvent("pause", new PauseEvent({ isPaused: Bool(true) })); } /** * Resumes the contract, re-enabling administrative actions. * Can only be called by the admin if `canPause` is `true`. */ async resume() { await this.ensureOwnerSignature(); this.canBePaused.getAndRequireEquals().assertTrue(); this.isPaused.set(Bool(false)); this.emitEvent("resume", new PauseEvent({ isPaused: Bool(false) })); } /** * Transfers ownership of the contract to a new admin. * @param to - The public key of the new owner. * @returns The public key of the previous admin. */ async transferOwnership(to) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); await this.ensureOwnerSignature(); const from = this.admin.getAndRequireEquals(); // Pending admin public key can be empty, it cancels the transfer this.pendingAdmin.set(to); this.emitEvent("ownershipTransfer", new OwnershipChangeEvent({ from, to, })); return from; } /** * Accept transfer of the ownership of the contract. * @returns The public key of the previous admin. */ async acceptOwnership() { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); const admin = this.admin.getAndRequireEquals(); const pendingAdmin = this.pendingAdmin.getAndRequireEquals(); pendingAdmin .equals(PublicKey.empty()) .assertFalse("Pending admin is not set"); // pendingAdmin can be different from the sender, but it should sign the tx const pendingAminUpdate = AccountUpdate.createSigned(pendingAdmin); pendingAminUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change this.admin.set(pendingAdmin); this.pendingAdmin.set(PublicKey.empty()); this.emitEvent("ownershipAccepted", new OwnershipChangeEvent({ from: admin, to: pendingAdmin, })); return admin; } async canChangeVerificationKey(vk, address, tokenId) { await this.ensureOwnerSignature(); return Bool(true); } /** * Determines if the name can be changed for a Collection. */ async canChangeName(name) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); return Bool(false); } /** * Determines if the creator can be changed for a Collection. */ async canChangeCreator(creator) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); return Bool(false); } /** * Determines if the base URI can be changed for a Collection. */ async canChangeBaseUri(baseUri) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); return Bool(false); } /** * Determines if the royalty fee can be changed for a Collection. */ async canChangeRoyalty(royaltyFee) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); await this.ensureOwnerSignature(); const flags = NFTAdminAllowFlags.unpack(this.flags.getAndRequireEquals()); return flags.allowChangeRoyalty; } /** * Determines if the transfer fee can be changed for a Collection. */ async canChangeTransferFee(transferFee) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); await this.ensureOwnerSignature(); const flags = NFTAdminAllowFlags.unpack(this.flags.getAndRequireEquals()); return flags.allowChangeTransferFee; } /** * Determines if the admin contract can be changed for a Collection. */ async canSetAdmin(admin) { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); return Bool(false); } /** * Determines if the collection can be paused. */ async canPause() { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); await this.ensureOwnerSignature(); const flags = NFTAdminAllowFlags.unpack(this.flags.getAndRequireEquals()); return flags.allowPauseCollection; } /** * Determines if the collection can be resumed. */ async canResume() { const isPaused = this.isPaused.getAndRequireEquals(); isPaused.assertFalse("Contract is paused"); await this.ensureOwnerSignature(); const flags = NFTAdminAllowFlags.unpack(this.flags.getAndRequireEquals()); return flags.allowPauseCollection; } } __decorate([ state(PublicKey), __metadata("design:type", Object) ], NFTAdmin.prototype, "admin", void 0); __decorate([ state(PublicKey), __metadata("design:type", Object) ], NFTAdmin.prototype, "pendingAdmin", void 0); __decorate([ state(Bool), __metadata("design:type", Object) ], NFTAdmin.prototype, "isPaused", void 0); __decorate([ state(Bool), __metadata("design:type", Object) ], NFTAdmin.prototype, "canBePaused", void 0); __decorate([ state(UInt8), __metadata("design:type", Object) ], NFTAdmin.prototype, "flags", void 0); __decorate([ method, __metadata("design:type", Function), __metadata("design:paramtypes", [VerificationKey]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "upgradeVerificationKey", null); __decorate([ method.returns(MintParamsOption), __metadata("design:type", Function), __metadata("design:paramtypes", [MintRequest]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canMint", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [NFTState, NFTState]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canUpdate", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [TransferEvent]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canTransfer", null); __decorate([ method, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "pause", null); __decorate([ method, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "resume", null); __decorate([ method.returns(PublicKey), __metadata("design:type", Function), __metadata("design:paramtypes", [PublicKey]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "transferOwnership", null); __decorate([ method.returns(PublicKey), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "acceptOwnership", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [VerificationKey, PublicKey, Field]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canChangeVerificationKey", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [Field]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canChangeName", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [PublicKey]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canChangeCreator", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [Field]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canChangeBaseUri", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [UInt32]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canChangeRoyalty", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [UInt64]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canChangeTransferFee", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", [PublicKey]), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canSetAdmin", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canPause", null); __decorate([ method.returns(Bool), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], NFTAdmin.prototype, "canResume", null); //# sourceMappingURL=admin.js.map