hive-stream 2.0.5 → 3.0.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/.claude/settings.local.json +12 -0
- package/.env.example +2 -2
- package/.travis.yml +11 -11
- package/CHANGELOG.md +166 -0
- package/CLAUDE.md +75 -0
- package/LICENSE +21 -21
- package/README.md +338 -238
- package/dist/actions.d.ts +41 -10
- package/dist/actions.js +126 -23
- package/dist/actions.js.map +1 -1
- package/dist/adapters/base.adapter.d.ts +25 -25
- package/dist/adapters/base.adapter.js +63 -49
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +50 -37
- package/dist/adapters/mongodb.adapter.js +363 -158
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +49 -0
- package/dist/adapters/postgresql.adapter.js +507 -0
- package/dist/adapters/postgresql.adapter.js.map +1 -0
- package/dist/adapters/sqlite.adapter.d.ts +40 -41
- package/dist/adapters/sqlite.adapter.js +470 -397
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.d.ts +6 -6
- package/dist/api.js +95 -55
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +16 -16
- package/dist/config.js +18 -18
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.d.ts +27 -14
- package/dist/contracts/coinflip.contract.js +253 -94
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/dice.contract.d.ts +37 -29
- package/dist/contracts/dice.contract.js +282 -155
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/lotto.contract.d.ts +20 -20
- package/dist/contracts/lotto.contract.js +246 -246
- package/dist/contracts/nft.contract.d.ts +24 -0
- package/dist/contracts/nft.contract.js +533 -0
- package/dist/contracts/nft.contract.js.map +1 -0
- package/dist/contracts/token.contract.d.ts +18 -0
- package/dist/contracts/token.contract.js +263 -0
- package/dist/contracts/token.contract.js.map +1 -0
- package/dist/exchanges/bittrex.d.ts +6 -6
- package/dist/exchanges/bittrex.js +34 -34
- package/dist/exchanges/coingecko.d.ts +5 -0
- package/dist/exchanges/coingecko.js +40 -0
- package/dist/exchanges/coingecko.js.map +1 -0
- package/dist/exchanges/exchange.d.ts +9 -9
- package/dist/exchanges/exchange.js +26 -26
- package/dist/hive-rates.d.ts +9 -9
- package/dist/hive-rates.js +121 -75
- package/dist/hive-rates.js.map +1 -1
- package/dist/index.d.ts +12 -11
- package/dist/index.js +33 -32
- package/dist/index.js.map +1 -1
- package/dist/streamer.d.ts +140 -93
- package/dist/streamer.js +793 -545
- package/dist/streamer.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/dist/test.js +25 -25
- package/dist/test.js.map +1 -1
- package/dist/types/hive-stream.d.ts +35 -6
- package/dist/types/hive-stream.js +2 -2
- package/dist/utils.d.ts +27 -27
- package/dist/utils.js +271 -261
- package/dist/utils.js.map +1 -1
- package/ecosystem.config.js +17 -17
- package/jest.config.js +8 -8
- package/package.json +53 -48
- package/test-contract-block.md +18 -18
- package/tests/actions.spec.ts +252 -0
- package/tests/adapters/actions-persistence.spec.ts +144 -0
- package/tests/adapters/postgresql.adapter.spec.ts +127 -0
- package/tests/adapters/sqlite.adapter.spec.ts +180 -42
- package/tests/contracts/coinflip.contract.spec.ts +221 -131
- package/tests/contracts/dice.contract.spec.ts +202 -159
- package/tests/contracts/entrants.json +728 -728
- package/tests/contracts/lotto.contract.spec.ts +323 -323
- package/tests/contracts/nft.contract.spec.ts +948 -0
- package/tests/contracts/token.contract.spec.ts +334 -0
- package/tests/helpers/mock-adapter.ts +214 -0
- package/tests/setup.ts +29 -18
- package/tests/streamer-actions.spec.ts +263 -0
- package/tests/streamer.spec.ts +248 -151
- package/tests/utils.spec.ts +91 -94
- package/tsconfig.build.json +3 -22
- package/tslint.json +20 -20
- package/wallaby.js +26 -26
- package/.env +0 -3
|
@@ -1,247 +1,247 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.LottoContract = void 0;
|
|
7
|
-
const utils_1 = require("@hiveio/dhive/lib/utils");
|
|
8
|
-
const utils_2 = require("./../utils");
|
|
9
|
-
const seedrandom_1 = __importDefault(require("seedrandom"));
|
|
10
|
-
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
11
|
-
const CONTRACT_NAME = 'hivelotto';
|
|
12
|
-
const ACCOUNT = 'beggars';
|
|
13
|
-
const FEE_ACCOUNT = 'beggars';
|
|
14
|
-
const TOKEN_SYMBOL = 'HIVE';
|
|
15
|
-
const VALID_CURRENCIES = ['HIVE'];
|
|
16
|
-
const VALID_DRAW_TYPES = ['hourly', 'daily'];
|
|
17
|
-
// How much does a ticket cost?
|
|
18
|
-
const COST = 10;
|
|
19
|
-
// Minimum number of entries required for draws to payout
|
|
20
|
-
const MIN_ENTRIES_HOURLY = 25;
|
|
21
|
-
const MIN_ENTRIES_DAILY = 100;
|
|
22
|
-
// How many winners to pick for the hourly draw
|
|
23
|
-
const HOURLY_WINNERS_PICK = 3;
|
|
24
|
-
// How many winners to pick for the daily draw
|
|
25
|
-
const DAILY_WINNERS_PICK = 10;
|
|
26
|
-
// Max tickets per user, prevents users from harvesting by overwhelmingly buying tickets
|
|
27
|
-
const MAX_TICKETS_PER_USER = 3;
|
|
28
|
-
// The percentage the site keeps (5%)
|
|
29
|
-
const PERCENTAGE = 5;
|
|
30
|
-
const COLLECTION_LOTTERY = 'lottery';
|
|
31
|
-
const COLLECTION_SETTINGS = 'settings';
|
|
32
|
-
const COLLECTION_WINNERS = 'winners';
|
|
33
|
-
function rng(previousBlockId, blockId, transactionId, entropy, maximum = 100) {
|
|
34
|
-
const random = (0, seedrandom_1.default)(`${previousBlockId}${blockId}${transactionId}${entropy}`).double();
|
|
35
|
-
const randomRoll = Math.floor(random * maximum) + 1;
|
|
36
|
-
return randomRoll;
|
|
37
|
-
}
|
|
38
|
-
class LottoContract {
|
|
39
|
-
// tslint:disable-next-line: variable-name
|
|
40
|
-
_instance;
|
|
41
|
-
adapter;
|
|
42
|
-
blockNumber;
|
|
43
|
-
blockId;
|
|
44
|
-
previousBlockId;
|
|
45
|
-
transactionId;
|
|
46
|
-
async create() {
|
|
47
|
-
this.adapter = this._instance.getAdapter();
|
|
48
|
-
const settings = await this.adapter.findOne(COLLECTION_SETTINGS, {});
|
|
49
|
-
if (!settings) {
|
|
50
|
-
this.adapter.insert(COLLECTION_SETTINGS, {
|
|
51
|
-
contractInitiated: new Date(),
|
|
52
|
-
enabled: true
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
destroy() {
|
|
57
|
-
// Runs every time unregister is run for this contract
|
|
58
|
-
// Close database connections, write to a database with state, etc
|
|
59
|
-
}
|
|
60
|
-
updateBlockInfo(blockNumber, blockId, previousBlockId, transactionId) {
|
|
61
|
-
// Lifecycle method which sets block info
|
|
62
|
-
this.blockNumber = blockNumber;
|
|
63
|
-
this.blockId = blockId;
|
|
64
|
-
this.previousBlockId = previousBlockId;
|
|
65
|
-
this.transactionId = transactionId;
|
|
66
|
-
}
|
|
67
|
-
async getBalance() {
|
|
68
|
-
const account = await this._instance['client'].database.getAccounts([ACCOUNT]);
|
|
69
|
-
if (account?.[0]) {
|
|
70
|
-
const balance = account[0].balance.split(' ');
|
|
71
|
-
const amount = balance[0];
|
|
72
|
-
return parseFloat(amount);
|
|
73
|
-
}
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
async getPreviousUserTicketsForCurrentDrawType(type, account) {
|
|
77
|
-
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: type });
|
|
78
|
-
if (!lotto[0] || !lotto[0].entries) {
|
|
79
|
-
return 0;
|
|
80
|
-
}
|
|
81
|
-
const userEntries = lotto[0].entries.filter(e => e.account === account);
|
|
82
|
-
return userEntries.length;
|
|
83
|
-
}
|
|
84
|
-
async buy(payload, { sender, amount }) {
|
|
85
|
-
const { type } = payload;
|
|
86
|
-
const amountTrim = amount.split(' ');
|
|
87
|
-
const amountParsed = parseFloat(amountTrim[0]);
|
|
88
|
-
const amountCurrency = amountTrim[1].trim();
|
|
89
|
-
const transaction = await this._instance.getTransaction(this.blockNumber, this.transactionId);
|
|
90
|
-
const verify = await this._instance.verifyTransfer(transaction, sender, ACCOUNT, amount);
|
|
91
|
-
if (verify) {
|
|
92
|
-
// User sent an invalid currency
|
|
93
|
-
if (!VALID_CURRENCIES.includes(amountCurrency)) {
|
|
94
|
-
await this._instance.transferHiveTokens(ACCOUNT, sender, amountTrim[0], amountTrim[1], `[Refund] You sent an invalid currency.`);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
// User did not specify a valid entry type, refund them
|
|
98
|
-
if (!VALID_DRAW_TYPES.includes(type)) {
|
|
99
|
-
await this._instance.transferHiveTokens(ACCOUNT, sender, amountTrim[0], amountTrim[1], `[Refund] You specified an invalid draw type`);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
// If the user has already entered the maximum allowed times, refund them
|
|
103
|
-
const previousEntriesCount = await this.getPreviousUserTicketsForCurrentDrawType(type, sender);
|
|
104
|
-
if (previousEntriesCount === MAX_TICKETS_PER_USER) {
|
|
105
|
-
await this._instance.transferHiveTokens(ACCOUNT, sender, amountTrim[0], amountTrim[1], `[Refund] You have exceeded the allowed number of entries`);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
// User sent too much, refund the difference
|
|
109
|
-
if (amountParsed > COST) {
|
|
110
|
-
const difference = new bignumber_js_1.default(amountParsed).minus(COST).toFixed(3);
|
|
111
|
-
await this._instance.transferHiveTokens(ACCOUNT, sender, difference, amountTrim[1], `[Refund] A ticket costs ${COST} HIVE. You sent ${amount}. You were refunded ${difference} HIVE.`);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
// Find an active lotto draw that is of status "active" and our type
|
|
115
|
-
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: type });
|
|
116
|
-
// We have a lotto
|
|
117
|
-
if (lotto.length) {
|
|
118
|
-
const draw = lotto[0];
|
|
119
|
-
draw.entries.push({
|
|
120
|
-
account: sender,
|
|
121
|
-
transactionId: this.transactionId,
|
|
122
|
-
date: new Date()
|
|
123
|
-
});
|
|
124
|
-
return await this.adapter.replace(COLLECTION_LOTTERY, { _id: draw._id }, draw);
|
|
125
|
-
}
|
|
126
|
-
// We need to create a new lotto, no active draws for this type
|
|
127
|
-
const entries = [{
|
|
128
|
-
account: sender,
|
|
129
|
-
transactionId: this.transactionId,
|
|
130
|
-
date: new Date()
|
|
131
|
-
}];
|
|
132
|
-
return await this.adapter.insert(COLLECTION_LOTTERY, { status: 'active', type: type, entries });
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async drawHourlyLottery() {
|
|
136
|
-
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: 'hourly' });
|
|
137
|
-
// We found an hourly draw
|
|
138
|
-
if (lotto.length) {
|
|
139
|
-
const draw = lotto[0];
|
|
140
|
-
const total = draw.entries.length;
|
|
141
|
-
// Number of entrants is less than the minimum
|
|
142
|
-
if (total < MIN_ENTRIES_HOURLY) {
|
|
143
|
-
const entrants = draw.entries.reduce((arr, entrant) => {
|
|
144
|
-
arr.push(entrant.account);
|
|
145
|
-
return arr;
|
|
146
|
-
}, []);
|
|
147
|
-
await this._instance.transferHiveTokensMultiple(ACCOUNT, entrants, '10.000', 'HIVE', '[Refund] The hourly lotto draw did not have enough contestants.');
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const balance = await this.getBalance();
|
|
151
|
-
// Number of entrants multiplied by the entry cost is the total for this draw
|
|
152
|
-
const winningsAmount = new bignumber_js_1.default(total).multipliedBy(COST).toNumber();
|
|
153
|
-
// Calculate how much the account gets to keep
|
|
154
|
-
const percentageFee = new bignumber_js_1.default(winningsAmount).dividedBy(100).multipliedBy(PERCENTAGE);
|
|
155
|
-
// The amount minus the percentage to pay out to winners
|
|
156
|
-
const payoutTotal = new bignumber_js_1.default(winningsAmount).minus(percentageFee);
|
|
157
|
-
// Amount each winner gets
|
|
158
|
-
const amountPerWinner = new bignumber_js_1.default(payoutTotal).dividedBy(HOURLY_WINNERS_PICK).toFixed(3);
|
|
159
|
-
// Send fee percentage to fee account
|
|
160
|
-
if (ACCOUNT !== FEE_ACCOUNT) {
|
|
161
|
-
await this._instance.transferHiveTokens(ACCOUNT, FEE_ACCOUNT, percentageFee.toFixed(3), 'HIVE', 'percentage fee');
|
|
162
|
-
}
|
|
163
|
-
// Winnings exceed balance
|
|
164
|
-
if (payoutTotal.toNumber() > balance) {
|
|
165
|
-
throw new Error('Balance is less than amount to pay out');
|
|
166
|
-
}
|
|
167
|
-
const winners = await this.getWinners(HOURLY_WINNERS_PICK, draw.entries);
|
|
168
|
-
if (winners) {
|
|
169
|
-
const winnerStrings = winners.reduce((arr, winner) => {
|
|
170
|
-
arr.push(winner.account);
|
|
171
|
-
return arr;
|
|
172
|
-
}, []);
|
|
173
|
-
await this._instance.transferHiveTokensMultiple(ACCOUNT, winnerStrings, amountPerWinner, TOKEN_SYMBOL, `Congratulations you won the hourly lottery. You won ${amountPerWinner} ${TOKEN_SYMBOL}. Winners: ${winnerStrings.join(', ')}`);
|
|
174
|
-
const losers = draw.entries
|
|
175
|
-
.filter(e => {
|
|
176
|
-
return !winnerStrings.includes(e.account);
|
|
177
|
-
})
|
|
178
|
-
.reduce((unique, value) => {
|
|
179
|
-
return unique.includes(value.account) ? unique : [...unique, value.account];
|
|
180
|
-
}, []);
|
|
181
|
-
if (losers) {
|
|
182
|
-
await this._instance.transferHiveTokensMultiple(ACCOUNT, losers, '0.001', TOKEN_SYMBOL, `Sorry, you didn't win the hourly draw. Winners: ${winnerStrings.join(', ')}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return winners;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
async drawDailyLottery() {
|
|
189
|
-
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: 'daily' });
|
|
190
|
-
// We found an hourly draw
|
|
191
|
-
if (lotto.length) {
|
|
192
|
-
const draw = lotto[0];
|
|
193
|
-
const total = draw.entries.length;
|
|
194
|
-
// Number of entrants is less than the minimum
|
|
195
|
-
if (total < MIN_ENTRIES_DAILY) {
|
|
196
|
-
for (const entrant of draw.entries) {
|
|
197
|
-
await this._instance.transferHiveTokens(ACCOUNT, entrant.account, '10.000', 'HIVE', '[Refund] The hourly lotto draw did not have enough contestants.');
|
|
198
|
-
await utils_2.Utils.sleep(3000);
|
|
199
|
-
}
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const balance = await this.getBalance();
|
|
203
|
-
// Number of entrants multiplied by the entry cost is the total for this draw
|
|
204
|
-
const winningsAmount = new bignumber_js_1.default(total).multipliedBy(COST).toNumber();
|
|
205
|
-
// Calculate how much the account gets to keep
|
|
206
|
-
const percentageFee = new bignumber_js_1.default(winningsAmount).dividedBy(100).multipliedBy(PERCENTAGE);
|
|
207
|
-
// The amount minus the percentage to pay out to winners
|
|
208
|
-
const payoutTotal = new bignumber_js_1.default(winningsAmount).minus(percentageFee);
|
|
209
|
-
// Amount each winner gets
|
|
210
|
-
const amountPerWinner = new bignumber_js_1.default(payoutTotal).dividedBy(DAILY_WINNERS_PICK).toFixed(3);
|
|
211
|
-
// Send fee percentage to fee account
|
|
212
|
-
if (ACCOUNT !== FEE_ACCOUNT) {
|
|
213
|
-
await this._instance.transferHiveTokens(ACCOUNT, FEE_ACCOUNT, percentageFee.toFixed(3), 'HIVE', 'percentage fee');
|
|
214
|
-
}
|
|
215
|
-
// Winnings exceed balance
|
|
216
|
-
if (payoutTotal.toNumber() > balance) {
|
|
217
|
-
throw new Error('Balance is less than amount to pay out');
|
|
218
|
-
}
|
|
219
|
-
const winners = await this.getWinners(DAILY_WINNERS_PICK, draw.entries);
|
|
220
|
-
if (winners) {
|
|
221
|
-
const winnerStrings = winners.reduce((arr, winner) => {
|
|
222
|
-
arr.push(winner.account);
|
|
223
|
-
return arr;
|
|
224
|
-
}, []);
|
|
225
|
-
await this._instance.transferHiveTokensMultiple(ACCOUNT, winnerStrings, amountPerWinner, TOKEN_SYMBOL, `Congratulations you won the daily lottery. You won ${amountPerWinner} ${TOKEN_SYMBOL}`);
|
|
226
|
-
}
|
|
227
|
-
return winners;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
async getWinners(count, entries) {
|
|
231
|
-
let winners = [];
|
|
232
|
-
utils_2.Utils.shuffle(entries);
|
|
233
|
-
for (const entry of entries) {
|
|
234
|
-
if (winners.length < count) {
|
|
235
|
-
const winner = entries[rng(this.previousBlockId + `${(0, seedrandom_1.default)().double()}`, this.blockId + `${(0, seedrandom_1.default)().double()}`, this.transactionId + `${(0, seedrandom_1.default)().double()}`, (0, seedrandom_1.default)().double(), entries.length - 1)];
|
|
236
|
-
winners.push(winner);
|
|
237
|
-
await (0, utils_1.sleep)(300);
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return winners;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
exports.LottoContract = LottoContract;
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LottoContract = void 0;
|
|
7
|
+
const utils_1 = require("@hiveio/dhive/lib/utils");
|
|
8
|
+
const utils_2 = require("./../utils");
|
|
9
|
+
const seedrandom_1 = __importDefault(require("seedrandom"));
|
|
10
|
+
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
11
|
+
const CONTRACT_NAME = 'hivelotto';
|
|
12
|
+
const ACCOUNT = 'beggars';
|
|
13
|
+
const FEE_ACCOUNT = 'beggars';
|
|
14
|
+
const TOKEN_SYMBOL = 'HIVE';
|
|
15
|
+
const VALID_CURRENCIES = ['HIVE'];
|
|
16
|
+
const VALID_DRAW_TYPES = ['hourly', 'daily'];
|
|
17
|
+
// How much does a ticket cost?
|
|
18
|
+
const COST = 10;
|
|
19
|
+
// Minimum number of entries required for draws to payout
|
|
20
|
+
const MIN_ENTRIES_HOURLY = 25;
|
|
21
|
+
const MIN_ENTRIES_DAILY = 100;
|
|
22
|
+
// How many winners to pick for the hourly draw
|
|
23
|
+
const HOURLY_WINNERS_PICK = 3;
|
|
24
|
+
// How many winners to pick for the daily draw
|
|
25
|
+
const DAILY_WINNERS_PICK = 10;
|
|
26
|
+
// Max tickets per user, prevents users from harvesting by overwhelmingly buying tickets
|
|
27
|
+
const MAX_TICKETS_PER_USER = 3;
|
|
28
|
+
// The percentage the site keeps (5%)
|
|
29
|
+
const PERCENTAGE = 5;
|
|
30
|
+
const COLLECTION_LOTTERY = 'lottery';
|
|
31
|
+
const COLLECTION_SETTINGS = 'settings';
|
|
32
|
+
const COLLECTION_WINNERS = 'winners';
|
|
33
|
+
function rng(previousBlockId, blockId, transactionId, entropy, maximum = 100) {
|
|
34
|
+
const random = (0, seedrandom_1.default)(`${previousBlockId}${blockId}${transactionId}${entropy}`).double();
|
|
35
|
+
const randomRoll = Math.floor(random * maximum) + 1;
|
|
36
|
+
return randomRoll;
|
|
37
|
+
}
|
|
38
|
+
class LottoContract {
|
|
39
|
+
// tslint:disable-next-line: variable-name
|
|
40
|
+
_instance;
|
|
41
|
+
adapter;
|
|
42
|
+
blockNumber;
|
|
43
|
+
blockId;
|
|
44
|
+
previousBlockId;
|
|
45
|
+
transactionId;
|
|
46
|
+
async create() {
|
|
47
|
+
this.adapter = this._instance.getAdapter();
|
|
48
|
+
const settings = await this.adapter.findOne(COLLECTION_SETTINGS, {});
|
|
49
|
+
if (!settings) {
|
|
50
|
+
this.adapter.insert(COLLECTION_SETTINGS, {
|
|
51
|
+
contractInitiated: new Date(),
|
|
52
|
+
enabled: true
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
destroy() {
|
|
57
|
+
// Runs every time unregister is run for this contract
|
|
58
|
+
// Close database connections, write to a database with state, etc
|
|
59
|
+
}
|
|
60
|
+
updateBlockInfo(blockNumber, blockId, previousBlockId, transactionId) {
|
|
61
|
+
// Lifecycle method which sets block info
|
|
62
|
+
this.blockNumber = blockNumber;
|
|
63
|
+
this.blockId = blockId;
|
|
64
|
+
this.previousBlockId = previousBlockId;
|
|
65
|
+
this.transactionId = transactionId;
|
|
66
|
+
}
|
|
67
|
+
async getBalance() {
|
|
68
|
+
const account = await this._instance['client'].database.getAccounts([ACCOUNT]);
|
|
69
|
+
if (account?.[0]) {
|
|
70
|
+
const balance = account[0].balance.split(' ');
|
|
71
|
+
const amount = balance[0];
|
|
72
|
+
return parseFloat(amount);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
async getPreviousUserTicketsForCurrentDrawType(type, account) {
|
|
77
|
+
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: type });
|
|
78
|
+
if (!lotto[0] || !lotto[0].entries) {
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
const userEntries = lotto[0].entries.filter(e => e.account === account);
|
|
82
|
+
return userEntries.length;
|
|
83
|
+
}
|
|
84
|
+
async buy(payload, { sender, amount }) {
|
|
85
|
+
const { type } = payload;
|
|
86
|
+
const amountTrim = amount.split(' ');
|
|
87
|
+
const amountParsed = parseFloat(amountTrim[0]);
|
|
88
|
+
const amountCurrency = amountTrim[1].trim();
|
|
89
|
+
const transaction = await this._instance.getTransaction(this.blockNumber, this.transactionId);
|
|
90
|
+
const verify = await this._instance.verifyTransfer(transaction, sender, ACCOUNT, amount);
|
|
91
|
+
if (verify) {
|
|
92
|
+
// User sent an invalid currency
|
|
93
|
+
if (!VALID_CURRENCIES.includes(amountCurrency)) {
|
|
94
|
+
await this._instance.transferHiveTokens(ACCOUNT, sender, amountTrim[0], amountTrim[1], `[Refund] You sent an invalid currency.`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// User did not specify a valid entry type, refund them
|
|
98
|
+
if (!VALID_DRAW_TYPES.includes(type)) {
|
|
99
|
+
await this._instance.transferHiveTokens(ACCOUNT, sender, amountTrim[0], amountTrim[1], `[Refund] You specified an invalid draw type`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// If the user has already entered the maximum allowed times, refund them
|
|
103
|
+
const previousEntriesCount = await this.getPreviousUserTicketsForCurrentDrawType(type, sender);
|
|
104
|
+
if (previousEntriesCount === MAX_TICKETS_PER_USER) {
|
|
105
|
+
await this._instance.transferHiveTokens(ACCOUNT, sender, amountTrim[0], amountTrim[1], `[Refund] You have exceeded the allowed number of entries`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// User sent too much, refund the difference
|
|
109
|
+
if (amountParsed > COST) {
|
|
110
|
+
const difference = new bignumber_js_1.default(amountParsed).minus(COST).toFixed(3);
|
|
111
|
+
await this._instance.transferHiveTokens(ACCOUNT, sender, difference, amountTrim[1], `[Refund] A ticket costs ${COST} HIVE. You sent ${amount}. You were refunded ${difference} HIVE.`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Find an active lotto draw that is of status "active" and our type
|
|
115
|
+
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: type });
|
|
116
|
+
// We have a lotto
|
|
117
|
+
if (lotto.length) {
|
|
118
|
+
const draw = lotto[0];
|
|
119
|
+
draw.entries.push({
|
|
120
|
+
account: sender,
|
|
121
|
+
transactionId: this.transactionId,
|
|
122
|
+
date: new Date()
|
|
123
|
+
});
|
|
124
|
+
return await this.adapter.replace(COLLECTION_LOTTERY, { _id: draw._id }, draw);
|
|
125
|
+
}
|
|
126
|
+
// We need to create a new lotto, no active draws for this type
|
|
127
|
+
const entries = [{
|
|
128
|
+
account: sender,
|
|
129
|
+
transactionId: this.transactionId,
|
|
130
|
+
date: new Date()
|
|
131
|
+
}];
|
|
132
|
+
return await this.adapter.insert(COLLECTION_LOTTERY, { status: 'active', type: type, entries });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async drawHourlyLottery() {
|
|
136
|
+
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: 'hourly' });
|
|
137
|
+
// We found an hourly draw
|
|
138
|
+
if (lotto.length) {
|
|
139
|
+
const draw = lotto[0];
|
|
140
|
+
const total = draw.entries.length;
|
|
141
|
+
// Number of entrants is less than the minimum
|
|
142
|
+
if (total < MIN_ENTRIES_HOURLY) {
|
|
143
|
+
const entrants = draw.entries.reduce((arr, entrant) => {
|
|
144
|
+
arr.push(entrant.account);
|
|
145
|
+
return arr;
|
|
146
|
+
}, []);
|
|
147
|
+
await this._instance.transferHiveTokensMultiple(ACCOUNT, entrants, '10.000', 'HIVE', '[Refund] The hourly lotto draw did not have enough contestants.');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const balance = await this.getBalance();
|
|
151
|
+
// Number of entrants multiplied by the entry cost is the total for this draw
|
|
152
|
+
const winningsAmount = new bignumber_js_1.default(total).multipliedBy(COST).toNumber();
|
|
153
|
+
// Calculate how much the account gets to keep
|
|
154
|
+
const percentageFee = new bignumber_js_1.default(winningsAmount).dividedBy(100).multipliedBy(PERCENTAGE);
|
|
155
|
+
// The amount minus the percentage to pay out to winners
|
|
156
|
+
const payoutTotal = new bignumber_js_1.default(winningsAmount).minus(percentageFee);
|
|
157
|
+
// Amount each winner gets
|
|
158
|
+
const amountPerWinner = new bignumber_js_1.default(payoutTotal).dividedBy(HOURLY_WINNERS_PICK).toFixed(3);
|
|
159
|
+
// Send fee percentage to fee account
|
|
160
|
+
if (ACCOUNT !== FEE_ACCOUNT) {
|
|
161
|
+
await this._instance.transferHiveTokens(ACCOUNT, FEE_ACCOUNT, percentageFee.toFixed(3), 'HIVE', 'percentage fee');
|
|
162
|
+
}
|
|
163
|
+
// Winnings exceed balance
|
|
164
|
+
if (payoutTotal.toNumber() > balance) {
|
|
165
|
+
throw new Error('Balance is less than amount to pay out');
|
|
166
|
+
}
|
|
167
|
+
const winners = await this.getWinners(HOURLY_WINNERS_PICK, draw.entries);
|
|
168
|
+
if (winners) {
|
|
169
|
+
const winnerStrings = winners.reduce((arr, winner) => {
|
|
170
|
+
arr.push(winner.account);
|
|
171
|
+
return arr;
|
|
172
|
+
}, []);
|
|
173
|
+
await this._instance.transferHiveTokensMultiple(ACCOUNT, winnerStrings, amountPerWinner, TOKEN_SYMBOL, `Congratulations you won the hourly lottery. You won ${amountPerWinner} ${TOKEN_SYMBOL}. Winners: ${winnerStrings.join(', ')}`);
|
|
174
|
+
const losers = draw.entries
|
|
175
|
+
.filter(e => {
|
|
176
|
+
return !winnerStrings.includes(e.account);
|
|
177
|
+
})
|
|
178
|
+
.reduce((unique, value) => {
|
|
179
|
+
return unique.includes(value.account) ? unique : [...unique, value.account];
|
|
180
|
+
}, []);
|
|
181
|
+
if (losers) {
|
|
182
|
+
await this._instance.transferHiveTokensMultiple(ACCOUNT, losers, '0.001', TOKEN_SYMBOL, `Sorry, you didn't win the hourly draw. Winners: ${winnerStrings.join(', ')}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return winners;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async drawDailyLottery() {
|
|
189
|
+
const lotto = await this.adapter.find(COLLECTION_LOTTERY, { status: 'active', type: 'daily' });
|
|
190
|
+
// We found an hourly draw
|
|
191
|
+
if (lotto.length) {
|
|
192
|
+
const draw = lotto[0];
|
|
193
|
+
const total = draw.entries.length;
|
|
194
|
+
// Number of entrants is less than the minimum
|
|
195
|
+
if (total < MIN_ENTRIES_DAILY) {
|
|
196
|
+
for (const entrant of draw.entries) {
|
|
197
|
+
await this._instance.transferHiveTokens(ACCOUNT, entrant.account, '10.000', 'HIVE', '[Refund] The hourly lotto draw did not have enough contestants.');
|
|
198
|
+
await utils_2.Utils.sleep(3000);
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const balance = await this.getBalance();
|
|
203
|
+
// Number of entrants multiplied by the entry cost is the total for this draw
|
|
204
|
+
const winningsAmount = new bignumber_js_1.default(total).multipliedBy(COST).toNumber();
|
|
205
|
+
// Calculate how much the account gets to keep
|
|
206
|
+
const percentageFee = new bignumber_js_1.default(winningsAmount).dividedBy(100).multipliedBy(PERCENTAGE);
|
|
207
|
+
// The amount minus the percentage to pay out to winners
|
|
208
|
+
const payoutTotal = new bignumber_js_1.default(winningsAmount).minus(percentageFee);
|
|
209
|
+
// Amount each winner gets
|
|
210
|
+
const amountPerWinner = new bignumber_js_1.default(payoutTotal).dividedBy(DAILY_WINNERS_PICK).toFixed(3);
|
|
211
|
+
// Send fee percentage to fee account
|
|
212
|
+
if (ACCOUNT !== FEE_ACCOUNT) {
|
|
213
|
+
await this._instance.transferHiveTokens(ACCOUNT, FEE_ACCOUNT, percentageFee.toFixed(3), 'HIVE', 'percentage fee');
|
|
214
|
+
}
|
|
215
|
+
// Winnings exceed balance
|
|
216
|
+
if (payoutTotal.toNumber() > balance) {
|
|
217
|
+
throw new Error('Balance is less than amount to pay out');
|
|
218
|
+
}
|
|
219
|
+
const winners = await this.getWinners(DAILY_WINNERS_PICK, draw.entries);
|
|
220
|
+
if (winners) {
|
|
221
|
+
const winnerStrings = winners.reduce((arr, winner) => {
|
|
222
|
+
arr.push(winner.account);
|
|
223
|
+
return arr;
|
|
224
|
+
}, []);
|
|
225
|
+
await this._instance.transferHiveTokensMultiple(ACCOUNT, winnerStrings, amountPerWinner, TOKEN_SYMBOL, `Congratulations you won the daily lottery. You won ${amountPerWinner} ${TOKEN_SYMBOL}`);
|
|
226
|
+
}
|
|
227
|
+
return winners;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async getWinners(count, entries) {
|
|
231
|
+
let winners = [];
|
|
232
|
+
utils_2.Utils.shuffle(entries);
|
|
233
|
+
for (const entry of entries) {
|
|
234
|
+
if (winners.length < count) {
|
|
235
|
+
const winner = entries[rng(this.previousBlockId + `${(0, seedrandom_1.default)().double()}`, this.blockId + `${(0, seedrandom_1.default)().double()}`, this.transactionId + `${(0, seedrandom_1.default)().double()}`, (0, seedrandom_1.default)().double(), entries.length - 1)];
|
|
236
|
+
winners.push(winner);
|
|
237
|
+
await (0, utils_1.sleep)(300);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return winners;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
exports.LottoContract = LottoContract;
|
|
247
247
|
//# sourceMappingURL=lotto.contract.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Streamer } from '../streamer';
|
|
2
|
+
export declare class NFTContract {
|
|
3
|
+
_instance: Streamer;
|
|
4
|
+
private adapter;
|
|
5
|
+
private blockNumber;
|
|
6
|
+
private blockId;
|
|
7
|
+
private previousBlockId;
|
|
8
|
+
private transactionId;
|
|
9
|
+
create(): Promise<void>;
|
|
10
|
+
destroy(): void;
|
|
11
|
+
updateBlockInfo(blockNumber: number, blockId: string, previousBlockId: string, transactionId: string): void;
|
|
12
|
+
private initializeNFTTables;
|
|
13
|
+
private createCollection;
|
|
14
|
+
private mintNFT;
|
|
15
|
+
private updateNFT;
|
|
16
|
+
private transferNFT;
|
|
17
|
+
private burnNFT;
|
|
18
|
+
private listNFT;
|
|
19
|
+
private unlistNFT;
|
|
20
|
+
private buyNFT;
|
|
21
|
+
private getTokenInfo;
|
|
22
|
+
private getCollectionInfo;
|
|
23
|
+
private getUserTokens;
|
|
24
|
+
}
|