Skip to content

Commit cae2de1

Browse files
committed
Account
1 parent 6d20eba commit cae2de1

File tree

5 files changed

+122
-91
lines changed

5 files changed

+122
-91
lines changed

Diff for: account/AccountSubscription.js

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
const wait = (millis) => {
2+
return new Promise(resolve => {
3+
setTimeout(resolve, millis);
4+
});
5+
}
6+
7+
export class AccountSubscription {
8+
constructor(tonweb, accountAddress, startTime, onTransaction) {
9+
this.tonweb = tonweb;
10+
this.accountAddress = accountAddress;
11+
this.startTime = startTime; // start unixtime (stored in your database), transactions made earlier will be discarded.
12+
this.onTransaction = onTransaction;
13+
}
14+
15+
async start() {
16+
const getTransactions = async (time, offsetTransactionLT, offsetTransactionHash, retryCount) => {
17+
const COUNT = 10;
18+
19+
if (offsetTransactionLT) {
20+
console.log(`Get ${COUNT} transactions before transaction ${offsetTransactionLT}:${offsetTransactionHash}`);
21+
} else {
22+
console.log(`Get last ${COUNT} transactions`);
23+
}
24+
25+
// TON transaction has composite ID: account address (on which the transaction took place) + transaction LT (logical time) + transaction hash.
26+
// So TxID = address+LT+hash, these three parameters uniquely identify the transaction.
27+
// In our case, we are monitoring one wallet and the address is `accountAddress`.
28+
29+
let transactions;
30+
31+
try {
32+
transactions = await this.tonweb.provider.getTransactions(this.accountAddress, COUNT, offsetTransactionLT, offsetTransactionHash);
33+
} catch (e) {
34+
console.error(e);
35+
// if an API error occurs, try again
36+
retryCount++;
37+
if (retryCount < 10) {
38+
await wait(retryCount * 1000);
39+
return getTransactions(time, offsetTransactionLT, offsetTransactionHash, retryCount);
40+
} else {
41+
return 0;
42+
}
43+
}
44+
45+
console.log(`Got ${transactions.length} transactions`);
46+
47+
if (!transactions.length) {
48+
// If you use your own API instance make sure the code contains this fix https://github.com/toncenter/ton-http-api/commit/a40a31c62388f122b7b7f3da7c5a6f706f3d2405
49+
// If you use public toncenter.com then everything is OK.
50+
return time;
51+
}
52+
53+
if (!time) time = transactions[0].utime;
54+
55+
for (const tx of transactions) {
56+
57+
if (tx.utime < this.startTime) {
58+
return time;
59+
}
60+
61+
await this.onTransaction(tx);
62+
}
63+
64+
if (transactions.length === 1) {
65+
return time;
66+
}
67+
68+
const lastTx = transactions[transactions.length - 1];
69+
return await getTransactions(time, lastTx.transaction_id.lt, lastTx.transaction_id.hash, 0);
70+
}
71+
72+
73+
let isProcessing = false;
74+
75+
const tick = async () => {
76+
if (isProcessing) return;
77+
isProcessing = true;
78+
79+
try {
80+
const result = await getTransactions(undefined, undefined, undefined, 0);
81+
if (result > 0) {
82+
this.startTime = result; // store in your database
83+
}
84+
} catch (e) {
85+
console.error(e);
86+
}
87+
88+
isProcessing = false;
89+
}
90+
91+
setInterval(tick, 10 * 1000); // poll every 10 seconds
92+
tick();
93+
}
94+
}

Diff for: account/README.MD

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Subscribing to account transactions
2+
3+
`getTransactions` HTTP API method has a limit on the number of transactions it can return at a time.
4+
5+
Thus, you may sometimes need to make several requests to get all transaction of the account, the code [AccountSubscription.js](AccountSubscription.js) implements this.

Diff for: block/BlockSubscriptionIndex.js

+7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ export class BlockSubscriptionIndex {
3636
.then(response => response.error ? Promise.reject(response.error) : response)
3737
}
3838

