hive-stream 3.0.2 → 3.0.3
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/DOCUMENTATION.md +50 -2
- package/README.md +44 -3
- package/dist/adapters/base.adapter.d.ts +5 -0
- package/dist/adapters/base.adapter.js +9 -0
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +6 -6
- package/dist/adapters/mongodb.adapter.js +36 -21
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +7 -0
- package/dist/adapters/postgresql.adapter.js +46 -19
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.d.ts +4 -0
- package/dist/adapters/sqlite.adapter.js +10 -0
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.d.ts +13 -3
- package/dist/api.js +96 -62
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +7 -1
- package/dist/config.js.map +1 -1
- package/dist/contracts/auctionhouse.contract.d.ts +4 -0
- package/dist/contracts/auctionhouse.contract.js +234 -0
- package/dist/contracts/auctionhouse.contract.js.map +1 -0
- package/dist/contracts/booking.contract.d.ts +4 -0
- package/dist/contracts/booking.contract.js +225 -0
- package/dist/contracts/booking.contract.js.map +1 -0
- package/dist/contracts/bountyboard.contract.d.ts +4 -0
- package/dist/contracts/bountyboard.contract.js +233 -0
- package/dist/contracts/bountyboard.contract.js.map +1 -0
- package/dist/contracts/bundlemarketplace.contract.d.ts +4 -0
- package/dist/contracts/bundlemarketplace.contract.js +195 -0
- package/dist/contracts/bundlemarketplace.contract.js.map +1 -0
- package/dist/contracts/charitymatch.contract.d.ts +4 -0
- package/dist/contracts/charitymatch.contract.js +172 -0
- package/dist/contracts/charitymatch.contract.js.map +1 -0
- package/dist/contracts/coinflip.contract.js +7 -1
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/crowdfund.contract.d.ts +4 -0
- package/dist/contracts/crowdfund.contract.js +290 -0
- package/dist/contracts/crowdfund.contract.js.map +1 -0
- package/dist/contracts/dcabot.contract.d.ts +4 -0
- package/dist/contracts/dcabot.contract.js +217 -0
- package/dist/contracts/dcabot.contract.js.map +1 -0
- package/dist/contracts/dice.contract.js +7 -1
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/domainregistry.contract.d.ts +4 -0
- package/dist/contracts/domainregistry.contract.js +232 -0
- package/dist/contracts/domainregistry.contract.js.map +1 -0
- package/dist/contracts/exchange.contract.js +209 -168
- package/dist/contracts/exchange.contract.js.map +1 -1
- package/dist/contracts/fanclub.contract.d.ts +4 -0
- package/dist/contracts/fanclub.contract.js +193 -0
- package/dist/contracts/fanclub.contract.js.map +1 -0
- package/dist/contracts/giftcard.contract.d.ts +4 -0
- package/dist/contracts/giftcard.contract.js +158 -0
- package/dist/contracts/giftcard.contract.js.map +1 -0
- package/dist/contracts/grantrounds.contract.d.ts +4 -0
- package/dist/contracts/grantrounds.contract.js +265 -0
- package/dist/contracts/grantrounds.contract.js.map +1 -0
- package/dist/contracts/groupbuy.contract.d.ts +4 -0
- package/dist/contracts/groupbuy.contract.js +198 -0
- package/dist/contracts/groupbuy.contract.js.map +1 -0
- package/dist/contracts/helpers.d.ts +64 -0
- package/dist/contracts/helpers.js +159 -0
- package/dist/contracts/helpers.js.map +1 -0
- package/dist/contracts/insurancepool.contract.d.ts +4 -0
- package/dist/contracts/insurancepool.contract.js +281 -0
- package/dist/contracts/insurancepool.contract.js.map +1 -0
- package/dist/contracts/invoice.contract.d.ts +4 -0
- package/dist/contracts/invoice.contract.js +193 -0
- package/dist/contracts/invoice.contract.js.map +1 -0
- package/dist/contracts/launchpad.contract.d.ts +4 -0
- package/dist/contracts/launchpad.contract.js +225 -0
- package/dist/contracts/launchpad.contract.js.map +1 -0
- package/dist/contracts/lotto.contract.js +53 -37
- package/dist/contracts/lotto.contract.js.map +1 -1
- package/dist/contracts/multisigtreasury.contract.d.ts +4 -0
- package/dist/contracts/multisigtreasury.contract.js +245 -0
- package/dist/contracts/multisigtreasury.contract.js.map +1 -0
- package/dist/contracts/nft.contract.d.ts +1 -0
- package/dist/contracts/nft.contract.js +236 -192
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/oraclebounty.contract.d.ts +4 -0
- package/dist/contracts/oraclebounty.contract.js +250 -0
- package/dist/contracts/oraclebounty.contract.js.map +1 -0
- package/dist/contracts/payroll.contract.d.ts +4 -0
- package/dist/contracts/payroll.contract.js +232 -0
- package/dist/contracts/payroll.contract.js.map +1 -0
- package/dist/contracts/paywall.contract.d.ts +4 -0
- package/dist/contracts/paywall.contract.js +185 -0
- package/dist/contracts/paywall.contract.js.map +1 -0
- package/dist/contracts/poll.contract.js +2 -0
- package/dist/contracts/poll.contract.js.map +1 -1
- package/dist/contracts/predictionmarket.contract.d.ts +4 -0
- package/dist/contracts/predictionmarket.contract.js +213 -0
- package/dist/contracts/predictionmarket.contract.js.map +1 -0
- package/dist/contracts/proposaltimelock.contract.d.ts +4 -0
- package/dist/contracts/proposaltimelock.contract.js +250 -0
- package/dist/contracts/proposaltimelock.contract.js.map +1 -0
- package/dist/contracts/questpass.contract.d.ts +4 -0
- package/dist/contracts/questpass.contract.js +214 -0
- package/dist/contracts/questpass.contract.js.map +1 -0
- package/dist/contracts/referral.contract.d.ts +4 -0
- package/dist/contracts/referral.contract.js +238 -0
- package/dist/contracts/referral.contract.js.map +1 -0
- package/dist/contracts/rental.contract.d.ts +4 -0
- package/dist/contracts/rental.contract.js +221 -0
- package/dist/contracts/rental.contract.js.map +1 -0
- package/dist/contracts/revenuesplit.contract.d.ts +4 -0
- package/dist/contracts/revenuesplit.contract.js +211 -0
- package/dist/contracts/revenuesplit.contract.js.map +1 -0
- package/dist/contracts/rps.contract.js +17 -1
- package/dist/contracts/rps.contract.js.map +1 -1
- package/dist/contracts/savings.contract.d.ts +4 -0
- package/dist/contracts/savings.contract.js +208 -0
- package/dist/contracts/savings.contract.js.map +1 -0
- package/dist/contracts/subscription.contract.d.ts +4 -0
- package/dist/contracts/subscription.contract.js +241 -0
- package/dist/contracts/subscription.contract.js.map +1 -0
- package/dist/contracts/sweepstakes.contract.d.ts +4 -0
- package/dist/contracts/sweepstakes.contract.js +209 -0
- package/dist/contracts/sweepstakes.contract.js.map +1 -0
- package/dist/contracts/ticketing.contract.d.ts +4 -0
- package/dist/contracts/ticketing.contract.js +185 -0
- package/dist/contracts/ticketing.contract.js.map +1 -0
- package/dist/contracts/tipjar.contract.js +2 -0
- package/dist/contracts/tipjar.contract.js.map +1 -1
- package/dist/contracts/token.contract.js +135 -125
- package/dist/contracts/token.contract.js.map +1 -1
- package/dist/index.d.ts +39 -0
- package/dist/index.js +71 -1
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +7 -0
- package/dist/metadata.js +64 -1
- package/dist/metadata.js.map +1 -1
- package/dist/providers/block-provider.d.ts +22 -0
- package/dist/providers/block-provider.js +3 -0
- package/dist/providers/block-provider.js.map +1 -0
- package/dist/providers/haf-client.d.ts +30 -0
- package/dist/providers/haf-client.js +119 -0
- package/dist/providers/haf-client.js.map +1 -0
- package/dist/providers/haf-provider.d.ts +49 -0
- package/dist/providers/haf-provider.js +256 -0
- package/dist/providers/haf-provider.js.map +1 -0
- package/dist/providers/hive-provider.d.ts +13 -0
- package/dist/providers/hive-provider.js +25 -0
- package/dist/providers/hive-provider.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.js +21 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/streamer.d.ts +21 -1
- package/dist/streamer.js +187 -62
- package/dist/streamer.js.map +1 -1
- package/dist/utils.js +11 -2
- package/dist/utils.js.map +1 -1
- package/package.json +16 -1
- package/.claude/settings.local.json +0 -12
- package/.env.example +0 -3
- package/.travis.yml +0 -11
- package/AGENTS.md +0 -35
- package/CLAUDE.md +0 -75
- package/ecosystem.config.js +0 -17
- package/examples/contracts/README.md +0 -8
- package/examples/contracts/exchange.ts +0 -38
- package/examples/contracts/poll.ts +0 -21
- package/examples/contracts/rps.ts +0 -19
- package/examples/contracts/tipjar.ts +0 -19
- package/jest.config.js +0 -9
- package/test-contract-block.md +0 -19
- package/tests/actions.spec.ts +0 -252
- package/tests/adapters/actions-persistence.spec.ts +0 -144
- package/tests/adapters/postgresql.adapter.spec.ts +0 -127
- package/tests/adapters/sqlite.adapter.spec.ts +0 -181
- package/tests/config-input.spec.ts +0 -90
- package/tests/contracts/coinflip.contract.spec.ts +0 -94
- package/tests/contracts/dice.contract.spec.ts +0 -87
- package/tests/contracts/entrants.json +0 -729
- package/tests/contracts/exchange.contract.spec.ts +0 -84
- package/tests/contracts/lotto.contract.spec.ts +0 -59
- package/tests/contracts/nft.contract.spec.ts +0 -948
- package/tests/contracts/token.contract.spec.ts +0 -90
- package/tests/exchanges/coingecko.exchange.spec.ts +0 -169
- package/tests/exchanges/exchange.base.spec.ts +0 -246
- package/tests/helpers/mock-adapter.ts +0 -214
- package/tests/helpers/mock-fetch.ts +0 -165
- package/tests/hive-chain-features.spec.ts +0 -319
- package/tests/hive-rates.spec.ts +0 -443
- package/tests/integration/hive-rates.integration.spec.ts +0 -35
- package/tests/metadata.spec.ts +0 -63
- package/tests/setup.ts +0 -30
- package/tests/streamer-actions.spec.ts +0 -274
- package/tests/streamer.spec.ts +0 -342
- package/tests/types/rates.spec.ts +0 -216
- package/tests/utils.spec.ts +0 -113
- package/tsconfig.build.json +0 -4
- package/tslint.json +0 -21
- package/wallaby.js +0 -26
|
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.NFTContract = void 0;
|
|
7
7
|
exports.createNFTContract = createNFTContract;
|
|
8
8
|
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
9
|
+
const utils_1 = require("../utils");
|
|
9
10
|
const contract_1 = require("./contract");
|
|
11
|
+
const helpers_1 = require("./helpers");
|
|
10
12
|
const CONTRACT_NAME = 'hivenft';
|
|
11
13
|
class NFTContract {
|
|
12
14
|
_instance;
|
|
@@ -17,6 +19,7 @@ class NFTContract {
|
|
|
17
19
|
transactionId;
|
|
18
20
|
async create() {
|
|
19
21
|
this.adapter = this._instance.getAdapter();
|
|
22
|
+
(0, helpers_1.ensureSqlAdapter)(this.adapter);
|
|
20
23
|
await this.initializeNFTTables();
|
|
21
24
|
}
|
|
22
25
|
destroy() {
|
|
@@ -29,82 +32,84 @@ class NFTContract {
|
|
|
29
32
|
this.transactionId = transactionId;
|
|
30
33
|
}
|
|
31
34
|
async initializeNFTTables() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
35
|
+
await this.adapter.query(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS nft_collections (
|
|
37
|
+
symbol TEXT PRIMARY KEY,
|
|
38
|
+
name TEXT NOT NULL,
|
|
39
|
+
description TEXT,
|
|
40
|
+
creator TEXT NOT NULL,
|
|
41
|
+
max_supply INTEGER,
|
|
42
|
+
current_supply INTEGER NOT NULL DEFAULT 0,
|
|
43
|
+
royalty REAL DEFAULT 0,
|
|
44
|
+
base_uri TEXT,
|
|
45
|
+
allow_updates BOOLEAN DEFAULT TRUE,
|
|
46
|
+
updateable_by_owner BOOLEAN DEFAULT FALSE,
|
|
47
|
+
created_at DATETIME NOT NULL
|
|
48
|
+
)
|
|
49
|
+
`);
|
|
50
|
+
await this.adapter.query(`
|
|
51
|
+
CREATE TABLE IF NOT EXISTS nft_tokens (
|
|
52
|
+
token_id TEXT NOT NULL,
|
|
53
|
+
collection_symbol TEXT NOT NULL,
|
|
54
|
+
owner TEXT NOT NULL,
|
|
55
|
+
metadata TEXT,
|
|
56
|
+
attributes TEXT,
|
|
57
|
+
minted_at DATETIME NOT NULL,
|
|
58
|
+
minted_by TEXT NOT NULL,
|
|
59
|
+
burned BOOLEAN DEFAULT FALSE,
|
|
60
|
+
PRIMARY KEY (token_id, collection_symbol),
|
|
61
|
+
FOREIGN KEY (collection_symbol) REFERENCES nft_collections(symbol)
|
|
62
|
+
)
|
|
63
|
+
`);
|
|
64
|
+
await this.adapter.query(`
|
|
65
|
+
CREATE TABLE IF NOT EXISTS nft_listings (
|
|
66
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
+
token_id TEXT NOT NULL,
|
|
68
|
+
collection_symbol TEXT NOT NULL,
|
|
69
|
+
seller TEXT NOT NULL,
|
|
70
|
+
price TEXT NOT NULL,
|
|
71
|
+
currency TEXT NOT NULL DEFAULT 'HIVE',
|
|
72
|
+
listed_at DATETIME NOT NULL,
|
|
73
|
+
active BOOLEAN DEFAULT TRUE,
|
|
74
|
+
FOREIGN KEY (token_id, collection_symbol) REFERENCES nft_tokens(token_id, collection_symbol)
|
|
75
|
+
)
|
|
76
|
+
`);
|
|
77
|
+
await this.adapter.query(`
|
|
78
|
+
CREATE TABLE IF NOT EXISTS nft_transfers (
|
|
79
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
80
|
+
token_id TEXT NOT NULL,
|
|
81
|
+
collection_symbol TEXT NOT NULL,
|
|
82
|
+
from_account TEXT NOT NULL,
|
|
83
|
+
to_account TEXT NOT NULL,
|
|
84
|
+
transfer_type TEXT NOT NULL,
|
|
85
|
+
price TEXT,
|
|
86
|
+
currency TEXT,
|
|
87
|
+
block_number INTEGER NOT NULL,
|
|
88
|
+
transaction_id TEXT NOT NULL,
|
|
89
|
+
timestamp DATETIME NOT NULL,
|
|
90
|
+
FOREIGN KEY (token_id, collection_symbol) REFERENCES nft_tokens(token_id, collection_symbol)
|
|
91
|
+
)
|
|
92
|
+
`);
|
|
93
|
+
await this.adapter.query(`
|
|
94
|
+
CREATE INDEX IF NOT EXISTS idx_nft_tokens_owner ON nft_tokens(owner);
|
|
95
|
+
`);
|
|
96
|
+
await this.adapter.query(`
|
|
97
|
+
CREATE INDEX IF NOT EXISTS idx_nft_tokens_collection ON nft_tokens(collection_symbol);
|
|
98
|
+
`);
|
|
99
|
+
await this.adapter.query(`
|
|
100
|
+
CREATE INDEX IF NOT EXISTS idx_nft_listings_active ON nft_listings(active, collection_symbol);
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
async withTransaction(work) {
|
|
104
|
+
if (typeof this.adapter?.runInTransaction === 'function') {
|
|
105
|
+
return this.adapter.runInTransaction(work);
|
|
103
106
|
}
|
|
107
|
+
return work(this.adapter);
|
|
104
108
|
}
|
|
105
109
|
async createCollection(payload, { sender }) {
|
|
106
110
|
try {
|
|
107
111
|
const { symbol, name, description = '', maxSupply, royalty = 0, baseUri = '', allowUpdates = true, updateableByOwner = false } = payload;
|
|
112
|
+
const normalizedMaxSupply = maxSupply ?? null;
|
|
108
113
|
if (!symbol.match(/^[A-Z0-9]{1,20}$/)) {
|
|
109
114
|
throw new Error('Symbol must be 1-20 uppercase alphanumeric characters');
|
|
110
115
|
}
|
|
@@ -123,23 +128,25 @@ class NFTContract {
|
|
|
123
128
|
if (baseUri && baseUri.length > 500) {
|
|
124
129
|
throw new Error('Base URI must be 500 characters or less');
|
|
125
130
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
await this.adapter.query(`
|
|
131
|
-
INSERT INTO nft_collections (symbol, name, description, creator, max_supply, royalty, base_uri, allow_updates, updateable_by_owner, created_at)
|
|
132
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
133
|
-
`, [symbol, name, description, sender, maxSupply, royalty, baseUri, allowUpdates, updateableByOwner, new Date()]);
|
|
134
|
-
await this.adapter.addEvent(new Date(), CONTRACT_NAME, 'createCollection', payload, {
|
|
135
|
-
action: 'collection_created',
|
|
136
|
-
data: {
|
|
137
|
-
symbol,
|
|
138
|
-
name,
|
|
139
|
-
creator: sender,
|
|
140
|
-
maxSupply,
|
|
141
|
-
royalty
|
|
131
|
+
await this.withTransaction(async (adapter) => {
|
|
132
|
+
const existingCollection = await adapter.query('SELECT symbol FROM nft_collections WHERE symbol = ?', [symbol]);
|
|
133
|
+
if (existingCollection && existingCollection.length > 0) {
|
|
134
|
+
throw new Error(`Collection with symbol ${symbol} already exists`);
|
|
142
135
|
}
|
|
136
|
+
await adapter.query(`
|
|
137
|
+
INSERT INTO nft_collections (symbol, name, description, creator, max_supply, royalty, base_uri, allow_updates, updateable_by_owner, created_at)
|
|
138
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
139
|
+
`, [symbol, name, description, sender, normalizedMaxSupply, royalty, baseUri, allowUpdates, updateableByOwner, new Date()]);
|
|
140
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'createCollection', payload, {
|
|
141
|
+
action: 'collection_created',
|
|
142
|
+
data: {
|
|
143
|
+
symbol,
|
|
144
|
+
name,
|
|
145
|
+
creator: sender,
|
|
146
|
+
maxSupply,
|
|
147
|
+
royalty
|
|
148
|
+
}
|
|
149
|
+
});
|
|
143
150
|
});
|
|
144
151
|
console.log(`[NFTContract] Collection ${symbol} created by ${sender}`);
|
|
145
152
|
}
|
|
@@ -168,30 +175,34 @@ class NFTContract {
|
|
|
168
175
|
if (attributes && attributes.length > 1000) {
|
|
169
176
|
throw new Error('Attributes must be 1000 characters or less');
|
|
170
177
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
181
|
-
`, [tokenId, collectionSymbol, to, metadata, attributes, new Date(), sender]);
|
|
182
|
-
await this.adapter.query('UPDATE nft_collections SET current_supply = current_supply + 1 WHERE symbol = ?', [collectionSymbol]);
|
|
183
|
-
await this.adapter.query(`
|
|
184
|
-
INSERT INTO nft_transfers (token_id, collection_symbol, from_account, to_account, transfer_type, block_number, transaction_id, timestamp)
|
|
185
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
186
|
-
`, [tokenId, collectionSymbol, 'null', to, 'mint', this.blockNumber, this.transactionId, new Date()]);
|
|
187
|
-
await this.adapter.addEvent(new Date(), CONTRACT_NAME, 'mintNFT', payload, {
|
|
188
|
-
action: 'nft_minted',
|
|
189
|
-
data: {
|
|
190
|
-
tokenId,
|
|
191
|
-
collectionSymbol,
|
|
192
|
-
to,
|
|
193
|
-
mintedBy: sender
|
|
178
|
+
await this.withTransaction(async (adapter) => {
|
|
179
|
+
// Re-read supply inside the transaction to prevent race conditions
|
|
180
|
+
const collectionCheck = await adapter.query('SELECT max_supply, current_supply FROM nft_collections WHERE symbol = ?', [collectionSymbol]);
|
|
181
|
+
if (collectionCheck[0]?.max_supply && collectionCheck[0].current_supply >= collectionCheck[0].max_supply) {
|
|
182
|
+
throw new Error('Collection has reached maximum supply');
|
|
183
|
+
}
|
|
184
|
+
const existingToken = await adapter.query('SELECT token_id FROM nft_tokens WHERE token_id = ? AND collection_symbol = ?', [tokenId, collectionSymbol]);
|
|
185
|
+
if (existingToken && existingToken.length > 0) {
|
|
186
|
+
throw new Error(`Token ${tokenId} already exists in collection ${collectionSymbol}`);
|
|
194
187
|
}
|
|
188
|
+
await adapter.query(`
|
|
189
|
+
INSERT INTO nft_tokens (token_id, collection_symbol, owner, metadata, attributes, minted_at, minted_by)
|
|
190
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
191
|
+
`, [tokenId, collectionSymbol, to, metadata, attributes, new Date(), sender]);
|
|
192
|
+
await adapter.query('UPDATE nft_collections SET current_supply = current_supply + 1 WHERE symbol = ?', [collectionSymbol]);
|
|
193
|
+
await adapter.query(`
|
|
194
|
+
INSERT INTO nft_transfers (token_id, collection_symbol, from_account, to_account, transfer_type, block_number, transaction_id, timestamp)
|
|
195
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
196
|
+
`, [tokenId, collectionSymbol, 'null', to, 'mint', this.blockNumber, this.transactionId, new Date()]);
|
|
197
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'mintNFT', payload, {
|
|
198
|
+
action: 'nft_minted',
|
|
199
|
+
data: {
|
|
200
|
+
tokenId,
|
|
201
|
+
collectionSymbol,
|
|
202
|
+
to,
|
|
203
|
+
mintedBy: sender
|
|
204
|
+
}
|
|
205
|
+
});
|
|
195
206
|
});
|
|
196
207
|
console.log(`[NFTContract] NFT ${tokenId} minted in collection ${collectionSymbol} to ${to}`);
|
|
197
208
|
}
|
|
@@ -246,16 +257,18 @@ class NFTContract {
|
|
|
246
257
|
throw new Error('No update fields provided');
|
|
247
258
|
}
|
|
248
259
|
updateValues.push(tokenId, collectionSymbol);
|
|
249
|
-
await this.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
260
|
+
await this.withTransaction(async (adapter) => {
|
|
261
|
+
await adapter.query(`UPDATE nft_tokens SET ${updateFields.join(', ')} WHERE token_id = ? AND collection_symbol = ?`, updateValues);
|
|
262
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'updateNFT', payload, {
|
|
263
|
+
action: 'nft_updated',
|
|
264
|
+
data: {
|
|
265
|
+
tokenId,
|
|
266
|
+
collectionSymbol,
|
|
267
|
+
updatedBy: sender,
|
|
268
|
+
metadata: metadata !== undefined ? metadata : 'unchanged',
|
|
269
|
+
attributes: attributes !== undefined ? attributes : 'unchanged'
|
|
270
|
+
}
|
|
271
|
+
});
|
|
259
272
|
});
|
|
260
273
|
console.log(`[NFTContract] NFT ${tokenId} updated by ${sender}`);
|
|
261
274
|
}
|
|
@@ -278,23 +291,25 @@ class NFTContract {
|
|
|
278
291
|
if (tokenData.owner === to) {
|
|
279
292
|
throw new Error('Cannot transfer to the same address');
|
|
280
293
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
await this.adapter.query('UPDATE nft_tokens SET owner = ? WHERE token_id = ? AND collection_symbol = ?', [to, tokenId, collectionSymbol]);
|
|
286
|
-
await this.adapter.query(`
|
|
287
|
-
INSERT INTO nft_transfers (token_id, collection_symbol, from_account, to_account, transfer_type, block_number, transaction_id, timestamp)
|
|
288
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
289
|
-
`, [tokenId, collectionSymbol, sender, to, 'transfer', this.blockNumber, this.transactionId, new Date()]);
|
|
290
|
-
await this.adapter.addEvent(new Date(), CONTRACT_NAME, 'transferNFT', payload, {
|
|
291
|
-
action: 'nft_transferred',
|
|
292
|
-
data: {
|
|
293
|
-
tokenId,
|
|
294
|
-
collectionSymbol,
|
|
295
|
-
from: sender,
|
|
296
|
-
to
|
|
294
|
+
await this.withTransaction(async (adapter) => {
|
|
295
|
+
const activeListings = await adapter.query('SELECT id FROM nft_listings WHERE token_id = ? AND collection_symbol = ? AND seller = ? AND active = TRUE', [tokenId, collectionSymbol, sender]);
|
|
296
|
+
if (activeListings && activeListings.length > 0) {
|
|
297
|
+
await adapter.query('UPDATE nft_listings SET active = FALSE WHERE token_id = ? AND collection_symbol = ? AND seller = ?', [tokenId, collectionSymbol, sender]);
|
|
297
298
|
}
|
|
299
|
+
await adapter.query('UPDATE nft_tokens SET owner = ? WHERE token_id = ? AND collection_symbol = ?', [to, tokenId, collectionSymbol]);
|
|
300
|
+
await adapter.query(`
|
|
301
|
+
INSERT INTO nft_transfers (token_id, collection_symbol, from_account, to_account, transfer_type, block_number, transaction_id, timestamp)
|
|
302
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
303
|
+
`, [tokenId, collectionSymbol, sender, to, 'transfer', this.blockNumber, this.transactionId, new Date()]);
|
|
304
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'transferNFT', payload, {
|
|
305
|
+
action: 'nft_transferred',
|
|
306
|
+
data: {
|
|
307
|
+
tokenId,
|
|
308
|
+
collectionSymbol,
|
|
309
|
+
from: sender,
|
|
310
|
+
to
|
|
311
|
+
}
|
|
312
|
+
});
|
|
298
313
|
});
|
|
299
314
|
console.log(`[NFTContract] NFT ${tokenId} transferred from ${sender} to ${to}`);
|
|
300
315
|
}
|
|
@@ -314,20 +329,22 @@ class NFTContract {
|
|
|
314
329
|
if (tokenData.owner !== sender) {
|
|
315
330
|
throw new Error('Only the token owner can burn the NFT');
|
|
316
331
|
}
|
|
317
|
-
await this.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
332
|
+
await this.withTransaction(async (adapter) => {
|
|
333
|
+
await adapter.query('UPDATE nft_listings SET active = FALSE WHERE token_id = ? AND collection_symbol = ?', [tokenId, collectionSymbol]);
|
|
334
|
+
await adapter.query('UPDATE nft_tokens SET burned = TRUE WHERE token_id = ? AND collection_symbol = ?', [tokenId, collectionSymbol]);
|
|
335
|
+
await adapter.query('UPDATE nft_collections SET current_supply = current_supply - 1 WHERE symbol = ?', [collectionSymbol]);
|
|
336
|
+
await adapter.query(`
|
|
337
|
+
INSERT INTO nft_transfers (token_id, collection_symbol, from_account, to_account, transfer_type, block_number, transaction_id, timestamp)
|
|
338
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
339
|
+
`, [tokenId, collectionSymbol, sender, 'null', 'burn', this.blockNumber, this.transactionId, new Date()]);
|
|
340
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'burnNFT', payload, {
|
|
341
|
+
action: 'nft_burned',
|
|
342
|
+
data: {
|
|
343
|
+
tokenId,
|
|
344
|
+
collectionSymbol,
|
|
345
|
+
burnedBy: sender
|
|
346
|
+
}
|
|
347
|
+
});
|
|
331
348
|
});
|
|
332
349
|
console.log(`[NFTContract] NFT ${tokenId} burned by ${sender}`);
|
|
333
350
|
}
|
|
@@ -354,20 +371,22 @@ class NFTContract {
|
|
|
354
371
|
if (!['HIVE', 'HBD'].includes(currency)) {
|
|
355
372
|
throw new Error('Currency must be HIVE or HBD');
|
|
356
373
|
}
|
|
357
|
-
await this.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
374
|
+
await this.withTransaction(async (adapter) => {
|
|
375
|
+
await adapter.query('UPDATE nft_listings SET active = FALSE WHERE token_id = ? AND collection_symbol = ? AND seller = ?', [tokenId, collectionSymbol, sender]);
|
|
376
|
+
await adapter.query(`
|
|
377
|
+
INSERT INTO nft_listings (token_id, collection_symbol, seller, price, currency, listed_at)
|
|
378
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
379
|
+
`, [tokenId, collectionSymbol, sender, price, currency, new Date()]);
|
|
380
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'listNFT', payload, {
|
|
381
|
+
action: 'nft_listed',
|
|
382
|
+
data: {
|
|
383
|
+
tokenId,
|
|
384
|
+
collectionSymbol,
|
|
385
|
+
seller: sender,
|
|
386
|
+
price,
|
|
387
|
+
currency
|
|
388
|
+
}
|
|
389
|
+
});
|
|
371
390
|
});
|
|
372
391
|
console.log(`[NFTContract] NFT ${tokenId} listed by ${sender} for ${price} ${currency}`);
|
|
373
392
|
}
|
|
@@ -383,14 +402,16 @@ class NFTContract {
|
|
|
383
402
|
if (!listing || listing.length === 0) {
|
|
384
403
|
throw new Error(`No active listing found for token ${tokenId}`);
|
|
385
404
|
}
|
|
386
|
-
await this.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
405
|
+
await this.withTransaction(async (adapter) => {
|
|
406
|
+
await adapter.query('UPDATE nft_listings SET active = FALSE WHERE token_id = ? AND collection_symbol = ? AND seller = ?', [tokenId, collectionSymbol, sender]);
|
|
407
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'unlistNFT', payload, {
|
|
408
|
+
action: 'nft_unlisted',
|
|
409
|
+
data: {
|
|
410
|
+
tokenId,
|
|
411
|
+
collectionSymbol,
|
|
412
|
+
seller: sender
|
|
413
|
+
}
|
|
414
|
+
});
|
|
394
415
|
});
|
|
395
416
|
console.log(`[NFTContract] NFT ${tokenId} unlisted by ${sender}`);
|
|
396
417
|
}
|
|
@@ -421,32 +442,42 @@ class NFTContract {
|
|
|
421
442
|
const collection = await this.adapter.query('SELECT royalty, creator FROM nft_collections WHERE symbol = ?', [collectionSymbol]);
|
|
422
443
|
let royaltyAmount = new bignumber_js_1.default(0);
|
|
423
444
|
let sellerAmount = paidAmount;
|
|
445
|
+
let royaltyRecipient = null;
|
|
424
446
|
if (collection && collection.length > 0 && collection[0].royalty > 0) {
|
|
425
447
|
royaltyAmount = paidAmount.multipliedBy(collection[0].royalty);
|
|
426
448
|
sellerAmount = paidAmount.minus(royaltyAmount);
|
|
427
449
|
if (royaltyAmount.gt(0) && collection[0].creator !== listingData.seller) {
|
|
428
|
-
|
|
450
|
+
royaltyRecipient = collection[0].creator;
|
|
429
451
|
}
|
|
430
452
|
}
|
|
431
|
-
await this.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
453
|
+
await this.withTransaction(async (adapter) => {
|
|
454
|
+
await adapter.query('UPDATE nft_tokens SET owner = ? WHERE token_id = ? AND collection_symbol = ?', [sender, tokenId, collectionSymbol]);
|
|
455
|
+
await adapter.query('UPDATE nft_listings SET active = FALSE WHERE token_id = ? AND collection_symbol = ?', [tokenId, collectionSymbol]);
|
|
456
|
+
await adapter.query(`
|
|
457
|
+
INSERT INTO nft_transfers (token_id, collection_symbol, from_account, to_account, transfer_type, price, currency, block_number, transaction_id, timestamp)
|
|
458
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
459
|
+
`, [tokenId, collectionSymbol, listingData.seller, sender, 'sale', amount, asset, this.blockNumber, this.transactionId, new Date()]);
|
|
460
|
+
await adapter.addEvent(new Date(), CONTRACT_NAME, 'buyNFT', payload, {
|
|
461
|
+
action: 'nft_sold',
|
|
462
|
+
data: {
|
|
463
|
+
tokenId,
|
|
464
|
+
collectionSymbol,
|
|
465
|
+
seller: listingData.seller,
|
|
466
|
+
buyer: sender,
|
|
467
|
+
price: amount,
|
|
468
|
+
currency: asset,
|
|
469
|
+
royaltyAmount: royaltyAmount.toFixed(3),
|
|
470
|
+
sellerAmount: sellerAmount.toFixed(3)
|
|
471
|
+
}
|
|
472
|
+
});
|
|
449
473
|
});
|
|
474
|
+
// Pay the seller using the Streamer's transfer method
|
|
475
|
+
const contractAccount = this._instance.config?.HIVE_ACCOUNT || 'beggars';
|
|
476
|
+
await this._instance.transferHiveTokens(contractAccount, listingData.seller, sellerAmount.toFixed(3), asset, `NFT sale: ${tokenId} from ${collectionSymbol}`);
|
|
477
|
+
// Pay royalty to creator if applicable
|
|
478
|
+
if (royaltyRecipient && royaltyAmount.gt(0)) {
|
|
479
|
+
await this._instance.transferHiveTokens(contractAccount, royaltyRecipient, royaltyAmount.toFixed(3), asset, `NFT royalty: ${tokenId} from ${collectionSymbol}`);
|
|
480
|
+
}
|
|
450
481
|
console.log(`[NFTContract] NFT ${tokenId} sold to ${sender} for ${amount} ${asset}`);
|
|
451
482
|
}
|
|
452
483
|
catch (error) {
|
|
@@ -572,9 +603,22 @@ function createNFTContract(options = {}) {
|
|
|
572
603
|
unlistNFT: (0, contract_1.action)((payload, ctx) => callWithContext(payload, ctx, instance.unlistNFT.bind(instance), { sender: ctx.sender }), {
|
|
573
604
|
trigger: 'custom_json'
|
|
574
605
|
}),
|
|
575
|
-
buyNFT: (0, contract_1.action)((payload, ctx) => {
|
|
606
|
+
buyNFT: (0, contract_1.action)(async (payload, ctx) => {
|
|
576
607
|
const amountRaw = ctx.transfer?.rawAmount || '';
|
|
608
|
+
if (!amountRaw || !amountRaw.includes(' ')) {
|
|
609
|
+
throw new Error('Invalid transfer amount format');
|
|
610
|
+
}
|
|
577
611
|
const [amount, asset] = amountRaw.split(' ');
|
|
612
|
+
if (!amount || !asset || isNaN(Number(amount))) {
|
|
613
|
+
throw new Error('Invalid transfer amount');
|
|
614
|
+
}
|
|
615
|
+
// Verify the transfer actually happened on-chain
|
|
616
|
+
const transaction = await utils_1.Utils.getTransaction(instance._instance['client'], ctx.block.number, ctx.transaction.id);
|
|
617
|
+
const contractAccount = instance._instance.config?.HIVE_ACCOUNT || 'beggars';
|
|
618
|
+
const verified = await utils_1.Utils.verifyTransfer(transaction, ctx.sender, contractAccount, amountRaw);
|
|
619
|
+
if (!verified) {
|
|
620
|
+
throw new Error('Transfer verification failed');
|
|
621
|
+
}
|
|
578
622
|
return callWithContext(payload, ctx, instance.buyNFT.bind(instance), {
|
|
579
623
|
sender: ctx.sender,
|
|
580
624
|
amount,
|