@openora/core 0.4.1-canary.91 → 0.4.1-canary.93
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/.tsbuildinfo +1 -1
- package/dist/admin-console/contract/index.d.ts +12 -0
- package/dist/admin-console/contract/index.d.ts.map +1 -1
- package/dist/admin-console/plugin.d.ts +18 -1
- package/dist/admin-console/plugin.d.ts.map +1 -1
- package/dist/admin-console/router/index.d.ts +6 -0
- package/dist/admin-console/router/index.d.ts.map +1 -1
- package/dist/analytics/plugin.d.ts +18 -1
- package/dist/analytics/plugin.d.ts.map +1 -1
- package/dist/audit/plugin.d.ts +18 -1
- package/dist/audit/plugin.d.ts.map +1 -1
- package/dist/casino/gaming/plugin.d.ts +18 -1
- package/dist/casino/gaming/plugin.d.ts.map +1 -1
- package/dist/casino/lobby/plugin.d.ts +18 -1
- package/dist/casino/lobby/plugin.d.ts.map +1 -1
- package/dist/cms/plugin.d.ts +18 -1
- package/dist/cms/plugin.d.ts.map +1 -1
- package/dist/compliance/plugin.d.ts +18 -1
- package/dist/compliance/plugin.d.ts.map +1 -1
- package/dist/contracts/adapters/audit.d.ts +1 -1
- package/dist/contracts/adapters/audit.d.ts.map +1 -1
- package/dist/contracts/adapters/audit.js.map +1 -1
- package/dist/contracts/adapters/index.d.ts +4 -2
- package/dist/contracts/adapters/index.d.ts.map +1 -1
- package/dist/contracts/adapters/index.js +2 -1
- package/dist/contracts/adapters/index.js.map +1 -1
- package/dist/contracts/adapters/payment.d.ts +99 -0
- package/dist/contracts/adapters/payment.d.ts.map +1 -1
- package/dist/contracts/adapters/payment.js +6 -0
- package/dist/contracts/adapters/payment.js.map +1 -1
- package/dist/contracts/adapters/rate-limit.d.ts +1 -0
- package/dist/contracts/adapters/rate-limit.d.ts.map +1 -1
- package/dist/contracts/adapters/rate-limit.js +1 -0
- package/dist/contracts/adapters/rate-limit.js.map +1 -1
- package/dist/contracts/adapters/wallet-asset-catalog.d.ts +34 -0
- package/dist/contracts/adapters/wallet-asset-catalog.d.ts.map +1 -0
- package/dist/contracts/adapters/wallet-asset-catalog.js +3 -0
- package/dist/contracts/adapters/wallet-asset-catalog.js.map +1 -0
- package/dist/contracts/schemas/__tests__/common-schemas.test.js +2 -1
- package/dist/contracts/schemas/__tests__/common-schemas.test.js.map +1 -1
- package/dist/contracts/schemas/common.d.ts.map +1 -1
- package/dist/contracts/schemas/common.js +7 -4
- package/dist/contracts/schemas/common.js.map +1 -1
- package/dist/contracts/schemas/events.d.ts +15 -0
- package/dist/contracts/schemas/events.d.ts.map +1 -1
- package/dist/contracts/schemas/events.js +16 -2
- package/dist/contracts/schemas/events.js.map +1 -1
- package/dist/contracts/schemas/platform-config.d.ts +45 -0
- package/dist/contracts/schemas/platform-config.d.ts.map +1 -1
- package/dist/contracts/schemas/platform-config.js +47 -0
- package/dist/contracts/schemas/platform-config.js.map +1 -1
- package/dist/contracts/schemas/wallet-tx.d.ts +38 -1
- package/dist/contracts/schemas/wallet-tx.d.ts.map +1 -1
- package/dist/contracts/schemas/wallet-tx.js +32 -3
- package/dist/contracts/schemas/wallet-tx.js.map +1 -1
- package/dist/engagement/chat/plugin.d.ts +18 -1
- package/dist/engagement/chat/plugin.d.ts.map +1 -1
- package/dist/engagement/chat-commands/plugin.d.ts +18 -1
- package/dist/engagement/chat-commands/plugin.d.ts.map +1 -1
- package/dist/engagement/notifications/plugin.d.ts +18 -1
- package/dist/engagement/notifications/plugin.d.ts.map +1 -1
- package/dist/engagement/social/plugin.d.ts +18 -1
- package/dist/engagement/social/plugin.d.ts.map +1 -1
- package/dist/engagement/social-transfers/plugin.d.ts +18 -1
- package/dist/engagement/social-transfers/plugin.d.ts.map +1 -1
- package/dist/iam/plugin.d.ts +18 -1
- package/dist/iam/plugin.d.ts.map +1 -1
- package/dist/iam/service/iam.service.d.ts +1 -1
- package/dist/pam/identity/plugin.d.ts +18 -1
- package/dist/pam/identity/plugin.d.ts.map +1 -1
- package/dist/pam/player-management/plugin.d.ts +18 -1
- package/dist/pam/player-management/plugin.d.ts.map +1 -1
- package/dist/pam/player-note/plugin.d.ts +18 -1
- package/dist/pam/player-note/plugin.d.ts.map +1 -1
- package/dist/pam/profile/plugin.d.ts +18 -1
- package/dist/pam/profile/plugin.d.ts.map +1 -1
- package/dist/pam/tag/plugin.d.ts +18 -1
- package/dist/pam/tag/plugin.d.ts.map +1 -1
- package/dist/server/auth/permissions.d.ts +36 -0
- package/dist/server/auth/permissions.d.ts.map +1 -1
- package/dist/server/auth/permissions.js +6 -0
- package/dist/server/auth/permissions.js.map +1 -1
- package/dist/server/db/__tests__/query-helpers.test.js +38 -1
- package/dist/server/db/__tests__/query-helpers.test.js.map +1 -1
- package/dist/server/db/index.d.ts +1 -1
- package/dist/server/db/index.d.ts.map +1 -1
- package/dist/server/db/index.js +1 -1
- package/dist/server/db/index.js.map +1 -1
- package/dist/server/db/query-helpers.d.ts +4 -0
- package/dist/server/db/query-helpers.d.ts.map +1 -1
- package/dist/server/db/query-helpers.js +31 -0
- package/dist/server/db/query-helpers.js.map +1 -1
- package/dist/server/runtime/core-token-catalog.d.ts +18 -1
- package/dist/server/runtime/core-token-catalog.d.ts.map +1 -1
- package/dist/server/runtime/core-token-catalog.js +3 -1
- package/dist/server/runtime/core-token-catalog.js.map +1 -1
- package/dist/testing/mock.d.ts +12 -1
- package/dist/testing/mock.d.ts.map +1 -1
- package/dist/testing/mock.js +16 -0
- package/dist/testing/mock.js.map +1 -1
- package/dist/wallet/__tests__/settlement-network.test.d.ts +2 -0
- package/dist/wallet/__tests__/settlement-network.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/settlement-network.test.js +54 -0
- package/dist/wallet/__tests__/settlement-network.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-asset.router.int.test.d.ts +2 -0
- package/dist/wallet/__tests__/wallet-asset.router.int.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/wallet-asset.router.int.test.js +234 -0
- package/dist/wallet/__tests__/wallet-asset.router.int.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-auto-rule.router.int.test.js +5 -3
- package/dist/wallet/__tests__/wallet-auto-rule.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-auto-withdrawal-config.router.int.test.js +4 -2
- package/dist/wallet/__tests__/wallet-auto-withdrawal-config.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-auto-withdrawal.int.test.js +6 -3
- package/dist/wallet/__tests__/wallet-auto-withdrawal.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-custody-stubs.router.test.d.ts +2 -0
- package/dist/wallet/__tests__/wallet-custody-stubs.router.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/wallet-custody-stubs.router.test.js +80 -0
- package/dist/wallet/__tests__/wallet-custody-stubs.router.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-kyc-gate.int.test.js +2 -1
- package/dist/wallet/__tests__/wallet-kyc-gate.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-webhook.router.int.test.js +119 -4
- package/dist/wallet/__tests__/wallet-webhook.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.rate-limit.int.test.js +2 -1
- package/dist/wallet/__tests__/wallet.rate-limit.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.router.int.test.js +23 -3
- package/dist/wallet/__tests__/wallet.router.int.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.service.int.test.js +265 -12
- package/dist/wallet/__tests__/wallet.service.int.test.js.map +1 -1
- package/dist/wallet/adapters/wallet-asset-catalog.service.d.ts +14 -0
- package/dist/wallet/adapters/wallet-asset-catalog.service.d.ts.map +1 -0
- package/dist/wallet/adapters/wallet-asset-catalog.service.js +37 -0
- package/dist/wallet/adapters/wallet-asset-catalog.service.js.map +1 -0
- package/dist/wallet/admin-reporting.d.ts +2 -2
- package/dist/wallet/contract/index.d.ts +428 -4
- package/dist/wallet/contract/index.d.ts.map +1 -1
- package/dist/wallet/contract/index.js +203 -5
- package/dist/wallet/contract/index.js.map +1 -1
- package/dist/wallet/drizzle/migrations/0010_worthless_quasimodo.sql +84 -0
- package/dist/wallet/drizzle/migrations/meta/0010_snapshot.json +1565 -0
- package/dist/wallet/drizzle/migrations/meta/_journal.json +7 -0
- package/dist/wallet/plugin.d.ts +18 -1
- package/dist/wallet/plugin.d.ts.map +1 -1
- package/dist/wallet/plugin.js +19 -2
- package/dist/wallet/plugin.js.map +1 -1
- package/dist/wallet/router/index.d.ts +235 -3
- package/dist/wallet/router/index.d.ts.map +1 -1
- package/dist/wallet/router/index.js +117 -20
- package/dist/wallet/router/index.js.map +1 -1
- package/dist/wallet/schema/index.d.ts +984 -3
- package/dist/wallet/schema/index.d.ts.map +1 -1
- package/dist/wallet/schema/index.js +141 -2
- package/dist/wallet/schema/index.js.map +1 -1
- package/dist/wallet/service/wallet.service.d.ts +70 -6
- package/dist/wallet/service/wallet.service.d.ts.map +1 -1
- package/dist/wallet/service/wallet.service.js +379 -16
- package/dist/wallet/service/wallet.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, it, expect,
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
2
2
|
import { findOneOrThrow } from '@openora/core/server';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { call, ORPCError } from '@orpc/server';
|
|
5
5
|
import { createTestDb } from '@openora/core/testing';
|
|
6
|
-
import { mock, makeEventBus, testContext, makeAuditWriter, makeAdminGuard, makeIdentityReader, } from '../../testing/mock.js';
|
|
6
|
+
import { mock, makeEventBus, testContext, makeAuditWriter, makeAdminGuard, makeIdentityReader, makePaymentProviderRegistry, } from '../../testing/mock.js';
|
|
7
7
|
import { migrate } from '../migrate.js';
|
|
8
8
|
import { wallet, walletTransaction } from '../schema/index.js';
|
|
9
9
|
import { createWalletRouter } from '../router/index.js';
|
|
@@ -26,15 +26,20 @@ function realWalletService() {
|
|
|
26
26
|
drizzle: db.drizzle,
|
|
27
27
|
events: makeEventBus(),
|
|
28
28
|
payment: mock({}),
|
|
29
|
+
paymentProviders: makePaymentProviderRegistry(),
|
|
29
30
|
audit: makeAuditWriter(),
|
|
30
31
|
identityReader: makeIdentityReader(),
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
function routerWith(adminGuard) {
|
|
34
|
-
return createWalletRouter(realWalletService(), adminGuard, makeAuditWriter(),
|
|
35
|
+
return createWalletRouter(realWalletService(), adminGuard, makeAuditWriter(), makePaymentProviderRegistry());
|
|
35
36
|
}
|
|
36
37
|
const transactionDenyingGuard = () => makeAdminGuard({ deny: ['transaction'], caller: { userId: 'caller-1', role: 'support' } });
|
|
37
38
|
const allowingGuard = () => makeAdminGuard({ caller: { userId: 'caller-1' } });
|
|
39
|
+
const adjustmentDenyingGuard = () => makeAdminGuard({
|
|
40
|
+
deny: ['player:adjust-balance'],
|
|
41
|
+
caller: { userId: 'caller-1', role: 'admin' },
|
|
42
|
+
});
|
|
38
43
|
async function seedLedger(userId, amounts) {
|
|
39
44
|
const row = findOneOrThrow(await db.drizzle.db.insert(wallet).values({ userId, currency: 'USD' }).returning(), new Error('seedLedger: query returned no row'));
|
|
40
45
|
for (const amount of amounts) {
|
|
@@ -70,4 +75,19 @@ describe('wallet router listPlayerTransactions authz', () => {
|
|
|
70
75
|
expect(result.items[0]?.amount).toBe('10.000000000000000000');
|
|
71
76
|
});
|
|
72
77
|
});
|
|
78
|
+
describe('wallet router manualAdjustment authz', () => {
|
|
79
|
+
it('requires the player:adjust-balance grant and changes nothing on denial', async () => {
|
|
80
|
+
const userId = randomUUID();
|
|
81
|
+
await expect(call(routerWith(adjustmentDenyingGuard()).manualAdjustment, {
|
|
82
|
+
userId,
|
|
83
|
+
direction: 'credit',
|
|
84
|
+
amount: '5',
|
|
85
|
+
currency: 'USD',
|
|
86
|
+
reason: 'compensation',
|
|
87
|
+
idempotencyKey: randomUUID(),
|
|
88
|
+
}, { context: CTX })).rejects.toMatchObject({ code: 'FORBIDDEN' });
|
|
89
|
+
expect(await db.drizzle.db.select().from(wallet)).toHaveLength(0);
|
|
90
|
+
expect(await db.drizzle.db.select().from(walletTransaction)).toHaveLength(0);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
73
93
|
//# sourceMappingURL=wallet.router.int.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet.router.int.test.js","sourceRoot":"","sources":["../../../src/wallet/__tests__/wallet.router.int.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"wallet.router.int.test.js","sourceRoot":"","sources":["../../../src/wallet/__tests__/wallet.router.int.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,12 +2,13 @@ import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from 'vites
|
|
|
2
2
|
import { findOneOrThrow } from '@openora/core/server';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { and, eq, sql } from 'drizzle-orm';
|
|
5
|
+
import { DEFAULT_PAYMENT_PROVIDER, } from '@openora/core/contracts';
|
|
5
6
|
import { createTestDb } from '@openora/core/testing';
|
|
6
7
|
import { migrate as migrateProfile } from '@openora/core/pam/migrate/profile';
|
|
7
|
-
import { mock, makeEventBus, makeIdentityReader, NO_CLIENT_META, makeAuditWriter, } from '../../testing/mock.js';
|
|
8
|
+
import { mock, makeEventBus, makeIdentityReader, NO_CLIENT_META, makeAuditWriter, makePaymentProviderRegistry, } from '../../testing/mock.js';
|
|
8
9
|
import { migrate } from '../migrate.js';
|
|
9
|
-
import { wallet, walletBalance, walletTransaction, walletDepositAddress } from '../schema/index.js';
|
|
10
|
-
import { WalletService, WalletNotFoundError, WithdrawalNotFoundError, WithdrawalNotPendingError, InsufficientBalanceError, AmbiguousDepositAddressError, CurrencyMismatchError, IdempotencyKeyReuseError, DepositAddressUnsupportedError, DestinationAddressRequiredError, } from '../service/wallet.service.js';
|
|
10
|
+
import { wallet, walletBalance, walletTransaction, walletDepositAddress, walletAsset, } from '../schema/index.js';
|
|
11
|
+
import { WalletService, WalletNotFoundError, WithdrawalNotFoundError, WithdrawalNotPendingError, InsufficientBalanceError, AmbiguousDepositAddressError, CurrencyMismatchError, IdempotencyKeyReuseError, DepositAddressUnsupportedError, DestinationAddressRequiredError, AmbiguousNetworkError, BelowMinimumWithdrawalError, WithdrawalDisabledError, } from '../service/wallet.service.js';
|
|
11
12
|
import { WithdrawalQueueItemSchema } from '../contract/index.js';
|
|
12
13
|
let db;
|
|
13
14
|
function makePsp() {
|
|
@@ -25,12 +26,18 @@ function makeService(overrides = {}) {
|
|
|
25
26
|
drizzle: db.drizzle,
|
|
26
27
|
events: events,
|
|
27
28
|
payment: mock(psp),
|
|
29
|
+
paymentProviders: makePaymentProviderRegistry(),
|
|
28
30
|
audit,
|
|
29
31
|
identityReader: makeIdentityReader(),
|
|
30
32
|
...overrides,
|
|
31
33
|
});
|
|
32
34
|
return { svc, events, psp, audit };
|
|
33
35
|
}
|
|
36
|
+
function playerIdentityReader() {
|
|
37
|
+
const identityReader = makeIdentityReader();
|
|
38
|
+
vi.mocked(identityReader.getPlayerIdByUserId).mockResolvedValue(randomUUID());
|
|
39
|
+
return identityReader;
|
|
40
|
+
}
|
|
34
41
|
const queueService = () => makeService().svc;
|
|
35
42
|
function makeDirectory(summaries) {
|
|
36
43
|
return mock({
|
|
@@ -90,7 +97,7 @@ afterAll(async () => {
|
|
|
90
97
|
await db.drop();
|
|
91
98
|
});
|
|
92
99
|
beforeEach(async () => {
|
|
93
|
-
await db.drizzle.db.execute(sql `TRUNCATE ${walletTransaction}, ${walletDepositAddress}, ${wallet} RESTART IDENTITY CASCADE`);
|
|
100
|
+
await db.drizzle.db.execute(sql `TRUNCATE ${walletTransaction}, ${walletDepositAddress}, ${walletAsset}, ${wallet} RESTART IDENTITY CASCADE`);
|
|
94
101
|
});
|
|
95
102
|
describe('WalletService.deposit (real PG)', () => {
|
|
96
103
|
it('creates the wallet on a first deposit, credits it, and writes one completed fiat ledger row', async () => {
|
|
@@ -121,7 +128,7 @@ describe('WalletService.deposit (real PG)', () => {
|
|
|
121
128
|
it('records the crypto rail for a crypto currency', async () => {
|
|
122
129
|
const { svc } = makeService();
|
|
123
130
|
const w = await seedWallet({ currency: 'BTC' });
|
|
124
|
-
await svc.deposit({ userId: w.userId, amount: '1', currency: 'BTC', provider: '
|
|
131
|
+
await svc.deposit({ userId: w.userId, amount: '1', currency: 'BTC', provider: 'custody' });
|
|
125
132
|
const rows = await txRows(w.id);
|
|
126
133
|
expect(rows[0]).toMatchObject({ type: 'deposit', rail: 'crypto' });
|
|
127
134
|
});
|
|
@@ -151,6 +158,96 @@ describe('WalletService.deposit (real PG)', () => {
|
|
|
151
158
|
expect(await balanceOf(w.userId, 'EUR')).toBe(10);
|
|
152
159
|
});
|
|
153
160
|
});
|
|
161
|
+
describe('WalletService.manualAdjust (real PG)', () => {
|
|
162
|
+
it('credits, debits, writes an immutable ledger row and replays exactly once', async () => {
|
|
163
|
+
const { svc, audit, psp } = makeService({ identityReader: playerIdentityReader() });
|
|
164
|
+
const player = await seedWallet({ balance: '10' });
|
|
165
|
+
const adminId = randomUUID();
|
|
166
|
+
const idempotencyKey = randomUUID();
|
|
167
|
+
const credit = await svc.manualAdjust({
|
|
168
|
+
adminId,
|
|
169
|
+
userId: player.userId,
|
|
170
|
+
direction: 'credit',
|
|
171
|
+
amount: '5',
|
|
172
|
+
currency: 'USD',
|
|
173
|
+
reason: 'settlement correction',
|
|
174
|
+
idempotencyKey,
|
|
175
|
+
...NO_CLIENT_META,
|
|
176
|
+
});
|
|
177
|
+
const replay = await svc.manualAdjust({
|
|
178
|
+
adminId,
|
|
179
|
+
userId: player.userId,
|
|
180
|
+
direction: 'credit',
|
|
181
|
+
amount: '5',
|
|
182
|
+
currency: 'USD',
|
|
183
|
+
reason: 'settlement correction',
|
|
184
|
+
idempotencyKey,
|
|
185
|
+
...NO_CLIENT_META,
|
|
186
|
+
});
|
|
187
|
+
const debit = await svc.manualAdjust({
|
|
188
|
+
adminId,
|
|
189
|
+
userId: player.userId,
|
|
190
|
+
direction: 'debit',
|
|
191
|
+
amount: '3',
|
|
192
|
+
currency: 'USD',
|
|
193
|
+
reason: 'duplicate credit reversal',
|
|
194
|
+
idempotencyKey: randomUUID(),
|
|
195
|
+
...NO_CLIENT_META,
|
|
196
|
+
});
|
|
197
|
+
expect(replay).toEqual(credit);
|
|
198
|
+
expect(await balanceOf(player.userId)).toBe(12);
|
|
199
|
+
expect(await txById(credit.transactionId)).toMatchObject({
|
|
200
|
+
type: 'manual_credit',
|
|
201
|
+
status: 'completed',
|
|
202
|
+
reviewedBy: adminId,
|
|
203
|
+
reviewReason: 'settlement correction',
|
|
204
|
+
});
|
|
205
|
+
expect(await txById(debit.transactionId)).toMatchObject({ type: 'manual_debit' });
|
|
206
|
+
expect(audit.recordInTransaction).toHaveBeenCalledTimes(2);
|
|
207
|
+
expect(audit.recordInTransaction).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
208
|
+
action: 'wallet.manual_adjustment.created',
|
|
209
|
+
actorId: adminId,
|
|
210
|
+
resourceId: credit.transactionId,
|
|
211
|
+
}));
|
|
212
|
+
expect(psp.processDeposit).not.toHaveBeenCalled();
|
|
213
|
+
expect(psp.processWithdrawal).not.toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
it('rejects an overdraw without a ledger or audit entry', async () => {
|
|
216
|
+
const { svc, audit } = makeService({ identityReader: playerIdentityReader() });
|
|
217
|
+
const player = await seedWallet({ balance: '10' });
|
|
218
|
+
await expect(svc.manualAdjust({
|
|
219
|
+
adminId: randomUUID(),
|
|
220
|
+
userId: player.userId,
|
|
221
|
+
direction: 'debit',
|
|
222
|
+
amount: '11',
|
|
223
|
+
currency: 'USD',
|
|
224
|
+
reason: 'correction',
|
|
225
|
+
idempotencyKey: randomUUID(),
|
|
226
|
+
...NO_CLIENT_META,
|
|
227
|
+
})).rejects.toBeInstanceOf(InsufficientBalanceError);
|
|
228
|
+
expect(await balanceOf(player.userId)).toBe(10);
|
|
229
|
+
expect(await txRows(player.id)).toHaveLength(0);
|
|
230
|
+
expect(audit.recordInTransaction).not.toHaveBeenCalled();
|
|
231
|
+
});
|
|
232
|
+
it('creates a selected-currency balance for a player without a wallet', async () => {
|
|
233
|
+
const { svc } = makeService({ identityReader: playerIdentityReader() });
|
|
234
|
+
const userId = randomUUID();
|
|
235
|
+
await svc.manualAdjust({
|
|
236
|
+
adminId: randomUUID(),
|
|
237
|
+
userId,
|
|
238
|
+
direction: 'credit',
|
|
239
|
+
amount: '7',
|
|
240
|
+
currency: 'EUR',
|
|
241
|
+
reason: 'goodwill',
|
|
242
|
+
idempotencyKey: randomUUID(),
|
|
243
|
+
...NO_CLIENT_META,
|
|
244
|
+
});
|
|
245
|
+
expect(await svc.getBalances(userId)).toEqual({
|
|
246
|
+
activeCurrency: 'EUR',
|
|
247
|
+
balances: [{ currency: 'EUR', balance: '7.000000000000000000' }],
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|
|
154
251
|
describe('WalletService.getBalances / setActiveCurrency (real PG)', () => {
|
|
155
252
|
it('returns every held currency ordered by code alongside the active one', async () => {
|
|
156
253
|
const { svc } = makeService();
|
|
@@ -216,6 +313,99 @@ describe('WalletService.withdraw (real PG)', () => {
|
|
|
216
313
|
destinationAddress: 'bc1qexample',
|
|
217
314
|
});
|
|
218
315
|
});
|
|
316
|
+
it('pins the only payable network on the transaction', async () => {
|
|
317
|
+
const { svc } = makeService();
|
|
318
|
+
const w = await seedWallet({ balance: '2', currency: 'BTC' });
|
|
319
|
+
await db.drizzle.db.insert(walletAsset).values({
|
|
320
|
+
currency: 'BTC',
|
|
321
|
+
network: 'BITCOIN',
|
|
322
|
+
providerAssetId: 'BTC',
|
|
323
|
+
minDeposit: '0',
|
|
324
|
+
minWithdrawal: '0',
|
|
325
|
+
withdrawalFee: '0',
|
|
326
|
+
});
|
|
327
|
+
const result = await svc.withdraw({
|
|
328
|
+
userId: w.userId,
|
|
329
|
+
amount: '1',
|
|
330
|
+
currency: 'BTC',
|
|
331
|
+
destinationAddress: 'bc1qexample',
|
|
332
|
+
...NO_CLIENT_META,
|
|
333
|
+
});
|
|
334
|
+
expect(await txById(result.transactionId)).toMatchObject({ network: 'BITCOIN' });
|
|
335
|
+
});
|
|
336
|
+
it('demands a network when the currency is payable on several, holding no funds', async () => {
|
|
337
|
+
const { svc } = makeService();
|
|
338
|
+
const w = await seedWallet({ balance: '100', currency: 'USDT' });
|
|
339
|
+
await db.drizzle.db.insert(walletAsset).values(['ERC20', 'TRC20'].map((network) => ({
|
|
340
|
+
currency: 'USDT',
|
|
341
|
+
network,
|
|
342
|
+
providerAssetId: `USDT_${network}`,
|
|
343
|
+
minDeposit: '0',
|
|
344
|
+
minWithdrawal: '0',
|
|
345
|
+
withdrawalFee: '0',
|
|
346
|
+
})));
|
|
347
|
+
await expect(svc.withdraw({
|
|
348
|
+
userId: w.userId,
|
|
349
|
+
amount: '10',
|
|
350
|
+
currency: 'USDT',
|
|
351
|
+
destinationAddress: '0xexample',
|
|
352
|
+
...NO_CLIENT_META,
|
|
353
|
+
})).rejects.toBeInstanceOf(AmbiguousNetworkError);
|
|
354
|
+
expect(await balanceOf(w.userId)).toBe(100);
|
|
355
|
+
});
|
|
356
|
+
it('enforces the minimum of the chosen chain, not the currency', async () => {
|
|
357
|
+
const { svc } = makeService();
|
|
358
|
+
const w = await seedWallet({ balance: '100', currency: 'USDT' });
|
|
359
|
+
await db.drizzle.db.insert(walletAsset).values([
|
|
360
|
+
{
|
|
361
|
+
currency: 'USDT',
|
|
362
|
+
network: 'ERC20',
|
|
363
|
+
providerAssetId: 'USDT_ERC20',
|
|
364
|
+
minDeposit: '0',
|
|
365
|
+
minWithdrawal: '20',
|
|
366
|
+
withdrawalFee: '0',
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
currency: 'USDT',
|
|
370
|
+
network: 'BEP20',
|
|
371
|
+
providerAssetId: 'USDT_BSC',
|
|
372
|
+
minDeposit: '0',
|
|
373
|
+
minWithdrawal: '1',
|
|
374
|
+
withdrawalFee: '0',
|
|
375
|
+
},
|
|
376
|
+
]);
|
|
377
|
+
const args = {
|
|
378
|
+
userId: w.userId,
|
|
379
|
+
amount: '5',
|
|
380
|
+
currency: 'USDT',
|
|
381
|
+
destinationAddress: '0xexample',
|
|
382
|
+
...NO_CLIENT_META,
|
|
383
|
+
};
|
|
384
|
+
await expect(svc.withdraw({ ...args, network: 'ERC20' })).rejects.toBeInstanceOf(BelowMinimumWithdrawalError);
|
|
385
|
+
const ok = await svc.withdraw({ ...args, network: 'BEP20' });
|
|
386
|
+
expect(await txById(ok.transactionId)).toMatchObject({ network: 'BEP20' });
|
|
387
|
+
});
|
|
388
|
+
it('fails closed when the currency is configured but payable nowhere', async () => {
|
|
389
|
+
const { svc } = makeService();
|
|
390
|
+
const w = await seedWallet({ balance: '100', currency: 'USDT' });
|
|
391
|
+
await db.drizzle.db.insert(walletAsset).values({
|
|
392
|
+
currency: 'USDT',
|
|
393
|
+
network: 'ERC20',
|
|
394
|
+
providerAssetId: 'USDT_ERC20',
|
|
395
|
+
minDeposit: '0',
|
|
396
|
+
minWithdrawal: '0',
|
|
397
|
+
withdrawalFee: '0',
|
|
398
|
+
withdrawalEnabled: false,
|
|
399
|
+
});
|
|
400
|
+
await expect(svc.withdraw({
|
|
401
|
+
userId: w.userId,
|
|
402
|
+
amount: '10',
|
|
403
|
+
currency: 'USDT',
|
|
404
|
+
destinationAddress: '0xexample',
|
|
405
|
+
...NO_CLIENT_META,
|
|
406
|
+
})).rejects.toBeInstanceOf(WithdrawalDisabledError);
|
|
407
|
+
expect(await balanceOf(w.userId)).toBe(100);
|
|
408
|
+
});
|
|
219
409
|
it('throws DestinationAddressRequiredError for a crypto withdrawal with no address', async () => {
|
|
220
410
|
const { svc } = makeService();
|
|
221
411
|
const w = await seedWallet({ balance: '2', currency: 'BTC' });
|
|
@@ -478,7 +668,7 @@ describe('WalletService idempotency - withdraw (real PG)', () => {
|
|
|
478
668
|
});
|
|
479
669
|
});
|
|
480
670
|
describe('WalletService.approveWithdrawal (real PG)', () => {
|
|
481
|
-
it('moves pending -> completed
|
|
671
|
+
it('moves pending -> completed and emits approved then completed', async () => {
|
|
482
672
|
const { svc, events, psp } = makeService();
|
|
483
673
|
const w = await seedWallet({ balance: '60' });
|
|
484
674
|
const adminId = randomUUID();
|
|
@@ -489,7 +679,7 @@ describe('WalletService.approveWithdrawal (real PG)', () => {
|
|
|
489
679
|
expect(await txById(pending.id)).toMatchObject({
|
|
490
680
|
status: 'completed',
|
|
491
681
|
reviewedBy: adminId,
|
|
492
|
-
providerName:
|
|
682
|
+
providerName: DEFAULT_PAYMENT_PROVIDER,
|
|
493
683
|
});
|
|
494
684
|
expect(await balanceOf(w.userId)).toBe(60);
|
|
495
685
|
expect(emittedTopics(events)).toEqual([
|
|
@@ -497,12 +687,53 @@ describe('WalletService.approveWithdrawal (real PG)', () => {
|
|
|
497
687
|
'wallet.withdrawal.completed',
|
|
498
688
|
]);
|
|
499
689
|
});
|
|
500
|
-
it('
|
|
690
|
+
it('resolves providerName from the default binding when the pair has no catalog row', async () => {
|
|
501
691
|
const { svc } = makeService();
|
|
502
692
|
const w = await seedWallet({ currency: 'BTC' });
|
|
503
693
|
const pending = await seedTx(w.id, { currency: 'BTC', rail: 'crypto', amount: '1' });
|
|
504
694
|
await svc.approveWithdrawal(randomUUID(), pending.id);
|
|
505
|
-
expect(await txById(pending.id)).toMatchObject({ providerName:
|
|
695
|
+
expect(await txById(pending.id)).toMatchObject({ providerName: DEFAULT_PAYMENT_PROVIDER });
|
|
696
|
+
});
|
|
697
|
+
it("resolves providerName from the (currency, network) catalog row's own providerName", async () => {
|
|
698
|
+
const { svc } = makeService();
|
|
699
|
+
await db.drizzle.db.insert(walletAsset).values({
|
|
700
|
+
currency: 'USDT',
|
|
701
|
+
network: 'ERC20',
|
|
702
|
+
providerAssetId: 'USDT_ERC20',
|
|
703
|
+
minDeposit: '1',
|
|
704
|
+
minWithdrawal: '1',
|
|
705
|
+
withdrawalFee: '1',
|
|
706
|
+
providerName: 'vendor-a',
|
|
707
|
+
});
|
|
708
|
+
const w = await seedWallet({ currency: 'USDT' });
|
|
709
|
+
const pending = await seedTx(w.id, {
|
|
710
|
+
currency: 'USDT',
|
|
711
|
+
network: 'ERC20',
|
|
712
|
+
rail: 'crypto',
|
|
713
|
+
amount: '5',
|
|
714
|
+
});
|
|
715
|
+
await svc.approveWithdrawal(randomUUID(), pending.id);
|
|
716
|
+
expect(await txById(pending.id)).toMatchObject({ providerName: 'vendor-a' });
|
|
717
|
+
});
|
|
718
|
+
it('falls back to the default binding when the catalog row has a null providerName', async () => {
|
|
719
|
+
const { svc } = makeService();
|
|
720
|
+
await db.drizzle.db.insert(walletAsset).values({
|
|
721
|
+
currency: 'USDT',
|
|
722
|
+
network: 'TRC20',
|
|
723
|
+
providerAssetId: 'USDT_TRC20',
|
|
724
|
+
minDeposit: '1',
|
|
725
|
+
minWithdrawal: '1',
|
|
726
|
+
withdrawalFee: '1',
|
|
727
|
+
});
|
|
728
|
+
const w = await seedWallet({ currency: 'USDT' });
|
|
729
|
+
const pending = await seedTx(w.id, {
|
|
730
|
+
currency: 'USDT',
|
|
731
|
+
network: 'TRC20',
|
|
732
|
+
rail: 'crypto',
|
|
733
|
+
amount: '5',
|
|
734
|
+
});
|
|
735
|
+
await svc.approveWithdrawal(randomUUID(), pending.id);
|
|
736
|
+
expect(await txById(pending.id)).toMatchObject({ providerName: DEFAULT_PAYMENT_PROVIDER });
|
|
506
737
|
});
|
|
507
738
|
it('marks failed, refunds the hold, emits failed, and rethrows when the PSP throws', async () => {
|
|
508
739
|
const { svc, events, psp } = makeService();
|
|
@@ -763,7 +994,7 @@ describe('WalletService.getOrCreateDepositAddress (real PG)', () => {
|
|
|
763
994
|
userId,
|
|
764
995
|
currency: 'BTC',
|
|
765
996
|
address: 'bc1qexisting',
|
|
766
|
-
providerName: '
|
|
997
|
+
providerName: 'custody',
|
|
767
998
|
});
|
|
768
999
|
expect(await svc.getOrCreateDepositAddress(userId, 'BTC')).toEqual({
|
|
769
1000
|
address: 'bc1qexisting',
|
|
@@ -783,7 +1014,29 @@ describe('WalletService.getOrCreateDepositAddress (real PG)', () => {
|
|
|
783
1014
|
.select()
|
|
784
1015
|
.from(walletDepositAddress)
|
|
785
1016
|
.where(eq(walletDepositAddress.userId, userId));
|
|
786
|
-
expect(stored).toMatchObject({ address: 'bc1qnew', providerName:
|
|
1017
|
+
expect(stored).toMatchObject({ address: 'bc1qnew', providerName: DEFAULT_PAYMENT_PROVIDER });
|
|
1018
|
+
});
|
|
1019
|
+
it("persists the (currency, network) catalog row's own providerName", async () => {
|
|
1020
|
+
await db.drizzle.db.insert(walletAsset).values({
|
|
1021
|
+
currency: 'USDT',
|
|
1022
|
+
network: 'ERC20',
|
|
1023
|
+
providerAssetId: 'USDT_ERC20',
|
|
1024
|
+
minDeposit: '1',
|
|
1025
|
+
minWithdrawal: '1',
|
|
1026
|
+
withdrawalFee: '1',
|
|
1027
|
+
providerName: 'vendor-a',
|
|
1028
|
+
});
|
|
1029
|
+
const userId = randomUUID();
|
|
1030
|
+
const issueDepositAddress = vi.fn(async () => ({ address: '0xnew' }));
|
|
1031
|
+
const { svc } = makeService({
|
|
1032
|
+
payment: mock({ ...makePsp(), issueDepositAddress }),
|
|
1033
|
+
});
|
|
1034
|
+
await svc.getOrCreateDepositAddress(userId, 'USDT', 'ERC20');
|
|
1035
|
+
const [stored] = await db.drizzle.db
|
|
1036
|
+
.select()
|
|
1037
|
+
.from(walletDepositAddress)
|
|
1038
|
+
.where(eq(walletDepositAddress.userId, userId));
|
|
1039
|
+
expect(stored).toMatchObject({ address: '0xnew', providerName: 'vendor-a' });
|
|
787
1040
|
});
|
|
788
1041
|
it('issues one address when two calls race on the same user and currency', async () => {
|
|
789
1042
|
const userId = randomUUID();
|
|
@@ -811,7 +1064,7 @@ describe('WalletService.creditDepositByAddress (real PG)', () => {
|
|
|
811
1064
|
async function seedAddress(userId, address, currency = 'BTC', network) {
|
|
812
1065
|
await db.drizzle.db
|
|
813
1066
|
.insert(walletDepositAddress)
|
|
814
|
-
.values({ userId, currency, address, network, providerName: '
|
|
1067
|
+
.values({ userId, currency, address, network, providerName: 'custody' });
|
|
815
1068
|
}
|
|
816
1069
|
it('resolves the address, credits the wallet, and emits deposit.completed', async () => {
|
|
817
1070
|
const { svc, events } = makeService();
|