initkkk
This commit is contained in:
@@ -30,9 +30,8 @@ export const WalletController = {
|
||||
* GET /api/wallets — все адреса юзера.
|
||||
*/
|
||||
async getWallets(req: Request, res: Response) {
|
||||
const userId = '01KPKAFN6J1NJBY15DX8JE2QYB';
|
||||
try {
|
||||
const wallets = await WalletModel.findByUserId(userId);
|
||||
const wallets = await WalletModel.findByUserId(req.auth!.userId);
|
||||
res.json({
|
||||
success: true,
|
||||
data: wallets.map((w) => ({
|
||||
@@ -42,7 +41,7 @@ export const WalletController = {
|
||||
})),
|
||||
});
|
||||
} catch (err: any) {
|
||||
logger.error(`getWallets failed for user ${userId}: ${err.stack || err.message}`);
|
||||
logger.error(`getWallets failed for user ${req.auth!.userId}: ${err.stack || err.message}`);
|
||||
res.status(500).json({ success: false, error: 'Internal error' });
|
||||
}
|
||||
},
|
||||
@@ -54,7 +53,7 @@ export const WalletController = {
|
||||
* Возвращает: ТОЛЬКО адреса. Mnemonic клиенту не отдаём.
|
||||
*/
|
||||
async createWallet(req: Request, res: Response) {
|
||||
const userId = '01KPKAFN6J1NJBY15DX8JE2QYB';
|
||||
const userId = req.auth!.userId;
|
||||
|
||||
if (!isCryptoReady()) {
|
||||
res.status(503).json({ success: false, error: 'Crypto service not ready' });
|
||||
@@ -65,20 +64,36 @@ export const WalletController = {
|
||||
try {
|
||||
await UserModel.ensureExists(userId);
|
||||
|
||||
if (await UserModel.hasMnemonic(userId)) {
|
||||
// H14 — gate wallet creation behind KYC verification (опционально, controlled by env).
|
||||
// Если REQUIRE_KYC_FOR_WALLET=true в env, требуется kyc_verified=true.
|
||||
if (process.env.REQUIRE_KYC_FOR_WALLET === 'true') {
|
||||
const isVerified = await UserModel.isKycVerified(userId);
|
||||
if (!isVerified) {
|
||||
res.status(403).json({ success: false, error: 'KYC verification required before wallet creation' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// H27 — claim placeholder ПЕРЕД derive. Loser race не тратит CPU + memory secrets.
|
||||
// Атомарно: UPDATE WHERE encrypted_mnemonic IS NULL SET = 'PENDING_DERIVATION'
|
||||
const claimResult = await UserModel.claimWalletSlot(userId);
|
||||
if (claimResult === 'already_has') {
|
||||
res.status(409).json({ success: false, error: 'Wallet already exists' });
|
||||
return;
|
||||
}
|
||||
if (claimResult === 'no_user') {
|
||||
res.status(404).json({ success: false, error: 'User not found or deleted' });
|
||||
return;
|
||||
}
|
||||
// claimResult === 'claimed' — proceed
|
||||
|
||||
mnemonic = generateMnemonic();
|
||||
const derived = await deriveAllAddresses(mnemonic);
|
||||
const blob = encryptMnemonic(mnemonic);
|
||||
|
||||
const created = await db.transaction(async (trx) => {
|
||||
const claimed = await UserModel.setEncryptedMnemonicIfAbsent(userId, blob, trx);
|
||||
if (!claimed) {
|
||||
throw new ConflictError();
|
||||
}
|
||||
// H32 — finalize placeholder (must succeed since claim won earlier)
|
||||
await UserModel.finalizeWalletSlot(userId, blob, trx);
|
||||
await WalletModel.createMany(
|
||||
derived.map((w) => ({
|
||||
user_id: userId,
|
||||
@@ -88,8 +103,7 @@ export const WalletController = {
|
||||
})),
|
||||
trx,
|
||||
);
|
||||
// Дублируем ETH-адрес в users.erc20 — это поле прода-схемы
|
||||
// (custodial wallet's ETH address, доступный через простой SELECT без JOIN'а на wallets).
|
||||
// Дублируем ETH-адрес в users.erc20
|
||||
const ethWallet = derived.find((w) => w.chain === 'ETH');
|
||||
if (ethWallet) {
|
||||
await UserModel.setErc20Address(userId, ethWallet.address, trx);
|
||||
|
||||
Reference in New Issue
Block a user