@moneypot/hub 1.9.0-dev.1 → 1.9.0-dev.10
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/dist/src/db/index.js +1 -1
- package/dist/src/format-currency.d.ts +9 -0
- package/dist/src/format-currency.js +40 -0
- package/dist/src/index.js +1 -1
- package/dist/src/logger.d.ts +2 -2
- package/dist/src/logger.js +1 -1
- package/dist/src/plugins/hub-make-outcome-bet.d.ts +4 -3
- package/dist/src/plugins/hub-make-outcome-bet.js +34 -9
- package/dist/src/process-transfers/index.js +1 -1
- package/dist/src/process-withdrawal-request.js +1 -1
- package/package.json +1 -1
package/dist/src/db/index.js
CHANGED
|
@@ -132,7 +132,7 @@ export async function getTransferCursor(pgClient, { casinoId, }) {
|
|
|
132
132
|
return row?.cursor;
|
|
133
133
|
}
|
|
134
134
|
export async function setTransferCursor(pgClient, { cursor, casinoId, }) {
|
|
135
|
-
logger.debug(cursor, `[setTransferCursor] Setting cursor`);
|
|
135
|
+
logger.debug({ cursor }, `[setTransferCursor] Setting cursor`);
|
|
136
136
|
await pgClient.query(`
|
|
137
137
|
insert into hub_hidden.transfer_cursor (casino_id, cursor)
|
|
138
138
|
values ($1, $2)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function formatCurrency(amount: number, currency: {
|
|
2
|
+
displayUnitScale: number;
|
|
3
|
+
displayUnitName: string;
|
|
4
|
+
}, options?: {
|
|
5
|
+
excludeUnit?: boolean;
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function pluralize(word: string, count: number, suffix?: string): string;
|
|
8
|
+
export declare function getDecimalPlaces(displayUnitScale: number): number;
|
|
9
|
+
export declare function truncateDecimalPrecision(value: number, decimals: number): number;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function formatCurrency(amount, currency, options = {
|
|
2
|
+
excludeUnit: false,
|
|
3
|
+
}) {
|
|
4
|
+
const decimalPlaces = getDecimalPlaces(currency.displayUnitScale);
|
|
5
|
+
const scaledAmount = amount / (currency.displayUnitScale || 1);
|
|
6
|
+
const truncatedAmount = truncateDecimalPrecision(scaledAmount, decimalPlaces);
|
|
7
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
8
|
+
minimumFractionDigits: decimalPlaces,
|
|
9
|
+
maximumFractionDigits: decimalPlaces,
|
|
10
|
+
useGrouping: true,
|
|
11
|
+
});
|
|
12
|
+
const formatted = formatter.format(truncatedAmount);
|
|
13
|
+
if (options.excludeUnit) {
|
|
14
|
+
return formatted;
|
|
15
|
+
}
|
|
16
|
+
return `${formatted} ${pluralize(currency.displayUnitName, truncatedAmount)}`;
|
|
17
|
+
}
|
|
18
|
+
export function pluralize(word, count, suffix = "s") {
|
|
19
|
+
return count === 1 ? word : word + suffix;
|
|
20
|
+
}
|
|
21
|
+
export function getDecimalPlaces(displayUnitScale) {
|
|
22
|
+
return displayUnitScale < 10 ? 0 : Math.log10(displayUnitScale);
|
|
23
|
+
}
|
|
24
|
+
export function truncateDecimalPrecision(value, decimals) {
|
|
25
|
+
if (decimals < 0) {
|
|
26
|
+
throw new Error("Decimals must be a non-negative integer");
|
|
27
|
+
}
|
|
28
|
+
if (!Number.isFinite(value)) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
const str = value.toString();
|
|
32
|
+
const dotIndex = str.indexOf(".");
|
|
33
|
+
if (dotIndex === -1) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
const truncatedStr = decimals === 0
|
|
37
|
+
? str.substring(0, dotIndex)
|
|
38
|
+
: str.substring(0, dotIndex + decimals + 1);
|
|
39
|
+
return parseFloat(truncatedStr);
|
|
40
|
+
}
|
package/dist/src/index.js
CHANGED
|
@@ -22,7 +22,7 @@ async function initialize(options) {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
catch (e) {
|
|
25
|
-
logger.error("Error upgrading core schema"
|
|
25
|
+
logger.error(e, "Error upgrading core schema");
|
|
26
26
|
if (e instanceof DatabaseAheadError) {
|
|
27
27
|
logger.error(`${"⚠️".repeat(10)}\n@moneypot/hub database was reset to prepare for a production release and you must reset your database to continue. Please see <https://www.npmjs.com/package/@moneypot/hub#change-log> for more info.`);
|
|
28
28
|
process.exit(1);
|
package/dist/src/logger.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { type Logger } from "pino";
|
|
1
|
+
import pino, { type Logger } from "pino";
|
|
2
2
|
export { type Logger };
|
|
3
|
-
export declare const logger: Logger;
|
|
3
|
+
export declare const logger: pino.Logger;
|
package/dist/src/logger.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod";
|
|
2
2
|
import { DbOutcome } from "../db/index.js";
|
|
3
3
|
import { Result } from "../util.js";
|
|
4
4
|
declare const InputSchema: z.ZodObject<{
|
|
@@ -75,10 +75,11 @@ export type OutcomeBetConfig = {
|
|
|
75
75
|
export type OutcomeBetConfigMap<BetKind extends string> = {
|
|
76
76
|
[betKind in BetKind]: OutcomeBetConfig;
|
|
77
77
|
};
|
|
78
|
-
|
|
78
|
+
type AtLeastOneKey<T, Keys extends keyof T = keyof T> = Keys extends keyof T ? Required<Pick<T, Keys>> & Partial<Omit<T, Keys>> : never;
|
|
79
|
+
export type RiskLimits = AtLeastOneKey<{
|
|
79
80
|
maxWager?: number;
|
|
80
81
|
maxPayout?: number;
|
|
81
|
-
}
|
|
82
|
+
}>;
|
|
82
83
|
export type RiskPolicy = (args: {
|
|
83
84
|
currency: string;
|
|
84
85
|
wager: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { access, context, object, ObjectStep, sideEffect, } from "postgraphile/grafast";
|
|
2
2
|
import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod";
|
|
4
4
|
import { GraphQLError } from "graphql";
|
|
5
5
|
import { DbHashKind, dbLockPlayerBalanceAndHouseBankroll, exactlyOneRow, maybeOneRow, superuserPool, withPgPoolTransaction, } from "../db/index.js";
|
|
6
6
|
import { assert } from "tsafe";
|
|
@@ -8,6 +8,7 @@ import { dbInsertHubHash, dbLockHubHashChain, } from "../hash-chain/db-hash-chai
|
|
|
8
8
|
import { getIntermediateHash, getPreimageHash, } from "../hash-chain/get-hash.js";
|
|
9
9
|
import { makeFinalHash, pickRandomOutcome } from "../hash-chain/util.js";
|
|
10
10
|
import { logger } from "../logger.js";
|
|
11
|
+
import { formatCurrency } from "../format-currency.js";
|
|
11
12
|
const FLOAT_EPSILON = 1e-10;
|
|
12
13
|
function sum(ns) {
|
|
13
14
|
return ns.reduce((a, b) => a + b, 0);
|
|
@@ -64,9 +65,14 @@ const BetConfigsSchema = z.record(BetKindSchema, z.object({
|
|
|
64
65
|
.function()
|
|
65
66
|
.optional(),
|
|
66
67
|
}));
|
|
67
|
-
const RiskLimitsSchema = z
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const RiskLimitsSchema = z
|
|
69
|
+
.object({
|
|
70
|
+
maxWager: z.number().positive().optional(),
|
|
71
|
+
maxPayout: z.number().positive().optional(),
|
|
72
|
+
})
|
|
73
|
+
.strict()
|
|
74
|
+
.refine((v) => v.maxWager !== undefined || v.maxPayout !== undefined, {
|
|
75
|
+
message: "Provide at least one of maxWager or maxPayout.",
|
|
70
76
|
});
|
|
71
77
|
export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
72
78
|
BetConfigsSchema.parse(betConfigs);
|
|
@@ -154,7 +160,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
154
160
|
const dbCurrency = await superuserPool
|
|
155
161
|
.query({
|
|
156
162
|
text: `
|
|
157
|
-
SELECT key
|
|
163
|
+
SELECT key, display_unit_name, display_unit_scale
|
|
158
164
|
FROM hub.currency
|
|
159
165
|
WHERE key = $1
|
|
160
166
|
AND casino_id = $2
|
|
@@ -192,7 +198,13 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
192
198
|
const maxProfitMultiplier = Math.max(...input.outcomes.map((o) => o.profit));
|
|
193
199
|
const maxPayout = input.wager * maxProfitMultiplier;
|
|
194
200
|
if (maxPayout > dbHouseBankroll.amount) {
|
|
195
|
-
throw new GraphQLError(`House cannot cover potential payout (${maxPayout
|
|
201
|
+
throw new GraphQLError(`House cannot cover potential payout (${formatCurrency(maxPayout, {
|
|
202
|
+
displayUnitName: dbCurrency.display_unit_name,
|
|
203
|
+
displayUnitScale: dbCurrency.display_unit_scale,
|
|
204
|
+
})}). Bankroll: ${formatCurrency(dbHouseBankroll.amount, {
|
|
205
|
+
displayUnitName: dbCurrency.display_unit_name,
|
|
206
|
+
displayUnitScale: dbCurrency.display_unit_scale,
|
|
207
|
+
})}`);
|
|
196
208
|
}
|
|
197
209
|
const riskLimitsResult = RiskLimitsSchema.safeParse(betConfig.riskPolicy
|
|
198
210
|
? betConfig.riskPolicy({
|
|
@@ -202,16 +214,29 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
202
214
|
})
|
|
203
215
|
: {});
|
|
204
216
|
if (!riskLimitsResult.success) {
|
|
205
|
-
|
|
217
|
+
logger.error(riskLimitsResult.error, "Invalid risk policy");
|
|
218
|
+
throw new GraphQLError("Invalid risk policy");
|
|
206
219
|
}
|
|
207
220
|
const riskLimits = riskLimitsResult.data;
|
|
208
221
|
if (riskLimits.maxWager != null &&
|
|
209
222
|
input.wager > riskLimits.maxWager) {
|
|
210
|
-
throw new GraphQLError(`Wager exceeds limit (${riskLimits.maxWager
|
|
223
|
+
throw new GraphQLError(`Wager exceeds limit (${formatCurrency(riskLimits.maxWager, {
|
|
224
|
+
displayUnitName: dbCurrency.display_unit_name,
|
|
225
|
+
displayUnitScale: dbCurrency.display_unit_scale,
|
|
226
|
+
})}). Your wager: ${formatCurrency(input.wager, {
|
|
227
|
+
displayUnitName: dbCurrency.display_unit_name,
|
|
228
|
+
displayUnitScale: dbCurrency.display_unit_scale,
|
|
229
|
+
})}`);
|
|
211
230
|
}
|
|
212
231
|
if (riskLimits.maxPayout != null &&
|
|
213
232
|
maxPayout > riskLimits.maxPayout) {
|
|
214
|
-
throw new GraphQLError(`Payout exceeds limit (${riskLimits.maxPayout
|
|
233
|
+
throw new GraphQLError(`Payout exceeds limit (${formatCurrency(riskLimits.maxPayout, {
|
|
234
|
+
displayUnitName: dbCurrency.display_unit_name,
|
|
235
|
+
displayUnitScale: dbCurrency.display_unit_scale,
|
|
236
|
+
})}). Your payout: ${formatCurrency(maxPayout, {
|
|
237
|
+
displayUnitName: dbCurrency.display_unit_name,
|
|
238
|
+
displayUnitScale: dbCurrency.display_unit_scale,
|
|
239
|
+
})}`);
|
|
215
240
|
}
|
|
216
241
|
const dbHashChain = await dbLockHubHashChain(pgClient, {
|
|
217
242
|
userId: session.user_id,
|
|
@@ -46,7 +46,7 @@ export function initializeTransferProcessors({ signal, }) {
|
|
|
46
46
|
await listenForNewCasinos({ signal });
|
|
47
47
|
}
|
|
48
48
|
catch (e) {
|
|
49
|
-
logger.error(`Error initializing transfer processors
|
|
49
|
+
logger.error(e, `Error initializing transfer processors`);
|
|
50
50
|
}
|
|
51
51
|
})();
|
|
52
52
|
}
|
|
@@ -139,7 +139,7 @@ export async function processWithdrawalRequests({ casinoId, graphqlClient, }) {
|
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
catch (error) {
|
|
142
|
-
logger.error(`Failed to process withdrawal request ${request.id}
|
|
142
|
+
logger.error(error, `Failed to process withdrawal request ${request.id}`);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
}
|