uniswap 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,274 @@
1
+
2
+ # import 'UniswapV2Callee'
3
+ # import 'UniswapV2ERC20'
4
+
5
+ ## add for now too
6
+ # import 'UnsafeNoApprovalERC20'
7
+ # import 'PublicMintERC20'
8
+
9
+
10
+ # contract :IUniswapV2Factory, abstract: true do
11
+ # function :feeTo, {}, :external, :view, returns: :address do
12
+ # end
13
+ # end
14
+
15
+ class UniswapV2Pair < UniswapV2ERC20
16
+
17
+ event :Mint, sender: Address,
18
+ amount0: UInt,
19
+ amount1: UInt
20
+ event :Burn, sender: Address,
21
+ amount0: UInt,
22
+ amount1: UInt,
23
+ to: Address
24
+ event :Swap, sender: Address,
25
+ amount0In: UInt,
26
+ amount1In: UInt,
27
+ amount0Out: UInt,
28
+ amount1Out: UInt,
29
+ to: Address
30
+ event :Sync, reserve0: UInt, # :uint112,
31
+ reserve1: UInt # :uint112
32
+ event :PreSwapReserves, reserve0: UInt, # :uint112,
33
+ reserve1: UInt # :uint112
34
+
35
+
36
+ MINIMUM_LIQUIDITY = 10 ** 3 #=> 1000
37
+
38
+ storage factory: Address,
39
+ token0: Address,
40
+ token1: Address,
41
+ _reserve0: UInt, # uint112 ??
42
+ _reserve1: UInt, # uint112 ??
43
+ _blockTimestampLast: Timestamp, # uint32 - why?
44
+ price0CumulativeLast: UInt,
45
+ price1CumulativeLast: UInt,
46
+ kLast: UInt,
47
+ _unlocked: UInt ## use bool or is counter more than 0/1 e.g. 0/1/2?
48
+
49
+
50
+ sig []
51
+ def constructor
52
+ super
53
+ @factory = msg.sender
54
+ @unlocked = uint256( 1 )
55
+ end
56
+
57
+ # note: can't name method initialize because of ruby
58
+ sig [Address, Address], :external
59
+ def init( token0:, token1: )
60
+ assert msg.sender == @factory, 'ScribeSwap: FORBIDDEN'
61
+
62
+ @token0 = token0
63
+ @token1 = token1
64
+ end
65
+
66
+ sig [], :view, returns: [UInt, UInt, Timestamp]
67
+ def getReserves
68
+ return [@_reserve0,
69
+ @_reserve1,
70
+ @_blockTimestampLast]
71
+ end
72
+
73
+ sig [Address, Address, UInt]
74
+ def _safeTransfer( token:, to:, value:)
75
+ result = ERC20.at( token ).transfer(to: to, amount: value)
76
+
77
+ assert result, "ScribeSwap: TRANSFER_FAILED"
78
+ end
79
+
80
+ end # class UniswapV2Pair
81
+
82
+ __END__
83
+
84
+
85
+ function :_update, {
86
+ balance0: :uint256,
87
+ balance1: :uint256,
88
+ _reserve0: :uint112,
89
+ _reserve1: :uint112
90
+ }, :private do
91
+ require(balance0 <= (2 ** 112 - 1) && balance1 <= (2 ** 112 - 1), 'ScribeSwap: OVERFLOW')
92
+
93
+ # blockTimestamp = uint32(block.timestamp % 2 ** 32)
94
+ blockTimestamp = block.timestamp
95
+ timeElapsed = blockTimestamp - s.blockTimestampLast # overflow is desired
96
+
97
+ if timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0
98
+ # * never overflows, and + overflow is desired
99
+ s.price0CumulativeLast += uint256(uqdiv(encode(_reserve1), _reserve0)) * timeElapsed
100
+ s.price1CumulativeLast += uint256(uqdiv(encode(_reserve0), _reserve1)) * timeElapsed
101
+ end
102
+
103
+ emit :PreSwapReserves, reserve0: s.reserve0, reserve1: s.reserve1
104
+
105
+ s.reserve0 = uint112(balance0)
106
+ s.reserve1 = uint112(balance1)
107
+
108
+ s.blockTimestampLast = blockTimestamp
109
+ emit :Sync, reserve0: s.reserve0, reserve1: s.reserve1
110
+ end
111
+
112
+ function :encode, { y: :uint112 }, :internal, :pure, returns: :uint224 do
113
+ return uint224(y) * (2 ** 112)
114
+ end
115
+
116
+ function :uqdiv, { x: :uint224, y: :uint112 }, :internal, :pure, returns: :uint224 do
117
+ return x / uint224(y)
118
+ end
119
+
120
+ function :_mintFee, { _reserve0: :uint112, _reserve1: :uint112 }, :private, returns: :bool do
121
+ feeTo = UniswapV2Factory.at( s.factory ).feeTo
122
+ feeOn = feeTo != address(0)
123
+ _kLast = s.kLast
124
+
125
+
126
+ if feeOn
127
+ if _kLast != 0
128
+ ## note: sqrt NOT built-in - double check
129
+ rootK = Integer.sqrt(_reserve0 * _reserve1)
130
+ rootKLast = Integer.sqrt(_kLast)
131
+ if rootK > rootKLast
132
+ numerator = totalSupply * (rootK - rootKLast)
133
+ denominator = rootK * 5 + rootKLast
134
+ liquidity = numerator.div(denominator)
135
+ _mint(feeTo, liquidity) if liquidity > 0
136
+ end
137
+ end
138
+ elsif _kLast != 0
139
+ s.kLast = 0
140
+ end
141
+ feeOn
142
+ end
143
+
144
+ function :mint, { to: :address }, :public, returns: :uint256 do
145
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
146
+
147
+ _reserve0, _reserve1, _ = getReserves
148
+
149
+ balance0 = ERC20.at(s.token0).balanceOf( __address__ ) ## address(this)
150
+ balance1 = ERC20.at(s.token1).balanceOf( __address__ ) ## address(this)
151
+
152
+ amount0 = balance0 - _reserve0
153
+ amount1 = balance1 - _reserve1
154
+
155
+ feeOn = _mintFee(_reserve0, _reserve1)
156
+ _totalSupply = s.totalSupply
157
+
158
+
159
+
160
+ if _totalSupply == 0
161
+ ## note: sqrt NOT built-in - double check
162
+ liquidity = uint256( Integer.sqrt(amount0 * amount1)) - s.MINIMUM_LIQUIDITY
163
+ _mint(address(0), s.MINIMUM_LIQUIDITY)
164
+ else
165
+ liquidity = [
166
+ (amount0 * _totalSupply).div(_reserve0),
167
+ (amount1 * _totalSupply).div(_reserve1)
168
+ ].min
169
+ end
170
+
171
+ require(liquidity > 0, 'ScribeSwap: INSUFFICIENT_LIQUIDITY_MINTED')
172
+ _mint(to, liquidity)
173
+
174
+
175
+ _update(balance0, balance1, _reserve0, _reserve1)
176
+ s.kLast = s.reserve0 * s.reserve1 if feeOn
177
+
178
+ emit :Mint, sender: msg.sender, amount0: amount0, amount1: amount1
179
+
180
+ return liquidity
181
+ end
182
+
183
+ function :burn, { to: :address }, :external, :lock, returns: { amount0: :uint256, amount1: :uint256 } do
184
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
185
+
186
+ _reserve0, _reserve1, _ = getReserves
187
+ _token0 = s.token0
188
+ _token1 = s.token1
189
+ balance0 = ERC20.at(_token0).balanceOf( __address__ ) ## address(this)
190
+ balance1 = ERC20.at(_token1).balanceOf( __address__ )
191
+ liquidity = s.balanceOf[ __address__ ]
192
+
193
+ feeOn = _mintFee(_reserve0, _reserve1)
194
+ _totalSupply = s.totalSupply
195
+ amount0 = (liquidity * balance0).div(_totalSupply)
196
+ amount1 = (liquidity * balance1).div(_totalSupply)
197
+
198
+ require(amount0 > 0 && amount1 > 0, 'ScribeSwap: INSUFFICIENT_LIQUIDITY_BURNED')
199
+ _burn( __address__, liquidity)
200
+ _safeTransfer(_token0, to, amount0)
201
+ _safeTransfer(_token1, to, amount1)
202
+ balance0 = ERC20.at(_token0).balanceOf( __address__ )
203
+ balance1 = ERC20.at(_token1).balanceOf( __address__ )
204
+
205
+ _update(balance0, balance1, _reserve0, _reserve1)
206
+ s.kLast = s.reserve0 * s.reserve1 if feeOn
207
+
208
+ emit :Burn, sender: msg.sender, amount0: amount0, amount1: amount1, to: to
209
+
210
+ ## return { amount0: amount0, amount1: amount1 }
211
+ return [amount0, amount1]
212
+ end
213
+
214
+
215
+ function :swap, { amount0Out: :uint256, amount1Out: :uint256, to: :address, data: :bytes }, :external do
216
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
217
+
218
+ require(amount0Out > 0 || amount1Out > 0, 'ScribeSwap: INSUFFICIENT_OUTPUT_AMOUNT')
219
+ _reserve0, _reserve1, _ = getReserves
220
+ require(amount0Out < _reserve0 && amount1Out < _reserve1, 'ScribeSwap: INSUFFICIENT_LIQUIDITY')
221
+
222
+ balance0 = 0
223
+ balance1 = 0
224
+ _token0 = s.token0
225
+ _token1 = s.token1
226
+
227
+ require(to != _token0 && to != _token1, 'ScribeSwap: INVALID_TO')
228
+
229
+ _safeTransfer(_token0, to, amount0Out) if amount0Out > 0 # optimistically transfer tokens
230
+ _safeTransfer(_token1, to, amount1Out) if amount1Out > 0 # optimistically transfer tokens
231
+
232
+ UniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data) if data.length > 0
233
+
234
+ balance0 = ERC20(_token0).balanceOf(address(this))
235
+ balance1 = ERC20(_token1).balanceOf(address(this))
236
+
237
+ amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0
238
+ amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0
239
+
240
+ require(amount0In > 0 || amount1In > 0, 'ScribeSwap: INSUFFICIENT_INPUT_AMOUNT')
241
+
242
+ balance0Adjusted = balance0 * 1000 - amount0In * 3
243
+ balance1Adjusted = balance1 * 1000 - amount1In * 3
244
+
245
+ require(
246
+ balance0Adjusted * balance1Adjusted >= uint256(_reserve0) * _reserve1 * (1000 ** 2),
247
+ 'ScribeSwap: K'
248
+ )
249
+
250
+ _update(balance0, balance1, _reserve0, _reserve1)
251
+ emit :Swap, sender: msg.sender, amount0In: amount0In, amount1In: amount1In, amount0Out: amount0Out, amount1Out: amount1Out, to: to
252
+ end
253
+
254
+ function :skim, { to: :address }, :external do
255
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
256
+
257
+ _token0 = s.token0
258
+ _token1 = s.token1
259
+
260
+ _safeTransfer(_token0, to, ERC20(_token0).balanceOf(address(this)) - s.reserve0)
261
+ _safeTransfer(_token1, to, ERC20(_token1).balanceOf(address(this)) - s.reserve1)
262
+ end
263
+
264
+ function :sync, {}, :external do
265
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
266
+
267
+ _update(
268
+ ERC20(s.token0).balanceOf(address(this)),
269
+ ERC20(s.token1).balanceOf(address(this)),
270
+ s.reserve0,
271
+ s.reserve1
272
+ )
273
+ end
274
+ end
@@ -0,0 +1,323 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ import 'UniswapV2Factory'
4
+
5
+ contract :UniswapV2Router do
6
+ address :public, :factory
7
+ address :public, :WETH
8
+
9
+ constructor(_factory: :address, _WETH: :address) {
10
+ s.factory = _factory
11
+ s.WETH = _WETH
12
+ }
13
+
14
+ function :_addLiquidity, {
15
+ tokenA: :address,
16
+ tokenB: :address,
17
+ amountADesired: :uint256,
18
+ amountBDesired: :uint256,
19
+ amountAMin: :uint256,
20
+ amountBMin: :uint256
21
+ }, :internal, :virtual, returns: { amountA: :uint256, amountB: :uint256 } do
22
+ if UniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)
23
+ UniswapV2Factory(factory).createPair(tokenA, tokenB)
24
+ end
25
+
26
+ reserveA, reserveB = getReserves(s.factory, tokenA, tokenB)
27
+
28
+ if reserveA == 0 && reserveB == 0
29
+ return { amountA: amountADesired, amountB: amountBDesired }
30
+ else
31
+ amountBOptimal = quote(amountADesired, reserveA, reserveB)
32
+
33
+ if amountBOptimal <= amountBDesired
34
+ require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT')
35
+
36
+ return { amountA: amountADesired, amountB: amountBOptimal }
37
+ else
38
+ amountAOptimal = quote(amountBDesired, reserveB, reserveA)
39
+
40
+ require(amountAOptimal <= amountADesired, "ASSERT")
41
+
42
+ require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT')
43
+
44
+ return { amountA: amountAOptimal, amountB: amountBDesired }
45
+ end
46
+ end
47
+ end
48
+
49
+ function :addLiquidity, {
50
+ tokenA: :address,
51
+ tokenB: :address,
52
+ amountADesired: :uint256,
53
+ amountBDesired: :uint256,
54
+ amountAMin: :uint256,
55
+ amountBMin: :uint256,
56
+ to: :address,
57
+ deadline: :uint256
58
+ }, :public, :virtual, returns: { amountA: :uint256, amountB: :uint256, liquidity: :uint256 } do
59
+ require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
60
+
61
+ amountA, amountB = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin)
62
+
63
+ pair = pairFor(s.factory, tokenA, tokenB)
64
+
65
+ _safeTransferFrom(token: tokenA, from: msg.sender, to: pair, value: amountA)
66
+ _safeTransferFrom(token: tokenB, from: msg.sender, to: pair, value: amountB)
67
+
68
+ liquidity = UniswapV2Pair(pair).mint(to: to)
69
+
70
+ return { amountA: amountA, amountB: amountB, liquidity: liquidity }
71
+ end
72
+
73
+ function :removeLiquidity, {
74
+ tokenA: :address,
75
+ tokenB: :address,
76
+ liquidity: :uint256,
77
+ amountAMin: :uint256,
78
+ amountBMin: :uint256,
79
+ to: :address,
80
+ deadline: :uint256
81
+ }, :public, :virtual, returns: { amountA: :uint256, amountB: :uint256 } do
82
+ require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
83
+
84
+ pair = pairFor(s.factory, tokenA, tokenB)
85
+ UniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity)
86
+
87
+ amount0, amount1 = UniswapV2Pair(pair).burn(to)
88
+
89
+ token0, _ = sortTokens(tokenA, tokenB)
90
+
91
+ (amountA, amountB) = tokenA == token0 ? [amount0, amount1] : [amount1, amount0]
92
+
93
+ require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT')
94
+ require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT')
95
+
96
+ return { amountA: amountA, amountB: amountB }
97
+ end
98
+
99
+ function :swapExactTokensForTokens, {
100
+ amountIn: :uint256,
101
+ amountOutMin: :uint256,
102
+ path: [:address],
103
+ to: :address,
104
+ deadline: :uint256
105
+ }, :public, :virtual, returns: [:uint256] do
106
+ require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
107
+
108
+ amounts = getAmountsOut(factory, amountIn, path)
109
+
110
+ require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT')
111
+
112
+ _safeTransferFrom(
113
+ path[0], msg.sender, pairFor(factory, path[0], path[1]), amounts[0]
114
+ )
115
+
116
+ _swap(amounts, path, to)
117
+
118
+ return amounts
119
+ end
120
+
121
+ function :swapTokensForExactTokens, {
122
+ amountOut: :uint256,
123
+ amountInMax: :uint256,
124
+ path: [:address],
125
+ to: :address,
126
+ deadline: :uint256
127
+ }, :public, :virtual, returns: [:uint256] do
128
+ require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
129
+
130
+ amounts = getAmountsIn(factory, amountOut, path)
131
+ require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT')
132
+
133
+ _safeTransferFrom(
134
+ path[0], msg.sender, pairFor(factory, path[0], path[1]), amounts[0]
135
+ )
136
+
137
+ _swap(amounts, path, to)
138
+
139
+ return amounts
140
+ end
141
+
142
+ function :_swap, {
143
+ amounts: [:uint256],
144
+ path: [:address],
145
+ _to: :address
146
+ }, :internal, :virtual do
147
+ # TODO: how bad can this loop get?
148
+
149
+ for i in 0...(path.length - 1)
150
+ input, output = path[i], path[i + 1]
151
+ token0, _ = sortTokens(input, output)
152
+ amountOut = amounts[i + 1]
153
+ amount0Out, amount1Out = input == token0 ? [0, amountOut] : [amountOut, 0]
154
+ to = i < path.length - 2 ? pairFor(factory, output, path[i + 2]) : _to
155
+
156
+ UniswapV2Pair(pairFor(factory, input, output)).swap(amount0Out, amount1Out, to, "")
157
+ end
158
+
159
+ return nil
160
+ end
161
+
162
+ function :_safeTransferFrom, { token: :address, from: :address, to: :address, value: :uint256 }, :private do
163
+ result = ERC20(token).transferFrom(from: from, to: to, amount: value)
164
+
165
+ require(result, "ScribeSwap: TRANSFER_FAILED")
166
+ end
167
+
168
+ function :getAmountsOut, {
169
+ factory: :address,
170
+ amountIn: :uint256,
171
+ path: [:address]
172
+ }, :public, :view, returns: [:uint256] do
173
+ require(path.length >= 2, 'UniswapV2Library: INVALID_PATH')
174
+
175
+ amounts = array(:uint256, path.length)
176
+ amounts[0] = amountIn
177
+
178
+ for i in 0...(path.length - 1)
179
+ reserveIn, reserveOut = getReserves(factory, path[i], path[i + 1])
180
+ amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut)
181
+ end
182
+
183
+ return amounts
184
+ end
185
+
186
+ function :getAmountOut, {
187
+ amountIn: :uint256,
188
+ reserveIn: :uint256,
189
+ reserveOut: :uint256
190
+ }, :public, :view, returns: :uint256 do
191
+ require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
192
+ require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
193
+
194
+ amountInWithFee = amountIn * 997;
195
+ numerator = amountInWithFee * reserveOut
196
+ denominator = reserveIn * 1000 + amountInWithFee
197
+ amountOut = numerator.div(denominator)
198
+
199
+ return amountOut
200
+ end
201
+
202
+ function :getAmountsIn, {
203
+ factory: :address,
204
+ amountOut: :uint256,
205
+ path: [:address]
206
+ }, :public, :view, returns: [:uint256] do
207
+ require(path.length >= 2, 'UniswapV2Library: INVALID_PATH')
208
+
209
+ amounts = array(:uint256, path.length)
210
+ amounts[amounts.length - 1] = amountOut
211
+
212
+ for i in (path.length - 1).downto(1)
213
+ reserveIn, reserveOut = getReserves(factory, path[i - 1], path[i])
214
+ amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut)
215
+ end
216
+
217
+ return amounts
218
+ end
219
+
220
+ function :getAmountIn, {
221
+ amountOut: :uint256,
222
+ reserveIn: :uint256,
223
+ reserveOut: :uint256
224
+ }, :public, :view, returns: :uint256 do
225
+ require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT')
226
+ require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY')
227
+
228
+ numerator = reserveIn * amountOut * 1000
229
+ denominator = (reserveOut - amountOut) * 997
230
+ amountIn = (numerator.div(denominator)) + 1
231
+
232
+ return amountIn
233
+ end
234
+
235
+ function :quote, {
236
+ amountA: :uint256,
237
+ reserveA: :uint256,
238
+ reserveB: :uint256
239
+ }, :public, :pure, returns: :uint256 do
240
+ require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
241
+ require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
242
+
243
+ return (amountA * reserveB).div(reserveA);
244
+ end
245
+
246
+ function :getReserves, {
247
+ factory: :address,
248
+ tokenA: :address,
249
+ tokenB: :address
250
+ }, :public, :view, returns: { reserveA: :uint256, reserveB: :uint256 } do
251
+ token0, _ = sortTokens(tokenA, tokenB)
252
+
253
+ reserve0, reserve1, _ = UniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
254
+
255
+ (reserveA, reserveB) = tokenA == token0 ? [reserve0, reserve1] : [reserve1, reserve0]
256
+
257
+ return {
258
+ reserveA: reserveA,
259
+ reserveB: reserveB
260
+ }
261
+ end
262
+
263
+ function :pairFor, {
264
+ factory: :address,
265
+ tokenA: :address,
266
+ tokenB: :address
267
+ }, :internal, :pure, returns: :address do
268
+ token0, token1 = sortTokens(tokenA, tokenB)
269
+
270
+ return create2_address(
271
+ salt: keccak256(abi.encodePacked(token0, token1)),
272
+ deployer: factory,
273
+ contract_type: "UniswapV2Pair"
274
+ )
275
+ end
276
+
277
+ function :sortTokens, { tokenA: :address, tokenB: :address }, :internal, :pure, returns: { token0: :address, token1: :address } do
278
+ require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES')
279
+
280
+ token0, token1 = tokenA.cast(:uint256) < tokenB.cast(:uint256) ? [tokenA, tokenB] : [tokenB, tokenA]
281
+
282
+ require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS')
283
+
284
+ return { token0: token0, token1: token1 }
285
+ end
286
+
287
+ function :userStats, {
288
+ user: :address,
289
+ tokenA: :address,
290
+ tokenB: :address,
291
+ }, :public, :view, returns: {
292
+ userTokenABalance: :uint256,
293
+ userTokenBBalance: :uint256,
294
+ tokenAName: :string,
295
+ tokenBName: :string,
296
+ tokenAReserves: :uint256,
297
+ tokenBReserves: :uint256,
298
+ userLPBalance: :uint256,
299
+ pairAddress: :address
300
+ } do
301
+ tokenAReserves = 0
302
+ tokenBReserves = 0
303
+ userLPBalance = 0
304
+
305
+ if UniswapV2Factory(s.factory).getPair(tokenA, tokenB) != address(0)
306
+ tokenAReserves, tokenBReserves = getReserves(s.factory, tokenA, tokenB)
307
+
308
+ pair = UniswapV2Factory(s.factory).getPair(tokenA, tokenB)
309
+ userLPBalance = UniswapV2ERC20(pair).balanceOf(user)
310
+ end
311
+
312
+ return {
313
+ userTokenABalance: ERC20(tokenA).balanceOf(user),
314
+ userTokenBBalance: ERC20(tokenB).balanceOf(user),
315
+ tokenAName: ERC20(tokenA).name(),
316
+ tokenBName: ERC20(tokenB).name(),
317
+ tokenAReserves: tokenAReserves,
318
+ tokenBReserves: tokenBReserves,
319
+ userLPBalance: userLPBalance,
320
+ pairAddress: pair
321
+ }
322
+ end
323
+ end