Skip to content

Commit 3b74d2c

Browse files
committed
feat: cores scripts
1 parent 347def3 commit 3b74d2c

File tree

3 files changed

+409
-0
lines changed

3 files changed

+409
-0
lines changed

bin/assignCores.ts

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import '../src/config';
2+
import {
3+
Currency,
4+
EntityType,
5+
TransferRequest,
6+
TransferStatus,
7+
TransferType,
8+
} from '@dailydotdev/schema';
9+
import { parseBigInt } from '../src/common';
10+
import createOrGetConnection from '../src/db';
11+
import {
12+
UserTransaction,
13+
UserTransactionProcessor,
14+
UserTransactionStatus,
15+
} from '../src/entity/user/UserTransaction';
16+
import {
17+
createNjordAuth,
18+
getBalance,
19+
getNjordClient,
20+
updateBalanceCache,
21+
} from '../src/common/njord';
22+
import { TransferError } from '../src/errors';
23+
import { loadAuthKeys } from '../src/auth';
24+
import { parseArgs } from 'node:util';
25+
import { z } from 'zod';
26+
import fs from 'node:fs';
27+
import { parse } from 'csv-parse';
28+
29+
const main = async () => {
30+
const con = await createOrGetConnection();
31+
32+
try {
33+
const { values } = parseArgs({
34+
options: {
35+
usersPath: {
36+
type: 'string',
37+
short: 'p',
38+
},
39+
sender: {
40+
type: 'string',
41+
short: 's',
42+
default: 'system',
43+
},
44+
njordSender: {
45+
type: 'string',
46+
short: 'n',
47+
},
48+
origin: {
49+
type: 'string',
50+
short: 'o',
51+
},
52+
delimiter: {
53+
type: 'string',
54+
short: 'd',
55+
default: ',',
56+
},
57+
},
58+
});
59+
60+
const paramsSchema = z.object({
61+
// path to the file with user ids, with one (user,cores) per line
62+
usersPath: z.string(),
63+
64+
// which account to top up, it will appear on user wallet
65+
// defaults to system
66+
sender: z.string().nonempty().optional(),
67+
68+
// which njord account to top up, it will appear on njord side
69+
njordSender: z.string().nonempty(),
70+
71+
// should be unique or specify why the Cores were sent, it is also used dedup
72+
// in case of repeated script runs
73+
origin: z.string().min(4),
74+
75+
// delimiter used in the file
76+
delimiter: z.string().nonempty().default(','),
77+
});
78+
79+
const dataResult = paramsSchema.safeParse(values);
80+
81+
if (dataResult.error) {
82+
throw new Error(
83+
`Error '${dataResult.error.errors[0].path}': ${dataResult.error.errors[0].message}`,
84+
);
85+
}
86+
87+
const {
88+
usersPath,
89+
sender,
90+
njordSender,
91+
origin: requestOrigin,
92+
delimiter,
93+
} = dataResult.data;
94+
95+
const njordClient = getNjordClient();
96+
97+
loadAuthKeys();
98+
99+
const stream = fs
100+
.createReadStream(usersPath)
101+
.pipe(parse({ delimiter, from_line: 2 }));
102+
103+
stream.on('error', (err) => {
104+
console.error('Failed to read file: ', err.message);
105+
});
106+
107+
const userSchema = z.object({
108+
userId: z.string().nonempty(),
109+
cores: z.coerce.number().int().positive(),
110+
});
111+
112+
const users: z.infer<typeof userSchema>[] = [];
113+
114+
stream.on('data', ([userId, cores]) => {
115+
const userResult = userSchema.safeParse({
116+
userId,
117+
cores,
118+
});
119+
120+
if (userResult.error) {
121+
stream.destroy(new Error('Invalid user data in file'));
122+
123+
return;
124+
}
125+
126+
users.push(userResult.data);
127+
});
128+
129+
await new Promise((resolve, reject) => {
130+
stream.on('end', resolve);
131+
stream.on('error', reject);
132+
});
133+
134+
for (const { userId, cores } of users) {
135+
try {
136+
console.log(`Processing user: '${userId}', adding ${cores} Cores`);
137+
138+
const existingTransaction = await con
139+
.getRepository(UserTransaction)
140+
.createQueryBuilder()
141+
.where('"receiverId" = :receiverId AND status = :status', {
142+
receiverId: userId,
143+
status: UserTransactionStatus.Success,
144+
})
145+
.andWhere(`request->>'origin' = :requestOrigin`, {
146+
requestOrigin,
147+
})
148+
.getCount();
149+
150+
if (existingTransaction > 0) {
151+
throw new Error(`Transaction already exists for user: '${userId}'`);
152+
}
153+
154+
const response = await con.transaction(async (entityManager) => {
155+
const transaction = await entityManager
156+
.getRepository(UserTransaction)
157+
.save(
158+
entityManager.getRepository(UserTransaction).create({
159+
processor: UserTransactionProcessor.Njord,
160+
receiverId: userId,
161+
status: UserTransactionStatus.Success,
162+
productId: null,
163+
senderId: sender,
164+
value: cores,
165+
valueIncFees: cores,
166+
fee: 0,
167+
request: {
168+
origin: requestOrigin,
169+
},
170+
flags: {},
171+
}),
172+
);
173+
174+
const payload = new TransferRequest({
175+
idempotencyKey: transaction.id,
176+
transfers: [
177+
{
178+
transferType: TransferType.TRANSFER,
179+
currency: Currency.CORES,
180+
sender: {
181+
id: njordSender,
182+
type: EntityType.USER,
183+
},
184+
receiver: {
185+
id: transaction.receiverId,
186+
type: EntityType.USER,
187+
},
188+
amount: transaction.value,
189+
fee: {
190+
percentage: transaction.fee,
191+
},
192+
},
193+
],
194+
});
195+
196+
const response = await njordClient.transfer(
197+
payload,
198+
await createNjordAuth(payload),
199+
);
200+
201+
if (response.status !== TransferStatus.SUCCESS) {
202+
throw new TransferError(response);
203+
}
204+
205+
return response;
206+
});
207+
208+
console.log('Transfer success', response.idempotencyKey);
209+
210+
const { results } = response;
211+
212+
const result = results.find(
213+
(item) => item.transferType === TransferType.TRANSFER,
214+
);
215+
216+
if (!result) {
217+
throw new Error('No transfer result');
218+
}
219+
220+
const receiverBalance = parseBigInt(result.receiverBalance!.newBalance);
221+
222+
await updateBalanceCache({
223+
ctx: {
224+
userId: result.receiverId,
225+
},
226+
value: {
227+
amount: receiverBalance,
228+
},
229+
});
230+
231+
const userBalance = await getBalance({
232+
userId,
233+
});
234+
235+
if (userBalance.amount < cores) {
236+
throw new Error(
237+
'User balance might not be applied or cache busted, use checkNjordBalance script to verify',
238+
);
239+
}
240+
} catch (error) {
241+
console.error('Error in user:', userId);
242+
console.error((error as Error).message);
243+
}
244+
245+
console.log();
246+
}
247+
} catch (error) {
248+
console.error((error as Error).message);
249+
}
250+
251+
con.destroy();
252+
253+
process.exit(0);
254+
};
255+
256+
main();

bin/checkNjordBalance.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import '../src/config';
2+
3+
import { getFreshBalance } from '../src/common/njord';
4+
import { loadAuthKeys } from '../src/auth';
5+
import { parseArgs } from 'node:util';
6+
import { z } from 'zod';
7+
8+
const main = async () => {
9+
try {
10+
const { values } = parseArgs({
11+
options: {
12+
user: {
13+
type: 'string',
14+
short: 'u',
15+
},
16+
},
17+
});
18+
19+
const paramsSchema = z.object({
20+
// which njord account to check balance for
21+
user: z.string().nonempty(),
22+
});
23+
24+
const dataResult = paramsSchema.safeParse(values);
25+
26+
if (dataResult.error) {
27+
throw new Error(
28+
`Error '${dataResult.error.errors[0].path}': ${dataResult.error.errors[0].message}`,
29+
);
30+
}
31+
32+
const { user: userId } = dataResult.data;
33+
34+
loadAuthKeys();
35+
36+
console.log(
37+
await getFreshBalance({
38+
userId,
39+
}),
40+
);
41+
} catch (error) {
42+
console.error((error as Error).message);
43+
}
44+
45+
process.exit(0);
46+
};
47+
48+
main();

0 commit comments

Comments
 (0)