@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,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccountUpdate,
|
|
3
|
+
Bool,
|
|
4
|
+
method,
|
|
5
|
+
PublicKey,
|
|
6
|
+
SmartContract,
|
|
7
|
+
state,
|
|
8
|
+
State,
|
|
9
|
+
Permissions,
|
|
10
|
+
DeployArgs,
|
|
11
|
+
} from "o1js";
|
|
12
|
+
import { NFTCollectionContractConstructor } from "./collection.js";
|
|
13
|
+
import { NFTState } from "./types.js";
|
|
14
|
+
export {
|
|
15
|
+
NFTUpdateBase,
|
|
16
|
+
NFTUpdateContractConstructor,
|
|
17
|
+
NFTStandardUpdate,
|
|
18
|
+
NFTUpdateDeployProps,
|
|
19
|
+
DefineUpdateFactory,
|
|
20
|
+
};
|
|
21
|
+
type DefineUpdateFactory = (params: {
|
|
22
|
+
collectionContract: () => NFTCollectionContractConstructor;
|
|
23
|
+
}) => NFTUpdateContractConstructor;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The `NFTUpdateBase` interface defines the update functionalities required for managing an NFT update
|
|
27
|
+
*/
|
|
28
|
+
type NFTUpdateBase = SmartContract & {
|
|
29
|
+
/**
|
|
30
|
+
* Checks if an NFT can be updated from its current state (`input`) to a new state (`output`).
|
|
31
|
+
*
|
|
32
|
+
* @param collectionAddress - The public key of the NFT collection address.
|
|
33
|
+
* @param nftAddress - The public key of the NFT.
|
|
34
|
+
* @param input - The current state of the NFT.
|
|
35
|
+
* @param output - The desired new state of the NFT.
|
|
36
|
+
* @returns A `Promise` resolving to a `Bool` indicating whether the update is permitted.
|
|
37
|
+
*/
|
|
38
|
+
canUpdate(
|
|
39
|
+
collectionAddress: PublicKey,
|
|
40
|
+
nftAddress: PublicKey,
|
|
41
|
+
input: NFTState,
|
|
42
|
+
output: NFTState
|
|
43
|
+
): Promise<Bool>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Defines a constructor for contracts implementing `NFTUpdateBase`, accepting an `address` public key and returning an instance of `NFTUpdateBase`.
|
|
48
|
+
*
|
|
49
|
+
* @param address - The public key of the contract's owner.
|
|
50
|
+
* @returns An instance of `NFTUpdateBase`.
|
|
51
|
+
*/
|
|
52
|
+
type NFTUpdateContractConstructor = new (address: PublicKey) => NFTUpdateBase;
|
|
53
|
+
|
|
54
|
+
interface NFTUpdateDeployProps extends Exclude<DeployArgs, undefined> {
|
|
55
|
+
admin: PublicKey;
|
|
56
|
+
uri: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The **NFTStandardUpdate** contract is the default implementation of the `NFTUpdateBase` interface.
|
|
61
|
+
*/
|
|
62
|
+
class NFTStandardUpdate extends SmartContract implements NFTUpdateBase {
|
|
63
|
+
/**
|
|
64
|
+
* The public key of the contract's administrator.
|
|
65
|
+
*/
|
|
66
|
+
@state(PublicKey) admin = State<PublicKey>();
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Deploys the contract with initial settings.
|
|
70
|
+
* @param props - Deployment properties including admin, upgradeAuthority, uri, canPause, and isPaused.
|
|
71
|
+
*/
|
|
72
|
+
async deploy(props: NFTUpdateDeployProps) {
|
|
73
|
+
await super.deploy(props);
|
|
74
|
+
this.admin.set(props.admin);
|
|
75
|
+
this.account.zkappUri.set(props.uri);
|
|
76
|
+
this.account.permissions.set({
|
|
77
|
+
...Permissions.default(),
|
|
78
|
+
setVerificationKey: Permissions.VerificationKey.signature(),
|
|
79
|
+
setPermissions: Permissions.impossible(),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Ensures that the transaction is authorized by the contract owner.
|
|
85
|
+
* @returns A signed `AccountUpdate` from the admin.
|
|
86
|
+
*/
|
|
87
|
+
async ensureOwnerSignature(): Promise<AccountUpdate> {
|
|
88
|
+
const admin = this.admin.getAndRequireEquals();
|
|
89
|
+
const adminUpdate = AccountUpdate.createSigned(admin);
|
|
90
|
+
adminUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change
|
|
91
|
+
return adminUpdate;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@method.returns(Bool)
|
|
95
|
+
async canUpdate(
|
|
96
|
+
collectionAddress: PublicKey,
|
|
97
|
+
nftAddress: PublicKey,
|
|
98
|
+
input: NFTState,
|
|
99
|
+
output: NFTState
|
|
100
|
+
): Promise<Bool> {
|
|
101
|
+
await this.ensureOwnerSignature();
|
|
102
|
+
return Bool(true);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccountUpdate,
|
|
3
|
+
DeployArgs,
|
|
4
|
+
method,
|
|
5
|
+
Permissions,
|
|
6
|
+
PublicKey,
|
|
7
|
+
State,
|
|
8
|
+
state,
|
|
9
|
+
UInt64,
|
|
10
|
+
SmartContract,
|
|
11
|
+
Bool,
|
|
12
|
+
UInt32,
|
|
13
|
+
Field,
|
|
14
|
+
Struct,
|
|
15
|
+
assert,
|
|
16
|
+
Provable,
|
|
17
|
+
} from "o1js";
|
|
18
|
+
import {
|
|
19
|
+
UInt64Option,
|
|
20
|
+
TransferEvent,
|
|
21
|
+
NFTCollectionContractConstructor,
|
|
22
|
+
NFTApprovalBase,
|
|
23
|
+
NFTCollectionBase,
|
|
24
|
+
NFTTransactionContext,
|
|
25
|
+
TransferExtendedParams,
|
|
26
|
+
TransferParams,
|
|
27
|
+
} from "../interfaces/index.js";
|
|
28
|
+
|
|
29
|
+
const MAX_SALE_FEE = 100000;
|
|
30
|
+
const MIN_STEP = 10; // 1% to previous bid
|
|
31
|
+
|
|
32
|
+
export class AuctionPacked extends Struct({
|
|
33
|
+
ownerX: Field,
|
|
34
|
+
collectionX: Field,
|
|
35
|
+
nftX: Field,
|
|
36
|
+
auctioneerX: Field,
|
|
37
|
+
bidderX: Field,
|
|
38
|
+
data: Field,
|
|
39
|
+
}) {}
|
|
40
|
+
|
|
41
|
+
export class Auction extends Struct({
|
|
42
|
+
owner: PublicKey,
|
|
43
|
+
collection: PublicKey,
|
|
44
|
+
nft: PublicKey,
|
|
45
|
+
auctioneer: PublicKey,
|
|
46
|
+
bidder: PublicKey,
|
|
47
|
+
minimumPrice: UInt64,
|
|
48
|
+
transferFee: UInt64,
|
|
49
|
+
/** The sale fee percentage (e.g., 1000 = 1%, 100 = 0.1%, 10000 = 10%, 100000 = 100%). */
|
|
50
|
+
saleFee: UInt32,
|
|
51
|
+
auctionEndTime: UInt32,
|
|
52
|
+
withdrawPeriod: UInt32, // in slots
|
|
53
|
+
isOwnerPaid: Bool,
|
|
54
|
+
isNFTtransferred: Bool,
|
|
55
|
+
isNFTwithdrawn: Bool,
|
|
56
|
+
}) {
|
|
57
|
+
pack(): AuctionPacked {
|
|
58
|
+
const data = Field.fromBits([
|
|
59
|
+
...this.minimumPrice.value.toBits(64),
|
|
60
|
+
...this.transferFee.value.toBits(64),
|
|
61
|
+
...this.saleFee.value.toBits(32),
|
|
62
|
+
...this.auctionEndTime.value.toBits(32),
|
|
63
|
+
...this.withdrawPeriod.value.toBits(32),
|
|
64
|
+
this.owner.isOdd,
|
|
65
|
+
this.collection.isOdd,
|
|
66
|
+
this.nft.isOdd,
|
|
67
|
+
this.auctioneer.isOdd,
|
|
68
|
+
this.bidder.isOdd,
|
|
69
|
+
this.isOwnerPaid,
|
|
70
|
+
this.isNFTtransferred,
|
|
71
|
+
this.isNFTwithdrawn,
|
|
72
|
+
]);
|
|
73
|
+
return new AuctionPacked({
|
|
74
|
+
ownerX: this.owner.x,
|
|
75
|
+
collectionX: this.collection.x,
|
|
76
|
+
nftX: this.nft.x,
|
|
77
|
+
auctioneerX: this.auctioneer.x,
|
|
78
|
+
bidderX: this.bidder.x,
|
|
79
|
+
data,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
static unpack(packed: AuctionPacked): Auction {
|
|
83
|
+
const bits = packed.data.toBits(64 + 64 + 32 + 32 + 32 + 8);
|
|
84
|
+
const ownerX = packed.ownerX;
|
|
85
|
+
const collectionX = packed.collectionX;
|
|
86
|
+
const nftX = packed.nftX;
|
|
87
|
+
const auctioneerX = packed.auctioneerX;
|
|
88
|
+
const bidderX = packed.bidderX;
|
|
89
|
+
const ownerIsOdd = bits[64 + 64 + 32 + 32 + 32];
|
|
90
|
+
const collectionIsOdd = bits[64 + 64 + 32 + 32 + 32 + 1];
|
|
91
|
+
const nftIsOdd = bits[64 + 64 + 32 + 32 + 32 + 2];
|
|
92
|
+
const auctioneerIsOdd = bits[64 + 64 + 32 + 32 + 32 + 3];
|
|
93
|
+
const bidderIsOdd = bits[64 + 64 + 32 + 32 + 32 + 4];
|
|
94
|
+
const isOwnerPaid = bits[64 + 64 + 32 + 32 + 32 + 5];
|
|
95
|
+
const isNFTtransferred = bits[64 + 64 + 32 + 32 + 32 + 6];
|
|
96
|
+
const isNFTwithdrawn = bits[64 + 64 + 32 + 32 + 32 + 7];
|
|
97
|
+
const owner = PublicKey.from({ x: ownerX, isOdd: ownerIsOdd });
|
|
98
|
+
const collection = PublicKey.from({
|
|
99
|
+
x: collectionX,
|
|
100
|
+
isOdd: collectionIsOdd,
|
|
101
|
+
});
|
|
102
|
+
const nft = PublicKey.from({ x: nftX, isOdd: nftIsOdd });
|
|
103
|
+
const auctioneer = PublicKey.from({
|
|
104
|
+
x: auctioneerX,
|
|
105
|
+
isOdd: auctioneerIsOdd,
|
|
106
|
+
});
|
|
107
|
+
const bidder = PublicKey.from({
|
|
108
|
+
x: bidderX,
|
|
109
|
+
isOdd: bidderIsOdd,
|
|
110
|
+
});
|
|
111
|
+
const minimumPrice = UInt64.Unsafe.fromField(
|
|
112
|
+
Field.fromBits(bits.slice(0, 64))
|
|
113
|
+
);
|
|
114
|
+
const transferFee = UInt64.Unsafe.fromField(
|
|
115
|
+
Field.fromBits(bits.slice(64, 64 + 64))
|
|
116
|
+
);
|
|
117
|
+
const saleFee = UInt32.Unsafe.fromField(
|
|
118
|
+
Field.fromBits(bits.slice(64 + 64, 64 + 64 + 32))
|
|
119
|
+
);
|
|
120
|
+
const auctionEndTime = UInt32.Unsafe.fromField(
|
|
121
|
+
Field.fromBits(bits.slice(64 + 64 + 32, 64 + 64 + 32 + 32))
|
|
122
|
+
);
|
|
123
|
+
const withdrawPeriod = UInt32.Unsafe.fromField(
|
|
124
|
+
Field.fromBits(bits.slice(64 + 64 + 32 + 32, 64 + 64 + 32 + 32 + 32))
|
|
125
|
+
);
|
|
126
|
+
return new Auction({
|
|
127
|
+
owner,
|
|
128
|
+
collection,
|
|
129
|
+
nft,
|
|
130
|
+
auctioneer,
|
|
131
|
+
bidder,
|
|
132
|
+
minimumPrice,
|
|
133
|
+
transferFee,
|
|
134
|
+
saleFee,
|
|
135
|
+
auctionEndTime,
|
|
136
|
+
withdrawPeriod,
|
|
137
|
+
isOwnerPaid,
|
|
138
|
+
isNFTtransferred,
|
|
139
|
+
isNFTwithdrawn,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export class AuctionState extends Struct({
|
|
145
|
+
bidAmount: UInt64,
|
|
146
|
+
auction: Auction,
|
|
147
|
+
settled: Bool,
|
|
148
|
+
}) {}
|
|
149
|
+
|
|
150
|
+
export interface NonFungibleTokenAuctionContractDeployProps
|
|
151
|
+
extends Exclude<DeployArgs, undefined> {
|
|
152
|
+
/** The minimum price. */
|
|
153
|
+
minimumPrice: UInt64;
|
|
154
|
+
/** The auction end time. */
|
|
155
|
+
auctionEndTime: UInt32;
|
|
156
|
+
/** The collection of the NFT. */
|
|
157
|
+
collection: PublicKey;
|
|
158
|
+
/** The address of the NFT. */
|
|
159
|
+
nft: PublicKey;
|
|
160
|
+
/** The owner of the NFT. */
|
|
161
|
+
owner: PublicKey;
|
|
162
|
+
/** The auctioneer of the NFT. */
|
|
163
|
+
auctioneer: PublicKey;
|
|
164
|
+
/** The transfer fee. */
|
|
165
|
+
transferFee: UInt64;
|
|
166
|
+
/** The sale fee. */
|
|
167
|
+
saleFee: UInt32;
|
|
168
|
+
/** The withdraw period. */
|
|
169
|
+
withdrawPeriod: UInt32;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export class AuctionBidEvent extends Struct({
|
|
173
|
+
bidder: PublicKey,
|
|
174
|
+
price: UInt64,
|
|
175
|
+
}) {}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Creates a new NFT Collection Contract class.
|
|
179
|
+
*
|
|
180
|
+
* @param params - Constructor parameters including admin and upgrade contracts, and network ID.
|
|
181
|
+
* @returns The Collection class extending TokenContract and implementing required interfaces.
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
export function AuctionFactory(params: {
|
|
185
|
+
collectionContract: () => NFTCollectionContractConstructor;
|
|
186
|
+
}) {
|
|
187
|
+
const { collectionContract } = params;
|
|
188
|
+
|
|
189
|
+
class NonFungibleTokenAuctionContract
|
|
190
|
+
extends SmartContract
|
|
191
|
+
implements NFTApprovalBase
|
|
192
|
+
{
|
|
193
|
+
@state(AuctionPacked) auctionData = State<AuctionPacked>();
|
|
194
|
+
@state(UInt64) bidAmount = State<UInt64>(UInt64.zero);
|
|
195
|
+
@state(Bool) settled = State<Bool>(Bool(false));
|
|
196
|
+
|
|
197
|
+
async deploy(args: NonFungibleTokenAuctionContractDeployProps) {
|
|
198
|
+
await super.deploy(args);
|
|
199
|
+
assert(
|
|
200
|
+
args.saleFee.lessThanOrEqual(UInt32.from(MAX_SALE_FEE)),
|
|
201
|
+
"Sale fee is too high"
|
|
202
|
+
);
|
|
203
|
+
this.auctionData.set(
|
|
204
|
+
new Auction({
|
|
205
|
+
owner: args.owner,
|
|
206
|
+
collection: args.collection,
|
|
207
|
+
nft: args.nft,
|
|
208
|
+
auctioneer: args.auctioneer,
|
|
209
|
+
minimumPrice: args.minimumPrice,
|
|
210
|
+
transferFee: args.transferFee,
|
|
211
|
+
saleFee: args.saleFee,
|
|
212
|
+
auctionEndTime: args.auctionEndTime,
|
|
213
|
+
withdrawPeriod: args.withdrawPeriod,
|
|
214
|
+
bidder: PublicKey.empty(),
|
|
215
|
+
isOwnerPaid: Bool(false),
|
|
216
|
+
isNFTtransferred: Bool(false),
|
|
217
|
+
isNFTwithdrawn: Bool(false),
|
|
218
|
+
}).pack()
|
|
219
|
+
);
|
|
220
|
+
this.settled.set(Bool(false));
|
|
221
|
+
this.bidAmount.set(UInt64.zero);
|
|
222
|
+
this.account.permissions.set({
|
|
223
|
+
...Permissions.default(),
|
|
224
|
+
send: Permissions.proof(),
|
|
225
|
+
setVerificationKey:
|
|
226
|
+
Permissions.VerificationKey.impossibleDuringCurrentVersion(),
|
|
227
|
+
setPermissions: Permissions.impossible(),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
events = {
|
|
232
|
+
bid: AuctionBidEvent,
|
|
233
|
+
settleAuction: TransferParams,
|
|
234
|
+
canTransfer: TransferEvent,
|
|
235
|
+
settlePayment: UInt64,
|
|
236
|
+
settleAuctioneerPayment: UInt64,
|
|
237
|
+
withdraw: UInt64,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
getCollectionContract(address: PublicKey): NFTCollectionBase {
|
|
241
|
+
const CollectionContract = collectionContract();
|
|
242
|
+
return new CollectionContract(address);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
calculateSaleFee(params: {
|
|
246
|
+
price: UInt64;
|
|
247
|
+
saleFee: UInt32;
|
|
248
|
+
transferFee: UInt64;
|
|
249
|
+
}): UInt64 {
|
|
250
|
+
const { price, saleFee, transferFee } = params;
|
|
251
|
+
saleFee.assertLessThanOrEqual(
|
|
252
|
+
UInt32.from(MAX_SALE_FEE),
|
|
253
|
+
"Sale fee is too high"
|
|
254
|
+
);
|
|
255
|
+
return price.div(MAX_SALE_FEE).mul(UInt64.from(saleFee));
|
|
256
|
+
}
|
|
257
|
+
// anyone can call this method to bid, paying the bid amount for the bidder
|
|
258
|
+
@method.returns(Auction)
|
|
259
|
+
async bid(price: UInt64, bidder: PublicKey): Promise<Auction> {
|
|
260
|
+
const settled = this.settled.getAndRequireEquals();
|
|
261
|
+
settled.assertFalse("Auction already finished");
|
|
262
|
+
|
|
263
|
+
const bidAmount = this.bidAmount.getAndRequireEquals();
|
|
264
|
+
this.account.balance.requireBetween(bidAmount, UInt64.MAXINT());
|
|
265
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
266
|
+
price.assertGreaterThanOrEqual(
|
|
267
|
+
auction.minimumPrice,
|
|
268
|
+
"Bid should be greater or equal than the minimum price"
|
|
269
|
+
);
|
|
270
|
+
price.assertGreaterThan(
|
|
271
|
+
bidAmount.add(bidAmount.div(1000).mul(UInt64.from(MIN_STEP))),
|
|
272
|
+
"Bid should be greater than the existing bid plus the minimum step"
|
|
273
|
+
);
|
|
274
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
275
|
+
UInt32.from(0),
|
|
276
|
+
auction.auctionEndTime
|
|
277
|
+
);
|
|
278
|
+
const sender = this.sender.getUnconstrained();
|
|
279
|
+
const senderUpdate = AccountUpdate.createSigned(sender);
|
|
280
|
+
// if there is no bidder, this AccountUpdate will be ignored, similar to AccountUpdate.createIf()
|
|
281
|
+
const returnUpdate = AccountUpdate.create(auction.bidder);
|
|
282
|
+
senderUpdate.body.useFullCommitment = Bool(true);
|
|
283
|
+
returnUpdate.body.useFullCommitment = Bool(true);
|
|
284
|
+
// return the previous bidder's bid
|
|
285
|
+
this.balance.subInPlace(bidAmount);
|
|
286
|
+
returnUpdate.balance.addInPlace(bidAmount);
|
|
287
|
+
// get the new bid deposit
|
|
288
|
+
senderUpdate.balance.subInPlace(price);
|
|
289
|
+
this.balance.addInPlace(price);
|
|
290
|
+
this.bidAmount.set(price);
|
|
291
|
+
auction.bidder = bidder;
|
|
292
|
+
this.auctionData.set(auction.pack());
|
|
293
|
+
this.emitEvent("bid", new AuctionBidEvent({ bidder, price }));
|
|
294
|
+
return auction;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@method.returns(AuctionState)
|
|
298
|
+
async getAuctionState(): Promise<AuctionState> {
|
|
299
|
+
return new AuctionState({
|
|
300
|
+
auction: Auction.unpack(this.auctionData.getAndRequireEquals()),
|
|
301
|
+
bidAmount: this.bidAmount.getAndRequireEquals(),
|
|
302
|
+
settled: this.settled.getAndRequireEquals(),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// anyone can call this method to settle the auction
|
|
307
|
+
// but it is intended to be called by the auctioneer
|
|
308
|
+
// because the auctioneer is the one who will get the auction commission
|
|
309
|
+
// and pay the royalty to NFT creator
|
|
310
|
+
// This method is atomic, so it will settle the auction
|
|
311
|
+
@method async settleAuction() {
|
|
312
|
+
const settled = this.settled.getAndRequireEquals();
|
|
313
|
+
settled.assertFalse("Auction already settled");
|
|
314
|
+
this.settled.set(Bool(true));
|
|
315
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
316
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
317
|
+
auction.auctionEndTime.add(1),
|
|
318
|
+
UInt32.MAXINT()
|
|
319
|
+
);
|
|
320
|
+
const nftAddress = auction.nft;
|
|
321
|
+
|
|
322
|
+
const bidAmount = this.bidAmount.getAndRequireEquals();
|
|
323
|
+
auction.bidder.equals(PublicKey.empty()).assertFalse("No bidder");
|
|
324
|
+
bidAmount.assertGreaterThanOrEqual(
|
|
325
|
+
auction.minimumPrice,
|
|
326
|
+
"Bidder does not have enough balance"
|
|
327
|
+
);
|
|
328
|
+
const collection = this.getCollectionContract(auction.collection);
|
|
329
|
+
const transferParams = new TransferParams({
|
|
330
|
+
address: nftAddress,
|
|
331
|
+
from: this.address,
|
|
332
|
+
to: auction.bidder,
|
|
333
|
+
price: UInt64Option.fromValue(bidAmount),
|
|
334
|
+
context: new NFTTransactionContext({
|
|
335
|
+
custom: [Field(1), Field(0), Field(0)],
|
|
336
|
+
}),
|
|
337
|
+
});
|
|
338
|
+
await collection.transferByProof(transferParams);
|
|
339
|
+
this.emitEvent("settleAuction", transferParams);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// and pay the royalty to NFT creator
|
|
343
|
+
// This method is atomic, so it will settle the auction
|
|
344
|
+
@method async withdrawNFT() {
|
|
345
|
+
const settled = this.settled.getAndRequireEquals();
|
|
346
|
+
settled.assertFalse("Auction already settled");
|
|
347
|
+
this.settled.set(Bool(true));
|
|
348
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
349
|
+
auction.isNFTwithdrawn.assertFalse("NFT already withdrawn");
|
|
350
|
+
auction.isNFTtransferred.assertFalse("NFT already transferred");
|
|
351
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
352
|
+
auction.auctionEndTime.add(auction.withdrawPeriod),
|
|
353
|
+
UInt32.MAXINT()
|
|
354
|
+
);
|
|
355
|
+
const nftAddress = auction.nft;
|
|
356
|
+
|
|
357
|
+
const collection = this.getCollectionContract(auction.collection);
|
|
358
|
+
const transferParams = new TransferParams({
|
|
359
|
+
address: nftAddress,
|
|
360
|
+
from: this.address,
|
|
361
|
+
to: auction.owner,
|
|
362
|
+
price: UInt64Option.none(),
|
|
363
|
+
context: new NFTTransactionContext({
|
|
364
|
+
custom: [Field(2), Field(0), Field(0)],
|
|
365
|
+
}),
|
|
366
|
+
});
|
|
367
|
+
await collection.transferByProof(transferParams);
|
|
368
|
+
this.emitEvent("settleAuction", transferParams);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@method.returns(Bool)
|
|
372
|
+
async canTransfer(params: TransferExtendedParams): Promise<Bool> {
|
|
373
|
+
this.settled.requireEquals(Bool(true));
|
|
374
|
+
const isSale = params.context.custom[0].equals(Field(1));
|
|
375
|
+
const isWithdraw = params.context.custom[0].equals(Field(2));
|
|
376
|
+
isSale.or(isWithdraw).assertTrue("Invalid context");
|
|
377
|
+
|
|
378
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
379
|
+
auction.isNFTtransferred.assertFalse("NFT already transferred");
|
|
380
|
+
auction.isNFTwithdrawn.assertFalse("NFT already withdrawn");
|
|
381
|
+
const collectionAddress = auction.collection;
|
|
382
|
+
const nftAddress = auction.nft;
|
|
383
|
+
const owner = auction.owner;
|
|
384
|
+
const bidder = auction.bidder;
|
|
385
|
+
const bidAmount = this.bidAmount.getAndRequireEquals();
|
|
386
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
387
|
+
auction.auctionEndTime.add(
|
|
388
|
+
Provable.if(isSale, UInt32.from(1), auction.withdrawPeriod)
|
|
389
|
+
),
|
|
390
|
+
UInt32.MAXINT()
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
params.collection.assertEquals(collectionAddress);
|
|
394
|
+
params.nft.assertEquals(nftAddress);
|
|
395
|
+
params.from
|
|
396
|
+
.equals(owner)
|
|
397
|
+
.and(params.approved.equals(this.address))
|
|
398
|
+
.or(
|
|
399
|
+
params.from
|
|
400
|
+
.equals(this.address)
|
|
401
|
+
.and(params.approved.equals(PublicKey.empty()))
|
|
402
|
+
)
|
|
403
|
+
.assertTrue("Only owner or auction can transfer");
|
|
404
|
+
params.price.isSome.assertEquals(isSale);
|
|
405
|
+
params.price
|
|
406
|
+
.orElse(UInt64.zero)
|
|
407
|
+
.assertEquals(Provable.if(isSale, bidAmount, UInt64.zero));
|
|
408
|
+
params.price
|
|
409
|
+
.orElse(UInt64.MAXINT())
|
|
410
|
+
.assertGreaterThanOrEqual(
|
|
411
|
+
auction.minimumPrice,
|
|
412
|
+
"Bid should be greater or equal than the minimum price"
|
|
413
|
+
);
|
|
414
|
+
params.to.assertEquals(Provable.if(isSale, bidder, auction.owner));
|
|
415
|
+
const fee = params.fee.orElse(UInt64.zero);
|
|
416
|
+
fee.assertLessThanOrEqual(
|
|
417
|
+
Provable.if(isSale, bidAmount, UInt64.MAXINT()),
|
|
418
|
+
"Fee is higher than the bid"
|
|
419
|
+
);
|
|
420
|
+
const saleFee = this.calculateSaleFee({
|
|
421
|
+
price: bidAmount,
|
|
422
|
+
saleFee: auction.saleFee,
|
|
423
|
+
transferFee: auction.transferFee,
|
|
424
|
+
});
|
|
425
|
+
fee.assertLessThanOrEqual(
|
|
426
|
+
Provable.if(isSale, saleFee, UInt64.MAXINT()),
|
|
427
|
+
"Fee is higher than the sale fee"
|
|
428
|
+
);
|
|
429
|
+
auction.isNFTtransferred = isSale;
|
|
430
|
+
auction.isNFTwithdrawn = isWithdraw;
|
|
431
|
+
this.auctionData.set(auction.pack());
|
|
432
|
+
|
|
433
|
+
this.emitEvent(
|
|
434
|
+
"canTransfer",
|
|
435
|
+
new TransferEvent({
|
|
436
|
+
...params,
|
|
437
|
+
})
|
|
438
|
+
);
|
|
439
|
+
return Bool(true);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
@method
|
|
443
|
+
async settlePayment(): Promise<void> {
|
|
444
|
+
this.settled.getAndRequireEquals().assertTrue("Auction not settled");
|
|
445
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
446
|
+
auction.isOwnerPaid.assertFalse("Owner is not paid yet");
|
|
447
|
+
auction.isNFTtransferred.assertTrue("NFT not transferred");
|
|
448
|
+
const bidAmount = this.bidAmount.getAndRequireEquals();
|
|
449
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
450
|
+
auction.auctionEndTime.add(1),
|
|
451
|
+
UInt32.MAXINT()
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
const payment = bidAmount.sub(
|
|
455
|
+
this.calculateSaleFee({
|
|
456
|
+
price: bidAmount,
|
|
457
|
+
saleFee: auction.saleFee,
|
|
458
|
+
transferFee: auction.transferFee,
|
|
459
|
+
})
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
this.account.balance.requireBetween(payment, UInt64.MAXINT());
|
|
463
|
+
const ownerUpdate = AccountUpdate.create(auction.owner);
|
|
464
|
+
ownerUpdate.balance.addInPlace(payment);
|
|
465
|
+
this.balance.subInPlace(payment);
|
|
466
|
+
ownerUpdate.body.useFullCommitment = Bool(true);
|
|
467
|
+
|
|
468
|
+
auction.isOwnerPaid = Bool(true);
|
|
469
|
+
this.auctionData.set(auction.pack());
|
|
470
|
+
|
|
471
|
+
this.emitEvent("settlePayment", payment);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/*
|
|
475
|
+
const balance = this.account.balance.getAndRequireEquals();
|
|
476
|
+
is not stable and sometimes gives 0 on devnet during proving, so we put the amount as a parameter
|
|
477
|
+
This method can be called many times by anyone, allowing the auctioneer to use the hardware wallet and bots
|
|
478
|
+
*/
|
|
479
|
+
@method
|
|
480
|
+
async settleAuctioneerPayment(amount: UInt64): Promise<void> {
|
|
481
|
+
this.settled.getAndRequireEquals().assertTrue("Auction not settled");
|
|
482
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
483
|
+
auction.isOwnerPaid.assertTrue(
|
|
484
|
+
"Owner is not paid yet, first call settlePayment"
|
|
485
|
+
);
|
|
486
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
487
|
+
auction.auctionEndTime.add(1),
|
|
488
|
+
UInt32.MAXINT()
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
this.account.balance.requireBetween(amount, UInt64.MAXINT());
|
|
492
|
+
|
|
493
|
+
const auctioneerUpdate = AccountUpdate.create(auction.auctioneer);
|
|
494
|
+
auctioneerUpdate.balance.addInPlace(amount);
|
|
495
|
+
this.balance.subInPlace(amount);
|
|
496
|
+
auctioneerUpdate.body.useFullCommitment = Bool(true);
|
|
497
|
+
|
|
498
|
+
this.emitEvent("settleAuctioneerPayment", amount);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Withdraw the deposit from the auction
|
|
503
|
+
* in case the auction is not settled during the WITHDRAW_PERIOD
|
|
504
|
+
* for any reason
|
|
505
|
+
* Anybody can call this method to allow the use of bots by the auctioneer or bidder
|
|
506
|
+
*/
|
|
507
|
+
@method
|
|
508
|
+
async withdraw(): Promise<void> {
|
|
509
|
+
const auction = Auction.unpack(this.auctionData.getAndRequireEquals());
|
|
510
|
+
auction.isNFTtransferred.assertFalse("NFT already transferred");
|
|
511
|
+
this.network.globalSlotSinceGenesis.requireBetween(
|
|
512
|
+
auction.auctionEndTime.add(auction.withdrawPeriod),
|
|
513
|
+
UInt32.MAXINT()
|
|
514
|
+
);
|
|
515
|
+
const bidAmount = this.bidAmount.getAndRequireEquals();
|
|
516
|
+
const bidderUpdate = AccountUpdate.create(auction.bidder);
|
|
517
|
+
bidderUpdate.balance.addInPlace(bidAmount);
|
|
518
|
+
this.balance.subInPlace(bidAmount);
|
|
519
|
+
bidderUpdate.body.useFullCommitment = Bool(true);
|
|
520
|
+
this.settled.set(Bool(true));
|
|
521
|
+
|
|
522
|
+
this.emitEvent("withdraw", bidAmount);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return NonFungibleTokenAuctionContract;
|
|
527
|
+
}
|