-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
uniswap-contract-strongly-typed-example.ts
264 lines (217 loc) · 8.87 KB
/
uniswap-contract-strongly-typed-example.ts
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
import { BigNumber as BigNumberJs } from 'bignumber.js';
import { ethers, utils } from 'ethers';
import { BigNumber } from 'ethers/utils';
import { AbiExamples } from '../../abi-examples';
import { ContractContext as TokenContractContext } from './generated-typings/token-contract';
import { ContractContext as UniswapExchangeContractContext } from './generated-typings/uniswap-exchange-contract';
import { ContractContext as UniswapFactoryContractContext } from './generated-typings/uniswap-factory-contract';
const mockEthereumAddress = '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b';
// Connect to the network
const customHttpProvider = new ethers.providers.JsonRpcProvider(
'https://mainnet.infura.io/v3/280bb5b627394709938a7cc0b71a4a58'
);
class UniswapStronglyTypedExample {
// 0.5%
private readonly SLIPPAGE = 0.05;
// trade lasts for 15 minutes before it expiries
private readonly TRADE_DEADLINE_SECONDS = 15 * 60;
private _factoryContract: UniswapFactoryContractContext = this.buildUniswapFactoryContract();
constructor() {}
/**
* Gets how much token they will get for their trade minus all fees
* @param ethAmount The eth amount
*/
public async getTokenTradeAmountEthToErc20(
ethAmount: BigNumber
): Promise<string> {
const exchangeContract = await this.getExchangeContractForToken(
AbiExamples.funContractAddress
);
const price = await exchangeContract.getEthToTokenInputPrice(ethAmount);
this.logUniswapOutput(`Got the eth to token input price - ${price}`);
// Uniswap class - Got the eth to token input price - 102465873454
// big number ethers didnt have any shift by stuff so use
// this instead for this example
const tokenAmount = new BigNumberJs(price.toString()).shiftedBy(
AbiExamples.funDecimalPlaces * -1
);
this.logUniswapOutput(
`Got the fun token amount - ${tokenAmount.toString()}`
);
// Uniswap class - Got the fun token amount - 1024.65873454
// add some slippage
const tokenAmountWithSlippage = tokenAmount.minus(
tokenAmount.times(this.SLIPPAGE).toFixed()
);
this.logUniswapOutput(
`Fun token amount with the slippage taken off - ${tokenAmountWithSlippage.toString()}`
);
// Uniswap class - Fun token amount with the slippage taken off - 973.425797813
return tokenAmountWithSlippage.toString();
}
/**
* Get max amount of fun tokens you can buy
*/
public async maxAmountOfTokensToBuy(): Promise<string> {
const exchangeAddress = await this.getExchangeAddress(
AbiExamples.funContractAddress
);
const tokenContract = this.getTokenContract(AbiExamples.funContractAddress);
const tokenReserveRaw = await tokenContract.balanceOf(exchangeAddress);
this.logUniswapOutput(
`Got the token reserve raw value - ${tokenReserveRaw.toString()}`
);
// Uniswap class - Got the token reserve raw value - 1868161858283796
const tokenReserve = new BigNumberJs(tokenReserveRaw.toString()).shiftedBy(
AbiExamples.funDecimalPlaces * -1
);
this.logUniswapOutput(
`Token reserve raw value formatted to fun decimal places - ${tokenReserve}`
);
// Uniswap class - Token reserve raw value formatted to fun decimal places - 18681618.58283796
return tokenReserve.toString();
}
/**
* Make the trade encoding the data and sending the transaction
* @param ethAmount The eth amount
* @param minTokens The min tokens
*/
public async trade(
ethAmount: BigNumber,
minTokens: BigNumber
): Promise<string> {
const exchangeAddress = await this.getExchangeAddress(
AbiExamples.funContractAddress
);
const exchangeContract = this.getExchangeContractForTokenByExchangeAddress(
exchangeAddress
);
// you can use the same etherjs flows to send and sign transactions
// `contract.connect` will return a `ContractContext` so will still have
// all the typings exposed for you
const privateKey =
'0x0123456789012345678901234567890123456789012345678901234567890123';
const wallet = new ethers.Wallet(privateKey, customHttpProvider);
// Create a new instance of the Contract with a Signer, which allows
// update methods
const contractWithSigner = exchangeContract.connect(wallet);
const tx = await contractWithSigner.ethToTokenSwapInput(
minTokens,
this.generateTradeDeadlineUnixTime(),
{ value: utils.parseEther(ethAmount.toString()) }
);
this.logUniswapOutput(`Contract transaction ${tx.hash}`);
// Uniswap class - Contract transaction 0xaf0068dcf728afa5accd02172867627da4e6f946dfb8174a7be31f01b11d5364
// The operation is NOT complete yet; we must wait until it is mined
await tx.wait();
return tx.hash!;
}
/**
* Generates the trade dateline unix time
*/
private generateTradeDeadlineUnixTime(): string {
const timestamp =
((new Date().getTime() / 1e3) | 0) + this.TRADE_DEADLINE_SECONDS;
return timestamp.toString();
}
/**
* Get the exchange contract for the token
* @param erc20TokenContract The erc20 token contract
*/
private async getExchangeContractForToken(
erc20TokenContract: string
): Promise<UniswapExchangeContractContext> {
const exchangeAddress = await this.getExchangeAddress(erc20TokenContract);
return this.getExchangeContractForTokenByExchangeAddress(exchangeAddress);
}
/**
* Gets the exchange address for the erc20 token contract
* @param erc20TokenContract The erc20 token contract
*/
private async getExchangeAddress(
erc20TokenContract: string
): Promise<string> {
const exchangeAddress = await this._factoryContract.getExchange(
erc20TokenContract
);
this.logUniswapOutput(`Got the exchange address - ${exchangeAddress}`);
// Uniswap class - Got the exchange address - 0x60a87cC7Fca7E53867facB79DA73181B1bB4238B
return exchangeAddress;
}
/**
* Get the token contract for the erc20 token
* @param erc20TokenContract The erc20 token contract
*/
private getTokenContract(erc20TokenContract: string): TokenContractContext {
// Has to cast to unknown as we have made some typings changes to the
// contract interfaces which conflicts with `ethers` typings.
// This all work great but the compiler gets confused.
// Casting to unknown first then the `TokenContractContext` solves this.
return (new ethers.Contract(
erc20TokenContract,
AbiExamples.tokenAbi,
customHttpProvider
) as unknown) as TokenContractContext;
}
/**
* Get the exchange contract from the exchange address
* @param exchangeAddress The exchange address
*/
private getExchangeContractForTokenByExchangeAddress(
exchangeAddress: string
): UniswapExchangeContractContext {
// Has to cast to unknown as we have made some typings changes to the
// contract interfaces which conflicts with `ethers` typings.
// This all work great but the compiler gets confused.
// Casting to unknown first then the `UniswapExchangeContractContext` solves this.
return (new ethers.Contract(
exchangeAddress,
AbiExamples.uniswapExchangeAbi,
customHttpProvider
) as unknown) as UniswapExchangeContractContext;
}
/**
* Build the uniswap factory contract instance
*/
private buildUniswapFactoryContract(): UniswapFactoryContractContext {
// Has to cast to unknown as we have made some typings changes to the
// contract interfaces which conflicts with `ethers` typings.
// This all work great but the compiler gets confused.
// Casting to unknown first then the `UniswapFactoryContractContext` solves this.
return (new ethers.Contract(
AbiExamples.uniswapFactoryAddress,
AbiExamples.uniswapFactoryAbi,
customHttpProvider
) as unknown) as UniswapFactoryContractContext;
}
/**
* So you can tell what is the internal log or the example log when you run it
* @param message The message
*/
private logUniswapOutput(message: string): void {
console.log(`Uniswap class - ${message}`);
}
}
const example = async () => {
const ethAmount = ethers.utils.parseEther('0.01'); // 0.01 eth;
const uniswap = new UniswapStronglyTypedExample();
// get the max tokens
const maxTokens = await uniswap.maxAmountOfTokensToBuy();
console.log(maxTokens);
// 18681618.58283796
const getTokenTrade = await uniswap.getTokenTradeAmountEthToErc20(ethAmount);
console.log(getTokenTrade);
// 973.425797813
/**
* PLEASE NOTE:
*
* The below code is an example in how you would sign the transaction with the typings.
* If you run this script just using `node ./dist/ethers/uniswap-example/uniswap-contract-strongly-typed-example.js
* it will not work and throw errors in the trade calls as we dont have enough fun
*
*/
const transactionHash = await uniswap.trade(ethAmount, new BigNumber('900'));
console.log(transactionHash);
// 0x972c2155137efecb126dc5f4f72fb451753eab8f5fce45aad73e00861ae27fe1
};
example();