shufflecom-calculations 6.8.0 → 6.9.1

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.
@@ -9,12 +9,10 @@ export interface BaseCreateVipBonusInput {
9
9
  expiryDate?: Date;
10
10
  }
11
11
  export declare const VALID_RELOAD_VIP_BONUS_CADENCE: number[];
12
- export declare const AUTOMATED_RELOAD_SCHEDULE: {
13
- cadence: number;
14
- occurrence: number;
15
- firstOccurrenceActivationPeriod: number;
16
- expiryPerOccurrence: number;
17
- };
12
+ export declare const AUTOMATED_RELOAD_CYCLE_SECONDS: number;
13
+ export declare const DEFAULT_AUTOMATED_RELOAD_CADENCE: number;
14
+ export declare const AUTOMATED_RELOAD_EXPIRY_PER_OCCURRENCE: number;
15
+ export declare const AUTOMATED_RELOAD_FIRST_OCCURRENCE_ACTIVATION_PERIOD = 0;
18
16
  export declare const MIN_RELOAD_VIP_BONUS_OCCURRENCE = 3;
19
17
  export declare const MAX_RELOAD_VIP_BONUS_OCCURRENCE = 5000;
20
18
  type ParsedVipBonusData = {
@@ -24,6 +22,14 @@ type ParsedVipBonusData = {
24
22
  expiryPerOccurrence: number;
25
23
  endAt: Date;
26
24
  };
25
+ export type AutomatedReloadSchedule = {
26
+ cadence: number;
27
+ occurrence: number;
28
+ firstOccurrenceActivationPeriod: number;
29
+ expiryPerOccurrence: number;
30
+ };
31
+ export declare function automatedReloadSchedule(cadence?: number): AutomatedReloadSchedule;
32
+ export declare function reloadScheduleEndAt(date: dayjs.Dayjs, { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence }: AutomatedReloadSchedule): Date;
27
33
  export declare function parseCreateVipBonusBaseInput({ date, bonus }: {
28
34
  date: dayjs.Dayjs;
29
35
  bonus: BaseCreateVipBonusInput;
@@ -1,27 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseCreateVipBonusBaseInput = exports.MAX_RELOAD_VIP_BONUS_OCCURRENCE = exports.MIN_RELOAD_VIP_BONUS_OCCURRENCE = exports.AUTOMATED_RELOAD_SCHEDULE = exports.VALID_RELOAD_VIP_BONUS_CADENCE = void 0;
3
+ exports.parseCreateVipBonusBaseInput = exports.reloadScheduleEndAt = exports.automatedReloadSchedule = exports.MAX_RELOAD_VIP_BONUS_OCCURRENCE = exports.MIN_RELOAD_VIP_BONUS_OCCURRENCE = exports.AUTOMATED_RELOAD_FIRST_OCCURRENCE_ACTIVATION_PERIOD = exports.AUTOMATED_RELOAD_EXPIRY_PER_OCCURRENCE = exports.DEFAULT_AUTOMATED_RELOAD_CADENCE = exports.AUTOMATED_RELOAD_CYCLE_SECONDS = exports.VALID_RELOAD_VIP_BONUS_CADENCE = void 0;
4
4
  const vip_bonus_type_1 = require("./vip-bonus.type");
5
5
  const minuteInSeconds = (min) => min * 60;
6
6
  const hourInSeconds = (hour) => minuteInSeconds(hour * 60);
7
7
  const dayInSeconds = (day) => hourInSeconds(day * 24);
8
8
  exports.VALID_RELOAD_VIP_BONUS_CADENCE = [minuteInSeconds(10), hourInSeconds(1), hourInSeconds(8), dayInSeconds(1)];
9
- exports.AUTOMATED_RELOAD_SCHEDULE = {
10
- cadence: hourInSeconds(8),
11
- occurrence: 21,
12
- firstOccurrenceActivationPeriod: 0,
13
- expiryPerOccurrence: dayInSeconds(1),
14
- };
9
+ exports.AUTOMATED_RELOAD_CYCLE_SECONDS = hourInSeconds(168);
10
+ exports.DEFAULT_AUTOMATED_RELOAD_CADENCE = dayInSeconds(1);
11
+ exports.AUTOMATED_RELOAD_EXPIRY_PER_OCCURRENCE = dayInSeconds(1);
12
+ exports.AUTOMATED_RELOAD_FIRST_OCCURRENCE_ACTIVATION_PERIOD = 0;
15
13
  exports.MIN_RELOAD_VIP_BONUS_OCCURRENCE = 3;
16
14
  exports.MAX_RELOAD_VIP_BONUS_OCCURRENCE = 5000;
15
+ function automatedReloadSchedule(cadence = exports.DEFAULT_AUTOMATED_RELOAD_CADENCE) {
16
+ if (!exports.VALID_RELOAD_VIP_BONUS_CADENCE.includes(cadence)) {
17
+ throw new Error(`Vip Bonus (Automated Reload) cadence must be oneOf ${exports.VALID_RELOAD_VIP_BONUS_CADENCE.join(', ')} seconds`);
18
+ }
19
+ return {
20
+ cadence,
21
+ occurrence: exports.AUTOMATED_RELOAD_CYCLE_SECONDS / cadence,
22
+ firstOccurrenceActivationPeriod: exports.AUTOMATED_RELOAD_FIRST_OCCURRENCE_ACTIVATION_PERIOD,
23
+ expiryPerOccurrence: exports.AUTOMATED_RELOAD_EXPIRY_PER_OCCURRENCE,
24
+ };
25
+ }
26
+ exports.automatedReloadSchedule = automatedReloadSchedule;
27
+ function reloadScheduleEndAt(date, { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence }) {
28
+ return date
29
+ .add(firstOccurrenceActivationPeriod, 'hour')
30
+ .add(cadence * (occurrence - 1), 'second')
31
+ .add(expiryPerOccurrence, 'second')
32
+ .toDate();
33
+ }
34
+ exports.reloadScheduleEndAt = reloadScheduleEndAt;
17
35
  function parseCreateVipBonusBaseInput({ date, bonus }) {
36
+ var _a;
18
37
  if (bonus.type === vip_bonus_type_1.VipBonusType.AUTOMATED_RELOAD) {
19
- const { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence } = exports.AUTOMATED_RELOAD_SCHEDULE;
20
- return Object.assign(Object.assign({}, exports.AUTOMATED_RELOAD_SCHEDULE), { endAt: date
21
- .add(firstOccurrenceActivationPeriod, 'hour')
22
- .add(cadence * (occurrence - 1), 'second')
23
- .add(expiryPerOccurrence, 'second')
24
- .toDate() });
38
+ const schedule = automatedReloadSchedule((_a = bonus.cadence) !== null && _a !== void 0 ? _a : exports.DEFAULT_AUTOMATED_RELOAD_CADENCE);
39
+ return Object.assign(Object.assign({}, schedule), { endAt: reloadScheduleEndAt(date, schedule) });
25
40
  }
26
41
  if ((0, vip_bonus_type_1.isReloadVipBonusType)(bonus.type)) {
27
42
  const { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence } = bonus;
@@ -40,17 +55,12 @@ function parseCreateVipBonusBaseInput({ date, bonus }) {
40
55
  if (cadence > expiryPerOccurrence) {
41
56
  throw new Error('Vip Bonus (Reloaded) expiryPerOccurrence must be greater than or equal to cadence');
42
57
  }
43
- const endAt = date
44
- .add(firstOccurrenceActivationPeriod, 'hour')
45
- .add(cadence * (occurrence - 1), 'second')
46
- .add(expiryPerOccurrence, 'second')
47
- .toDate();
48
58
  return {
49
59
  cadence,
50
60
  occurrence,
51
61
  firstOccurrenceActivationPeriod,
52
62
  expiryPerOccurrence,
53
- endAt,
63
+ endAt: reloadScheduleEndAt(date, { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence }),
54
64
  };
55
65
  }
56
66
  if (!(bonus === null || bonus === void 0 ? void 0 : bonus.expiryDate) || date.isAfter(bonus.expiryDate)) {
@@ -1 +1 @@
1
- {"version":3,"file":"parse-create-vip-bonus.js","sourceRoot":"","sources":["../../src/utils/parse-create-vip-bonus.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parse-create-vip-bonus.js","sourceRoot":"","sources":["../../src/utils/parse-create-vip-bonus.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shufflecom-calculations",
3
- "version": "6.8.0",
3
+ "version": "6.9.1",
4
4
  "description": "",
5
5
  "types": "lib/index.d.ts",
6
6
  "main": "lib/index.js",
@@ -19,5 +19,5 @@
19
19
  },
20
20
  "author": "",
21
21
  "license": "ISC",
22
- "gitHead": "aa6d35e33a97dff5bb25838a0999b5afd4f97fa2"
22
+ "gitHead": "a5ff669f1b9885fe4639c13ec581553dd1ddc01b"
23
23
  }
@@ -1,4 +1,5 @@
1
1
  import BigNumber from 'bignumber.js';
2
+ import { calculatePayoutBN } from '../../utils/calculate-payout';
2
3
  import {
3
4
  ROLLERCOASTER_INTERNAL_PRICE_DECIMAL_PLACES,
4
5
  ROLLERCOASTER_MAX_STALE_CLIENT_TICKS,
@@ -185,7 +186,7 @@ export class RollercoasterRules {
185
186
  static grossPayoutForReason(bet: RollercoasterOpenBetState, reason: RollercoasterSettlementReason, price: BigNumber): BigNumber {
186
187
  switch (reason.kind) {
187
188
  case RollercoasterBetTerminalReason.TAKE_PROFIT:
188
- return bet.amount.multipliedBy(reason.takeProfitMultiplier);
189
+ return calculatePayoutBN(bet.amount, reason.takeProfitMultiplier);
189
190
  case RollercoasterBetTerminalReason.LIQUIDATION:
190
191
  return BigNumber(0);
191
192
  case RollercoasterBetTerminalReason.MAX_PAYOUT:
@@ -1,10 +1,11 @@
1
1
  import dayjs from 'dayjs';
2
2
  import {
3
- AUTOMATED_RELOAD_SCHEDULE,
3
+ AUTOMATED_RELOAD_CYCLE_SECONDS,
4
4
  BaseCreateVipBonusInput,
5
5
  MAX_RELOAD_VIP_BONUS_OCCURRENCE,
6
6
  MIN_RELOAD_VIP_BONUS_OCCURRENCE,
7
7
  VALID_RELOAD_VIP_BONUS_CADENCE,
8
+ automatedReloadSchedule,
8
9
  parseCreateVipBonusBaseInput,
9
10
  } from './parse-create-vip-bonus';
10
11
  import { VipBonusType } from './vip-bonus.type';
@@ -138,36 +139,55 @@ describe('parseCreateVipBonusBaseInput', () => {
138
139
  });
139
140
 
140
141
  describe('AUTOMATED_RELOAD', () => {
141
- it('applies the fixed schedule and ignores whatever the caller passed', () => {
142
+ it('honours the cadence and derives the rest, ignoring whatever else the caller passed', () => {
142
143
  const result = parseCreateVipBonusBaseInput({
143
144
  date,
144
145
  bonus: {
145
146
  type: VipBonusType.AUTOMATED_RELOAD,
146
- // deliberately wrong: the admin does not get to choose these
147
147
  cadence: VALID_RELOAD_VIP_BONUS_CADENCE[0],
148
+ // deliberately wrong: only the cadence is the admin's to choose
148
149
  occurrence: MIN_RELOAD_VIP_BONUS_OCCURRENCE,
149
150
  firstOccurrenceActivationPeriod: 99,
150
151
  expiryPerOccurrence: 1,
151
152
  },
152
153
  });
153
154
 
155
+ const schedule = automatedReloadSchedule(VALID_RELOAD_VIP_BONUS_CADENCE[0]);
156
+
154
157
  expect(result).toEqual({
155
- ...AUTOMATED_RELOAD_SCHEDULE,
158
+ ...schedule,
156
159
  endAt: date
157
- .add(AUTOMATED_RELOAD_SCHEDULE.firstOccurrenceActivationPeriod, 'hour')
158
- .add(AUTOMATED_RELOAD_SCHEDULE.cadence * (AUTOMATED_RELOAD_SCHEDULE.occurrence - 1), 'second')
159
- .add(AUTOMATED_RELOAD_SCHEDULE.expiryPerOccurrence, 'second')
160
+ .add(schedule.firstOccurrenceActivationPeriod, 'hour')
161
+ .add(schedule.cadence * (schedule.occurrence - 1), 'second')
162
+ .add(schedule.expiryPerOccurrence, 'second')
160
163
  .toDate(),
161
164
  });
162
165
  });
163
166
 
164
- it('needs no schedule fields at all', () => {
165
- expect(() => parseCreateVipBonusBaseInput({ date, bonus: { type: VipBonusType.AUTOMATED_RELOAD } })).not.toThrow();
167
+ it('needs no schedule fields at all, falling back to the default cadence', () => {
168
+ const result = parseCreateVipBonusBaseInput({ date, bonus: { type: VipBonusType.AUTOMATED_RELOAD } });
169
+
170
+ expect(result).toMatchObject(automatedReloadSchedule());
166
171
  });
167
172
 
168
173
  it('keeps a cadence the reloaded validator would also accept', () => {
169
- expect(VALID_RELOAD_VIP_BONUS_CADENCE).toContain(AUTOMATED_RELOAD_SCHEDULE.cadence);
170
- expect(AUTOMATED_RELOAD_SCHEDULE.expiryPerOccurrence).toBeGreaterThanOrEqual(AUTOMATED_RELOAD_SCHEDULE.cadence);
174
+ const schedule = automatedReloadSchedule();
175
+
176
+ expect(VALID_RELOAD_VIP_BONUS_CADENCE).toContain(schedule.cadence);
177
+ expect(schedule.expiryPerOccurrence).toBeGreaterThanOrEqual(schedule.cadence);
178
+ });
179
+
180
+ it('derives an occurrence count that spans the fixed cycle for every valid cadence', () => {
181
+ for (const cadence of VALID_RELOAD_VIP_BONUS_CADENCE) {
182
+ const schedule = automatedReloadSchedule(cadence);
183
+
184
+ expect(Number.isInteger(schedule.occurrence)).toBe(true);
185
+ expect(schedule.cadence * schedule.occurrence).toBe(AUTOMATED_RELOAD_CYCLE_SECONDS);
186
+ }
187
+ });
188
+
189
+ it('rejects a cadence outside the reload whitelist', () => {
190
+ expect(() => automatedReloadSchedule(12345)).toThrow();
171
191
  });
172
192
  });
173
193
 
@@ -16,12 +16,11 @@ const dayInSeconds = (day: number) => hourInSeconds(day * 24);
16
16
 
17
17
  export const VALID_RELOAD_VIP_BONUS_CADENCE = [minuteInSeconds(10), hourInSeconds(1), hourInSeconds(8), dayInSeconds(1)];
18
18
 
19
- export const AUTOMATED_RELOAD_SCHEDULE = {
20
- cadence: hourInSeconds(8),
21
- occurrence: 21,
22
- firstOccurrenceActivationPeriod: 0,
23
- expiryPerOccurrence: dayInSeconds(1),
24
- };
19
+ export const AUTOMATED_RELOAD_CYCLE_SECONDS = hourInSeconds(168);
20
+ export const DEFAULT_AUTOMATED_RELOAD_CADENCE = dayInSeconds(1);
21
+ export const AUTOMATED_RELOAD_EXPIRY_PER_OCCURRENCE = dayInSeconds(1);
22
+ export const AUTOMATED_RELOAD_FIRST_OCCURRENCE_ACTIVATION_PERIOD = 0;
23
+
25
24
  export const MIN_RELOAD_VIP_BONUS_OCCURRENCE = 3;
26
25
  // max occurance 5000 if set to high might cause slow performance in the frontend when handling each reload bonus occurance state
27
26
  export const MAX_RELOAD_VIP_BONUS_OCCURRENCE = 5000;
@@ -34,19 +33,42 @@ type ParsedVipBonusData = {
34
33
  endAt: Date;
35
34
  };
36
35
 
36
+ export type AutomatedReloadSchedule = {
37
+ cadence: number;
38
+ occurrence: number;
39
+ firstOccurrenceActivationPeriod: number;
40
+ expiryPerOccurrence: number;
41
+ };
42
+
43
+ export function automatedReloadSchedule(cadence: number = DEFAULT_AUTOMATED_RELOAD_CADENCE): AutomatedReloadSchedule {
44
+ if (!VALID_RELOAD_VIP_BONUS_CADENCE.includes(cadence)) {
45
+ throw new Error(`Vip Bonus (Automated Reload) cadence must be oneOf ${VALID_RELOAD_VIP_BONUS_CADENCE.join(', ')} seconds`);
46
+ }
47
+
48
+ return {
49
+ cadence,
50
+ occurrence: AUTOMATED_RELOAD_CYCLE_SECONDS / cadence,
51
+ firstOccurrenceActivationPeriod: AUTOMATED_RELOAD_FIRST_OCCURRENCE_ACTIVATION_PERIOD,
52
+ expiryPerOccurrence: AUTOMATED_RELOAD_EXPIRY_PER_OCCURRENCE,
53
+ };
54
+ }
55
+
56
+ export function reloadScheduleEndAt(
57
+ date: dayjs.Dayjs,
58
+ { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence }: AutomatedReloadSchedule,
59
+ ): Date {
60
+ return date
61
+ .add(firstOccurrenceActivationPeriod, 'hour')
62
+ .add(cadence * (occurrence - 1), 'second')
63
+ .add(expiryPerOccurrence, 'second')
64
+ .toDate();
65
+ }
66
+
37
67
  export function parseCreateVipBonusBaseInput({ date, bonus }: { date: dayjs.Dayjs; bonus: BaseCreateVipBonusInput }): ParsedVipBonusData {
38
- // Whatever the caller passed for the schedule is ignored — these are not the admin's to choose.
39
68
  if (bonus.type === VipBonusType.AUTOMATED_RELOAD) {
40
- const { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence } = AUTOMATED_RELOAD_SCHEDULE;
69
+ const schedule = automatedReloadSchedule(bonus.cadence ?? DEFAULT_AUTOMATED_RELOAD_CADENCE);
41
70
 
42
- return {
43
- ...AUTOMATED_RELOAD_SCHEDULE,
44
- endAt: date
45
- .add(firstOccurrenceActivationPeriod, 'hour')
46
- .add(cadence * (occurrence - 1), 'second')
47
- .add(expiryPerOccurrence, 'second')
48
- .toDate(),
49
- };
71
+ return { ...schedule, endAt: reloadScheduleEndAt(date, schedule) };
50
72
  }
51
73
 
52
74
  if (isReloadVipBonusType(bonus.type)) {
@@ -72,18 +94,12 @@ export function parseCreateVipBonusBaseInput({ date, bonus }: { date: dayjs.Dayj
72
94
  throw new Error('Vip Bonus (Reloaded) expiryPerOccurrence must be greater than or equal to cadence');
73
95
  }
74
96
 
75
- const endAt = date
76
- .add(firstOccurrenceActivationPeriod, 'hour')
77
- .add(cadence * (occurrence - 1), 'second')
78
- .add(expiryPerOccurrence, 'second')
79
- .toDate();
80
-
81
97
  return {
82
98
  cadence,
83
99
  occurrence,
84
100
  firstOccurrenceActivationPeriod,
85
101
  expiryPerOccurrence,
86
- endAt,
102
+ endAt: reloadScheduleEndAt(date, { cadence, occurrence, firstOccurrenceActivationPeriod, expiryPerOccurrence }),
87
103
  };
88
104
  }
89
105