@silvana-one/nft
Version:
Mina NFT library
437 lines • 17.1 kB
JavaScript
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