@minatokens/nft
Version:
Mina NFT Standard draft library
154 lines (138 loc) • 5.03 kB
text/typescript
import {
AccountUpdate,
DeployArgs,
method,
Permissions,
PublicKey,
State,
state,
UInt64,
SmartContract,
Bool,
Field,
} from "o1js";
import {
UInt64Option,
NFTCollectionContractConstructor,
NFTApprovalBase,
NFTCollectionBase,
NFTTransactionContext,
TransferExtendedParams,
TransferEvent,
} from "../interfaces/index.js";
export interface NonFungibleTokenOfferContractDeployProps
extends Exclude<DeployArgs, undefined> {
// /** The whitelist. */
// whitelist: Whitelist;
/** The offers. */
price: UInt64;
/** The collection of the NFT. */
collection: PublicKey;
/** The address of the NFT. */
nft: PublicKey;
/** The owner of the NFT. */
owner: PublicKey;
}
/**
* Creates a new NFT Collection Contract class.
*
* @param params - Constructor parameters including admin and upgrade contracts, and network ID.
* @returns The Collection class extending TokenContract and implementing required interfaces.
*/
export function OfferFactory(params: {
collectionContract: () => NFTCollectionContractConstructor;
}) {
const { collectionContract } = params;
class NonFungibleTokenOfferContract
extends SmartContract
implements NFTApprovalBase
{
owner = State<PublicKey>();
// @state(Whitelist) whitelist = State<Whitelist>();
price = State<UInt64>();
collection = State<PublicKey>();
nft = State<PublicKey>();
insideBuy = State<Bool>();
async deploy(args: NonFungibleTokenOfferContractDeployProps) {
await super.deploy(args);
this.owner.set(args.owner);
// this.whitelist.set(args.whitelist);
this.price.set(args.price);
this.collection.set(args.collection);
this.nft.set(args.nft);
this.insideBuy.set(Bool(false));
this.account.permissions.set({
...Permissions.default(),
send: Permissions.proof(),
setVerificationKey:
Permissions.VerificationKey.impossibleDuringCurrentVersion(),
setPermissions: Permissions.impossible(),
});
}
events = {
buy: TransferEvent,
};
getCollectionContract(address: PublicKey): NFTCollectionBase {
const CollectionContract = collectionContract();
return new CollectionContract(address);
}
async buy() {
const insideBuy = this.insideBuy.getAndRequireEquals();
insideBuy.assertFalse("Already inside buy method");
this.insideBuy.set(Bool(true));
const collectionAddress = this.collection.getAndRequireEquals();
const nftAddress = this.nft.getAndRequireEquals();
const price = this.price.getAndRequireEquals();
const collection = this.getCollectionContract(collectionAddress);
await collection.transferByProof({
address: nftAddress,
from: this.address,
to: this.sender.getUnconstrained(),
price: UInt64Option.fromValue(price),
context: new NFTTransactionContext({
custom: [Field(0), Field(0), Field(0)],
}),
});
}
// @method async sellWithApproval(nftAddress: NFTAddress, price: UInt64) {
// await this._sell(nftAddress, price);
// const buyer = this.buyer.getAndRequireEquals();
// const collection = new Collection(nftAddress.collection);
// await collection.sellWithApproval(nftAddress.nft, price, buyer);
// }
.returns(Bool)
async canTransfer(params: TransferExtendedParams): Promise<Bool> {
this.insideBuy.requireEquals(Bool(true));
// We do not set the insideBuy to false here as buy() can be called once only
// and then the offer is accepted and not valid anymore for other buyers
const collectionAddress = this.collection.getAndRequireEquals();
const nftAddress = this.nft.getAndRequireEquals();
const owner = this.owner.getAndRequireEquals();
const price = this.price.getAndRequireEquals();
const sender = this.sender.getUnconstrained();
params.collection.assertEquals(collectionAddress);
params.nft.assertEquals(nftAddress);
params.from.assertEquals(owner);
params.approved.assertEquals(this.address);
params.price.assertSome().assertEquals(price);
params.to.assertEquals(sender);
params.fee.orElse(UInt64.zero).assertLessThan(price, "Fee is too high");
const payment = price.sub(params.fee.orElse(UInt64.zero));
const senderUpdate = AccountUpdate.createSigned(sender);
senderUpdate.account.balance.requireBetween(payment, UInt64.MAXINT());
senderUpdate.balance.subInPlace(payment);
const ownerUpdate = AccountUpdate.create(owner);
ownerUpdate.balance.addInPlace(payment);
senderUpdate.body.useFullCommitment = Bool(true);
ownerUpdate.body.useFullCommitment = Bool(true);
this.emitEvent(
"buy",
new TransferEvent({
...params,
})
);
return Bool(true);
}
}
return NonFungibleTokenOfferContract;
}