0xfacet-uniswap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 67fdf9cf8741ea2e11cc2303c9a1af2777a368e8ef1eb85103534c8fe839bc41
4
+ data.tar.gz: d5315a440b27540f9dbde981d7b2d09401b6f1fabb06bb2556f8a07fd7830525
5
+ SHA512:
6
+ metadata.gz: 81f4c5700297ea41913c3d6c7a4e458a4610737aa51f7a7a43eca0a615108ea18db75da3f6e25fbb67fe90c54659da968ed0488d71108bb14ac0eed307828eec
7
+ data.tar.gz: 4d6881f200b049da793e15b45355f06d5f4cd100ea4b5cb029af4fd95e538f2ea679d9805a59984f9ed96f6f392aff8a56edb95e08bec40ea423ab15744d6f1b
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2023-11-16
2
+
3
+ * Everything is new. First release
data/Manifest.txt ADDED
@@ -0,0 +1,12 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/0xfacet/uniswap.rb
6
+ lib/0xfacet/uniswap/UniswapSetupZapV2.rb
7
+ lib/0xfacet/uniswap/UniswapV2Callee.rb
8
+ lib/0xfacet/uniswap/UniswapV2ERC20.rb
9
+ lib/0xfacet/uniswap/UniswapV2Factory.rb
10
+ lib/0xfacet/uniswap/UniswapV2Pair.rb
11
+ lib/0xfacet/uniswap/UniswapV2Router.rb
12
+ lib/0xfacet/uniswap/UniswapV2RouterWithRewards.rb
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # 0xFacet Rubidity O.G. Uniswap V2 Contracts
2
+
3
+ 0xfacet-uniswap - rubidity o.g. uniswap v2 contracts (incl. UniswapV2Factory, UniswapV2Pair, UniswapV2Router, etc.)
4
+
5
+ * home :: [github.com/s6ruby/rubidity.review](https://github.com/s6ruby/rubidity.review)
6
+ * bugs :: [github.com/s6ruby/rubidity.review/issues](https://github.com/s6ruby/rubidity/issues)
7
+ * gem :: [rubygems.org/gems/0xfacet-uniswap](https://rubygems.org/gems/0xfacet-uniswap)
8
+ * rdoc :: [rubydoc.info/gems/0xfacet-uniswap](http://rubydoc.info/gems/0xfacet-uniswap)
9
+
10
+
11
+
12
+ ## About
13
+
14
+ What's happening herè?
15
+
16
+ The idea is to look at the facet vm code as-is (that is, NOT suggesting new or alternate syntax and semantics) in the review / commentary
17
+ and start to (re)package / modular-ize
18
+ code in "place holder" gems (waiting for adoption by the founders) such as 0xfacet and 0xfacet-typed and 0xfacet-rubidity.
19
+
20
+ See [Rubidity O.G. (Dumb Contracts) Public Code Review / (More) Tests / Gems & More »](https://github.com/s6ruby/rubidity.review)
21
+
22
+
23
+
24
+
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'hoe'
2
+
3
+ # require './lib/0xfacet/uniswap/version.rb'
4
+
5
+
6
+ Hoe.spec '0xfacet-uniswap' do
7
+
8
+ self.version = '0.0.1'
9
+
10
+ self.summary = '0xfacet-uniswap - rubidity o.g. uniswap v2 contracts (incl. UniswapV2Factory, UniswapV2Pair, UniswapV2Router, etc.)'
11
+ self.description = summary
12
+
13
+ self.urls = { home: 'https://github.com/s6ruby/rubidity.review' }
14
+
15
+ self.author = 'Gerald Bauer'
16
+ self.email = 'gerald.bauer@gmail.com'
17
+
18
+ # switch extension to .markdown for gihub formatting
19
+ self.readme_file = 'README.md'
20
+ self.history_file = 'CHANGELOG.md'
21
+
22
+ self.extra_deps = [
23
+ ['0xfacet-rubidity', '>= 0.0.1'],
24
+ ['0xfacet-contracts', '>= 0.0.1'],
25
+ ]
26
+
27
+
28
+ self.licenses = ['Public Domain']
29
+
30
+ self.spec_extras = {
31
+ required_ruby_version: '>= 2.3'
32
+ }
33
+ end
@@ -0,0 +1,156 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ import 'UniswapV2Factory'
4
+ import 'UnsafeNoApprovalERC20'
5
+ import 'UniswapV2Router'
6
+ import 'PublicMintERC20'
7
+
8
+ contract :UniswapSetupZapV2 do
9
+ array :address, :public, :factories
10
+ array :address, :public, :tokenAs
11
+ array :address, :public, :tokenBs
12
+ array :address, :public, :pairs
13
+ array :address, :public, :routers
14
+
15
+ string :public, :name
16
+
17
+ event :ZapOneSetup, { factory: :address, tokenA: :address, tokenB: :address, pair: :address, router: :address }
18
+
19
+ constructor() {
20
+ s.name = 'UniswapSetupZapV2'
21
+ }
22
+
23
+ function :zapDumbSwap, { etherAddress: :address, admin: :address }, :public do
24
+ ether = ERC20(etherAddress)
25
+ etherBalance = ether.balanceOf(address(this))
26
+
27
+ require(etherBalance > 10.ether, 'Not enough ether')
28
+
29
+ factory = new UniswapV2Factory(_feeToSetter: address(this))
30
+ factory.setFeeTo(admin)
31
+ factory.setFeeToSetter(admin)
32
+
33
+ router = new UniswapV2Router(
34
+ _factory: factory,
35
+ _WETH: address(0)
36
+ )
37
+
38
+ ether.approve(router, (2 ** 255))
39
+
40
+ names = array(:string)
41
+ symbols = array(:string)
42
+
43
+ names.push("Chameleon")
44
+ symbols.push("CHAM")
45
+
46
+ names.push("Mirage")
47
+ symbols.push("MIR")
48
+
49
+ names.push("Emerald")
50
+ symbols.push("EMD")
51
+
52
+ names.push("Oasis")
53
+ symbols.push("OAS")
54
+
55
+ names.push("Paradox")
56
+ symbols.push("PARA")
57
+
58
+ names.push("Peridot")
59
+ symbols.push("PERI")
60
+
61
+ for i in 0...names.length
62
+ supply = ((i + 1) * 2000).ether
63
+
64
+ token = new PublicMintERC20(
65
+ name: names[i],
66
+ symbol: symbols[i],
67
+ maxSupply: supply,
68
+ perMintLimit: supply,
69
+ decimals: 18
70
+ )
71
+
72
+ token.mint(amount: supply)
73
+ token.approve(router, (2 ** 255))
74
+
75
+ router.addLiquidity(
76
+ tokenA: etherAddress,
77
+ tokenB: token,
78
+ amountADesired: etherBalance.div(names.length),
79
+ amountBDesired: supply,
80
+ amountAMin: 0,
81
+ amountBMin: 0,
82
+ to: admin,
83
+ deadline: block.timestamp + 1000
84
+ )
85
+ end
86
+
87
+ return nil
88
+ end
89
+
90
+ function :doZapOld, :public do
91
+ factory = new UniswapV2Factory(_feeToSetter: msg.sender)
92
+
93
+ tokenA = new UnsafeNoApprovalERC20(
94
+ name: "TokenA (#{block.number})",
95
+ symbol: "TKA",
96
+ )
97
+
98
+ tokenB = new UnsafeNoApprovalERC20(
99
+ name: "TokenB (#{block.number})",
100
+ symbol: "TKB",
101
+ )
102
+
103
+ router = new UniswapV2Router(
104
+ _factory: factory,
105
+ _WETH: address(this)
106
+ )
107
+
108
+ pair = factory.createPair(tokenA, tokenB)
109
+
110
+ tokenA.airdrop(to: msg.sender, amount: 1e6.ether)
111
+ tokenB.airdrop(to: msg.sender, amount: 1e6.ether)
112
+
113
+ s.factories.push(factory)
114
+ s.tokenAs.push(tokenA)
115
+ s.tokenBs.push(tokenB)
116
+ s.pairs.push(pair)
117
+ s.routers.push(router)
118
+
119
+ emit :ZapOneSetup, { factory: factory, tokenA: tokenA, tokenB: tokenB, pair: pair, router: router }
120
+ end
121
+
122
+ function :lastZap, :public, :view, returns: { factory: :address, tokenA: :address, tokenB: :address, pair: :address, router: :address } do
123
+ return {
124
+ factory: s.factories[s.factories.length - 1],
125
+ tokenA: s.tokenAs[s.tokenAs.length - 1],
126
+ tokenB: s.tokenBs[s.tokenBs.length - 1],
127
+ pair: s.pairs[s.pairs.length - 1],
128
+ router: s.routers[s.routers.length - 1]
129
+ }
130
+ end
131
+
132
+ function :userStats, {
133
+ user: :address,
134
+ router: :address,
135
+ factory: :address,
136
+ tokenA: :address,
137
+ tokenB: :address,
138
+ }, :public, :view, returns: {
139
+ userTokenABalance: :uint256,
140
+ userTokenBBalance: :uint256,
141
+ tokenAReserves: :uint256,
142
+ tokenBReserves: :uint256,
143
+ userLPBalance: :uint256
144
+ } do
145
+ tokenAReserves, tokenBReserves = UniswapV2Router(router).getReserves(factory, tokenA, tokenB)
146
+ pair = UniswapV2Factory(factory).getPair(tokenA, tokenB)
147
+
148
+ return {
149
+ userTokenABalance: ERC20(tokenA).balanceOf(user),
150
+ userTokenBBalance: ERC20(tokenB).balanceOf(user),
151
+ tokenAReserves: tokenAReserves,
152
+ tokenBReserves: tokenBReserves,
153
+ userLPBalance: UniswapV2ERC20(pair).balanceOf(user)
154
+ }
155
+ end
156
+ end
@@ -0,0 +1,10 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ contract :UniswapV2Callee, abstract: true do
4
+ function :uniswapV2Call, {
5
+ sender: :address,
6
+ amount0: :uint256,
7
+ amount1: :uint256,
8
+ data: :bytes
9
+ }, :virtual, :external
10
+ end
@@ -0,0 +1,13 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ import 'ERC20'
4
+
5
+ contract :UniswapV2ERC20, is: :ERC20, abstract: true do
6
+ constructor() {
7
+ ERC20.constructor(
8
+ name: "ScribeSwap V2",
9
+ symbol: "SCR",
10
+ decimals: 18
11
+ )
12
+ }
13
+ end
@@ -0,0 +1,63 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ import 'UniswapV2Pair'
4
+
5
+ contract :UniswapV2Factory do
6
+ address :public, :feeTo
7
+ address :public, :feeToSetter
8
+
9
+ mapping ({ address: mapping({ address: :address })}), :public, :getPair
10
+ array :address, :public, :allPairs
11
+
12
+ event :PairCreated, { token0: :address, token1: :address, pair: :address, pairLength: :uint256 }
13
+
14
+ constructor(_feeToSetter: :address) {
15
+ s.feeToSetter = _feeToSetter
16
+ }
17
+
18
+ function :allPairsLength, :public, :view, returns: :uint256 do
19
+ return s.allPairs.length
20
+ end
21
+
22
+ function :getAllPairs, :public, :view, returns: [:address] do
23
+ return s.allPairs
24
+ end
25
+
26
+ function :createPair, { tokenA: :address, tokenB: :address }, :public, returns: :address do
27
+ require(tokenA != tokenB, 'Scribeswap: IDENTICAL_ADDRESSES')
28
+
29
+ token0, token1 = tokenA.cast(:uint256) < tokenB.cast(:uint256) ? [tokenA, tokenB] : [tokenB, tokenA]
30
+
31
+ require(token0 != address(0), "Scribeswap: ZERO_ADDRESS");
32
+ require(s.getPair[token0][token1] == address(0), "Scribeswap: PAIR_EXISTS");
33
+
34
+ salt = keccak256(abi.encodePacked(token0, token1))
35
+
36
+ pair = new UniswapV2Pair({ salt: salt })
37
+ pair.init(token0, token1)
38
+
39
+ s.getPair[token0][token1] = pair;
40
+ s.getPair[token1][token0] = pair;
41
+
42
+ s.allPairs.push(pair)
43
+ emit(:PairCreated, { token0: token0, token1: token1, pair: pair, pairLength: s.allPairs.length })
44
+
45
+ return pair
46
+ end
47
+
48
+ function :setFeeTo, { _feeTo: :address }, :public do
49
+ require(msg.sender == feeToSetter, "Scribeswap: FORBIDDEN")
50
+
51
+ s.feeTo = _feeTo
52
+
53
+ return nil
54
+ end
55
+
56
+ function :setFeeToSetter, { _feeToSetter: :address }, :public do
57
+ require(msg.sender == feeToSetter, "Scribeswap: FORBIDDEN")
58
+
59
+ s.feeToSetter = _feeToSetter
60
+
61
+ return nil
62
+ end
63
+ end
@@ -0,0 +1,253 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ import 'UniswapV2Callee'
4
+ import 'UniswapV2ERC20'
5
+
6
+ contract :IUniswapV2Factory, abstract: true do
7
+ function :feeTo, :external, :view, returns: :address
8
+ end
9
+
10
+ contract :UniswapV2Pair, is: :UniswapV2ERC20 do
11
+ uint256 :public, :MINIMUM_LIQUIDITY
12
+
13
+ address :public, :factory
14
+ address :public, :token0
15
+ address :public, :token1
16
+
17
+ uint112 :private, :reserve0
18
+ uint112 :private, :reserve1
19
+ uint32 :private, :blockTimestampLast
20
+
21
+ uint256 :public, :price0CumulativeLast
22
+ uint256 :public, :price1CumulativeLast
23
+ uint256 :public, :kLast
24
+
25
+ uint256 :private, :unlocked
26
+
27
+ function :getReserves, {}, :public, :view, returns: {
28
+ _reserve0: :uint112, _reserve1: :uint112, _blockTimestampLast: :uint32
29
+ } do
30
+ return {
31
+ _reserve0: s.reserve0,
32
+ _reserve1: s.reserve1,
33
+ _blockTimestampLast: s.blockTimestampLast
34
+ }
35
+ end
36
+
37
+ function :_safeTransfer, { token: :address, to: :address, value: :uint256 }, :private do
38
+ result = ERC20(token).transfer(to: to, amount: value)
39
+
40
+ require(result, "ScribeSwap: TRANSFER_FAILED")
41
+ end
42
+
43
+ event :Mint, { sender: :address, amount0: :uint256, amount1: :uint256 }
44
+ event :Burn, { sender: :address, amount0: :uint256, amount1: :uint256, to: :address }
45
+ event :Swap, {
46
+ sender: :address,
47
+ amount0In: :uint256,
48
+ amount1In: :uint256,
49
+ amount0Out: :uint256,
50
+ amount1Out: :uint256,
51
+ to: :address
52
+ }
53
+ event :Sync, { reserve0: :uint112, reserve1: :uint112 }
54
+ event :PreSwapReserves, { reserve0: :uint112, reserve1: :uint112 }
55
+
56
+ constructor() {
57
+ UniswapV2ERC20.constructor()
58
+
59
+ s.factory = msg.sender
60
+
61
+ s.MINIMUM_LIQUIDITY = 10 ** 3
62
+ s.unlocked = 1
63
+ }
64
+
65
+ # Can't call it initialize bc of Ruby (for now)
66
+ function :init, { _token0: :address, _token1: :address }, :external do
67
+ require(msg.sender == s.factory, 'ScribeSwap: FORBIDDEN')
68
+
69
+ s.token0 = _token0
70
+ s.token1 = _token1
71
+
72
+ return nil
73
+ end
74
+
75
+ function :_update, {
76
+ balance0: :uint256,
77
+ balance1: :uint256,
78
+ _reserve0: :uint112,
79
+ _reserve1: :uint112
80
+ }, :private do
81
+ require(balance0 <= (2 ** 112 - 1) && balance1 <= (2 ** 112 - 1), 'ScribeSwap: OVERFLOW')
82
+
83
+ blockTimestamp = uint32(block.timestamp % 2 ** 32)
84
+ timeElapsed = blockTimestamp - s.blockTimestampLast # overflow is desired
85
+
86
+ if timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0
87
+ # * never overflows, and + overflow is desired
88
+ s.price0CumulativeLast += uint256(uqdiv(encode(_reserve1), _reserve0)) * timeElapsed
89
+ s.price1CumulativeLast += uint256(uqdiv(encode(_reserve0), _reserve1)) * timeElapsed
90
+ end
91
+
92
+ emit :PreSwapReserves, reserve0: s.reserve0, reserve1: s.reserve1
93
+
94
+ s.reserve0 = uint112(balance0)
95
+ s.reserve1 = uint112(balance1)
96
+
97
+ s.blockTimestampLast = blockTimestamp
98
+ emit :Sync, reserve0: s.reserve0, reserve1: s.reserve1
99
+ end
100
+
101
+ function :encode, { y: :uint112 }, :internal, :pure, returns: :uint224 do
102
+ return uint224(y) * (2 ** 112)
103
+ end
104
+
105
+ function :uqdiv, { x: :uint224, y: :uint112 }, :internal, :pure, returns: :uint224 do
106
+ return x / uint224(y)
107
+ end
108
+
109
+ function :_mintFee, { _reserve0: :uint112, _reserve1: :uint112 }, :private, returns: :bool do
110
+ feeTo = IUniswapV2Factory(s.factory).feeTo
111
+ feeOn = feeTo != address(0)
112
+ _kLast = s.kLast
113
+
114
+ if feeOn
115
+ if _kLast != 0
116
+ rootK = sqrt(_reserve0 * _reserve1)
117
+ rootKLast = sqrt(_kLast)
118
+ if rootK > rootKLast
119
+ numerator = totalSupply * (rootK - rootKLast)
120
+ denominator = rootK * 5 + rootKLast
121
+ liquidity = numerator.div(denominator)
122
+ _mint(feeTo, liquidity) if liquidity > 0
123
+ end
124
+ end
125
+ elsif _kLast != 0
126
+ s.kLast = 0
127
+ end
128
+ feeOn
129
+ end
130
+
131
+ function :mint, { to: :address }, :public, returns: :uint256 do
132
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
133
+
134
+ _reserve0, _reserve1, _ = getReserves
135
+
136
+ balance0 = ERC20(s.token0).balanceOf(address(this))
137
+ balance1 = ERC20(s.token1).balanceOf(address(this))
138
+ amount0 = balance0 - _reserve0
139
+ amount1 = balance1 - _reserve1
140
+
141
+ feeOn = _mintFee(_reserve0, _reserve1)
142
+ _totalSupply = s.totalSupply
143
+ if _totalSupply == 0
144
+ liquidity = sqrt(amount0 * amount1) - s.MINIMUM_LIQUIDITY
145
+ _mint(address(0), s.MINIMUM_LIQUIDITY)
146
+ else
147
+ liquidity = [
148
+ (amount0 * _totalSupply).div(_reserve0),
149
+ (amount1 * _totalSupply).div(_reserve1)
150
+ ].min
151
+ end
152
+
153
+ require(liquidity > 0, 'ScribeSwap: INSUFFICIENT_LIQUIDITY_MINTED')
154
+ _mint(to, liquidity)
155
+
156
+ _update(balance0, balance1, _reserve0, _reserve1)
157
+ s.kLast = s.reserve0 * s.reserve1 if feeOn
158
+
159
+ emit :Mint, sender: msg.sender, amount0: amount0, amount1: amount1
160
+
161
+ return liquidity
162
+ end
163
+
164
+ function :burn, { to: :address }, :external, :lock, returns: { amount0: :uint256, amount1: :uint256 } do
165
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
166
+
167
+ _reserve0, _reserve1, _ = getReserves
168
+ _token0 = s.token0
169
+ _token1 = s.token1
170
+ balance0 = ERC20(_token0).balanceOf(address(this))
171
+ balance1 = ERC20(_token1).balanceOf(address(this))
172
+ liquidity = s.balanceOf[address(this)]
173
+
174
+ feeOn = _mintFee(_reserve0, _reserve1)
175
+ _totalSupply = s.totalSupply
176
+ amount0 = (liquidity * balance0).div(_totalSupply)
177
+ amount1 = (liquidity * balance1).div(_totalSupply)
178
+
179
+ require(amount0 > 0 && amount1 > 0, 'ScribeSwap: INSUFFICIENT_LIQUIDITY_BURNED')
180
+ _burn(address(this), liquidity)
181
+ _safeTransfer(_token0, to, amount0)
182
+ _safeTransfer(_token1, to, amount1)
183
+ balance0 = ERC20(_token0).balanceOf(address(this))
184
+ balance1 = ERC20(_token1).balanceOf(address(this))
185
+
186
+ _update(balance0, balance1, _reserve0, _reserve1)
187
+ s.kLast = s.reserve0 * s.reserve1 if feeOn
188
+
189
+ emit :Burn, sender: msg.sender, amount0: amount0, amount1: amount1, to: to
190
+
191
+ return { amount0: amount0, amount1: amount1 }
192
+ end
193
+
194
+ function :swap, { amount0Out: :uint256, amount1Out: :uint256, to: :address, data: :bytes }, :external do
195
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
196
+
197
+ require(amount0Out > 0 || amount1Out > 0, 'ScribeSwap: INSUFFICIENT_OUTPUT_AMOUNT')
198
+ _reserve0, _reserve1, _ = getReserves
199
+ require(amount0Out < _reserve0 && amount1Out < _reserve1, 'ScribeSwap: INSUFFICIENT_LIQUIDITY')
200
+
201
+ balance0 = 0
202
+ balance1 = 0
203
+ _token0 = s.token0
204
+ _token1 = s.token1
205
+
206
+ require(to != _token0 && to != _token1, 'ScribeSwap: INVALID_TO')
207
+
208
+ _safeTransfer(_token0, to, amount0Out) if amount0Out > 0 # optimistically transfer tokens
209
+ _safeTransfer(_token1, to, amount1Out) if amount1Out > 0 # optimistically transfer tokens
210
+
211
+ UniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data) if data.length > 0
212
+
213
+ balance0 = ERC20(_token0).balanceOf(address(this))
214
+ balance1 = ERC20(_token1).balanceOf(address(this))
215
+
216
+ amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0
217
+ amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0
218
+
219
+ require(amount0In > 0 || amount1In > 0, 'ScribeSwap: INSUFFICIENT_INPUT_AMOUNT')
220
+
221
+ balance0Adjusted = balance0 * 1000 - amount0In * 3
222
+ balance1Adjusted = balance1 * 1000 - amount1In * 3
223
+
224
+ require(
225
+ balance0Adjusted * balance1Adjusted >= uint256(_reserve0) * _reserve1 * (1000 ** 2),
226
+ 'ScribeSwap: K'
227
+ )
228
+
229
+ _update(balance0, balance1, _reserve0, _reserve1)
230
+ emit :Swap, sender: msg.sender, amount0In: amount0In, amount1In: amount1In, amount0Out: amount0Out, amount1Out: amount1Out, to: to
231
+ end
232
+
233
+ function :skim, { to: :address }, :external do
234
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
235
+
236
+ _token0 = s.token0
237
+ _token1 = s.token1
238
+
239
+ _safeTransfer(_token0, to, ERC20(_token0).balanceOf(address(this)) - s.reserve0)
240
+ _safeTransfer(_token1, to, ERC20(_token1).balanceOf(address(this)) - s.reserve1)
241
+ end
242
+
243
+ function :sync, {}, :external do
244
+ require(s.unlocked == 1, 'ScribeSwap: LOCKED')
245
+
246
+ _update(
247
+ ERC20(s.token0).balanceOf(address(this)),
248
+ ERC20(s.token1).balanceOf(address(this)),
249
+ s.reserve0,
250
+ s.reserve1
251
+ )
252
+ end
253
+ 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
@@ -0,0 +1,360 @@
1
+ pragma :rubidity, "1.0.0"
2
+
3
+ import 'Upgradeable'
4
+ import 'UniswapV2Router'
5
+
6
+ contract :UniswapV2RouterWithRewards, is: [:UniswapV2Router, :Upgradeable], upgradeable: true do
7
+ event :FeeAccrued, {
8
+ total: :uint256,
9
+ toStakers: :uint256,
10
+ toSwappers: :uint256,
11
+ toProtocol: :uint256
12
+ }
13
+
14
+ address :public, :feeAdmin
15
+ uint256 :public, :feeBPS
16
+
17
+ uint256 :public, :swapperFeePct
18
+ uint256 :public, :stakerFeePct
19
+ uint256 :public, :protocolFeePct
20
+
21
+ mapping ({ address: :uint256 }), :public, :stakerRewardsPool
22
+ uint256 :public, :swapperRewardsPool
23
+ uint256 :public, :protocolFeePool
24
+
25
+ mapping ({ address: mapping(address: :uint256) }), :public, :stakedLP
26
+ mapping ({ address: mapping(address: :uint256) }), :public, :rewardDebt
27
+ mapping ({ address: :uint256 }), :public, :totalStakedLP
28
+ mapping ({ address: :uint256 }), :public, :accRewardsPerShare
29
+
30
+ array :address, :public, :topFiveSwappers, initial_length: 5
31
+ mapping ({ address: :uint256 }), :public, :feesGeneratedBySwapper
32
+ mapping ({ address: :uint256 }), :public, :swapperRewards
33
+
34
+ uint256 :public, :decimals
35
+
36
+ constructor(
37
+ _factory: :address,
38
+ _WETH: :address,
39
+ feeBPS: :uint256,
40
+ swapperFeePct: :uint256,
41
+ stakerFeePct: :uint256,
42
+ protocolFeePct: :uint256,
43
+ feeAdmin: :address
44
+ ) {
45
+ updateFees(
46
+ feeBPS: feeBPS,
47
+ swapperFeePct: swapperFeePct,
48
+ stakerFeePct: stakerFeePct,
49
+ protocolFeePct: protocolFeePct,
50
+ feeAdmin: feeAdmin
51
+ )
52
+
53
+ s.decimals = 18
54
+
55
+ UniswapV2Router.constructor(_factory: _factory, _WETH: _WETH)
56
+ Upgradeable.constructor(upgradeAdmin: msg.sender)
57
+ }
58
+
59
+ function :updateFees, {
60
+ feeBPS: :uint256,
61
+ swapperFeePct: :uint256,
62
+ stakerFeePct: :uint256,
63
+ protocolFeePct: :uint256,
64
+ feeAdmin: :address
65
+ }, :public do
66
+ require(msg.sender == s.feeAdmin || s.feeAdmin == address(0), 'Only fee admin can update fees')
67
+ require(feeBPS <= 10000, 'Fee cannot be greater than 100%')
68
+ require(
69
+ swapperFeePct + stakerFeePct + protocolFeePct == 100,
70
+ 'Fees must add up to 100%'
71
+ )
72
+ require(feeAdmin != address(0), 'Fee admin cannot be address(0)')
73
+
74
+ s.feeBPS = feeBPS
75
+ s.stakerFeePct = stakerFeePct
76
+ s.swapperFeePct = swapperFeePct
77
+ s.protocolFeePct = protocolFeePct
78
+
79
+ s.feeAdmin = feeAdmin
80
+ nil
81
+ end
82
+
83
+ function :stakeLP, { lpToken: :address, amount: :uint256 }, :public, returns: :bool do
84
+ lpPair = UniswapV2Pair(lpToken)
85
+ token0 = lpPair.token0()
86
+ token1 = lpPair.token1()
87
+
88
+ require(token0 == s.WETH || token1 == s.WETH, 'One of the tokens must be WETH')
89
+ require(amount > 0, 'Stake amount must be greater than 0')
90
+
91
+ updateStakeAmount(lpToken: lpToken, amount: amount, isStaking: true, user: msg.sender)
92
+
93
+ ERC20(lpToken).transferFrom(msg.sender, address(this), amount)
94
+ end
95
+
96
+ function :unstakeLP, { lpToken: :address, amount: :uint256 }, :public, returns: :bool do
97
+ require(s.stakedLP[msg.sender][lpToken] >= amount, 'Insufficient staked amount')
98
+ require(amount > 0, 'Unstake amount must be greater than 0')
99
+
100
+ updateStakeAmount(lpToken: lpToken, amount: amount, isStaking: false, user: msg.sender)
101
+
102
+ ERC20(lpToken).transfer(msg.sender, amount)
103
+ end
104
+
105
+ function :withdrawStakingRewards, { lpToken: :address }, :public do
106
+ updateStakeAmount(lpToken: lpToken, amount: 0, isStaking: true, user: msg.sender)
107
+ end
108
+
109
+ function :updateStakeAmount, {
110
+ lpToken: :address,
111
+ amount: :uint256,
112
+ isStaking: :bool,
113
+ user: :address
114
+ }, :internal do
115
+ updateStakingRewards(lpToken: lpToken)
116
+
117
+ pending = pendingStakingRewards(user: user, lpToken: lpToken)
118
+
119
+ require(pending > 0 || amount > 0, 'Nothing to do')
120
+
121
+ if pending > 0
122
+ ERC20(s.WETH).transfer(user, pending)
123
+ end
124
+
125
+ if isStaking
126
+ s.stakedLP[user][lpToken] += amount
127
+ s.totalStakedLP[lpToken] += amount
128
+ else
129
+ s.stakedLP[user][lpToken] -= amount
130
+ s.totalStakedLP[lpToken] -= amount
131
+ end
132
+
133
+ unScaledDebt = s.stakedLP[user][lpToken] * s.accRewardsPerShare[lpToken]
134
+
135
+ s.rewardDebt[user][lpToken] = unScaledDebt.div(1.ether)
136
+ nil
137
+ end
138
+
139
+ function :swapExactTokensForTokens, {
140
+ amountIn: :uint256,
141
+ amountOutMin: :uint256,
142
+ path: [:address],
143
+ to: :address,
144
+ deadline: :uint256
145
+ }, :public, :virtual, :override, returns: [:uint256] do
146
+ amounts = UniswapV2Router.swapExactTokensForTokens(
147
+ amountIn: amountIn - calculateFeeAmount(amountIn),
148
+ amountOutMin: amountOutMin,
149
+ path: path,
150
+ to: to,
151
+ deadline: deadline
152
+ )
153
+
154
+ feeInWETH = chargeFeeInWETH(amountIn, path[0])
155
+
156
+ lpToken = pairFor(factory, path[0], path[1])
157
+
158
+ s.stakerRewardsPool[lpToken] += (feeInWETH * s.stakerFeePct).div(100)
159
+ s.protocolFeePool += (feeInWETH * s.protocolFeePct).div(100)
160
+
161
+ updateTopFiveSwappers(
162
+ currentSwapper: msg.sender,
163
+ currentFee: feeInWETH
164
+ )
165
+
166
+ emit :FeeAccrued, {
167
+ total: feeInWETH,
168
+ toStakers: (feeInWETH * s.stakerFeePct).div(100),
169
+ toSwappers: (feeInWETH * s.swapperFeePct).div(100),
170
+ toProtocol: (feeInWETH * s.protocolFeePct).div(100)
171
+ }
172
+
173
+ amounts
174
+ end
175
+
176
+ function :swapTokensForExactTokens, {
177
+ amountOut: :uint256,
178
+ amountInMax: :uint256,
179
+ path: [:address],
180
+ to: :address,
181
+ deadline: :uint256
182
+ }, :public, :virtual, :override, returns: [:uint256] do
183
+ amounts = UniswapV2Router.swapTokensForExactTokens(
184
+ amountOut: amountOut + calculateFeeAmount(amountOut),
185
+ amountInMax: amountInMax,
186
+ path: path,
187
+ to: to,
188
+ deadline: deadline
189
+ )
190
+
191
+ feeInWETH = chargeFeeInWETH(amountOut, path[1])
192
+
193
+ lpToken = pairFor(factory, path[0], path[1])
194
+
195
+ s.stakerRewardsPool[lpToken] += (feeInWETH * s.stakerFeePct).div(100)
196
+ s.protocolFeePool += (feeInWETH * s.protocolFeePct).div(100)
197
+
198
+ updateTopFiveSwappers(
199
+ currentSwapper: msg.sender,
200
+ currentFee: feeInWETH
201
+ )
202
+
203
+ emit :FeeAccrued, {
204
+ total: feeInWETH,
205
+ toStakers: (feeInWETH * s.stakerFeePct).div(100),
206
+ toSwappers: (feeInWETH * s.swapperFeePct).div(100),
207
+ toProtocol: (feeInWETH * s.protocolFeePct).div(100)
208
+ }
209
+
210
+ amounts
211
+ end
212
+
213
+ function :calculateFeeAmount, { amount: :uint256 }, :public, :view, returns: :uint256 do
214
+ return (amount * s.feeBPS).div(10000)
215
+ end
216
+
217
+ function :calculateAccRewardsPerShare, { lpToken: :address }, :internal, :view, returns: :uint256 do
218
+ accRewardsPerShare = s.accRewardsPerShare[lpToken]
219
+
220
+ if s.totalStakedLP[lpToken] > 0 && s.stakerRewardsPool[lpToken] > 0
221
+ accRewardPerShareIncrement = (s.stakerRewardsPool[lpToken] * 1.ether).div(s.totalStakedLP[lpToken])
222
+ accRewardsPerShare += accRewardPerShareIncrement
223
+ end
224
+
225
+ return accRewardsPerShare
226
+ end
227
+
228
+ function :updateStakingRewards, { lpToken: :address }, :internal do
229
+ s.accRewardsPerShare[lpToken] = calculateAccRewardsPerShare(lpToken: lpToken)
230
+ s.stakerRewardsPool[lpToken] = 0
231
+ nil
232
+ end
233
+
234
+ function :pendingStakingRewards, { user: :address, lpToken: :address }, :public, :view, returns: :uint256 do
235
+ accRewardsPerShare = calculateAccRewardsPerShare(lpToken: lpToken)
236
+
237
+ topLine = (s.stakedLP[user][lpToken] * accRewardsPerShare).div(1.ether)
238
+
239
+ return topLine - s.rewardDebt[user][lpToken]
240
+ end
241
+
242
+ function :chargeFeeInWETH, {
243
+ amount: :uint256,
244
+ token: :address
245
+ }, :internal, :virtual, returns: :uint256 do
246
+ feeAmount = calculateFeeAmount(amount)
247
+
248
+ if token == s.WETH
249
+ ERC20(token).transferFrom(
250
+ from: msg.sender,
251
+ to: address(this),
252
+ amount: feeAmount
253
+ )
254
+
255
+ return feeAmount
256
+ end
257
+
258
+ path = array(:address, 2)
259
+ path[0] = token
260
+ path[1] = s.WETH
261
+
262
+ feeInWETH = UniswapV2Router.swapExactTokensForTokens(
263
+ amountIn: feeAmount,
264
+ amountOutMin: 0,
265
+ path: path,
266
+ to: address(this),
267
+ deadline: block.timestamp + 1
268
+ )[1]
269
+
270
+ return feeInWETH
271
+ end
272
+
273
+ function :updateSwapperRewards, :internal do
274
+ nonNullSwapperCount = 0
275
+
276
+ forLoop(
277
+ condition: ->(i) { i < s.topFiveSwappers.length },
278
+ max_iterations: 5
279
+ ) do |i|
280
+ if s.topFiveSwappers[i] != address(0)
281
+ nonNullSwapperCount += 1
282
+ end
283
+ end
284
+
285
+ return if nonNullSwapperCount == 0
286
+
287
+ individualSwapperReward = s.swapperRewardsPool.div(nonNullSwapperCount)
288
+
289
+ forLoop(
290
+ condition: ->(i) { i < s.topFiveSwappers.length },
291
+ max_iterations: 5
292
+ ) do |i|
293
+ swapper = s.topFiveSwappers[i]
294
+ if swapper != address(0)
295
+ s.swapperRewards[swapper] += individualSwapperReward
296
+ end
297
+ end
298
+
299
+ s.swapperRewardsPool = 0
300
+ nil
301
+ end
302
+
303
+ function :withdrawProtocolRewards, { to: :address }, :public, returns: :bool do
304
+ require(msg.sender == s.feeAdmin, "Only fee admin can withdraw protocol rewards")
305
+
306
+ amount = s.protocolFeePool
307
+ require(amount > 0, "No rewards to withdraw")
308
+
309
+ s.protocolFeePool = 0
310
+
311
+ ERC20(s.WETH).transfer(to, amount)
312
+ end
313
+
314
+ function :withdrawSwapperRewards, :public, returns: :bool do
315
+ updateSwapperRewards()
316
+
317
+ amount = s.swapperRewards[msg.sender]
318
+ require(amount > 0, "No rewards to withdraw")
319
+
320
+ s.swapperRewards[msg.sender] = 0
321
+
322
+ ERC20(s.WETH).transfer(msg.sender, amount)
323
+ end
324
+
325
+ function :updateTopFiveSwappers, { currentSwapper: :address, currentFee: :uint256 }, :internal do
326
+ updateSwapperRewards()
327
+
328
+ s.feesGeneratedBySwapper[currentSwapper] += currentFee
329
+ s.swapperRewardsPool += (currentFee * s.swapperFeePct).div(100)
330
+
331
+ newTotal = s.feesGeneratedBySwapper[currentSwapper]
332
+
333
+ forLoop(
334
+ condition: ->(i) { i < s.topFiveSwappers.length },
335
+ max_iterations: 5
336
+ ) do |i|
337
+ return if s.topFiveSwappers[i] == currentSwapper
338
+ end
339
+
340
+ minFee = 2 ** 256 - 1
341
+ minIndex = 0
342
+
343
+ forLoop(
344
+ condition: -> i { i < s.topFiveSwappers.length },
345
+ max_iterations: 5
346
+ ) do |i|
347
+ swapper = s.topFiveSwappers[i]
348
+ if swapper == address(0) || s.feesGeneratedBySwapper[swapper] < minFee
349
+ minFee = s.feesGeneratedBySwapper[swapper]
350
+ minIndex = i
351
+ end
352
+ end
353
+
354
+ if newTotal > minFee
355
+ s.topFiveSwappers[minIndex] = currentSwapper
356
+ end
357
+
358
+ nil
359
+ end
360
+ end
@@ -0,0 +1,4 @@
1
+
2
+ puts "hello, 0xfacet uniswap contracts!"
3
+
4
+ # note: cannot load rubidity contracts as is - require rubidity loader!
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 0xfacet-uniswap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: 0xfacet-rubidity
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: 0xfacet-contracts
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '7'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '4.0'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: hoe
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '4.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '4.0'
75
+ description: 0xfacet-uniswap - rubidity o.g. uniswap v2 contracts (incl. UniswapV2Factory,
76
+ UniswapV2Pair, UniswapV2Router, etc.)
77
+ email: gerald.bauer@gmail.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files:
81
+ - CHANGELOG.md
82
+ - Manifest.txt
83
+ - README.md
84
+ files:
85
+ - CHANGELOG.md
86
+ - Manifest.txt
87
+ - README.md
88
+ - Rakefile
89
+ - lib/0xfacet/uniswap.rb
90
+ - lib/0xfacet/uniswap/UniswapSetupZapV2.rb
91
+ - lib/0xfacet/uniswap/UniswapV2Callee.rb
92
+ - lib/0xfacet/uniswap/UniswapV2ERC20.rb
93
+ - lib/0xfacet/uniswap/UniswapV2Factory.rb
94
+ - lib/0xfacet/uniswap/UniswapV2Pair.rb
95
+ - lib/0xfacet/uniswap/UniswapV2Router.rb
96
+ - lib/0xfacet/uniswap/UniswapV2RouterWithRewards.rb
97
+ homepage: https://github.com/s6ruby/rubidity.review
98
+ licenses:
99
+ - Public Domain
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options:
103
+ - "--main"
104
+ - README.md
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '2.3'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.4.10
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: 0xfacet-uniswap - rubidity o.g. uniswap v2 contracts (incl. UniswapV2Factory,
122
+ UniswapV2Pair, UniswapV2Router, etc.)
123
+ test_files: []