39+
let isProcessing = false;
40+
3941
const tick = async () => {
42+
if (isProcessing) return;
43+
isProcessing = true;
44+
4045
try {
4146
const masterchainInfo = await this.tonweb.provider.getMasterchainInfo(); // get last masterchain info from node
4247
const lastMasterchainBlockNumber = masterchainInfo.last.seqno;
@@ -57,6 +62,8 @@ export class BlockSubscriptionIndex {
5762
} catch (e) {
5863
console.error(e);
5964
}
65+
66+
isProcessing = false;
6067
}
6168

6269
setInterval(tick, 1000); // new masterchain block created every ~5 seconds

Diff for: block/README.MD

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Transactions are made on the blockchain. Transactions are packaged into blocks.
44

5-
In order to process new payments, you need to keep track of new blocks in the blockchain, get transactions from blocks, and find transactions that relate to your service.
5+
In order to process new payments on multiple accounts, you need to keep track of new blocks in the blockchain, get transactions from blocks, and find transactions that relate to your service.
66

77
In TON blockchain, blocks go in parallel in masterchain and shardchains.
88

Diff for: deposits-single-wallet.js

+15-90
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Here we will look at how to accept deposits to a single wallet.
2+
Here we will look at how to accept Toncoin deposits to a single wallet.
33
44
So you are accepting payments (deposits) in Toncoins:
55
@@ -20,6 +20,7 @@ So you are accepting payments (deposits) in Toncoins:
2020
*/
2121

2222
import TonWeb from "tonweb";
23+
import {AccountSubscription} from "./account/AccountSubscription.js";
2324

2425
const isMainnet = false;
2526

@@ -30,100 +31,24 @@ const tonweb = isMainnet ?
3031
new TonWeb(new TonWeb.HttpProvider('https://toncenter.com/api/v2/jsonRPC', {apiKey: 'YOUR_MAINNET_API_KEY'})) :
3132
new TonWeb(new TonWeb.HttpProvider('https://testnet.toncenter.com/api/v2/jsonRPC', {apiKey: 'YOUR_TESTNET_API_KEY'}));
3233

33-
let isProcessing = false;
34-
let startTime = 0; // start unixtime (stored in your database), transactions made earlier will be discarded.
3534
const MY_WALLET_ADDRESS = 'EQBvI0aFLnw2QbZgjMPCLRdtRHxhUyinQudg6sdiohIwg5jL';
3635

37-
const wait = (millis) => {
38-
return new Promise(resolve => {
39-
setTimeout(resolve, millis);
40-
});
41-
}
42-
43-
const getTransactions = async (time, offsetTransactionLT, offsetTransactionHash, retryCount) => {
44-
const COUNT = 10;
45-
46-
if (offsetTransactionLT) {
47-
console.log(`Get ${COUNT} transactions before transaction ${offsetTransactionLT}:${offsetTransactionHash}`);
48-
} else {
49-
console.log(`Get last ${COUNT} transactions`);
50-
}
51-
52-
// TON transaction has composite ID: account address (on which the transaction took place) + transaction LT (logical time) + transaction hash.
53-
// So TxID = address+LT+hash, these three parameters uniquely identify the transaction.
54-
// In our case, we are monitoring one wallet and the address is MY_WALLET_ADDRESS.
55-
56-
let transactions;
57-
58-
try {
59-
transactions = await tonweb.provider.getTransactions(MY_WALLET_ADDRESS, COUNT, offsetTransactionLT, offsetTransactionHash);
60-
} catch (e) {
61-
console.error(e);
62-
// if an API error occurs, try again
63-
retryCount++;
64-
if (retryCount < 10) {
65-
await wait(retryCount * 1000);
66-
return getTransactions(time, offsetTransactionLT, offsetTransactionHash, retryCount);
67-
} else {
68-
return 0;
69-
}
70-
}
71-
72-
console.log(`Got ${transactions.length} transactions`);
73-
74-
if (!transactions.length) {
75-
// If you use your own API instance make sure the code contains this fix https://github.com/toncenter/ton-http-api/commit/a40a31c62388f122b7b7f3da7c5a6f706f3d2405
76-
// If you use public toncenter.com then everything is OK.
77-
return time;
78-
}
79-
80-
if (!time) time = transactions[0].utime;
36+
const onTransaction = async (tx) => {
37+
// If incoming message source address is defined and no outgoing messages - this is incoming Toncoins.
38+
// ATTENTION: ALWAYS CHECK THAT THERE WERE NO OUTGOING MESSAGES.
8139

82-
for (const tx of transactions) {
40+
if (tx.in_msg.source && tx.out_msgs.length === 0) {
41+
const value = tx.in_msg.value; // amount in nano-Toncoins (1 Toncoin = 1e9 nano-Toncoins)
42+
const senderAddress = tx.in_msg.source; // sender address
43+
const payload = tx.in_msg.message; // transfer text comment (in our case, the user should send the UUID as a text comment)
8344

84-
if (tx.utime < startTime) {
85-
return time;
86-
}
45+
// here you find the payment in your database by UUID,
46+
// check that the payment has not been processed yet and the amount matches,
47+
// save to the database that this payment has been processed.
8748

88-
// If incoming message source address is defined and no outgoing messages - this is incoming coins.
89-
// ATTENTION: always check that there were no outgoing messages.
90-
91-
if (tx.in_msg.source && tx.out_msgs.length === 0) {
92-
const value = tx.in_msg.value; // amount in nano-Toncoins (1 Toncoin = 1e9 nano-Toncoins)
93-
const senderAddress = tx.in_msg.source; // sender address
94-
const payload = tx.in_msg.message; // transfer text comment (in our case, the user should send the UUID as a text comment)
95-
96-
// here you find the payment in your database by UUID,
97-
// check that the payment has not been processed yet and the amount matches,
98-
// save to the database that this payment has been processed.
99-
100-
console.log(`Receive ${TonWeb.utils.fromNano(value)} TON from ${senderAddress} with comment "${payload}"`);
101-
}
102-
}
103-
104-
if (transactions.length === 1) {
105-
return time;
49+
console.log(`Receive ${TonWeb.utils.fromNano(value)} TON from ${senderAddress} with comment "${payload}"`);
10650
}
107-
108-
const lastTx = transactions[transactions.length - 1];
109-
return await getTransactions(time, lastTx.transaction_id.lt, lastTx.transaction_id.hash, 0);
110-
}
111-
112-
const tick = async () => {
113-
if (isProcessing) return;
114-
isProcessing = true;
115-
116-
try {
117-
const result = await getTransactions(undefined, undefined, undefined, 0);
118-
if (result > 0) {
119-
startTime = result; // store in your database
120-
}
121-
} catch (e) {
122-
console.error(e);
123-
}
124-
125-
isProcessing = false;
12651
}
12752

128-
setInterval(tick, 10 * 1000); // poll every 10 seconds
129-
tick();
53+
const accountSubscription = new AccountSubscription(tonweb, MY_WALLET_ADDRESS, 0, onTransaction);
54+
accountSubscription.start();

0 commit comments

Comments
 (0)