0xfacet-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.
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: []