-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathapproval.js
More file actions
150 lines (135 loc) · 5.27 KB
/
approval.js
File metadata and controls
150 lines (135 loc) · 5.27 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Wallet token approvals
* These approvals should be executed for every liquidator and arbitrage wallet once in order for liquidation or interacting with the
* Swap contract can work flawlessly
*/
import C from '../controller/contract';
import W from '../secrets/accounts';
import Util from '../util/helper';
import conf from '../config/config';
const web3 = C.web3;
const BN = web3.utils.BN;
const MAX_UINT256 = new BN('2').pow(new BN('256')).sub(new BN('1'));
const amount = MAX_UINT256;
const amountThreshold = amount.div(new BN('10')).mul(new BN('9')); // 90% of uint256 might be enough
const tokenAddresses = C.getAllTokenAddresses();
const maxPendingTransactions = 4;
async function approve() {
await approveLiquidatorWallets();
await approveArbitrageWallets();
await approveRolloverWallets();
}
async function approveLiquidatorWallets() {
console.log("start approving liquidator wallets")
for (let wallet of W.liquidator) {
const walletAddress = wallet.adr;
for (let tokenContractAddress of tokenAddresses) {
await approveToken(tokenContractAddress, walletAddress, conf.sovrynProtocolAdr);
}
}
}
async function approveArbitrageWallets() {
console.log("start approving arbitrage wallets")
for (let wallet of W.arbitrage) {
const walletAddress = wallet.adr;
for (let tokenContractAddress of tokenAddresses) {
// approve both the AMM and RBTC proxy to spend the tokens
// in theory, RBTC proxy should be enough....
await approveToken(tokenContractAddress, walletAddress, conf.swapsImpl);
await approveToken(tokenContractAddress, walletAddress, conf.wRbtcWrapper);
}
}
}
async function approveRolloverWallets() {
console.log("start approving rollover wallets")
for (let wallet of W.rollover) {
const walletAddress = wallet.adr;
for (let tokenContractAddress of tokenAddresses) {
await approveToken(tokenContractAddress, walletAddress, conf.sovrynProtocolAdr);
}
}
}
const alreadyApproved = {};
let numPendingTransactions = 0;
async function approveToken(tokenAddress, ownerAddress, spenderAddress) {
const tokenSymbol = C.getTokenSymbol(tokenAddress);
tokenAddress = tokenAddress.toLowerCase();
ownerAddress = ownerAddress.toLowerCase();
spenderAddress = spenderAddress.toLowerCase();
const cacheKey = `${tokenAddress}-${ownerAddress}-${spenderAddress}`;
if(alreadyApproved[cacheKey]) {
console.log(`already approved ${spenderAddress} to spend ${tokenSymbol} on behalf of ${ownerAddress}`);
return;
}
alreadyApproved[cacheKey] = true;
const tokenContract = C.getTokenInstance(tokenAddress);
if(!tokenContract) {
throw new Error(`unknown token: ${tokenAddress}`);
}
const allowance = await tokenContract.methods.allowance(ownerAddress, spenderAddress).call();
if(new BN(allowance).gt(amountThreshold)) {
console.log(`${spenderAddress} already has enough allowance to spend ${tokenSymbol} on behalf of ${ownerAddress}`);
return;
}
await waitForPendingTransactions();
const nonce = await web3.eth.getTransactionCount(ownerAddress, 'pending');
const gasPrice = await C.getGasPrice();
console.log(`approving ${spenderAddress} to spend unlimited ${tokenSymbol} on behalf of ${ownerAddress} (nonce ${nonce})`);
numPendingTransactions++;
const txHash = await new Promise((resolve, reject) => {
tokenContract.methods.approve(spenderAddress, amount).send({
from: ownerAddress,
nonce,
gas: 200000,
gasPrice,
}).once(
'transactionHash',
hash => resolve(hash)
).catch(
error => reject(error)
);
});
console.log("tx hash:", txHash);
waitForTransaction(txHash).then(txReceipt => {
numPendingTransactions--;
if(txReceipt.status) {
console.log(`Approval transaction successful for ${tokenSymbol}: ${txHash}`);
} else {
console.error(`Errored approval transaction for ${tokenSymbol}: ${txHash}`);
}
}).catch(e => {
console.error('Error waiting approval transaction', e);
});
}
async function waitForPendingTransactions(max = maxPendingTransactions) {
let iteration = 0;
while (numPendingTransactions >= max) {
if(iteration % 10 === 0) {
// avoid spam by only logging every 10 seconds
console.log(`Waiting until there are less than ${max} pending transactions (currently ${numPendingTransactions})...`)
}
iteration++;
await Util.wasteTime(1);
}
}
async function waitForTransaction(txHash) {
while(true) {
let txReceipt = await web3.eth.getTransactionReceipt(txHash);
if(txReceipt) {
return txReceipt;
}
await Util.wasteTime(3);
}
}
if(require.main === module) {
approve().then(() => {
console.log('All wallets approved. Waiting for all transactions');
waitForPendingTransactions(1).then(() => {
console.log('All wallets approved successfully.')
}).catch(e => {
console.error('Error waiting for transactions', e);
});
}).catch(e => {
console.error('Error in approval:', e);
})
}