Skip to content

Commit 33ede6d

Browse files
authored
Merge pull request #32 from tipccjs/30-duplicate-transaction-detection
feat: Implement duplicate transaction emitation protection
2 parents 7d5e762 + 8270c9b commit 33ede6d

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,34 @@
33
Welcome to the tip.cc API Client npm package!
44

55
# Installation
6+
67
Simply create an npm project if you don't have an already, and install the package.
8+
79
```
810
npm init
911
npm i tipcc.js
1012
```
1113

1214
# Getting Started
15+
1316
> Tip: Want to get started without an introduction? Check out our [documentation](https://tipccjs.org/).
1417
1518
You can create a simple TipccClient like this:
19+
1620
```js
1721
import { TipccClient } from 'tipcc.js';
1822

1923
const client = TipccClient(myToken);
2024

2125
client.on('ready', () => {
22-
console.log('TipccClient is ready!')
26+
console.log('TipccClient is ready!');
2327
});
2428
```
29+
2530
`myToken` is your tip.cc API key.
2631

2732
## A note on API values
33+
2834
The tip.cc API uses the smallest denomination of currencies, giving values in atomic units.
2935
For an explanation of how this works, use [Ethereum's wei](https://www.investopedia.com/terms/w/wei.asp) as an example.
3036

@@ -33,6 +39,7 @@ tipcc.js uses the bignumber.js package to handle these numbers, and our API will
3339
For a in-depth explanation of BigNumbers and available features, check their own [documentation](https://mikemcl.github.io/bignumber.js/).
3440

3541
## Wallets
42+
3643
To get your balance on tip.cc, use the [WalletManager](https://tipccjs.org/classes/WalletManager):
3744

3845
```js
@@ -42,13 +49,16 @@ client.on('ready', async () => {
4249
return console.log('No BNB wallet found. Have you received any BNB?');
4350
}
4451

45-
console.log(`We've got ${wallet.balance.value} ${wallet.code} on our BNB wallet`);
52+
console.log(
53+
`We've got ${wallet.balance.value} ${wallet.code} on our BNB wallet`,
54+
);
4655

4756
console.log(`This is approximately ${wallet.balance.usdValue} USD`);
4857
});
4958
```
5059

5160
## Transactions
61+
5262
To receive transactions as events, use [TransactionManager](https://tipccjs.org/classes/TransactionManager)'s events:
5363

5464
```js
@@ -62,14 +72,19 @@ client.transactions.on('tip', (transaction) => {
6272
```
6373

6474
You can also get a single or many transactions by id:
75+
6576
```js
6677
client.on('ready', async () => {
6778
const oneTransaction = await client.transactions.fetch('one-id');
68-
const manyTransactions = await client.transactions.fetchMany(['this-id', 'another-id']);
79+
const manyTransactions = await client.transactions.fetchMany([
80+
'this-id',
81+
'another-id',
82+
]);
6983
});
7084
```
7185

7286
Getting transactions based on a filter is also possible:
87+
7388
```js
7489
client.on('ready', async () => {
7590
const transactionsByFilter = await client.transactions.fetchAll({
@@ -83,6 +98,7 @@ Using no filter will get all transactions for the bot/user.
8398
This is not recommended, unless you know what you're doing.
8499

85100
## Exchange rates
101+
86102
Use the [ExchangeRateCache](https://tipccjs.org/classes/ExchangeRateCache) to get exchange rates:
87103

88104
```js
@@ -97,6 +113,7 @@ client.on('ready', async () => {
97113
```
98114
99115
This is also accessible on other structures, such as wallets:
116+
100117
```js
101118
client.on('ready', async () => {
102119
const wallet = await client.wallets.fetch('BTC');
@@ -109,11 +126,13 @@ client.on('ready', async () => {
109126
```
110127
111128
## Currencies
129+
112130
The client provides caches for cryptocurrencies ([CryptocurrencyCache](https://tipccjs.org/classes/CryptocurrencyCache)) and fiats ([FiatCache](https://tipccjs.org/classes/FiatCache)).
113131
114132
This may be useful when you need some basic information about a currency.
115133
116134
Getting a cryptocurrency:
135+
117136
```js
118137
client.on('ready', async () => {
119138
const btc = client.cryptos.get('BTC');
@@ -127,6 +146,7 @@ client.on('ready', async () => {
127146
```
128147
129148
Getting a fiat:
149+
130150
```js
131151
client.on('ready', async () => {
132152
const usd = client.fiats.get('USD');
@@ -140,14 +160,17 @@ client.on('ready', async () => {
140160
```
141161
142162
## Exploring
163+
143164
Feel free to check out our [documentation](https://tipccjs.org/) to learn about our API and how you can use it.
144165
145166
Notice that the examples above are bits of code separated from each other.
146167
You can often use provided properties to get your task done with fewer lines of code by combining the structures explained.
147168
148169
# License
170+
149171
This project is licensed under the [MIT License](https://github.com/tipccjs/tipcc.js/blob/main/LICENSE).
150172
151173
# Disclaimer
174+
152175
The authors of this package are not the authors of [tip.cc](https://tip.cc).
153176
We are not responsible for any loss of funds caused by incorrect usage, bugs, exploits, or other causes when using this package.

src/structures/managers/TransactionManager.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { Transaction } from '../Transaction';
1212
import { Cache } from '../Cache';
1313
import { EventEmitter } from 'stream';
14+
import { CacheSet } from '../../utils/CacheSet';
1415

1516
interface ValueTransaction {
1617
recipient_s: string | string[];
@@ -51,6 +52,10 @@ export class TransactionManager extends EventEmitter {
5152

5253
private _pollingRetries = 0;
5354

55+
private _processedTransactions: CacheSet<string> = new CacheSet(
56+
24 * 60 * 60 * 1000,
57+
);
58+
5459
public constructor(payload: {
5560
client: TipccClient;
5661
cacheTtl?: number;
@@ -165,12 +170,20 @@ export class TransactionManager extends EventEmitter {
165170
await this.client.cryptos.refresh();
166171
refreshedCurrencies = true;
167172
}
168-
if (this.client.cryptos.get(transaction.amount.currencyCode))
173+
if (this.client.cryptos.get(transaction.amount.currencyCode)) {
174+
if (this._processedTransactions.has(transaction.id)) {
175+
console.warn(
176+
`Event emittion cancelled: Transaction ${transaction.id} has already been emitted`,
177+
);
178+
continue;
179+
}
180+
this._processedTransactions.add(transaction.id);
169181
this.emit(transaction.type, transaction);
170-
else
182+
} else {
171183
console.warn(
172184
`Event emittion cancelled: Unknown currency code ${transaction.amount.currencyCode} for transaction ${transaction.id}`,
173185
);
186+
}
174187
}
175188

176189
this._lastPoll = now;

src/utils/CacheSet.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export class CacheSet<T> extends Set {
2+
private _cacheTtl: number;
3+
4+
private _timeouts: Map<T, NodeJS.Timeout> = new Map();
5+
6+
constructor(cacheTtl: number) {
7+
super();
8+
this._cacheTtl = cacheTtl;
9+
}
10+
11+
public add(value: T): this {
12+
super.add(value);
13+
if (this._timeouts.has(value)) clearTimeout(this._timeouts.get(value)!);
14+
this._timeouts.set(
15+
value,
16+
setTimeout(() => this.delete(value), this._cacheTtl),
17+
);
18+
return this;
19+
}
20+
21+
public delete(value: T): boolean {
22+
if (this._timeouts.has(value)) clearTimeout(this._timeouts.get(value)!);
23+
return super.delete(value);
24+
}
25+
26+
public clear(): void {
27+
for (const timeout of this._timeouts.values()) clearTimeout(timeout);
28+
super.clear();
29+
}
30+
}

0 commit comments

Comments
 (0)