-
Notifications
You must be signed in to change notification settings - Fork 32
/
deposits-multi-wallets.js
136 lines (103 loc) · 6.37 KB
/
deposits-multi-wallets.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
Here we will look at how to accept Toncoin deposits. Each user will have their own deposit address.
1. You once generated a key pair and get corresponding address of your HOT wallet as described in the `common.js`.
All payments from the user will first go to the user's deposit wallet, and then go to the HOT wallet.
2. When your user has to pay, you generate new wallet for him (as described in `common.js`) and store keys in your database.
3. You tell the user - please send N Toncoins to this wallet address.
You can use a deeplink, by clicking on which the user will open the wallet app with all the fields filled in, if the wallet app is installed.
ton://transfer/<wallet_address>?amount=<amount_in_nano>
4. Your backend is constantly subscribed to blocks appearing on the network.
It is convenient to use the Index HTTP API of toncenter.com: https://toncenter.com/api/v3/# or https://testnet.toncenter.com/api/v3/#
5. Your backend iterates the transactions of each block, and if the transaction occurred on one of the deposit wallets, it is processed as a deposit.
For security, we double-check each deposit transaction (its parameters and that the transaction exists) with an additional direct request to the node.
*/
import TonWeb from "tonweb";
import {BlockSubscriptionRaw} from "./block/BlockSubscriptionRaw.js";
import {BlockSubscriptionIndex} from "./block/BlockSubscriptionIndex.js";
const BN = TonWeb.utils.BN;
const IS_TESTNET = true;
const TONCENTER_API_KEY = IS_TESTNET ? 'YOUR_TESTNET_API_KEY' : 'YOUR_MAINNET_API_KEY'; // obtain on https://toncenter.com
// You can use your own instance of TON-HTTP-API or public toncenter.com
const NODE_API_URL = IS_TESTNET ? 'https://testnet.toncenter.com/api/v2/jsonRPC' : 'https://toncenter.com/api/v2/jsonRPC';
const INDEX_API_URL = IS_TESTNET ? 'https://testnet.toncenter.com/api/index/' : 'https://toncenter.com/api/index/';
const tonweb = new TonWeb(new TonWeb.HttpProvider(NODE_API_URL, {apiKey: TONCENTER_API_KEY}));
const MY_HOT_WALLET_ADDRESS = 'UQB7AhB4fP7SWtnfnIMcVUkwIgVLKqijlcpjNEPUVontys5I';
/**
* @param address {string}
* @return {boolean}
*/
const isDepositAddress = async (address) => {
new TonWeb.Address(address).toString(true, true, false); // non-bounceable format used for wallets
// more about address forms - https://docs.ton.org/learn/overviews/addresses#raw-and-user-friendly-addresses
// check in DB that this address is one of deposit addresses of your service
return false;
}
const createWallet = (keyPair) => {
const WalletClass = tonweb.wallet.all.v3R2;
const wallet = new WalletClass(tonweb.provider, {
publicKey: keyPair.publicKey
});
return wallet;
}
/**
* @param tx {Object}
* @return {Promise<void>}
*/
const processDeposit = async (tx) => {
const balance = new BN(await tonweb.provider.getBalance(tx.address.account_address));
if (balance.gt(new BN(0))) {
const keyPair = TonWeb.utils.nacl.sign.keyPair(); // get key pair for this deposit wallet from your database
const depositWallet = createWallet(keyPair);
const seqno = await depositWallet.methods.seqno().call();
const transfer = await depositWallet.methods.transfer({
secretKey: keyPair.secretKey,
toAddress: MY_HOT_WALLET_ADDRESS,
amount: 0,
seqno: seqno,
payload: '123', // if necessary, here you can set a unique payload to distinguish the incoming payment to the hot wallet
sendMode: 128 + 32, // mode 128 is used for messages that are to carry all the remaining balance; mode 32 means that the current account must be destroyed if its resulting balance is zero;
});
// IMPORTANT:
// We send all balance from deposit wallet to hot wallet and destroy deposit wallet smart contract.
// After destroy deposit wallet account will be `unitialized`.
// Don't worry, you can always deploy it again with the next transfer (and then immediately destroy it).
// TON has a micro fee for storage, which is occasionally debited from the balance of smart contracts simply for the fact that it's data is stored in the blockchain.
// If there is nothing on the balance, then after a while the account will be frozen.
// To avoid this and to be able to always use this address for this user, we destroy the account after each transfer.
// Destroyed accounts do not store data and therefore do not pay for storage.
await transfer.send();
// In real life, you need to create a new transfer task, and repeat it until the balance of the deposit wallet is positive.
// In case the API `send` call for some reason was not executed the first time.
// You can process incoming coins on the hot wallet as described in `deposits-single-wallet.js`
}
}
const onTransaction = async (tx) => {
// ATTENTION: ALWAYS CHECK THAT THERE WERE NO OUTGOING MESSAGES.
// It is important to check that Toncoins did not bounce back in case of an error.
// To do this, we check that there was only an incoming message and there were no outgoing messages.
if (tx.out_msgs.length > 0) {
return;
}
// If the `tx.account` address is in your database of deposit addresses
// then we check and process the deposit
if (await isDepositAddress(tx.account)) {
// For security, we double-check each deposit transaction with an additional direct request to the node
const result = await tonweb.provider.getTransactions(tx.account, 1, tx.lt, tx.hash);
if (result.length < 1) {
throw new Error('no transaction in node');
}
const txFromNode = result[0];
// You can check `in_msg` and `out_msgs` parameters between `tx` and `txFromNode`
await processDeposit(txFromNode); // use tx from your own node
}
}
const init = async () => {
const masterchainInfo = await tonweb.provider.getMasterchainInfo(); // get last masterchain info from node
const lastMasterchainBlockNumber = masterchainInfo.last.seqno;
console.log(`Starts from ${lastMasterchainBlockNumber} masterchain block`);
// const blockSubscription = new BlockSubscriptionRaw(tonweb, lastMasterchainBlockNumber, onTransaction);
// or
const blockSubscription = new BlockSubscriptionIndex(tonweb, lastMasterchainBlockNumber, onTransaction, INDEX_API_URL, TONCENTER_API_KEY);
await blockSubscription.start();
}
init();