├── .dex-comparisons
├── .gitignore
├── .gitmodules
├── .husky
└── pre-commit
├── .npmignore
├── 4naly3er-report.md
├── 512x512-MF.png
├── LICENSE.txt
├── README.md
├── basin(green)-512x512 (1).png
├── basin(green)-512x512.png
├── basin.pdf
├── discord-export
├── Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html
├── Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files
│ ├── 0-EB806.png
│ ├── 0170e083c6b7b4b253ce74396a594013-2CF6E.png
│ ├── 021eb22c1e54e7d6b7976608d2a09da4-3028E.png
│ ├── 059d0fe5cf95abe78825804be97230dc-0FE6E.png
│ ├── 074b45b02c7fe972a78a7939d12f7367-39D75.png
│ ├── 0ab6fddedfdca4dee0b54ab41ab4faad-3EFDA.png
│ ├── 1237352656457043998-78A28.png
│ ├── 1237582204905979995-AD6AE.png
│ ├── 14ea78b80318f2bf28da987fc2be9912-F4E16.png
│ ├── 1f389-5C738.svg
│ ├── 1f3c1-445DC.svg
│ ├── 1f3c5-82E3F.svg
│ ├── 1f43a-EB486.svg
│ ├── 1f440-6C64D.svg
│ ├── 1f446-9CC34.svg
│ ├── 1f44b-8A059.svg
│ ├── 1f44d-1f3fb-ED2AA.svg
│ ├── 1f44d-27259.svg
│ ├── 1f4aa-2FD27.svg
│ ├── 1f4af-4CFF5.svg
│ ├── 1f4b8-E3468.svg
│ ├── 1f4c6-44E30.svg
│ ├── 1f50d-195C0.svg
│ ├── 1f525-8FE4F.svg
│ ├── 1f614-BB5EE.svg
│ ├── 1f61d-6A1D5.svg
│ ├── 1f642-83E8A.svg
│ ├── 1f64c-1f3fb-01614.svg
│ ├── 1f64f-22B8D.svg
│ ├── 1f6a8-A8AB3.svg
│ ├── 1f911-F346C.svg
│ ├── 1f914-15707.svg
│ ├── 1f916-AD810.svg
│ ├── 1fae1-B19DE.svg
│ ├── 2-ADBB4.png
│ ├── 26a0-D845B.svg
│ ├── 2705-0589F.svg
│ ├── 2764-A3D25.svg
│ ├── 281c37c156e582a637fd070f083d02db-ACBCC.png
│ ├── 300410401819ea00f31fbcdbf9f5080a-23405.png
│ ├── 357510ed1e5265aafb02bc7942ddaa82-01F7E.png
│ ├── 407f6dcef34069853b9ca2321a06fb98-1DD94.png
│ ├── 453eac052c6c6b7a61d2f55848c5bdc6-FCF15.png
│ ├── 4832bd21924fffa7b010e0bb57758aa2-E6DDC.png
│ ├── 4eaf99d01e4b5042454b4a6a8809687a-330D1.png
│ ├── 5215abf963d5747c38f10ffb2c034e41-02918.png
│ ├── 546f74664414743e95af6c3045a13083-799B6.png
│ ├── 55b9ac870fd4a1b4fc5a8c0550c27aa4-D1820.png
│ ├── 5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png
│ ├── 62a9e9fdf868396a9674f71e04ffab94-4A6BA.png
│ ├── 6678d08791c986738fd9018c9cb8212b-D895F.png
│ ├── 67594ee4b4d1fc03bca468327a0d145b-BD76A.png
│ ├── 701300a5bdd3bf5640bd1370251533e2-0DC47.png
│ ├── 7194c28496e51d0a5c8e9b15e2ffcb3b-8BFA7.png
│ ├── 759305215cc64ed169073b720e944acc-B2606.png
│ ├── 78025b51fbd5016bf80d568e3b199528-E03F8.png
│ ├── 970d2e2f00cd7ef2134a1a3f21326349-404EA.png
│ ├── Beanstalk-BC9BA
│ ├── Screenshot_2024-08-14_at_12.01.40_PM-19370.png
│ ├── Screenshot_2024-08-14_at_12.15.01_PM-1D17E.png
│ ├── a476320e0ef8160f4704597ba8e9b4b8-ECB13.png
│ ├── ac37f50421d713952c5567868c0ce8fc-83194.png
│ ├── bdd37d345b2d35b5513f03a81143aa4a-51209.png
│ ├── c9cb30134c634c9e02d0c64df4922803-98E33.png
│ ├── curve-stablecoin-9DF7A
│ ├── d8e05392a6dee96280405f9825554ff7-54BEB.png
│ ├── fc008e9401593faa330c1e49ff785e7a-1856A.png
│ ├── ggsans-italic-400-E988B.woff2
│ ├── ggsans-italic-500-0777F.woff2
│ ├── ggsans-italic-600-CB411.woff2
│ ├── ggsans-italic-700-891AC.woff2
│ ├── ggsans-italic-800-D36B0.woff2
│ ├── ggsans-normal-400-1456D.woff2
│ ├── ggsans-normal-500-89CE5.woff2
│ ├── ggsans-normal-600-C1EA8.woff2
│ ├── ggsans-normal-700-1949A.woff2
│ ├── ggsans-normal-800-58487.woff2
│ ├── gjirlfriend-2FC5D.mp4
│ ├── gjirlfriend-A5592.mp4
│ ├── highlight.min-D8D27.js
│ ├── i-am-back-estoy-de-vuelta-FC687.mp4
│ ├── image-64AA5.png
│ ├── image-7F0F5.png
│ ├── lottie.min-99657.js
│ └── solarized-dark.min-BA98F.css
├── Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt
└── Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files
│ ├── Beanstalk-BC9BA
│ ├── Screenshot_2024-08-14_at_12.01.40_PM-19370.png
│ ├── Screenshot_2024-08-14_at_12.15.01_PM-1D17E.png
│ ├── curve-stablecoin-9DF7A
│ ├── gjirlfriend-729B5.png
│ ├── gjirlfriend-A5592.mp4
│ ├── i-am-back-estoy-de-vuelta-721B0.png
│ ├── image-64AA5.png
│ └── image-7F0F5.png
├── foundry.toml
├── mocks
├── functions
│ ├── MockEmptyFunction.sol
│ └── MockFunctionBad.sol
├── pumps
│ ├── MockFailPump.sol
│ └── MockPump.sol
├── tokens
│ ├── MockToken.sol
│ ├── MockTokenFeeOnTransfer.sol
│ ├── MockTokenNoName.sol
│ └── ReentrantMockToken.sol
└── wells
│ ├── MockInitFailWell.sol
│ ├── MockReserveWell.sol
│ ├── MockStaticWell.sol
│ └── MockWellUpgradeable.sol
├── multi-flow-pump.pdf
├── out_of_scope.txt
├── package.json
├── remappings.txt
├── requirements.txt
├── scope.txt
├── script
├── bin
│ ├── deploy-local.sh
│ ├── doc.sh
│ └── env.sh
├── deploy
│ ├── Aquifer.s.sol
│ ├── AquiferWell.s.sol
│ ├── MockPump.s.sol
│ ├── Well.s.sol
│ └── helpers
│ │ └── Logger.sol
├── dex-comparisons.js
├── helpers
│ └── WellDeployer.sol
└── simulations
│ └── stableswap
│ ├── StableswapCalcRatiosLiqSim.s.sol
│ └── StableswapCalcRatiosSwapSim.s.sol
├── src
├── Aquifer.sol
├── Well.sol
├── WellUpgradeable.sol
├── functions
│ ├── ConstantProduct.sol
│ ├── ConstantProduct2.sol
│ ├── ProportionalLPToken.sol
│ ├── ProportionalLPToken2.sol
│ ├── Stable2.sol
│ └── StableLUT
│ │ └── Stable2LUT1.sol
├── interfaces
│ ├── IAquifer.sol
│ ├── IBeanstalkWellFunction.sol
│ ├── ILookupTable.sol
│ ├── IMultiFlowPumpWellFunction.sol
│ ├── IWell.sol
│ ├── IWellErrors.sol
│ ├── IWellFunction.sol
│ └── pumps
│ │ ├── ICumulativePump.sol
│ │ ├── IInstantaneousPump.sol
│ │ ├── IMultiFlowPumpErrors.sol
│ │ └── IPump.sol
├── libraries
│ ├── ABDKMathQuad.sol
│ ├── LibBytes.sol
│ ├── LibBytes16.sol
│ ├── LibClone.sol
│ ├── LibContractInfo.sol
│ ├── LibLastReserveBytes.sol
│ ├── LibMath.sol
│ ├── LibWellConstructor.sol
│ └── LibWellUpgradeableConstructor.sol
├── pumps
│ └── MultiFlowPump.sol
└── utils
│ ├── Clone.sol
│ └── ClonePlus.sol
├── test
├── Aquifer.t.sol
├── LiquidityHelper.sol
├── Stable2
│ ├── LookupTable.t.sol
│ ├── Well.Stable2.AddLiquidity.t.sol
│ ├── Well.Stable2.Bore.t.sol
│ ├── Well.Stable2.RemoveLiquidity.t.sol
│ ├── Well.Stable2.RemoveLiquidityImbalanced.t.sol
│ ├── Well.Stable2.RemoveLiquidityOneToken.t.sol
│ ├── Well.Stable2.Shift.t.sol
│ ├── Well.Stable2.Skim.t.sol
│ ├── Well.Stable2.SwapFrom.t.sol
│ └── Well.Stable2.SwapTo.t.sol
├── SwapHelper.sol
├── TestHelper.sol
├── Well.AddLiquidity.t.sol
├── Well.AddLiquidityFeeOnTransfer.Fee.t.sol
├── Well.AddLiquidityFeeOnTransfer.NoFee.t.sol
├── Well.Bore.t.sol
├── Well.DuplicateTokens.t.sol
├── Well.FeeOnTransfer.t.sol
├── Well.ReadOnlyReentrancy.t.sol
├── Well.RemoveLiquidity.t.sol
├── Well.RemoveLiquidityImbalanced.t.sol
├── Well.RemoveLiquidityOneToken.t.sol
├── Well.Shift.t.sol
├── Well.Skim.t.sol
├── Well.SucceedOnPumpFailure.t.sol
├── Well.SwapFrom.t.sol
├── Well.SwapFromFeeOnTransfer.Fee.t.sol
├── Well.SwapFromFeeOnTransfer.NoFee.t.sol
├── Well.SwapTo.t.sol
├── Well.Sync.t.sol
├── Well.Tokens.t.sol
├── Well.UpdatePump.t.sol
├── WellUpgradeable.t.sol
├── beanstalk
│ ├── BeanstalkConstantProduct.calcReserveAtRatioLiquidity.t.sol
│ ├── BeanstalkConstantProduct.calcReserveAtRatioSwap.t.sol
│ ├── BeanstalkConstantProduct2.calcReserveAtRatioLiquidity.t.sol
│ ├── BeanstalkConstantProduct2.calcReserveAtRatioSwap.t.sol
│ ├── BeanstalkStable2.calcReserveAtRatioLiquidity.t.sol
│ └── BeanstalkStable2.calcReserveAtRatioSwap.t.sol
├── differential
│ ├── ConstantProduct.py
│ ├── cap_reserves.py
│ └── powu.py
├── functions
│ ├── ConstantProduct.t.sol
│ ├── ConstantProduct2.t.sol
│ ├── Stable2.t.sol
│ └── WellFunctionHelper.sol
├── helpers
│ └── Users.sol
├── integration
│ ├── GasMetering.sol
│ ├── IntegrationTestGasComparisons.sol
│ ├── IntegrationTestHelper.sol
│ └── interfaces
│ │ ├── ICurve.sol
│ │ ├── IPipeline.sol
│ │ └── IUniswap.sol
├── invariant
│ ├── Handler.t.sol
│ └── Invariants.t.sol
├── libraries
│ ├── LibBytes.t.sol
│ ├── LibBytes16.t.sol
│ ├── LibContractInfo.t.sol
│ ├── LibLastReserveBytes.t.sol
│ ├── LibMath.t.sol
│ └── TestABDK.t.sol
└── pumps
│ ├── Pump.CapReserves.t.sol
│ ├── Pump.Fuzz.t.sol
│ ├── Pump.Helpers.t.sol
│ ├── Pump.Longevity.t.sol
│ ├── Pump.NotInitialized.t.sol
│ ├── Pump.TimeWeightedAverage.t.sol
│ ├── Pump.Update.t.sol
│ ├── PumpHelpers.sol
│ └── simulate.py
└── yarn.lock
/.dex-comparisons:
--------------------------------------------------------------------------------
1 | IntegrationTestGasComparisons:testFuzz_uniswapV2_WethDaiUsdc_Swap(uint256) (runs: 5000, μ: 158307, ~: 158307)
2 | IntegrationTestGasComparisons:testFuzz_uniswapV2_WethDai_AddLiquidity(uint256) (runs: 5000, μ: 102974, ~: 102974)
3 | IntegrationTestGasComparisons:testFuzz_uniswapV2_WethDai_RemoveLiquidity(uint256) (runs: 5000, μ: 41103, ~: 41125)
4 | IntegrationTestGasComparisons:testFuzz_uniswapV2_WethDai_Swap(uint256) (runs: 5000, μ: 88314, ~: 88314)
5 | IntegrationTestGasComparisons:testFuzz_uniswapV3_WethDaiUsdc_Swap(uint256) (runs: 5000, μ: 815083, ~: 931705)
6 | IntegrationTestGasComparisons:testFuzz_uniswapV3_WethDai_Swap(uint256) (runs: 5000, μ: 297519, ~: 369033)
7 | IntegrationTestGasComparisons:testFuzz_wells_WethDaiUsdc_Shift(uint256) (runs: 5000, μ: 145708, ~: 145720)
8 | IntegrationTestGasComparisons:testFuzz_wells_WethDaiUsdc_Swap(uint256) (runs: 5000, μ: 311285, ~: 311280)
9 | IntegrationTestGasComparisons:testFuzz_wells_WethDai_AddLiquidity(uint256) (runs: 5000, μ: 137449, ~: 137443)
10 | IntegrationTestGasComparisons:testFuzz_wells_WethDai_RemoveLiquidity(uint256) (runs: 5000, μ: 108617, ~: 109640)
11 | IntegrationTestGasComparisons:testFuzz_wells_WethDai_Swap(uint256) (runs: 5000, μ: 122079, ~: 122283)
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiler files
2 | cache/
3 | out/
4 |
5 | .vscode
6 |
7 | # Ignores development broadcast logs
8 | /broadcast
9 |
10 | # Dotenv file
11 | .env
12 |
13 | # Ignore generated CSV with DEX gas comparisons
14 | .dex-comparisons.csv
15 |
16 | # Other
17 | node_modules
18 | stash
19 | lcov.info
20 | .DS_Store
21 | test/output/
22 |
23 | # Python
24 | __pycache__
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/prb-math"]
2 | path = lib/prb-math
3 | url = https://github.com/paulrberg/prb-math
4 | branch = v3
5 | [submodule "lib/openzeppelin-contracts"]
6 | path = lib/openzeppelin-contracts
7 | url = https://github.com/OpenZeppelin/openzeppelin-contracts
8 | branch = v4.9.3
9 | [submodule "lib/openzeppelin-contracts-upgradeable"]
10 | path = lib/openzeppelin-contracts-upgradeable
11 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
12 | branch = v4.9.3
13 | [submodule "lib/forge-std"]
14 | path = lib/forge-std
15 | url = https://github.com/foundry-rs/forge-std
16 | branch = v1.5.5
17 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | forge fmt --check
5 |
6 | npx lint-staged
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Compiler files
2 | cache/
3 |
4 | # Ignores development broadcast logs
5 | /broadcast
6 |
7 | # Dotenv file
8 | .env
9 |
10 | # Ignore generated CSV with DEX gas comparisons
11 | .dex-comparisons.csv
12 |
13 | # Other
14 | node_modules
15 | stash
16 | lcov.info
17 | .DS_Store
18 | test/output/
19 |
20 |
21 | # Npm ignored files
22 | test
23 | .github
24 | .husky
--------------------------------------------------------------------------------
/512x512-MF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/512x512-MF.png
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023-present Publius
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/basin(green)-512x512 (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/basin(green)-512x512 (1).png
--------------------------------------------------------------------------------
/basin(green)-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/basin(green)-512x512.png
--------------------------------------------------------------------------------
/basin.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/basin.pdf
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/0-EB806.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/0-EB806.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/0170e083c6b7b4b253ce74396a594013-2CF6E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/0170e083c6b7b4b253ce74396a594013-2CF6E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/021eb22c1e54e7d6b7976608d2a09da4-3028E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/021eb22c1e54e7d6b7976608d2a09da4-3028E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/059d0fe5cf95abe78825804be97230dc-0FE6E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/059d0fe5cf95abe78825804be97230dc-0FE6E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/074b45b02c7fe972a78a7939d12f7367-39D75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/074b45b02c7fe972a78a7939d12f7367-39D75.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/0ab6fddedfdca4dee0b54ab41ab4faad-3EFDA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/0ab6fddedfdca4dee0b54ab41ab4faad-3EFDA.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1237352656457043998-78A28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1237352656457043998-78A28.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1237582204905979995-AD6AE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1237582204905979995-AD6AE.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/14ea78b80318f2bf28da987fc2be9912-F4E16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/14ea78b80318f2bf28da987fc2be9912-F4E16.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f389-5C738.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f3c1-445DC.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f3c5-82E3F.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f43a-EB486.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f440-6C64D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f446-9CC34.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f44b-8A059.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f44d-1f3fb-ED2AA.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f44d-27259.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f4aa-2FD27.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f4af-4CFF5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f4c6-44E30.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f50d-195C0.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f525-8FE4F.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f614-BB5EE.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f61d-6A1D5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f642-83E8A.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f64c-1f3fb-01614.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f64f-22B8D.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f6a8-A8AB3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f911-F346C.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f914-15707.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1f916-AD810.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/1fae1-B19DE.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/2-ADBB4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/2-ADBB4.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/26a0-D845B.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/2705-0589F.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/2764-A3D25.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/281c37c156e582a637fd070f083d02db-ACBCC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/281c37c156e582a637fd070f083d02db-ACBCC.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/300410401819ea00f31fbcdbf9f5080a-23405.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/300410401819ea00f31fbcdbf9f5080a-23405.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/357510ed1e5265aafb02bc7942ddaa82-01F7E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/357510ed1e5265aafb02bc7942ddaa82-01F7E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/407f6dcef34069853b9ca2321a06fb98-1DD94.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/407f6dcef34069853b9ca2321a06fb98-1DD94.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/453eac052c6c6b7a61d2f55848c5bdc6-FCF15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/453eac052c6c6b7a61d2f55848c5bdc6-FCF15.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/4832bd21924fffa7b010e0bb57758aa2-E6DDC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/4832bd21924fffa7b010e0bb57758aa2-E6DDC.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/4eaf99d01e4b5042454b4a6a8809687a-330D1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/4eaf99d01e4b5042454b4a6a8809687a-330D1.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/5215abf963d5747c38f10ffb2c034e41-02918.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/5215abf963d5747c38f10ffb2c034e41-02918.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/546f74664414743e95af6c3045a13083-799B6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/546f74664414743e95af6c3045a13083-799B6.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/55b9ac870fd4a1b4fc5a8c0550c27aa4-D1820.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/55b9ac870fd4a1b4fc5a8c0550c27aa4-D1820.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/62a9e9fdf868396a9674f71e04ffab94-4A6BA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/62a9e9fdf868396a9674f71e04ffab94-4A6BA.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/6678d08791c986738fd9018c9cb8212b-D895F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/6678d08791c986738fd9018c9cb8212b-D895F.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/67594ee4b4d1fc03bca468327a0d145b-BD76A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/67594ee4b4d1fc03bca468327a0d145b-BD76A.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/701300a5bdd3bf5640bd1370251533e2-0DC47.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/701300a5bdd3bf5640bd1370251533e2-0DC47.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/7194c28496e51d0a5c8e9b15e2ffcb3b-8BFA7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/7194c28496e51d0a5c8e9b15e2ffcb3b-8BFA7.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/759305215cc64ed169073b720e944acc-B2606.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/759305215cc64ed169073b720e944acc-B2606.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/78025b51fbd5016bf80d568e3b199528-E03F8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/78025b51fbd5016bf80d568e3b199528-E03F8.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/970d2e2f00cd7ef2134a1a3f21326349-404EA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/970d2e2f00cd7ef2134a1a3f21326349-404EA.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/Beanstalk-BC9BA:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/Beanstalk-BC9BA
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/Screenshot_2024-08-14_at_12.01.40_PM-19370.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/Screenshot_2024-08-14_at_12.01.40_PM-19370.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/Screenshot_2024-08-14_at_12.15.01_PM-1D17E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/Screenshot_2024-08-14_at_12.15.01_PM-1D17E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/a476320e0ef8160f4704597ba8e9b4b8-ECB13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/a476320e0ef8160f4704597ba8e9b4b8-ECB13.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ac37f50421d713952c5567868c0ce8fc-83194.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ac37f50421d713952c5567868c0ce8fc-83194.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/bdd37d345b2d35b5513f03a81143aa4a-51209.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/bdd37d345b2d35b5513f03a81143aa4a-51209.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/c9cb30134c634c9e02d0c64df4922803-98E33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/c9cb30134c634c9e02d0c64df4922803-98E33.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/curve-stablecoin-9DF7A:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/curve-stablecoin-9DF7A
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/d8e05392a6dee96280405f9825554ff7-54BEB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/d8e05392a6dee96280405f9825554ff7-54BEB.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/fc008e9401593faa330c1e49ff785e7a-1856A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/fc008e9401593faa330c1e49ff785e7a-1856A.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-400-E988B.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-400-E988B.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-500-0777F.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-500-0777F.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-600-CB411.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-600-CB411.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-700-891AC.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-700-891AC.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-800-D36B0.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-italic-800-D36B0.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-400-1456D.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-400-1456D.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-500-89CE5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-500-89CE5.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-600-C1EA8.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-600-C1EA8.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-700-1949A.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-700-1949A.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-800-58487.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/ggsans-normal-800-58487.woff2
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/gjirlfriend-2FC5D.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/gjirlfriend-2FC5D.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/gjirlfriend-A5592.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/gjirlfriend-A5592.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/i-am-back-estoy-de-vuelta-FC687.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/i-am-back-estoy-de-vuelta-FC687.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/image-64AA5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/image-64AA5.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/image-7F0F5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/image-7F0F5.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].html_Files/solarized-dark.min-BA98F.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#002b36;color:#839496}.hljs-comment,.hljs-quote{color:#586e75}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#073642}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/Beanstalk-BC9BA:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/Beanstalk-BC9BA
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/Screenshot_2024-08-14_at_12.01.40_PM-19370.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/Screenshot_2024-08-14_at_12.01.40_PM-19370.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/Screenshot_2024-08-14_at_12.15.01_PM-1D17E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/Screenshot_2024-08-14_at_12.15.01_PM-1D17E.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/curve-stablecoin-9DF7A:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/curve-stablecoin-9DF7A
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/gjirlfriend-729B5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/gjirlfriend-729B5.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/gjirlfriend-A5592.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/gjirlfriend-A5592.mp4
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/i-am-back-estoy-de-vuelta-721B0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/i-am-back-estoy-de-vuelta-721B0.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/image-64AA5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/image-64AA5.png
--------------------------------------------------------------------------------
/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/image-7F0F5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/discord-export/Code4rena - ARCHIVE-Q3-2024 - basin-jul29 [1265692874297839746].txt_Files/image-7F0F5.png
--------------------------------------------------------------------------------
/foundry.toml:
--------------------------------------------------------------------------------
1 | [profile.default]
2 | src = 'src'
3 | out = 'out'
4 | libs = ['lib', 'node_modules']
5 | fuzz = { runs = 256 }
6 | optimizer = true
7 | optimizer_runs = 400
8 | remappings = [
9 | '@openzeppelin/=node_modules/@openzeppelin/',
10 | ]
11 | block_number = 16826654
12 | block_timestamp = 1678803642
13 |
14 | [rpc_endpoints]
15 | mainnet = "${MAINNET_RPC_URL}"
16 |
17 | [profile.ci]
18 | no_match_test = "testSim"
19 | fuzz = { runs = 5_000, max_test_rejects = 1000000 }
20 |
21 | [profile.sim]
22 | match_test = "testSim"
23 | fuzz = { runs = 5_000, max_test_rejects = 1000000 }
24 |
25 | [fmt]
26 | ignore = ["src/libraries/LibClone.sol", "src/utils/Clone.sol", "src/libraries/ABDKMathQuad.sol"]
27 | int_types = "long"
28 | line_length = 120
29 | multiline_func_header = "params_first"
30 | number_underscore = "thousands"
31 | override_spacing = false
32 | quote_style = "double"
33 | tab_width = 4
34 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config
35 |
36 | [invariant]
37 | runs = 50
38 | depth = 500
39 | fail_on_revert = true
--------------------------------------------------------------------------------
/mocks/functions/MockEmptyFunction.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
8 |
9 | contract MockEmptyFunction is IWellFunction {
10 | function calcReserve(uint256[] memory, uint256, uint256, bytes calldata) external pure returns (uint256 d) {
11 | return 1;
12 | }
13 |
14 | function calcLpTokenSupply(uint256[] memory, bytes calldata) external pure returns (uint256 xj) {
15 | return 1;
16 | }
17 |
18 | function calcLPTokenUnderlying(
19 | uint256,
20 | uint256[] memory,
21 | uint256,
22 | bytes calldata
23 | ) external pure returns (uint256[] memory underlyingAmounts) {
24 | return underlyingAmounts;
25 | }
26 |
27 | function name() external pure override returns (string memory) {}
28 |
29 | function symbol() external pure override returns (string memory) {}
30 | }
31 |
--------------------------------------------------------------------------------
/mocks/functions/MockFunctionBad.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
8 |
9 | /**
10 | * @dev Implements a mock broken WellFunction implementation.
11 | *
12 | * Used to verify that {Well.getSwap} throws an error when a Well function
13 | * returns a reserve that is higher than Well reserves.
14 | *
15 | * DO NOT COPY IN PRODUCTION.
16 | */
17 | contract MockFunctionBad is IWellFunction {
18 | function calcLpTokenSupply(
19 | uint256[] memory reserves,
20 | bytes calldata
21 | ) external pure returns (uint256 lpTokenSupply) {
22 | return reserves[0] + reserves[1];
23 | }
24 |
25 | /// @dev returns non-zero regardless of reserves & lp token supply. WRONG!
26 | function calcReserve(uint256[] memory, uint256, uint256, bytes calldata) external pure returns (uint256 reserve) {
27 | return 1000;
28 | }
29 |
30 | function calcLPTokenUnderlying(
31 | uint256,
32 | uint256[] memory,
33 | uint256,
34 | bytes calldata
35 | ) external pure returns (uint256[] memory underlyingAmounts) {
36 | return underlyingAmounts;
37 | }
38 |
39 | function name() external pure override returns (string memory) {}
40 |
41 | function symbol() external pure override returns (string memory) {}
42 | }
43 |
--------------------------------------------------------------------------------
/mocks/pumps/MockFailPump.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import "src/interfaces/pumps/IPump.sol";
8 |
9 | /**
10 | * @title Mock Pump with a failing update function
11 | */
12 | contract MockFailPump is IPump {
13 | function update(uint256[] calldata, bytes calldata) external pure {
14 | revert();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/mocks/pumps/MockPump.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import "src/interfaces/pumps/IPump.sol";
8 |
9 | /**
10 | * @author Brendan
11 | * @title Mock Pump
12 | */
13 | contract MockPump is IPump {
14 | bytes public lastData;
15 |
16 | function update(uint256[] calldata, bytes calldata data) external {
17 | lastData = data;
18 | }
19 |
20 | function read(address, bytes calldata) external view returns (bytes memory data) {
21 | return lastData;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/mocks/tokens/MockToken.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import "oz/token/ERC20/extensions/ERC20Burnable.sol";
8 | import "oz/token/ERC20/extensions/draft-ERC20Permit.sol";
9 |
10 | /**
11 | * @author Brendan
12 | * @title Mock Token
13 | */
14 | contract MockToken is ERC20Burnable, ERC20Permit {
15 | uint8 private _decimals = 18;
16 |
17 | constructor(string memory name, string memory symbol, uint8 __decimals) ERC20(name, symbol) ERC20Permit(name) {
18 | _decimals = __decimals;
19 | }
20 |
21 | function mint(address account, uint256 amount) external returns (bool) {
22 | _mint(account, amount);
23 | return true;
24 | }
25 |
26 | function burnFrom(address account, uint256 amount) public override(ERC20Burnable) {
27 | ERC20Burnable.burnFrom(account, amount);
28 | }
29 |
30 | function burn(uint256 amount) public override(ERC20Burnable) {
31 | ERC20Burnable.burn(amount);
32 | }
33 |
34 | function decimals() public view virtual override returns (uint8) {
35 | return _decimals;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/mocks/tokens/MockTokenFeeOnTransfer.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import "oz/token/ERC20/extensions/ERC20Burnable.sol";
8 | import "oz/token/ERC20/extensions/draft-ERC20Permit.sol";
9 |
10 | /**
11 | * @author Brendan
12 | * @title Mock Token with a Fee on transfer
13 | */
14 | contract MockTokenFeeOnTransfer is ERC20Burnable, ERC20Permit {
15 | uint8 private _decimals = 18;
16 | mapping(address => uint256) private _balances;
17 |
18 | uint256 constant FEE_DIVISOR = 1e18;
19 |
20 | uint256 public fee = 0;
21 |
22 | constructor(string memory name, string memory symbol, uint8 __decimals) ERC20(name, symbol) ERC20Permit(name) {
23 | _decimals = __decimals;
24 | }
25 |
26 | function mint(address account, uint256 amount) external returns (bool) {
27 | _mint(account, amount);
28 | return true;
29 | }
30 |
31 | function burnFrom(address account, uint256 amount) public override(ERC20Burnable) {
32 | ERC20Burnable.burnFrom(account, amount);
33 | }
34 |
35 | function burn(uint256 amount) public override(ERC20Burnable) {
36 | ERC20Burnable.burn(amount);
37 | }
38 |
39 | function decimals() public view virtual override returns (uint8) {
40 | return _decimals;
41 | }
42 |
43 | function setFee(uint256 _fee) external {
44 | fee = _fee;
45 | }
46 |
47 | function transfer(address to, uint256 amount) public virtual override returns (bool) {
48 | return __transfer(_msgSender(), to, amount);
49 | }
50 |
51 | function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
52 | address spender = _msgSender();
53 | _spendAllowance(from, spender, amount);
54 | return __transfer(from, to, amount);
55 | }
56 |
57 | function __transfer(address from, address to, uint256 amount) internal returns (bool) {
58 | uint256 _fee = amount * fee / FEE_DIVISOR;
59 | uint256 amountSent = amount - _fee;
60 |
61 | _transfer(from, to, amountSent);
62 | _burn(from, _fee);
63 |
64 | return true;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/mocks/tokens/MockTokenNoName.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import "mocks/tokens/MockToken.sol";
8 |
9 | /**
10 | * @author Brendan
11 | * @title Mock Token No Name
12 | *
13 | */
14 | contract MockTokenNoName is MockToken {
15 | constructor(uint8 __decimals) MockToken("", "", __decimals) {}
16 |
17 | function name() public pure override returns (string memory) {
18 | revert();
19 | }
20 |
21 | function symbol() public pure override returns (string memory) {
22 | revert();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/mocks/tokens/ReentrantMockToken.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import "mocks/tokens/MockToken.sol";
8 |
9 | /**
10 | * @author Brendan
11 | * @title Reentrant Mock Token
12 | */
13 | contract ReentrantMockToken is MockToken {
14 | address private target;
15 | bytes private callData;
16 |
17 | constructor(string memory name, string memory symbol, uint8 __decimals) MockToken(name, symbol, __decimals) {}
18 |
19 | function setCall(address _target, bytes calldata _callData) external {
20 | target = _target;
21 | callData = _callData;
22 | }
23 |
24 | function _beforeTokenTransfer(address, address, uint256) internal virtual override {
25 | if (target != address(0)) {
26 | (bool success, bytes memory data) = target.call(callData);
27 | if (!success) {
28 | if (data.length == 0) revert();
29 | assembly {
30 | revert(add(32, data), mload(data))
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/mocks/wells/MockInitFailWell.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import {IPump} from "src/interfaces/pumps/IPump.sol";
8 |
9 | /**
10 | * @notice Mock Well that fails on various init calls.
11 | */
12 | contract MockInitFailWell {
13 | function initNoMessage() external pure {
14 | revert();
15 | }
16 |
17 | function initMessage() external pure {
18 | revert("Well: fail message");
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/mocks/wells/MockReserveWell.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import {IPump} from "src/interfaces/pumps/IPump.sol";
8 | import {Call} from "src/interfaces/IWell.sol";
9 |
10 | /**
11 | * @notice Mock Well that allows setting of reserves.
12 | */
13 | contract MockReserveWell {
14 | uint256[] reserves;
15 | Call _wellFunction;
16 |
17 |
18 | constructor() {
19 | reserves = new uint256[](2);
20 | }
21 |
22 | function setWellFunction(Call calldata __wellFunction) external {
23 | _wellFunction = __wellFunction;
24 | }
25 |
26 | function wellFunction() external view returns (Call memory) {
27 | return _wellFunction;
28 | }
29 |
30 | function setReserves(uint256[] memory _reserves) public {
31 | reserves = _reserves;
32 | }
33 |
34 | function getReserves() external view returns (uint256[] memory _reserves) {
35 | _reserves = reserves;
36 | }
37 |
38 | function update(address pump, uint256[] calldata _reserves, bytes calldata data) external {
39 | IPump(pump).update(reserves, data);
40 | setReserves(_reserves);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/mocks/wells/MockStaticWell.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | */
4 |
5 | pragma solidity ^0.8.20;
6 |
7 | import {console} from "test/TestHelper.sol";
8 | import {ReentrancyGuardUpgradeable} from "ozu/security/ReentrancyGuardUpgradeable.sol";
9 | import {IPump} from "src/interfaces/pumps/IPump.sol";
10 | import {MockReserveWell} from "mocks/wells/MockReserveWell.sol";
11 | import {ClonePlus} from "src/utils/ClonePlus.sol";
12 | import {Call, IERC20} from "src/Well.sol";
13 |
14 | /**
15 | * @title MockStaticWell
16 | * @author Silo Chad
17 | * @notice Simplified Well implementation which stores configuration in immutable
18 | * storage during construction.
19 | */
20 | contract MockStaticWell is ReentrancyGuardUpgradeable, ClonePlus {
21 | address immutable TOKEN0;
22 | address immutable TOKEN1;
23 | address immutable WELL_FUNCTION_TARGET;
24 | bytes32 immutable WELL_FUNCTION_DATA;
25 | address immutable PUMP0_TARGET;
26 | bytes32 immutable PUMP0_DATA;
27 | address immutable AQUIFER;
28 | bytes32 immutable WELL_DATA;
29 |
30 | string public name;
31 | string public symbol;
32 |
33 | constructor(
34 | IERC20[] memory _tokens,
35 | Call memory _wellFunction,
36 | Call[] memory _pumps,
37 | address _aquifer,
38 | bytes memory _wellData
39 | ) {
40 | require(_tokens.length == 2, "MockStaticWell: invalid tokens");
41 | require(_pumps.length == 1, "MockStaticWell: invalid pumps");
42 |
43 | TOKEN0 = address(_tokens[0]);
44 | TOKEN1 = address(_tokens[1]);
45 | WELL_FUNCTION_TARGET = _wellFunction.target;
46 | WELL_FUNCTION_DATA = bytes32(_wellFunction.data);
47 | PUMP0_TARGET = _pumps[0].target;
48 | PUMP0_DATA = bytes32(_pumps[0].data);
49 | AQUIFER = _aquifer;
50 | WELL_DATA = bytes32(_wellData);
51 | }
52 |
53 | function init(string memory _name, string memory _symbol) public initializer {
54 | name = _name;
55 | symbol = _symbol;
56 | }
57 |
58 | function tokens() public view returns (IERC20[] memory _tokens) {
59 | _tokens = new IERC20[](2);
60 | _tokens[0] = IERC20(TOKEN0);
61 | _tokens[1] = IERC20(TOKEN1);
62 | }
63 |
64 | function wellFunction() public view returns (Call memory _wellFunction) {
65 | _wellFunction = Call(WELL_FUNCTION_TARGET, bytes32ToBytes(WELL_FUNCTION_DATA));
66 | }
67 |
68 | function pumps() public view returns (Call[] memory _pumps) {
69 | _pumps = new Call[](1);
70 | _pumps[0] = Call(PUMP0_TARGET, bytes32ToBytes(PUMP0_DATA));
71 | }
72 |
73 | function aquifer() public view returns (address) {
74 | return AQUIFER;
75 | }
76 |
77 | function wellData() public view returns (bytes memory) {
78 | return bytes32ToBytes(WELL_DATA);
79 | }
80 |
81 | /// @dev Read a uint256 off the front of immutable storage applied during Clone.
82 | /// Since the immutable variables defined above are instantiated during
83 | /// construction, their length is included in the offset; i.e., immutable
84 | /// vars added using Clone begin at index 0. See {Clone._getImmutableArgsOffset}.
85 | function immutableDataFromClone() public pure returns (uint256) {
86 | return _getArgUint256(0);
87 | }
88 |
89 | /// @dev Inefficient way to convert bytes32 back to bytes without padding
90 | function bytes32ToBytes(bytes32 data) internal pure returns (bytes memory) {
91 | uint256 i = 0;
92 | while (i < 32 && uint8(data[i]) != 0) {
93 | ++i;
94 | }
95 | bytes memory result = new bytes(i);
96 | i = 0;
97 | while (i < 32 && data[i] != 0) {
98 | result[i] = data[i];
99 | ++i;
100 | }
101 | return result;
102 | }
103 |
104 | function isInitialized() external view returns (bool) {
105 | return _getInitializedVersion() > 0;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/mocks/wells/MockWellUpgradeable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {WellUpgradeable} from "src/WellUpgradeable.sol";
6 |
7 | // this needs to be here for upgrade checks
8 | /// @custom:oz-upgrades-from WellUpgradeable
9 | contract MockWellUpgradeable is WellUpgradeable {
10 |
11 | function getVersion(uint256 i) external pure returns (uint256) {
12 | return i;
13 | }
14 | }
--------------------------------------------------------------------------------
/multi-flow-pump.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-423n4/2024-07-basin/bbe3cafed46822b0d0b1e0b80f9dee623012bd92/multi-flow-pump.pdf
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@beanstalk/wells",
3 | "version": "1.2.0-prerelease0",
4 | "description": "A [{Well}](/src/Well.sol) is a constant function AMM that allows the provisioning of liquidity into a single pooled on-chain liquidity position.",
5 | "main": "index.js",
6 | "directories": {
7 | "lib": "lib",
8 | "test": "test"
9 | },
10 | "scripts": {
11 | "prepare": "husky install",
12 | "t": ". ./script/bin/env.sh; forge t -vvv",
13 | "deploy:local": ". ./script/bin/env.sh; ./script/bin/deploy-local.sh",
14 | "compare": "forge snapshot --match-path test/integration/IntegrationTestGasComparisons.sol --snap .dex-comparisons",
15 | "compare:check": "yarn compare --check",
16 | "compare:csv": "node ./script/dex-comparisons.js",
17 | "test": "forge t -vvv --ffi"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/BeanstalkFarms/Wells.git"
22 | },
23 | "keywords": [],
24 | "author": "",
25 | "license": "ISC",
26 | "bugs": {
27 | "url": "https://github.com/BeanstalkFarms/Wells/issues"
28 | },
29 | "homepage": "https://github.com/BeanstalkFarms/Wells#readme",
30 | "devDependencies": {
31 | "husky": "8.0.3"
32 | },
33 | "lint-staged": {
34 | "*.sol": "forge fmt"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/remappings.txt:
--------------------------------------------------------------------------------
1 | prb/math/=lib/prb-math/src/
2 | forge-std/=lib/forge-std/src/
3 | oz/=lib/openzeppelin-contracts/contracts/
4 | ozu/=lib/openzeppelin-contracts-upgradeable/contracts/
5 | src/=src/
6 | mocks=mocks/
7 | tests=tests/
8 | utils=utils/
9 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | cytoolz==0.12.3
2 | eth-hash==0.7.0
3 | eth-typing==3.5.2
4 | eth-utils==2.3.1
5 | eth_abi==5.1.0
6 | numpy==2.0.1
7 | pandas==2.2.2
8 | parsimonious==0.10.0
9 | python-dateutil==2.9.0.post0
10 | pytz==2024.1
11 | regex==2024.5.15
12 | setuptools==71.1.0
13 | six==1.16.0
14 | toolz==0.12.1
15 | typing_extensions==4.12.2
16 | tzdata==2024.1
17 |
--------------------------------------------------------------------------------
/scope.txt:
--------------------------------------------------------------------------------
1 | ./src/functions/Stable2.sol
2 | ./src/functions/StableLUT/Stable2LUT1.sol
3 | ./src/WellUpgradeable.sol
--------------------------------------------------------------------------------
/script/bin/deploy-local.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Private key for test account: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
4 | # NOTE: This is a test account provided by Hardhat/Forge for testing. It should
5 | # never be used in production.
6 | export PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
7 |
8 | forge script "script/deploy/$1.s.sol:Deploy$1" \
9 | --fork-url http://localhost:8545 \
10 | --broadcast \
11 | --sender "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
--------------------------------------------------------------------------------
/script/bin/doc.sh:
--------------------------------------------------------------------------------
1 | docker run -v $(pwd):/Wells ethereum/solc:stable --base-path ./Wells $(tr '\n' ' ' < remappings.txt) -o /Wells/out --userdoc --devdoc --overwrite /Wells/src/Well.sol
--------------------------------------------------------------------------------
/script/bin/env.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ## Usage:
4 | ## . ./export-env.sh ; $COMMAND
5 | ## . ./export-env.sh ; echo ${MINIENTREGA_FECHALIMITE}
6 | ##
7 | ## Source:
8 | ## https://stackoverflow.com/a/20909045
9 | ## Modified to surpress output of environment to stdout
10 | ### and to output a message if no .env file present.
11 |
12 | if [ ! -f '.env' ]; then
13 |
14 | echo "No .env file found, bypassing loading."
15 | return 0;
16 |
17 | fi
18 |
19 | unamestr=$(uname)
20 | if [ "$unamestr" = 'Linux' ]; then
21 |
22 | env $(grep -v '^#' .env | xargs -d '\n') &> /dev/null
23 |
24 | elif [ "$unamestr" = 'FreeBSD' ] || [ "$unamestr" = 'Darwin' ]; then
25 |
26 | env $(grep -v '^#' .env | xargs -0) &> /dev/null
27 |
28 | fi
--------------------------------------------------------------------------------
/script/deploy/Aquifer.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {Script} from "forge-std/Script.sol";
5 | import {Aquifer} from "src/Aquifer.sol";
6 |
7 | // Script to deploy an {Aquifer}.
8 | // see {Aquifer}.
9 | contract DeployAquifer is Script {
10 | function run() external {
11 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
12 | vm.startBroadcast(deployerPrivateKey);
13 | // Aquifer aquifer = new Aquifer();
14 | vm.stopBroadcast();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/script/deploy/AquiferWell.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {Script, console} from "forge-std/Script.sol";
5 | import {SafeERC20, IERC20} from "oz/token/ERC20/utils/SafeERC20.sol";
6 |
7 | import {IWell, Call} from "src/interfaces/IWell.sol";
8 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
9 | import {IPump} from "src/interfaces/pumps/IPump.sol";
10 |
11 | import {logger} from "script/deploy/helpers/Logger.sol";
12 | import {MockToken} from "mocks/tokens/MockToken.sol";
13 | import {MockPump} from "mocks/pumps/MockPump.sol";
14 |
15 | import {ConstantProduct2} from "src/functions/ConstantProduct2.sol";
16 | import {Well} from "src/Well.sol";
17 | import {Aquifer} from "src/Aquifer.sol";
18 |
19 | import {WellDeployer} from "script/helpers/WellDeployer.sol";
20 |
21 | /**
22 | * @dev Script to deploy a BEAN-ETH {Well} with a ConstantProduct2 pricing function
23 | * and MockPump via an Aquifer.
24 | */
25 | contract DeployAquiferWell is Script, WellDeployer {
26 | using SafeERC20 for IERC20;
27 |
28 | IERC20 constant BEAN = IERC20(0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab);
29 | IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // WETH9
30 |
31 | function run() external {
32 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
33 | vm.startBroadcast(deployerPrivateKey);
34 |
35 | // Tokens
36 | IERC20[] memory tokens = new IERC20[](2);
37 | tokens[0] = BEAN;
38 | tokens[1] = WETH;
39 |
40 | // Deploy Aquifer
41 | address aquifer = address(new Aquifer());
42 |
43 | // Well Function
44 | IWellFunction cp2 = new ConstantProduct2();
45 | Call memory wellFunction = Call(address(cp2), new bytes(0));
46 |
47 | // Pump
48 | IPump mockPump = new MockPump();
49 | Call[] memory pumps = new Call[](1);
50 | pumps[0] = Call(address(mockPump), new bytes(0));
51 |
52 | // Well implementation
53 | address wellImplementation = address(new Well());
54 |
55 | //bore well
56 | Well well = encodeAndBoreWell(aquifer, wellImplementation, tokens, wellFunction, pumps, bytes32(0));
57 |
58 | console.log("Deployed CP2 at address: ", address(cp2));
59 | console.log("Deployed Pump at address: ", address(pumps[0].target));
60 | logger.logWell(well);
61 |
62 | vm.stopBroadcast();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/script/deploy/MockPump.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {Script, console} from "forge-std/Script.sol";
5 | import {MockPump} from "mocks/pumps/MockPump.sol";
6 |
7 | // Script to deploy a {MockPump}.
8 | // Mockpump does not provide utility and is solely used for example.
9 | contract DeployMockPump is Script {
10 | function run() external {
11 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
12 | vm.startBroadcast(deployerPrivateKey);
13 |
14 | MockPump mockPump = new MockPump();
15 | console.log("Deployed MockPump at address: ", address(mockPump));
16 |
17 | vm.stopBroadcast();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/script/deploy/Well.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {console2} from "forge-std/console2.sol";
5 | import {console} from "forge-std/console.sol";
6 | import {Test} from "forge-std/Test.sol";
7 | import {Script} from "forge-std/Script.sol";
8 |
9 | import {WellDeployer} from "script/helpers/WellDeployer.sol";
10 | import {logger} from "script/deploy/helpers/Logger.sol";
11 | import {MockPump} from "mocks/pumps/MockPump.sol";
12 |
13 | import {Well, Call, IWellFunction, IPump, IERC20} from "src/Well.sol";
14 | import {ConstantProduct2} from "src/functions/ConstantProduct2.sol";
15 | import {Aquifer} from "src/Aquifer.sol";
16 |
17 | /**
18 | * @dev Deploys a BEAN:WETH ConstantProduct2 Well. Intended for testing.
19 | */
20 | contract DeployWell is Script, WellDeployer {
21 | IERC20 constant BEAN = IERC20(0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab);
22 | IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // WETH9
23 |
24 | function run() external {
25 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
26 | vm.startBroadcast(deployerPrivateKey);
27 |
28 | // Tokens
29 | IERC20[] memory tokens = new IERC20[](2);
30 | tokens[0] = BEAN;
31 | tokens[1] = WETH;
32 |
33 | // Well Function
34 | IWellFunction cp2 = new ConstantProduct2();
35 | Call memory wellFunction = Call(address(cp2), new bytes(0));
36 |
37 | // Pump
38 | IPump mockPump = new MockPump();
39 | Call[] memory pumps = new Call[](1);
40 | pumps[0] = Call(address(mockPump), new bytes(0));
41 |
42 | address aquifer = address(new Aquifer());
43 |
44 | address wellImplementation = address(new Well());
45 |
46 | // Well
47 | Well well = encodeAndBoreWell(aquifer, wellImplementation, tokens, wellFunction, pumps, bytes32(0));
48 |
49 | console.log("Deployed CP2 at address: ", address(cp2));
50 | console.log("Deployed Pump at address: ", address(pumps[0].target));
51 | logger.logWell(well);
52 |
53 | vm.stopBroadcast();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/script/deploy/helpers/Logger.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {console2} from "forge-std/console2.sol";
6 | import {console} from "forge-std/console.sol";
7 |
8 | import {Well} from "src/Well.sol";
9 |
10 | library logger {
11 | function logWell(Well well) public view {
12 | console.log("\nWELL:", address(well));
13 | console.log("Name \t", well.name());
14 | console.log("Symbol\t", well.symbol());
15 | console.log("Aquifer \t", well.aquifer());
16 | console.log("tokens[0]\t", address(well.tokens()[0]));
17 | console.log("tokens[1]\t", address(well.tokens()[1]));
18 | console.log("Data:");
19 | console.logBytes(well.wellData());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/script/dex-comparisons.js:
--------------------------------------------------------------------------------
1 | const fs = require('node:fs');
2 | const readline = require('node:readline');
3 |
4 | const FUZZ_RUNS = 5000;
5 |
6 | const estimateGasUSD = (gasLimit) => {
7 | const BASE_FEE = 30e-9; // 30 gwei
8 | const PRIO_FEE = 1e-9; // 1 gwei
9 | const ETH_PRICE_USD = 1500; // $1500
10 | return gasLimit * (BASE_FEE + PRIO_FEE) * ETH_PRICE_USD;
11 | }
12 |
13 | async function main() {
14 | const fileStream = fs.createReadStream('.dex-comparisons');
15 |
16 | const rl = readline.createInterface({
17 | input: fileStream,
18 | crlfDelay: Infinity,
19 | });
20 |
21 | let csvContent = 'DEX,PAIR,ACTION,AVERAGE,"EST. COST"\n';
22 |
23 | for await (const line of rl) {
24 | const uniqueTest = line
25 | .split('IntegrationTestGasComparisons:')[1]
26 | .split('testFuzz_')[1]
27 | .split('_');
28 |
29 | const dex = uniqueTest[0].toUpperCase();
30 | const pair = uniqueTest[1].toUpperCase();
31 | const test = uniqueTest[2].split('(uint256)');
32 | const testAction = test[0].toUpperCase();
33 | const testAverage = test[1]
34 | .split(`(runs: ${FUZZ_RUNS}, μ:`)[1]
35 | .split(', ~: ')[0]
36 | .trim();
37 |
38 | csvContent +=
39 | dex + ',' + pair + ',' + testAction + ',' + testAverage + ',' + `$${estimateGasUSD(parseInt(testAverage)).toFixed(2)}` + '\n';
40 | }
41 |
42 | try {
43 | fs.writeFileSync('./.dex-comparisons.csv', csvContent);
44 | console.log('Data written to file successfully.');
45 | } catch (err) {
46 | console.error('Failed to write to file: ' + err);
47 | throw err;
48 | }
49 | }
50 |
51 | main()
52 | .then(() => process.exit(0))
53 | .then(() => process.exit(1));
54 |
--------------------------------------------------------------------------------
/script/helpers/WellDeployer.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
5 | import {LibWellConstructor} from "src/libraries/LibWellConstructor.sol";
6 | import {Well, Call, IERC20} from "src/Well.sol";
7 | import {Aquifer} from "src/Aquifer.sol";
8 | import {WellUpgradeable} from "src/WellUpgradeable.sol";
9 | import {LibWellUpgradeableConstructor} from "src/libraries/LibWellUpgradeableConstructor.sol";
10 |
11 | abstract contract WellDeployer {
12 | /**
13 | * @notice Encode the Well's immutable data, and deploys the well.
14 | * @param _aquifer The address of the Aquifer which will deploy this Well.
15 | * @param _wellImplementation The address of the Well implementation.
16 | * @param _tokens A list of ERC20 tokens supported by the Well.
17 | * @param _wellFunction A single Call struct representing a call to the Well Function.
18 | * @param _pumps An array of Call structs representings calls to Pumps.
19 | * @param _salt The salt to deploy the Well with (`bytes32(0)` for none). See {LibClone}.
20 | */
21 | function encodeAndBoreWell(
22 | address _aquifer,
23 | address _wellImplementation,
24 | IERC20[] memory _tokens,
25 | Call memory _wellFunction,
26 | Call[] memory _pumps,
27 | bytes32 _salt
28 | ) internal returns (Well _well) {
29 | (bytes memory immutableData, bytes memory initData) =
30 | LibWellConstructor.encodeWellDeploymentData(_aquifer, _tokens, _wellFunction, _pumps);
31 | _well = Well(Aquifer(_aquifer).boreWell(_wellImplementation, immutableData, initData, _salt));
32 | }
33 |
34 | /**
35 | * @notice Encode the Well's immutable data, and deploys the well. Modified for upgradeable wells.
36 | * @param _aquifer The address of the Aquifer which will deploy this Well.
37 | * @param _wellImplementation The address of the Well implementation.
38 | * @param _tokens A list of ERC20 tokens supported by the Well.
39 | * @param _wellFunction A single Call struct representing a call to the Well Function.
40 | * @param _pumps An array of Call structs representings calls to Pumps.
41 | * @param _salt The salt to deploy the Well with (`bytes32(0)` for none). See {LibClone}.
42 | */
43 | function encodeAndBoreWellUpgradeable(
44 | address _aquifer,
45 | address _wellImplementation,
46 | IERC20[] memory _tokens,
47 | Call memory _wellFunction,
48 | Call[] memory _pumps,
49 | bytes32 _salt
50 | ) internal returns (WellUpgradeable _well) {
51 | (bytes memory immutableData, bytes memory initData) =
52 | LibWellUpgradeableConstructor.encodeWellDeploymentData(_aquifer, _tokens, _wellFunction, _pumps);
53 | _well = WellUpgradeable(Aquifer(_aquifer).boreWell(_wellImplementation, immutableData, initData, _salt));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/script/simulations/stableswap/StableswapCalcRatiosLiqSim.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: UNLICENSED
2 | pragma solidity ^0.8.20;
3 |
4 | import "forge-std/Script.sol";
5 | import {console} from "forge-std/console.sol";
6 | import {Stable2} from "src/functions/Stable2.sol";
7 | import {Stable2LUT1} from "src/functions/StableLUT/Stable2LUT1.sol";
8 |
9 | /**
10 | * Stable2 well function simulation and precalculations used
11 | * to produce the token ratios for the lookup table needed for the initial
12 | * `calcReserveAtRatioLiquidity` estimates.
13 | */
14 | contract StableswapCalcRatiosLiqSim is Script {
15 | function run() external {
16 | Stable2LUT1 stable2LUT1 = new Stable2LUT1();
17 | Stable2 stable2 = new Stable2(address(stable2LUT1));
18 | console.log("stable2.getAParameter(): %d", stable2LUT1.getAParameter());
19 | // initial reserves
20 | uint256 init_reserve_x = 1_000_000e18;
21 | uint256 init_reserve_y = 1_000_000e18;
22 | uint256[] memory reserves = new uint256[](2);
23 | reserves[0] = init_reserve_x;
24 | reserves[1] = init_reserve_y;
25 | uint256 reserve_y = init_reserve_y;
26 | bytes memory data = abi.encode(18, 18);
27 | uint256 price;
28 |
29 | // for n times (1...n) :
30 | // 1) modify reserve x_n-1 by some percentage (this changes the pool liquidity)
31 | // 3) calc price_n using calcRate(...)
32 |
33 | // csv header
34 | console.log("Price (P),Reserve (x),Reserve (y)");
35 |
36 | // calcReserveAtRatioLiquidity
37 | for (uint256 i; i < 20; i++) {
38 | // update reserves
39 | reserve_y = reserve_y * 88 / 100;
40 | reserves[1] = reserve_y;
41 | // mark price
42 | price = stable2.calcRate(reserves, 0, 1, data);
43 | console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
44 | }
45 |
46 | // reset reserves
47 | reserve_y = init_reserve_y;
48 |
49 | // calcReserveAtRatioLiquidity
50 | for (uint256 i; i < 20; i++) {
51 | // update reserves
52 | reserve_y = reserve_y * 98 / 100;
53 | reserves[1] = reserve_y;
54 | // mark price
55 | price = stable2.calcRate(reserves, 0, 1, data);
56 | console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
57 | }
58 |
59 | // reset reserves
60 | reserve_y = init_reserve_y;
61 |
62 | // calcReserveAtRatioLiquidity
63 | for (uint256 i; i < 20; i++) {
64 | // update reserves
65 | reserve_y = reserve_y * 102 / 100;
66 | reserves[1] = reserve_y;
67 | // mark price
68 | price = stable2.calcRate(reserves, 0, 1, data);
69 | console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
70 | }
71 |
72 | // reset reserves
73 | reserve_y = init_reserve_y;
74 |
75 | // calcReserveAtRatioLiquidity
76 | for (uint256 i; i < 20; i++) {
77 | // update reserves
78 | reserve_y = reserve_y * 112 / 100;
79 | reserves[1] = reserve_y;
80 | // mark price
81 | price = stable2.calcRate(reserves, 0, 1, data);
82 | console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
83 | }
84 |
85 | // Extreme prices
86 |
87 | // extreme low
88 | reserve_y = init_reserve_y * 1 / 28;
89 | reserves[1] = reserve_y;
90 | price = stable2.calcRate(reserves, 0, 1, data);
91 | console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
92 |
93 | // extreme high
94 | reserve_y = init_reserve_y * 2000;
95 | reserves[1] = reserve_y;
96 | price = stable2.calcRate(reserves, 0, 1, data);
97 | console.log("%d,%d,%d", price, init_reserve_x, reserve_y);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Aquifer.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {ReentrancyGuard} from "oz/security/ReentrancyGuard.sol";
6 |
7 | import {IAquifer} from "src/interfaces/IAquifer.sol";
8 | import {IWell} from "src/Well.sol";
9 | import {LibClone} from "src/libraries/LibClone.sol";
10 |
11 | /**
12 | * @title Aquifer
13 | * @author Brendan, Silo Chad, Brean
14 | * @notice Aquifer is a permissionless Well registry and factory.
15 | * @dev Aquifer deploys Wells by cloning a pre-deployed Well implementation.
16 | */
17 | contract Aquifer is IAquifer, ReentrancyGuard {
18 | using LibClone for address;
19 |
20 | // A mapping of Well address to the Well implementation addresses
21 | // Mapping gets set on Well deployment
22 | mapping(address => address) public wellImplementation;
23 |
24 | constructor() ReentrancyGuard() {}
25 |
26 | /**
27 | * @dev
28 | * Use `salt == 0` to deploy a new Well with `create`
29 | * Use `salt > 0` to deploy a new Well with `create2`
30 | */
31 | function boreWell(
32 | address implementation,
33 | bytes calldata immutableData,
34 | bytes calldata initFunctionCall,
35 | bytes32 salt
36 | ) external nonReentrant returns (address well) {
37 | if (immutableData.length > 0) {
38 | if (salt != bytes32(0)) {
39 | // Encode the salt with the `msg.sender` address to prevent frontrunning attack
40 | salt = keccak256(abi.encode(msg.sender, salt));
41 | well = implementation.cloneDeterministic(immutableData, salt);
42 | } else {
43 | well = implementation.clone(immutableData);
44 | }
45 | } else {
46 | if (salt != bytes32(0)) {
47 | // Encode the salt with the `msg.sender` address to prevent frontrunning attack
48 | salt = keccak256(abi.encode(msg.sender, salt));
49 | well = implementation.cloneDeterministic(salt);
50 | } else {
51 | well = implementation.clone();
52 | }
53 | }
54 |
55 | if (initFunctionCall.length > 0) {
56 | (bool success, bytes memory returnData) = well.call(initFunctionCall);
57 | if (!success) {
58 | // Next 5 lines are based on https://ethereum.stackexchange.com/a/83577
59 | if (returnData.length < 68) revert InitFailed("");
60 | assembly {
61 | returnData := add(returnData, 0x04)
62 | }
63 | revert InitFailed(abi.decode(returnData, (string)));
64 | }
65 | }
66 |
67 | if (!IWell(well).isInitialized()) {
68 | revert WellNotInitialized();
69 | }
70 |
71 | // The Aquifer address MUST be set, either (a) via immutable data during cloning,
72 | // or (b) as a storage variable during an init function call. In either case,
73 | // the address MUST match the address of the Aquifer that performed deployment.
74 | if (IWell(well).aquifer() != address(this)) {
75 | revert InvalidConfig();
76 | }
77 |
78 | // Save implementation
79 | wellImplementation[well] = implementation;
80 |
81 | emit BoreWell(
82 | well,
83 | implementation,
84 | IWell(well).tokens(),
85 | IWell(well).wellFunction(),
86 | IWell(well).pumps(),
87 | IWell(well).wellData()
88 | );
89 | }
90 |
91 | function predictWellAddress(
92 | address implementation,
93 | bytes calldata immutableData,
94 | bytes32 salt
95 | ) external view returns (address well) {
96 | // Aquifer doesn't support using a salt of 0 to deploy a Well at a deterministic address.
97 | if (salt == bytes32(0)) {
98 | revert InvalidSalt();
99 | }
100 | salt = keccak256(abi.encode(msg.sender, salt));
101 | if (immutableData.length > 0) {
102 | well = implementation.predictDeterministicAddress(immutableData, salt, address(this));
103 | } else {
104 | well = implementation.predictDeterministicAddress(salt, address(this));
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/functions/ConstantProduct.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IBeanstalkWellFunction} from "src/interfaces/IBeanstalkWellFunction.sol";
6 | import {ProportionalLPToken} from "src/functions/ProportionalLPToken.sol";
7 | import {LibMath} from "src/libraries/LibMath.sol";
8 |
9 | /**
10 | * @title ConstantProduct
11 | * @author Brendan
12 | * @notice Constant product pricing function for Wells with N tokens.
13 | * @dev Constant Product Wells use the formula:
14 | * `π(b_i) = (s / n)^n`
15 | *
16 | * Where:
17 | * `s` is the supply of LP tokens
18 | * `b_i` is the reserve at index `i`
19 | * `n` is the number of tokens in the Well
20 | *
21 | * Note: Using too many tokens in a Constant Product Well may result in overflow.
22 | */
23 | contract ConstantProduct is ProportionalLPToken, IBeanstalkWellFunction {
24 | using LibMath for uint256;
25 |
26 | uint256 constant CALC_RATE_PRECISION = 1e18;
27 |
28 | /// @dev `s = π(b_i)^(1/n) * n`
29 | function calcLpTokenSupply(
30 | uint256[] calldata reserves,
31 | bytes calldata
32 | ) external pure override returns (uint256 lpTokenSupply) {
33 | lpTokenSupply = _prodX(reserves).nthRoot(reserves.length) * reserves.length;
34 | }
35 |
36 | /// @dev `b_j = (s / n)^n / π_{i!=j}(b_i)`
37 | function calcReserve(
38 | uint256[] calldata reserves,
39 | uint256 j,
40 | uint256 lpTokenSupply,
41 | bytes calldata
42 | ) external pure override returns (uint256 reserve) {
43 | uint256 n = reserves.length;
44 | reserve = (lpTokenSupply / n) ** n;
45 | for (uint256 i; i < n; ++i) {
46 | if (i != j) reserve = reserve / reserves[i];
47 | }
48 | }
49 |
50 | function name() external pure override returns (string memory) {
51 | return "Constant Product";
52 | }
53 |
54 | function symbol() external pure override returns (string memory) {
55 | return "CP";
56 | }
57 |
58 | /// @dev calculate the mathematical product of an array of uint256[]
59 | function _prodX(uint256[] memory xs) private pure returns (uint256 pX) {
60 | pX = xs[0];
61 | uint256 length = xs.length;
62 | for (uint256 i = 1; i < length; ++i) {
63 | pX = pX * xs[i];
64 | }
65 | }
66 |
67 | /// @dev `b_j = (π(b_i) * r_j / (Σ_{i != j}(r_i)/(n-1)))^(1/n)`
68 | function calcReserveAtRatioSwap(
69 | uint256[] calldata reserves,
70 | uint256 j,
71 | uint256[] calldata ratios,
72 | bytes calldata
73 | ) external pure override returns (uint256 reserve) {
74 | uint256 sumRatio = 0;
75 | for (uint256 i; i < reserves.length; ++i) {
76 | if (i != j) sumRatio += ratios[i];
77 | }
78 | sumRatio /= reserves.length - 1;
79 | reserve = _prodX(reserves) * ratios[j] / sumRatio;
80 | reserve = reserve.nthRoot(reserves.length);
81 | }
82 |
83 | /// @dev `b_j = Σ_{i != j}(b_i * r_j / r_i) / (n-1)`
84 | function calcReserveAtRatioLiquidity(
85 | uint256[] calldata reserves,
86 | uint256 j,
87 | uint256[] calldata ratios,
88 | bytes calldata
89 | ) external pure override returns (uint256 reserve) {
90 | for (uint256 i; i < reserves.length; ++i) {
91 | if (i != j) {
92 | reserve += ratios[j] * reserves[i] / ratios[i];
93 | }
94 | }
95 | reserve /= reserves.length - 1;
96 | }
97 |
98 | function calcRate(
99 | uint256[] calldata reserves,
100 | uint256 i,
101 | uint256 j,
102 | bytes calldata
103 | ) external pure returns (uint256 rate) {
104 | return reserves[i] * CALC_RATE_PRECISION / reserves[j];
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/functions/ProportionalLPToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
6 | import {Math} from "oz/utils/math/Math.sol";
7 |
8 | /**
9 | * @title ProportionalLPToken
10 | * @notice Defines a proportional relationship between the supply of LP tokens
11 | * and the amount of each underlying token for an N-token Well.
12 | * @dev When removing `s` LP tokens with a Well with `S` LP token supply, the user
13 | * recieves `s * b_i / S` of each underlying token.
14 | */
15 | abstract contract ProportionalLPToken is IWellFunction {
16 | using Math for uint256;
17 |
18 | function calcLPTokenUnderlying(
19 | uint256 lpTokenAmount,
20 | uint256[] calldata reserves,
21 | uint256 lpTokenSupply,
22 | bytes calldata
23 | ) external pure returns (uint256[] memory underlyingAmounts) {
24 | underlyingAmounts = new uint256[](reserves.length);
25 | for (uint256 i; i < reserves.length; ++i) {
26 | underlyingAmounts[i] = lpTokenAmount.mulDiv(reserves[i], lpTokenSupply);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/functions/ProportionalLPToken2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
6 | import {Math} from "oz/utils/math/Math.sol";
7 |
8 | /**
9 | * @title ProportionalLPToken2
10 | * @notice Defines a proportional relationship between the supply of LP tokens
11 | * and the amount of each underlying token for a two-token Well.
12 | * @dev When removing `s` LP tokens with a Well with `S` LP token supply, the user
13 | * recieves `s * b_i / S` of each underlying token.
14 | */
15 | abstract contract ProportionalLPToken2 is IWellFunction {
16 | using Math for uint256;
17 |
18 | function calcLPTokenUnderlying(
19 | uint256 lpTokenAmount,
20 | uint256[] calldata reserves,
21 | uint256 lpTokenSupply,
22 | bytes calldata
23 | ) external pure returns (uint256[] memory underlyingAmounts) {
24 | underlyingAmounts = new uint256[](2);
25 | underlyingAmounts[0] = lpTokenAmount.mulDiv(reserves[0], lpTokenSupply);
26 | underlyingAmounts[1] = lpTokenAmount.mulDiv(reserves[1], lpTokenSupply);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/interfaces/IAquifer.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IERC20, SafeERC20} from "oz/token/ERC20/utils/SafeERC20.sol";
6 | import {IWell, Call} from "src/interfaces/IWell.sol";
7 |
8 | /**
9 | * @title IAquifer
10 | * @notice Interface for the Aquifer, a permissionless Well deployer and registry.
11 | */
12 | interface IAquifer {
13 | /**
14 | * @notice Thrown when the {init} function call on the Well reverts.
15 | */
16 | error InitFailed(string reason);
17 |
18 | /**
19 | * @notice Thrown when the user attempts to bore a Well with invalid configuration.
20 | */
21 | error InvalidConfig();
22 |
23 | /**
24 | * @notice Thrown a Well is bored, but not initialized.
25 | */
26 | error WellNotInitialized();
27 |
28 | /**
29 | * @notice Thrown when the user attempts to predict a Well's deterministic address with a salt of 0.
30 | */
31 | error InvalidSalt();
32 |
33 | /**
34 | * @notice Emitted when a Well is deployed.
35 | * @param well The address of the new Well
36 | * @param implementation The Well implementation address
37 | * @param tokens The tokens in the Well
38 | * @param wellFunction The Well function
39 | * @param pumps The pumps to bore in the Well
40 | * @param wellData The Well data to implement into the Well
41 | */
42 | event BoreWell(
43 | address well, address implementation, IERC20[] tokens, Call wellFunction, Call[] pumps, bytes wellData
44 | );
45 |
46 | /**
47 | * @notice Deploys a Well.
48 | * @param implementation The Well implementation to clone.
49 | * @param immutableData The data to append to the bytecode of the contract.
50 | * @param initFunctionCall The function call to initialize the Well. Set to empty bytes for no call.
51 | * @param salt The salt to deploy the Well with (`bytes32(0)` for none). See {LibClone}.
52 | * @return wellAddress The address of the new Well
53 | */
54 | function boreWell(
55 | address implementation,
56 | bytes calldata immutableData,
57 | bytes calldata initFunctionCall,
58 | bytes32 salt
59 | ) external returns (address wellAddress);
60 |
61 | /**
62 | * @notice Returns the implementation that a given Well was deployed with.
63 | * @param well The Well to get the implementation of
64 | * @return implementation The address of the implementation of a Well.
65 | * @dev Always verify that a Well was deployed by a trusted Aquifer using a trusted implementation before using.
66 | * If `wellImplementation == address(0)`, then the Aquifer did not deploy the Well.
67 | */
68 | function wellImplementation(address well) external view returns (address implementation);
69 | }
70 |
--------------------------------------------------------------------------------
/src/interfaces/IBeanstalkWellFunction.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IMultiFlowPumpWellFunction} from "src/interfaces/IMultiFlowPumpWellFunction.sol";
6 |
7 | /**
8 | * @title IBeanstalkWellFunction
9 | * @notice Defines all necessary functions for Beanstalk to support a Well Function in addition to functions defined in the primary interface.
10 | * It extends `IMultiFlowPumpWellFunction` as Beanstalk requires Wells to use MultiFlowPump in order to have access to manipulation resistant oracles.
11 | * Beanstalk requires 2 functions to solve for a given reserve value such that the average price between
12 | * the given reserve and all other reserves equals the average of the input ratios.
13 | * - `calcReserveAtRatioSwap` assumes the target ratios are reached through executing a swap. Note: `calcReserveAtRatioSwap` is included in {IMultiFlowPumpWellFunction}.
14 | * - `calcReserveAtRatioLiquidity` assumes the target ratios are reached through adding/removing liquidity.
15 | */
16 | interface IBeanstalkWellFunction is IMultiFlowPumpWellFunction {
17 | /**
18 | * @notice Calculates the `j` reserve such that `π_{i | i != j} (d reserves_j / d reserves_i) = π_{i | i != j}(ratios_j / ratios_i)`.
19 | * assumes that reserve_j is being added or removed in exchange for LP Tokens.
20 | * @dev used by Beanstalk to calculate the max deltaB that can be converted in/out of a Well.
21 | * @param reserves The reserves of the Well
22 | * @param j The index of the reserve to solve for
23 | * @param ratios The ratios of reserves to solve for
24 | * @param data Well function data provided on every call
25 | * @return reserve The resulting reserve at the jth index
26 | */
27 | function calcReserveAtRatioLiquidity(
28 | uint256[] calldata reserves,
29 | uint256 j,
30 | uint256[] calldata ratios,
31 | bytes calldata data
32 | ) external view returns (uint256 reserve);
33 | }
34 |
--------------------------------------------------------------------------------
/src/interfaces/ILookupTable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.17;
4 |
5 | /**
6 | * @title PriceReserveMapping
7 | * @author DeadmanWalking
8 | * @notice In order to reasonably use `calcReserveAtRatioSwap` and `calcReserveAtRatioLiquidity` on chain,
9 | * a lookup table contract is used to decrease the amount of iterations needed to converge into an answer.
10 | */
11 | interface ILookupTable {
12 | /**
13 | * @notice the lookup table returns a series of data, given a price point:
14 | * @param highPrice the closest price to the targetPrice, where targetPrice < highPrice.
15 | * @param highPriceI reserve i such that `calcRate(reserve, i, j, data)` == highPrice.
16 | * @param highPriceJ reserve j such that `calcRate(reserve, i, j, data)` == highPrice.
17 | * @param lowPrice the closest price to the targetPrice, where targetPrice > lowPrice.
18 | * @param lowPriceI reserve i such that `calcRate(reserve, i, j, data)` == lowPrice.
19 | * @param lowPriceJ reserve j such that `calcRate(reserve, i, j, data)` == lowPrice.
20 | * @param precision the initial reserve values. Assumes the inital reserve i == reserve j
21 | */
22 | struct PriceData {
23 | uint256 highPrice;
24 | uint256 highPriceI;
25 | uint256 highPriceJ;
26 | uint256 lowPrice;
27 | uint256 lowPriceI;
28 | uint256 lowPriceJ;
29 | uint256 precision;
30 | }
31 |
32 | function getRatiosFromPriceLiquidity(uint256) external view returns (PriceData memory);
33 | function getRatiosFromPriceSwap(uint256) external view returns (PriceData memory);
34 | function getAParameter() external view returns (uint256);
35 | }
36 |
--------------------------------------------------------------------------------
/src/interfaces/IMultiFlowPumpWellFunction.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
6 |
7 | /**
8 | * @title IMultiFlowPumpWellFunction
9 | * @dev A Well Function must implement IMultiFlowPumpWellFunction to be supported by
10 | * the Multi Flow Pump.
11 | */
12 | interface IMultiFlowPumpWellFunction is IWellFunction {
13 | /**
14 | * @notice Calculates the `j` reserve such that `π_{i | i != j} (d reserves_j / d reserves_i) = π_{i | i != j}(ratios_j / ratios_i)`.
15 | * assumes that reserve_j is being swapped for other reserves in the Well.
16 | * @dev used by Beanstalk to calculate the deltaB every Season
17 | * @param reserves The reserves of the Well
18 | * @param j The index of the reserve to solve for
19 | * @param ratios The ratios of reserves to solve for
20 | * @param data Well function data provided on every call
21 | * @return reserve The resulting reserve at the jth index
22 | */
23 | function calcReserveAtRatioSwap(
24 | uint256[] calldata reserves,
25 | uint256 j,
26 | uint256[] calldata ratios,
27 | bytes calldata data
28 | ) external view returns (uint256 reserve);
29 |
30 | /**
31 | * @notice Calculates the rate at which j can be exchanged for i.
32 | * @param reserves The reserves of the Well
33 | * @param i The index of the token for which the output is being calculated
34 | * @param j The index of the token for which 1 token is being exchanged
35 | * @param data Well function data provided on every call
36 | * @return rate The rate at which j can be exchanged for i
37 | * @dev should return with 36 decimal precision
38 | */
39 | function calcRate(
40 | uint256[] calldata reserves,
41 | uint256 i,
42 | uint256 j,
43 | bytes calldata data
44 | ) external view returns (uint256 rate);
45 | }
46 |
--------------------------------------------------------------------------------
/src/interfaces/IWellErrors.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {IERC20} from "oz/token/ERC20/IERC20.sol";
6 |
7 | /**
8 | * @title IWellErrors defines all Well errors.
9 | * @dev The errors are separated into a different interface as not all Well
10 | * implementations may share the same errors.
11 | */
12 | interface IWellErrors {
13 | /**
14 | * @notice Thrown when an operation would deliver fewer tokens than `minAmountOut`.
15 | */
16 | error SlippageOut(uint256 amountOut, uint256 minAmountOut);
17 |
18 | /**
19 | * @notice Thrown when an operation would require more tokens than `maxAmountIn`.
20 | */
21 | error SlippageIn(uint256 amountIn, uint256 maxAmountIn);
22 |
23 | /**
24 | * @notice Thrown if one or more tokens used in the operation are not supported by the Well.
25 | */
26 | error InvalidTokens();
27 |
28 | /**
29 | * @notice Thrown if this operation would cause an incorrect change in Well reserves.
30 | */
31 | error InvalidReserves();
32 |
33 | /**
34 | * @notice Thrown when a Well is bored with duplicate tokens.
35 | */
36 | error DuplicateTokens(IERC20 token);
37 |
38 | /**
39 | * @notice Thrown if an operation is executed after the provided `deadline` has passed.
40 | */
41 | error Expired();
42 | }
43 |
--------------------------------------------------------------------------------
/src/interfaces/IWellFunction.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title IWellFunction
7 | * @notice Defines a relationship between token reserves and LP token supply.
8 | * @dev Well Functions can contain arbitrary logic, but should be deterministic
9 | * if expected to be used alongside a Pump. When interacing with a Well or
10 | * Well Function, always verify that the Well Function is valid.
11 | */
12 | interface IWellFunction {
13 | /**
14 | * @notice Thrown if the user inputs a `j` value is out of bounds.
15 | */
16 | error InvalidJArgument();
17 |
18 | /**
19 | * @notice Calculates the `j`th reserve given a list of `reserves` and `lpTokenSupply`.
20 | * @param reserves A list of token reserves. The jth reserve will be ignored, but a placeholder must be provided.
21 | * @param j The index of the reserve to solve for
22 | * @param lpTokenSupply The supply of LP tokens
23 | * @param data Extra Well function data provided on every call
24 | * @return reserve The resulting reserve at the jth index
25 | * @dev Should round up to ensure that Well reserves are marginally higher to enforce calcLpTokenSupply(...) >= totalSupply()
26 | */
27 | function calcReserve(
28 | uint256[] memory reserves,
29 | uint256 j,
30 | uint256 lpTokenSupply,
31 | bytes calldata data
32 | ) external view returns (uint256 reserve);
33 |
34 | /**
35 | * @notice Gets the LP token supply given a list of reserves.
36 | * @param reserves A list of token reserves
37 | * @param data Extra Well function data provided on every call
38 | * @return lpTokenSupply The resulting supply of LP tokens
39 | * @dev Should round down to ensure so that the Well Token supply is marignally lower to enforce calcLpTokenSupply(...) >= totalSupply()
40 | */
41 | function calcLpTokenSupply(
42 | uint256[] memory reserves,
43 | bytes calldata data
44 | ) external view returns (uint256 lpTokenSupply);
45 |
46 | /**
47 | * @notice Calculates the amount of each reserve token underlying a given amount of LP tokens.
48 | * @param lpTokenAmount An amount of LP tokens
49 | * @param reserves A list of token reserves
50 | * @param lpTokenSupply The current supply of LP tokens
51 | * @param data Extra Well function data provided on every call
52 | * @return underlyingAmounts The amount of each reserve token that underlies the LP tokens
53 | * @dev The constraint totalSupply() <= calcLPTokenSupply(...) must be held in the case where
54 | * `lpTokenAmount` LP tokens are burned in exchanged for `underlyingAmounts`. If the constraint
55 | * does not hold, then the Well Function is invalid.
56 | */
57 | function calcLPTokenUnderlying(
58 | uint256 lpTokenAmount,
59 | uint256[] memory reserves,
60 | uint256 lpTokenSupply,
61 | bytes calldata data
62 | ) external view returns (uint256[] memory underlyingAmounts);
63 |
64 | /**
65 | * @notice Returns the name of the Well function.
66 | */
67 | function name() external view returns (string memory);
68 |
69 | /**
70 | * @notice Returns the symbol of the Well function.
71 | */
72 | function symbol() external view returns (string memory);
73 | }
74 |
--------------------------------------------------------------------------------
/src/interfaces/pumps/ICumulativePump.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title ICumulativePump
7 | * @notice Provides an interface for Pumps which calculate time-weighted average
8 | * reserves through the use of a cumulative reserve.
9 | */
10 | interface ICumulativePump {
11 | /**
12 | * @notice Reads the current cumulative reserves from the Pump
13 | * @param well The address of the Well
14 | * @param data data specific to the Well
15 | * @return cumulativeReserves The cumulative reserves from the Pump
16 | */
17 | function readCumulativeReserves(
18 | address well,
19 | bytes memory data
20 | ) external view returns (bytes memory cumulativeReserves);
21 |
22 | /**
23 | * @notice Reads the current cumulative reserves from the Pump
24 | * @param well The address of the Well
25 | * @param startCumulativeReserves The cumulative reserves to start the TWA from
26 | * @param startTimestamp The timestamp to start the TWA from
27 | * @param data data specific to the Well
28 | * @return twaReserves The time weighted average reserves from start timestamp to now
29 | * @return cumulativeReserves The current cumulative reserves from the Pump at the current timestamp
30 | */
31 | function readTwaReserves(
32 | address well,
33 | bytes calldata startCumulativeReserves,
34 | uint256 startTimestamp,
35 | bytes memory data
36 | ) external view returns (uint256[] memory twaReserves, bytes memory cumulativeReserves);
37 | }
38 |
--------------------------------------------------------------------------------
/src/interfaces/pumps/IInstantaneousPump.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title Instantaneous Pumps provide an Oracle for instantaneous reserves.
7 | */
8 | interface IInstantaneousPump {
9 | /**
10 | * @notice Reads instantaneous reserves from the Pump
11 | * @param well The address of the Well
12 | * @return reserves The instantaneous balanecs tracked by the Pump
13 | */
14 | function readInstantaneousReserves(
15 | address well,
16 | bytes memory data
17 | ) external view returns (uint256[] memory reserves);
18 | }
19 |
--------------------------------------------------------------------------------
/src/interfaces/pumps/IMultiFlowPumpErrors.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title IMultiFlowPumpErrors defines the errors for the MultiFlowPump.
7 | * @dev The errors are separated into a different interface as not all Pump
8 | * implementations may share the same errors.
9 | */
10 | interface IMultiFlowPumpErrors {
11 | error NotInitialized();
12 |
13 | error NoTimePassed();
14 |
15 | error TooManyTokens();
16 | }
17 |
--------------------------------------------------------------------------------
/src/interfaces/pumps/IPump.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title IPump defines the interface for a Pump.
7 | *
8 | * @dev Pumps are on-chain oracles that are updated upon each interaction with a {IWell}.
9 | * When reading a Pump, always verify the Pump's functionality.
10 | */
11 | interface IPump {
12 | /**
13 | * @notice Updates the Pump with the given reserves.
14 | * @param reserves The previous reserves of the tokens in the Well.
15 | * @param data data specific to the Well
16 | * @dev Pumps are updated every time a user swaps, adds liquidity, or
17 | * removes liquidity from a Well.
18 | */
19 | function update(uint256[] calldata reserves, bytes calldata data) external;
20 | }
21 |
--------------------------------------------------------------------------------
/src/libraries/LibBytes16.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title LibBytes16
7 | * @author Brendan
8 | * @notice Contains byte operations used during storage reads & writes for Pumps.
9 | *
10 | * {LibBytes16} tightly packs an array of `bytes16` values into `n / 2` storage
11 | * slots, where `n` is number of items to pack.
12 | */
13 | library LibBytes16 {
14 | /**
15 | * @dev Store packed bytes16 `reserves` starting at storage position `slot`.
16 | */
17 | function storeBytes16(bytes32 slot, bytes16[] memory reserves) internal {
18 | // Shortcut: two reserves can be packed into one slot without a loop
19 | if (reserves.length == 2) {
20 | assembly {
21 | sstore(slot, add(mload(add(reserves, 32)), shr(128, mload(add(reserves, 64)))))
22 | }
23 | } else {
24 | uint256 maxI = reserves.length / 2; // number of fully-packed slots
25 | uint256 iByte; // byte offset of the current reserve
26 | for (uint256 i; i < maxI; ++i) {
27 | iByte = i * 64;
28 | assembly {
29 | sstore(
30 | add(slot, i),
31 | add(mload(add(reserves, add(iByte, 32))), shr(128, mload(add(reserves, add(iByte, 64)))))
32 | )
33 | }
34 | }
35 | // If there is an odd number of reserves, create a slot with the last reserve
36 | // Since `i < maxI` above, the next byte offset `maxI * 64`
37 | // Equivalent to "reserves.length % 2 == 1", but cheaper.
38 | if (reserves.length & 1 == 1) {
39 | iByte = maxI * 64;
40 | assembly {
41 | sstore(
42 | add(slot, maxI),
43 | add(mload(add(reserves, add(iByte, 32))), shr(128, shl(128, sload(add(slot, maxI)))))
44 | )
45 | }
46 | }
47 | }
48 | }
49 |
50 | /**
51 | * @dev Read `n` packed uint128 reserves at storage position `slot`.
52 | */
53 | function readBytes16(bytes32 slot, uint256 n) internal view returns (bytes16[] memory reserves) {
54 | // Initialize array with length `n`, fill it in via assembly
55 | reserves = new bytes16[](n);
56 |
57 | // Shortcut: two reserves can be quickly unpacked from one slot
58 | if (n == 2) {
59 | assembly {
60 | mstore(add(reserves, 32), sload(slot))
61 | mstore(add(reserves, 64), shl(128, sload(slot)))
62 | }
63 | return reserves;
64 | }
65 |
66 | uint256 iByte;
67 | for (uint256 i = 1; i <= n; ++i) {
68 | // `iByte` is the byte position for the current slot:
69 | // i 1 2 3 4 5 6
70 | // iByte 0 0 1 1 2 2
71 | iByte = (i - 1) / 2;
72 | // Equivalent to "i % 2 == 1" but cheaper.
73 | if (i & 1 == 1) {
74 | assembly {
75 | mstore(
76 | // store at index i * 32; i = 0 is skipped by loop
77 | add(reserves, mul(i, 32)),
78 | sload(add(slot, iByte))
79 | )
80 | }
81 | } else {
82 | assembly {
83 | mstore(add(reserves, mul(i, 32)), shl(128, sload(add(slot, iByte))))
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/libraries/LibContractInfo.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | /**
6 | * @title LibContractInfo
7 | * @notice Contains logic to call functions that return information about a given contract.
8 | */
9 | library LibContractInfo {
10 | /**
11 | * @notice gets the symbol of a given contract
12 | * @param _contract The contract to get the symbol of
13 | * @return symbol The symbol of the contract
14 | * @dev if the contract does not have a symbol function, the first 4 bytes of the address are returned
15 | */
16 | function getSymbol(address _contract) internal view returns (string memory symbol) {
17 | (bool success, bytes memory data) = _contract.staticcall(abi.encodeWithSignature("symbol()"));
18 | symbol = new string(4);
19 | if (success) {
20 | symbol = abi.decode(data, (string));
21 | } else {
22 | assembly {
23 | mstore(add(symbol, 0x20), shl(224, shr(128, _contract)))
24 | }
25 | }
26 | }
27 |
28 | /**
29 | * @notice gets the name of a given contract
30 | * @param _contract The contract to get the name of
31 | * @return name The name of the contract
32 | * @dev if the contract does not have a name function, the first 8 bytes of the address are returned
33 | */
34 | function getName(address _contract) internal view returns (string memory name) {
35 | (bool success, bytes memory data) = _contract.staticcall(abi.encodeWithSignature("name()"));
36 | name = new string(8);
37 | if (success) {
38 | name = abi.decode(data, (string));
39 | } else {
40 | assembly {
41 | mstore(add(name, 0x20), shl(224, shr(128, _contract)))
42 | }
43 | }
44 | }
45 |
46 | /**
47 | * @notice gets the decimals of a given contract
48 | * @param _contract The contract to get the decimals of
49 | * @return decimals The decimals of the contract
50 | * @dev if the contract does not have a decimals function, 18 is returned
51 | */
52 | function getDecimals(address _contract) internal view returns (uint8 decimals) {
53 | (bool success, bytes memory data) = _contract.staticcall(abi.encodeWithSignature("decimals()"));
54 | decimals = success ? abi.decode(data, (uint8)) : 18; // default to 18 decimals
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/libraries/LibWellConstructor.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // forgefmt: disable-start
3 |
4 | pragma solidity ^0.8.20;
5 |
6 | import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
7 | import {Call, IERC20} from "src/Well.sol";
8 |
9 | library LibWellConstructor {
10 | /**
11 | * @notice Encode the Well's immutable data and init data.
12 | */
13 | function encodeWellDeploymentData(
14 | address _aquifer,
15 | IERC20[] memory _tokens,
16 | Call memory _wellFunction,
17 | Call[] memory _pumps
18 | ) internal view returns (bytes memory immutableData, bytes memory initData) {
19 | immutableData = encodeWellImmutableData(_aquifer, _tokens, _wellFunction, _pumps);
20 | initData = encodeWellInitFunctionCall(_tokens, _wellFunction);
21 | }
22 |
23 | /**
24 | * @notice Encode the Well's immutable data.
25 | * @param _aquifer The address of the Aquifer which will deploy this Well.
26 | * @param _tokens A list of ERC20 tokens supported by the Well.
27 | * @param _wellFunction A single Call struct representing a call to the Well Function.
28 | * @param _pumps An array of Call structs representings calls to Pumps.
29 | * @dev `immutableData` is tightly packed, however since `_tokens` itself is
30 | * an array, each address in the array will be padded up to 32 bytes.
31 | *
32 | * Arbitrary-length bytes are applied to the end of the encoded bytes array
33 | * for easy reading of statically-sized data.
34 | *
35 | */
36 | function encodeWellImmutableData(
37 | address _aquifer,
38 | IERC20[] memory _tokens,
39 | Call memory _wellFunction,
40 | Call[] memory _pumps
41 | ) internal pure returns (bytes memory immutableData) {
42 |
43 | immutableData = abi.encodePacked(
44 | _aquifer, // aquifer address
45 | _tokens.length, // number of tokens
46 | _wellFunction.target, // well function address
47 | _wellFunction.data.length, // well function data length
48 | _pumps.length, // number of pumps
49 | _tokens, // tokens array
50 | _wellFunction.data // well function data (bytes)
51 | );
52 | for (uint256 i; i < _pumps.length; ++i) {
53 | immutableData = abi.encodePacked(
54 | immutableData, // previously packed pumps
55 | _pumps[i].target, // pump address
56 | _pumps[i].data.length, // pump data length
57 | _pumps[i].data // pump data (bytes)
58 | );
59 | }
60 | }
61 |
62 | /**
63 | * @notice Encode a call to the {Well.init} function to set a standardized ERC20 name and symbol.
64 | */
65 | function encodeWellInitFunctionCall(
66 | IERC20[] memory _tokens,
67 | Call memory _wellFunction
68 | ) public view returns (bytes memory initFunctionCall) {
69 | string memory name = LibContractInfo.getSymbol(address(_tokens[0]));
70 | string memory symbol = name;
71 | for (uint256 i = 1; i < _tokens.length; ++i) {
72 | name = string.concat(name, ":", LibContractInfo.getSymbol(address(_tokens[i])));
73 | symbol = string.concat(symbol, LibContractInfo.getSymbol(address(_tokens[i])));
74 | }
75 | name = string.concat(name, " ", LibContractInfo.getName(_wellFunction.target), " Well");
76 | symbol = string.concat(symbol, LibContractInfo.getSymbol(_wellFunction.target), "w");
77 |
78 | // See {Well.init}.
79 | initFunctionCall = abi.encodeWithSignature("init(string,string)", name, symbol);
80 | }
81 |
82 | /**
83 | * @notice Encode a Call struct representing an arbitrary call to `target` with additional data `data`.
84 | */
85 | function encodeCall(address target, bytes memory data) public pure returns (Call memory) {
86 | return Call(target, data);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/libraries/LibWellUpgradeableConstructor.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // forgefmt: disable-start
3 |
4 | pragma solidity ^0.8.20;
5 |
6 | import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
7 | import {Call, IERC20} from "src/Well.sol";
8 | import {WellUpgradeable} from "src/WellUpgradeable.sol";
9 |
10 | library LibWellUpgradeableConstructor {
11 |
12 | /**
13 | * @notice Encode the Well's immutable data.
14 | */
15 | function encodeWellDeploymentData(
16 | address _aquifer,
17 | IERC20[] memory _tokens,
18 | Call memory _wellFunction,
19 | Call[] memory _pumps
20 | ) internal pure returns (bytes memory immutableData, bytes memory initData) {
21 | immutableData = encodeWellImmutableData(_aquifer, _tokens, _wellFunction, _pumps);
22 | initData = abi.encodeWithSelector(WellUpgradeable.initNoWellToken.selector);
23 | }
24 |
25 | /**
26 | * @notice Encode the Well's immutable data.
27 | * @param _aquifer The address of the Aquifer which will deploy this Well.
28 | * @param _tokens A list of ERC20 tokens supported by the Well.
29 | * @param _wellFunction A single Call struct representing a call to the Well Function.
30 | * @param _pumps An array of Call structs representings calls to Pumps.
31 | * @dev `immutableData` is tightly packed, however since `_tokens` itself is
32 | * an array, each address in the array will be padded up to 32 bytes.
33 | *
34 | * Arbitrary-length bytes are applied to the end of the encoded bytes array
35 | * for easy reading of statically-sized data.
36 | *
37 | */
38 | function encodeWellImmutableData(
39 | address _aquifer,
40 | IERC20[] memory _tokens,
41 | Call memory _wellFunction,
42 | Call[] memory _pumps
43 | ) internal pure returns (bytes memory immutableData) {
44 |
45 | immutableData = abi.encodePacked(
46 | _aquifer, // aquifer address
47 | _tokens.length, // number of tokens
48 | _wellFunction.target, // well function address
49 | _wellFunction.data.length, // well function data length
50 | _pumps.length, // number of pumps
51 | _tokens, // tokens array
52 | _wellFunction.data // well function data (bytes)
53 | );
54 | for (uint256 i; i < _pumps.length; ++i) {
55 | immutableData = abi.encodePacked(
56 | immutableData, // previously packed pumps
57 | _pumps[i].target, // pump address
58 | _pumps[i].data.length, // pump data length
59 | _pumps[i].data // pump data (bytes)
60 | );
61 | }
62 | }
63 |
64 | function encodeWellInitFunctionCall(
65 | IERC20[] memory _tokens,
66 | Call memory _wellFunction
67 | ) public view returns (bytes memory initFunctionCall) {
68 | string memory name = LibContractInfo.getSymbol(address(_tokens[0]));
69 | string memory symbol = name;
70 | for (uint256 i = 1; i < _tokens.length; ++i) {
71 | name = string.concat(name, ":", LibContractInfo.getSymbol(address(_tokens[i])));
72 | symbol = string.concat(symbol, LibContractInfo.getSymbol(address(_tokens[i])));
73 | }
74 | name = string.concat(name, " ", LibContractInfo.getName(_wellFunction.target), " Upgradeable Well");
75 | symbol = string.concat(symbol, LibContractInfo.getSymbol(_wellFunction.target), "uw");
76 |
77 | // See {Well.init}.
78 | initFunctionCall = abi.encodeWithSelector(WellUpgradeable.init.selector, name, symbol);
79 | }
80 |
81 | /**
82 | * @notice Encode a Call struct representing an arbitrary call to `target` with additional data `data`.
83 | */
84 | function encodeCall(address target, bytes memory data) public pure returns (Call memory) {
85 | return Call(target, data);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/utils/Clone.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BSD
2 | pragma solidity ^0.8.20;
3 |
4 | /// @title Clone
5 | /// @author zefram.eth, Saw-mon & Natalie
6 | /// @notice Provides helper functions for reading immutable args from calldata
7 | contract Clone {
8 |
9 | uint256 internal constant ONE_WORD = 0x20;
10 |
11 | /// @notice Reads an immutable arg with type address
12 | /// @param argOffset The offset of the arg in the packed data
13 | /// @return arg The arg value
14 | function _getArgAddress(uint256 argOffset)
15 | internal
16 | pure
17 | returns (address arg)
18 | {
19 | uint256 offset = _getImmutableArgsOffset();
20 | // solhint-disable-next-line no-inline-assembly
21 | assembly {
22 | arg := shr(0x60, calldataload(add(offset, argOffset)))
23 | }
24 | }
25 |
26 | /// @notice Reads an immutable arg with type uint256
27 | /// @param argOffset The offset of the arg in the packed data
28 | /// @return arg The arg value
29 | function _getArgUint256(uint256 argOffset)
30 | internal
31 | pure
32 | returns (uint256 arg)
33 | {
34 | uint256 offset = _getImmutableArgsOffset();
35 | // solhint-disable-next-line no-inline-assembly
36 | assembly {
37 | arg := calldataload(add(offset, argOffset))
38 | }
39 | }
40 |
41 | /// @notice Reads a uint256 array stored in the immutable args.
42 | /// @param argOffset The offset of the arg in the packed data
43 | /// @param arrLen Number of elements in the array
44 | /// @return arr The array
45 | function _getArgUint256Array(uint256 argOffset, uint256 arrLen)
46 | internal
47 | pure
48 | returns (uint256[] memory arr)
49 | {
50 | uint256 offset = _getImmutableArgsOffset() + argOffset;
51 | arr = new uint256[](arrLen);
52 |
53 | // solhint-disable-next-line no-inline-assembly
54 | assembly {
55 | calldatacopy(
56 | add(arr, ONE_WORD),
57 | offset,
58 | shl(5, arrLen)
59 | )
60 | }
61 | }
62 |
63 | /// @notice Reads an immutable arg with type uint64
64 | /// @param argOffset The offset of the arg in the packed data
65 | /// @return arg The arg value
66 | function _getArgUint64(uint256 argOffset)
67 | internal
68 | pure
69 | returns (uint64 arg)
70 | {
71 | uint256 offset = _getImmutableArgsOffset();
72 | // solhint-disable-next-line no-inline-assembly
73 | assembly {
74 | arg := shr(0xc0, calldataload(add(offset, argOffset)))
75 | }
76 | }
77 |
78 | /// @notice Reads an immutable arg with type uint8
79 | /// @param argOffset The offset of the arg in the packed data
80 | /// @return arg The arg value
81 | function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
82 | uint256 offset = _getImmutableArgsOffset();
83 | // solhint-disable-next-line no-inline-assembly
84 | assembly {
85 | arg := shr(0xf8, calldataload(add(offset, argOffset)))
86 | }
87 | }
88 |
89 | /// @return offset The offset of the packed immutable args in calldata
90 | function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
91 | // solhint-disable-next-line no-inline-assembly
92 | assembly {
93 | offset := sub(
94 | calldatasize(),
95 | shr(0xf0, calldataload(sub(calldatasize(), 2)))
96 | )
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/src/utils/ClonePlus.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BSD
2 | pragma solidity ^0.8.20;
3 |
4 | import {Clone} from "./Clone.sol";
5 | import {IERC20} from "oz/token/ERC20/utils/SafeERC20.sol";
6 |
7 | /// @title ClonePlus
8 | /// @notice Extends Clone with additional helper functions
9 | contract ClonePlus is Clone {
10 | /// @notice Reads a IERC20 array stored in the immutable args.
11 | /// @param argOffset The offset of the arg in the packed data
12 | /// @param arrLen Number of elements in the array
13 | /// @return arr The array
14 | function _getArgIERC20Array(uint256 argOffset, uint256 arrLen) internal pure returns (IERC20[] memory arr) {
15 | uint256 offset = _getImmutableArgsOffset() + argOffset;
16 | arr = new IERC20[](arrLen);
17 |
18 | // solhint-disable-next-line no-inline-assembly
19 | assembly {
20 | calldatacopy(add(arr, ONE_WORD), offset, shl(5, arrLen))
21 | }
22 | }
23 |
24 | /// @notice Reads a bytes data stored in the immutable args.
25 | /// @param argOffset The offset of the arg in the packed data
26 | /// @param bytesLen Number of bytes in the data
27 | /// @return data the bytes data
28 | function _getArgBytes(uint256 argOffset, uint256 bytesLen) internal pure returns (bytes memory data) {
29 | if (bytesLen == 0) return data;
30 | uint256 offset = _getImmutableArgsOffset() + argOffset;
31 | data = new bytes(bytesLen);
32 |
33 | // solhint-disable-next-line no-inline-assembly
34 | assembly {
35 | calldatacopy(add(data, ONE_WORD), offset, bytesLen)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/Stable2/Well.Stable2.Skim.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.17;
3 |
4 | import {TestHelper, Balances} from "test/TestHelper.sol";
5 |
6 | contract WellStable2SkimTest is TestHelper {
7 | function setUp() public {
8 | setupStable2Well();
9 | }
10 |
11 | function test_initialized() public {
12 | // Well should have liquidity
13 | Balances memory wellBalance = getBalances(address(well), well);
14 | assertEq(wellBalance.tokens[0], 1000e18);
15 | assertEq(wellBalance.tokens[1], 1000e18);
16 | }
17 |
18 | function testFuzz_skim(uint256[2] calldata amounts) public prank(user) {
19 | vm.assume(amounts[0] <= 800e18);
20 | vm.assume(amounts[1] <= 800e18);
21 |
22 | // Transfer from Test contract to Well
23 | tokens[0].transfer(address(well), amounts[0]);
24 | tokens[1].transfer(address(well), amounts[1]);
25 |
26 | Balances memory wellBalanceBeforeSkim = getBalances(address(well), well);
27 | // Verify that the Well has received the tokens
28 | assertEq(wellBalanceBeforeSkim.tokens[0], 1000e18 + amounts[0]);
29 | assertEq(wellBalanceBeforeSkim.tokens[1], 1000e18 + amounts[1]);
30 |
31 | // Get a user with a fresh address (no ERC20 tokens)
32 | address _user = users.getNextUserAddress();
33 | uint256[] memory reserves = new uint256[](2);
34 |
35 | // Verify that the user has no tokens
36 | Balances memory userBalanceBeforeSkim = getBalances(_user, well);
37 | reserves[0] = userBalanceBeforeSkim.tokens[0];
38 | reserves[1] = userBalanceBeforeSkim.tokens[1];
39 | assertEq(reserves[0], 0);
40 | assertEq(reserves[1], 0);
41 |
42 | well.skim(_user);
43 |
44 | Balances memory userBalanceAfterSkim = getBalances(_user, well);
45 | Balances memory wellBalanceAfterSkim = getBalances(address(well), well);
46 |
47 | // Since only 1000e18 of each token was added as liquidity, the Well's reserve
48 | // should be reset back to this.
49 | assertEq(wellBalanceAfterSkim.tokens[0], 1000e18);
50 | assertEq(wellBalanceAfterSkim.tokens[1], 1000e18);
51 |
52 | // The difference has been sent to _user.
53 | assertEq(userBalanceAfterSkim.tokens[0], amounts[0]);
54 | assertEq(userBalanceAfterSkim.tokens[1], amounts[1]);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/test/SwapHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, IERC20, Balances, Call, MockToken, Well, console, Snapshot} from "test/TestHelper.sol";
5 | import {MockFunctionBad} from "mocks/functions/MockFunctionBad.sol";
6 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
7 |
8 | /**
9 | * @dev Stores the expected change in balance for User & Well throughout a Swap.
10 | *
11 | * Gives upstream tests a way to specify expected changes based on the presence
12 | * of transfer fees. When a token involved in a swap incurs a fee on transfer,
13 | * one or both of the following is true:
14 | *
15 | * `wellReceives` < `userSpends`
16 | * `userReceives` < `wellSends`
17 | */
18 | struct SwapAction {
19 | uint256 i; // input token index
20 | uint256 j; // output token index
21 | uint256 userSpends;
22 | uint256 wellReceives;
23 | uint256 wellSends;
24 | uint256 userReceives;
25 | }
26 |
27 | /**
28 | * @dev Provides common assertions when testing Swaps.
29 | *
30 | * NOTE: Uses globals inherited from TestHelper.
31 | */
32 | contract SwapHelper is TestHelper {
33 | event AddLiquidity(uint256[] amounts, uint256 lpAmountOut, address recipient);
34 | event Swap(IERC20 fromToken, IERC20 toToken, uint256 amountIn, uint256 amountOut, address recipient);
35 |
36 | /// @dev Default Swap behavior assuming zero fee on transfer
37 | function beforeSwapFrom(
38 | uint256 i,
39 | uint256 j,
40 | uint256 amountIn
41 | ) internal returns (Snapshot memory, SwapAction memory) {
42 | SwapAction memory act;
43 |
44 | act.i = i;
45 | act.j = j;
46 | act.userSpends = amountIn;
47 | act.wellReceives = amountIn;
48 | act.wellSends = well.getSwapOut(tokens[i], tokens[j], amountIn);
49 | act.userReceives = act.wellSends;
50 |
51 | return beforeSwapFrom(act);
52 | }
53 |
54 | function beforeSwapFrom(SwapAction memory act) internal returns (Snapshot memory, SwapAction memory) {
55 | Snapshot memory bef = _newSnapshot();
56 |
57 | vm.expectEmit(true, true, true, true, address(well));
58 | emit Swap(tokens[act.i], tokens[act.j], act.wellReceives, act.wellSends, user);
59 |
60 | return (bef, act);
61 | }
62 |
63 | function afterSwapFrom(Snapshot memory bef, SwapAction memory act) public {
64 | Snapshot memory aft = _newSnapshot();
65 | uint256 i = act.i;
66 | uint256 j = act.j;
67 |
68 | // Check balances accounting
69 | assertEq(bef.user.tokens[i] - aft.user.tokens[i], act.userSpends, "Incorrect token[i] User balance");
70 | assertEq(aft.well.tokens[i], bef.well.tokens[i] + act.wellReceives, "Incorrect token[i] Well balance");
71 | assertEq(aft.well.tokens[j], bef.well.tokens[j] - act.wellSends, "Incorrect token[j] Well balance");
72 | assertEq(aft.user.tokens[j] - bef.user.tokens[j], act.userReceives, "Incorrect token[j] User balance");
73 |
74 | // Check that reserves were updated
75 | uint256[] memory reserves = well.getReserves();
76 | assertEq(aft.reserves[i], bef.reserves[i] + act.wellReceives, "Incorrect token[i] Well reserve");
77 | assertEq(aft.reserves[j], bef.reserves[i] - act.wellSends, "Incorrect token[i] Well reserve");
78 |
79 | // Check that no other balances or reserves were changed
80 | for (uint256 k = 0; k < reserves.length; ++k) {
81 | if (k == i || k == j) continue;
82 | assertEq(aft.well.tokens[k], bef.well.tokens[k], "token[k] Well balance changed unexpectedly");
83 | assertEq(aft.reserves[k], bef.reserves[k], "token[k] Well reserve changed unexpectedly");
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/test/Well.DuplicateTokens.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {console, Aquifer, IERC20, TestHelper} from "test/TestHelper.sol";
5 | import {LibWellConstructor} from "src/libraries/LibWellConstructor.sol";
6 | import {IWell} from "src/interfaces/IWell.sol";
7 | import {IAquifer} from "src/interfaces/IAquifer.sol";
8 |
9 | contract WellDuplicateTokens is TestHelper {
10 | function test_fail_on_duplicate_tokens() public {
11 | tokens = new IERC20[](2);
12 | tokens[0] = deployMockToken(0);
13 | tokens[1] = tokens[0];
14 | initUser();
15 | wellImplementation = deployWellImplementation();
16 | wellFunction = deployWellFunction();
17 | aquifer = new Aquifer();
18 | (bytes memory immutableData, bytes memory initData) =
19 | LibWellConstructor.encodeWellDeploymentData(address(aquifer), tokens, wellFunction, pumps);
20 |
21 | vm.expectRevert(abi.encodeWithSelector(IAquifer.InitFailed.selector, ""));
22 | Aquifer(aquifer).boreWell(wellImplementation, immutableData, initData, bytes32(0));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/Well.FeeOnTransfer.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {TestHelper, IERC20, Call, Balances, MockTokenFeeOnTransfer} from "test/TestHelper.sol";
6 | import {ConstantProduct2} from "src/functions/ConstantProduct2.sol";
7 | import {IWell} from "src/interfaces/IWell.sol";
8 | import {IWellErrors} from "src/interfaces/IWellErrors.sol";
9 |
10 | /**
11 | * @dev Functions that increase the Well's reserves without accounting for fees
12 | * (swapFrom, swapTo, addLiquidity) should revert if there actually are fees.
13 | */
14 | contract WellFeeOnTransferTest is TestHelper {
15 | event AddLiquidity(uint256[] tokenAmountsIn, uint256 lpAmountOut, address recipient);
16 | event RemoveLiquidity(uint256 lpAmountIn, uint256[] tokenAmountsOut, address recipient);
17 |
18 | function setUp() public {
19 | setupWellWithFeeOnTransfer(2);
20 | MockTokenFeeOnTransfer(address(tokens[0])).setFee(1e16);
21 | }
22 |
23 | function test_swapTo_feeOnTransfer() public prank(user) {
24 | uint256 minAmountOut = 500 * 1e18;
25 | uint256 amountIn = 1000 * 1e18;
26 |
27 | vm.expectRevert(IWellErrors.InvalidReserves.selector);
28 | well.swapTo(tokens[0], tokens[1], amountIn, minAmountOut, user, type(uint256).max);
29 | }
30 |
31 | function test_swapFrom_feeOnTransfer() public prank(user) {
32 | uint256 minAmountOut = 500 * 1e18;
33 | uint256 amountIn = 1000 * 1e18;
34 |
35 | vm.expectRevert(IWellErrors.InvalidReserves.selector);
36 | well.swapFrom(tokens[0], tokens[1], amountIn, minAmountOut, user, type(uint256).max);
37 | }
38 |
39 | function test_addLiquidity_feeOnTransfer() public prank(user) {
40 | uint256[] memory amounts = new uint256[](tokens.length);
41 |
42 | for (uint256 i; i < tokens.length; i++) {
43 | amounts[i] = 1000 * 1e18;
44 | }
45 | uint256 lpAmountOut = 1000 * 1e24;
46 |
47 | vm.expectRevert(IWellErrors.InvalidReserves.selector);
48 | well.addLiquidity(amounts, lpAmountOut, user, type(uint256).max);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/Well.ReadOnlyReentrancy.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {IERC20, Well, Strings} from "test/TestHelper.sol";
5 | import {SwapHelper} from "test/SwapHelper.sol";
6 | import {ReentrantMockToken} from "mocks/tokens/ReentrantMockToken.sol";
7 | import {IWell} from "src/interfaces/IWell.sol";
8 |
9 | contract WellReadOnlyReentrancyTest is SwapHelper {
10 | using Strings for uint256;
11 |
12 | ReentrantMockToken[] _tokens;
13 | uint256[] amounts;
14 |
15 | function setUp() public {
16 | uint256 _numberOfTokens = 2;
17 | _tokens = new ReentrantMockToken[](_numberOfTokens);
18 | tokens = new IERC20[](_numberOfTokens);
19 | for (uint256 i; i < _numberOfTokens; i++) {
20 | _tokens[i] = new ReentrantMockToken(
21 | string.concat("Token ", i.toString()), // name
22 | string.concat("TOKEN", i.toString()), // symbol
23 | 18 // decimals
24 | );
25 | tokens[i] = IERC20(_tokens[i]);
26 | }
27 | setupWell(deployWellFunction(), deployPumps(1), tokens);
28 |
29 | amounts = new uint256[](tokens.length);
30 | for (uint256 i; i < tokens.length; i++) {
31 | amounts[i] = 1000 * 1e18;
32 | }
33 | }
34 |
35 | function _checkReadOnlyReentrancy() internal {
36 | vm.expectRevert("ReentrancyGuard: reentrant call");
37 | well.addLiquidity(amounts, 0, user, type(uint256).max);
38 | }
39 |
40 | function test_readOnlyReentrancy_getReserves() public prank(user) {
41 | _tokens[0].setCall(address(well), abi.encode(IWell.getReserves.selector));
42 | _checkReadOnlyReentrancy();
43 | }
44 |
45 | function test_readOnlyReentrancy_getSwapOut() public prank(user) {
46 | _tokens[0].setCall(
47 | address(well), abi.encodeWithSelector(IWell.getSwapOut.selector, tokens[0], tokens[1], amounts[0])
48 | );
49 | _checkReadOnlyReentrancy();
50 | }
51 |
52 | function test_readOnlyReentrancy_getSwapIn() public prank(user) {
53 | _tokens[0].setCall(
54 | address(well), abi.encodeWithSelector(IWell.getSwapIn.selector, tokens[0], tokens[1], amounts[0])
55 | );
56 | _checkReadOnlyReentrancy();
57 | }
58 |
59 | function test_readOnlyReentrancy_getAddLiquidityOut() public prank(user) {
60 | _tokens[0].setCall(address(well), abi.encodeWithSelector(IWell.getAddLiquidityOut.selector, amounts));
61 | _checkReadOnlyReentrancy();
62 | }
63 |
64 | function test_readOnlyReentrancy_getRemoveLiquidityOut() public prank(user) {
65 | _tokens[0].setCall(address(well), abi.encodeWithSelector(IWell.getRemoveLiquidityOut.selector, amounts[0]));
66 | _checkReadOnlyReentrancy();
67 | }
68 |
69 | function test_readOnlyReentrancy_getRemoveLiquidityOneTokenOut() public prank(user) {
70 | _tokens[0].setCall(
71 | address(well), abi.encodeWithSelector(IWell.getRemoveLiquidityOneTokenOut.selector, amounts[0], tokens[0])
72 | );
73 | _checkReadOnlyReentrancy();
74 | }
75 |
76 | function test_readOnlyReentrancy_getRemoveLiquidityImbalancedIn() public prank(user) {
77 | _tokens[0].setCall(
78 | address(well), abi.encodeWithSelector(IWell.getRemoveLiquidityImbalancedIn.selector, amounts)
79 | );
80 | _checkReadOnlyReentrancy();
81 | }
82 |
83 | function test_readOnlyReentrancy_getShiftOut() public prank(user) {
84 | _tokens[0].setCall(address(well), abi.encodeWithSelector(IWell.getShiftOut.selector, tokens[0]));
85 | _checkReadOnlyReentrancy();
86 | }
87 |
88 | function test_readOnlyReentrancy_getSyncOut() public prank(user) {
89 | _tokens[0].setCall(address(well), abi.encode(IWell.getSyncOut.selector));
90 | _checkReadOnlyReentrancy();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/test/Well.Skim.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, Balances} from "test/TestHelper.sol";
5 |
6 | contract WellSkimStableSwapTest is TestHelper {
7 | function setUp() public {
8 | setupWell(2);
9 | }
10 |
11 | function test_initialized() public {
12 | // Well should have liquidity
13 | Balances memory wellBalance = getBalances(address(well), well);
14 | assertEq(wellBalance.tokens[0], 1000e18);
15 | assertEq(wellBalance.tokens[1], 1000e18);
16 | }
17 |
18 | function testFuzz_skim(uint256[2] calldata amounts) public prank(user) {
19 | vm.assume(amounts[0] <= 800e18);
20 | vm.assume(amounts[1] <= 800e18);
21 |
22 | // Transfer from Test contract to Well
23 | tokens[0].transfer(address(well), amounts[0]);
24 | tokens[1].transfer(address(well), amounts[1]);
25 |
26 | Balances memory wellBalanceBeforeSkim = getBalances(address(well), well);
27 | // Verify that the Well has received the tokens
28 | assertEq(wellBalanceBeforeSkim.tokens[0], 1000e18 + amounts[0]);
29 | assertEq(wellBalanceBeforeSkim.tokens[1], 1000e18 + amounts[1]);
30 |
31 | // Get a user with a fresh address (no ERC20 tokens)
32 | address _user = users.getNextUserAddress();
33 | uint256[] memory reserves = new uint256[](2);
34 |
35 | // Verify that the user has no tokens
36 | Balances memory userBalanceBeforeSkim = getBalances(_user, well);
37 | reserves[0] = userBalanceBeforeSkim.tokens[0];
38 | reserves[1] = userBalanceBeforeSkim.tokens[1];
39 | assertEq(reserves[0], 0);
40 | assertEq(reserves[1], 0);
41 |
42 | well.skim(_user);
43 |
44 | Balances memory userBalanceAfterSkim = getBalances(_user, well);
45 | Balances memory wellBalanceAfterSkim = getBalances(address(well), well);
46 |
47 | // Since only 1000e18 of each token was added as liquidity, the Well's reserve
48 | // should be reset back to this.
49 | assertEq(wellBalanceAfterSkim.tokens[0], 1000e18);
50 | assertEq(wellBalanceAfterSkim.tokens[1], 1000e18);
51 |
52 | // The difference has been sent to _user.
53 | assertEq(userBalanceAfterSkim.tokens[0], amounts[0]);
54 | assertEq(userBalanceAfterSkim.tokens[1], amounts[1]);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/test/Well.SucceedOnPumpFailure.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, Balances, Call} from "test/TestHelper.sol";
5 | import {MockFailPump} from "mocks/pumps/MockFailPump.sol";
6 |
7 | contract WellSucceedOnPumpFailure is TestHelper {
8 | MockFailPump _pump;
9 |
10 | function setUp() public {
11 | _pump = new MockFailPump();
12 | }
13 |
14 | // Check that the pump function fails as expected
15 | function test_fail() public {
16 | vm.expectRevert();
17 | uint256[] memory amounts = new uint256[](2);
18 | _pump.update(amounts, new bytes(0));
19 | }
20 |
21 | // Check that the Well doesn't fail if one of one fail
22 | function test_addLiquidty_onePump() public {
23 | Call[] memory pumps = new Call[](1);
24 | pumps[0].target = address(_pump);
25 | setupWell(2, pumps);
26 | // Check that the add liquidity call succeeded during well setup.
27 | assertGt(well.totalSupply(), 0);
28 | }
29 |
30 | // Check that the Well doesn't fail if m of n pumps fail
31 | function test_addLiquidty_twoPumps() public {
32 | Call[] memory pumps = new Call[](2);
33 | pumps[0].target = address(_pump);
34 | pumps[1].target = address(new MockFailPump());
35 | setupWell(2, pumps);
36 | // Check that the add liquidity call succeeded during well setup.
37 | assertGt(well.totalSupply(), 0);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/Well.SwapFrom.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {IERC20, Balances, Call, MockToken, Well, console} from "test/TestHelper.sol";
5 | import {SwapHelper, SwapAction, Snapshot} from "test/SwapHelper.sol";
6 | import {MockFunctionBad} from "mocks/functions/MockFunctionBad.sol";
7 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
8 | import {IWell} from "src/interfaces/IWell.sol";
9 | import {IWellErrors} from "src/interfaces/IWellErrors.sol";
10 |
11 | contract WellSwapFromTest is SwapHelper {
12 | function setUp() public {
13 | setupWell(2);
14 | }
15 |
16 | function test_getSwapOut() public {
17 | uint256 amountIn = 1000 * 1e18;
18 | uint256 amountOut = well.getSwapOut(tokens[0], tokens[1], amountIn);
19 |
20 | assertEq(amountOut, 500 * 1e18);
21 | }
22 |
23 | function testFuzz_getSwapOut_revertIf_insufficientWellBalance(uint256 amountIn, uint256 i) public prank(user) {
24 | // Swap token `i` -> all other tokens
25 | i = bound(i, 0, tokens.length);
26 |
27 | // Find an input amount that produces an output amount higher than what the Well has.
28 | // When the Well is deployed it has zero reserves, so any nonzero value should revert.
29 | amountIn = bound(amountIn, 1, type(uint128).max);
30 |
31 | // Deploy a new Well with a poorly engineered pricing function.
32 | // Its `getBalance` function can return an amount greater than the Well holds.
33 | IWellFunction badFunction = new MockFunctionBad();
34 | Well badWell = encodeAndBoreWell(
35 | address(aquifer), wellImplementation, tokens, Call(address(badFunction), ""), pumps, bytes32(0)
36 | );
37 |
38 | // Check assumption that reserves are empty
39 | Balances memory wellBalances = getBalances(address(badWell), badWell);
40 | assertEq(wellBalances.tokens[0], 0, "bad assumption: wellBalances.tokens[0] != 0");
41 | assertEq(wellBalances.tokens[1], 0, "bad assumption: wellBalances.tokens[1] != 0");
42 |
43 | for (uint256 j; j < tokens.length; ++j) {
44 | if (j != i) {
45 | vm.expectRevert(); // underflow
46 | badWell.getSwapOut(tokens[i], tokens[j], amountIn);
47 | }
48 | }
49 | }
50 |
51 | /// @dev Swaps should always revert if `fromToken` = `toToken`.
52 | function test_swapFrom_revertIf_sameToken() public prank(user) {
53 | vm.expectRevert(IWellErrors.InvalidTokens.selector);
54 | well.swapFrom(tokens[0], tokens[0], 100 * 1e18, 0, user, type(uint256).max);
55 | }
56 |
57 | /// @dev Slippage revert if minAmountOut is too high
58 | function test_swapFrom_revertIf_minAmountOutTooHigh() public prank(user) {
59 | uint256 amountIn = 1000 * 1e18;
60 | uint256 minAmountOut = 501 * 1e18; // actual: 500
61 | uint256 amountOut = 500 * 1e18;
62 |
63 | vm.expectRevert(abi.encodeWithSelector(IWellErrors.SlippageOut.selector, amountOut, minAmountOut));
64 | well.swapFrom(tokens[0], tokens[1], amountIn, minAmountOut, user, type(uint256).max);
65 | }
66 |
67 | function test_swapFrom_revertIf_expired() public {
68 | vm.expectRevert(IWellErrors.Expired.selector);
69 | well.swapFrom(tokens[0], tokens[1], 0, 0, user, block.timestamp - 1);
70 | }
71 |
72 | function testFuzz_swapFrom(uint256 amountIn) public prank(user) {
73 | amountIn = bound(amountIn, 0, tokens[0].balanceOf(user));
74 |
75 | (Snapshot memory bef, SwapAction memory act) = beforeSwapFrom(0, 1, amountIn);
76 | act.wellSends = well.swapFrom(tokens[0], tokens[1], amountIn, 0, user, type(uint256).max);
77 | afterSwapFrom(bef, act);
78 | checkInvariant(address(well));
79 | }
80 |
81 | /// @dev Zero hysteresis: token0 -> token1 -> token0 gives the same result
82 | function testFuzz_swapFrom_equalSwap(uint256 token0AmtIn) public prank(user) {
83 | token0AmtIn = bound(token0AmtIn, 0, tokens[0].balanceOf(user));
84 | uint256 token1Out = well.swapFrom(tokens[0], tokens[1], token0AmtIn, 0, user, type(uint256).max);
85 | uint256 token0Out = well.swapFrom(tokens[1], tokens[0], token1Out, 0, user, type(uint256).max);
86 | assertEq(token0Out, token0AmtIn);
87 | checkInvariant(address(well));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/test/Well.SwapFromFeeOnTransfer.NoFee.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, IERC20, Balances, Call, MockToken, Well} from "test/TestHelper.sol";
5 | import {SwapHelper, SwapAction, Snapshot} from "test/SwapHelper.sol";
6 | import {MockFunctionBad} from "mocks/functions/MockFunctionBad.sol";
7 | import {IWellFunction} from "src/interfaces/IWellFunction.sol";
8 | import {IWell} from "src/interfaces/IWell.sol";
9 | import {IWellErrors} from "src/interfaces/IWellErrors.sol";
10 |
11 | /**
12 | * @dev Tests {swapFromFeeOnTransfer} when tokens involved in the swap DO NOT
13 | * incur a fee on transfer.
14 | */
15 | contract WellSwapFromFeeOnTransferNoFeeTest is SwapHelper {
16 | function setUp() public {
17 | setupWell(2);
18 | }
19 |
20 | /// @dev Slippage revert if minAmountOut is too high.
21 | function test_swapFromFeeOnTransferNoFee_revertIf_minAmountOutTooHigh() public prank(user) {
22 | uint256 amountIn = 1000 * 1e18;
23 | uint256 minAmountOut = 501 * 1e18; // actual: 500
24 | uint256 amountOut = 500 * 1e18;
25 |
26 | vm.expectRevert(abi.encodeWithSelector(IWellErrors.SlippageOut.selector, amountOut, minAmountOut));
27 | well.swapFromFeeOnTransfer(tokens[0], tokens[1], amountIn, minAmountOut, user, type(uint256).max);
28 | }
29 |
30 | /// @dev Swaps should always revert if `fromToken` = `toToken`.
31 | function testFuzz_swapFromFeeOnTransferNoFee_revertIf_sameToken(uint128 amountIn) public prank(user) {
32 | MockToken(address(tokens[0])).mint(user, amountIn);
33 |
34 | vm.expectRevert(IWellErrors.InvalidTokens.selector);
35 | well.swapFromFeeOnTransfer(tokens[0], tokens[0], amountIn, 0, user, type(uint256).max);
36 | }
37 |
38 | /// @dev Note: this covers the case where there is a fee as well
39 | function test_swapFromFeeOnTransferNoFee_revertIf_expired() public {
40 | vm.expectRevert(IWellErrors.Expired.selector);
41 | well.swapFromFeeOnTransfer(tokens[0], tokens[1], 0, 0, user, block.timestamp - 1);
42 | }
43 |
44 | /// @dev With no fees, behavior is identical to {swapFrom}.
45 | function testFuzz_swapFromFeeOnTransfer_noFee(uint256 amountIn) public prank(user) {
46 | amountIn = bound(amountIn, 0, tokens[0].balanceOf(user));
47 |
48 | (Snapshot memory bef, SwapAction memory act) = beforeSwapFrom(0, 1, amountIn);
49 | well.swapFromFeeOnTransfer(tokens[0], tokens[1], amountIn, act.userReceives, user, type(uint256).max);
50 | afterSwapFrom(bef, act);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/Well.Tokens.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.20;
4 |
5 | import {Test, console} from "forge-std/Test.sol";
6 | import {Well, IERC20} from "test/TestHelper.sol";
7 |
8 | contract WellInternalTest is Well, Test {
9 | IERC20[] _tokens;
10 | IERC20 token0;
11 | IERC20 token1;
12 | IERC20 tokenMissing1;
13 | IERC20 tokenMissing2;
14 |
15 | function setUp() public {
16 | token0 = IERC20(address(1));
17 | token1 = IERC20(address(2));
18 | tokenMissing1 = IERC20(address(bytes20("not in well")));
19 | tokenMissing2 = IERC20(address(bytes20("also not in well")));
20 |
21 | _tokens = new IERC20[](2);
22 | _tokens[0] = IERC20(token0);
23 | _tokens[1] = IERC20(token1);
24 | }
25 |
26 | function test_getIJ() public {
27 | (uint256 i, uint256 j) = _getIJ(_tokens, token0, token1);
28 | assertEq(i, 0);
29 | assertEq(j, 1);
30 |
31 | (i, j) = _getIJ(_tokens, token1, token0);
32 | assertEq(i, 1);
33 | assertEq(j, 0);
34 | }
35 |
36 | function testFuzz_getIJ(uint256 n) public {
37 | n = bound(n, 2, 16);
38 |
39 | _tokens = new IERC20[](n);
40 | for (uint256 i = 1; i <= n; ++i) {
41 | _tokens[i - 1] = IERC20(address(uint160(i)));
42 | }
43 |
44 | // Check all combinations of tokens
45 | for (uint256 i; i < n; ++i) {
46 | for (uint256 j; j < n; ++j) {
47 | if (i == j) {
48 | vm.expectRevert(InvalidTokens.selector);
49 | }
50 | (uint256 i_, uint256 j_) = _getIJ(_tokens, _tokens[i], _tokens[j]);
51 | if (i != j) {
52 | assertEq(i_, i, "i");
53 | assertEq(j_, j, "j");
54 | }
55 | }
56 | }
57 | }
58 |
59 | function test_getIJ_revertIfIdentical() public {
60 | vm.expectRevert(InvalidTokens.selector);
61 | _getIJ(_tokens, token0, token0);
62 | }
63 |
64 | function test_getIJ_revertIfOneMissing() public {
65 | vm.expectRevert(InvalidTokens.selector);
66 | _getIJ(_tokens, tokenMissing1, token0); // i is missing
67 |
68 | vm.expectRevert(InvalidTokens.selector);
69 | _getIJ(_tokens, token0, tokenMissing1); // j is missing
70 | }
71 |
72 | function test_getIJ_revertIfBothMissing() public {
73 | vm.expectRevert(InvalidTokens.selector);
74 | _getIJ(_tokens, tokenMissing1, tokenMissing2);
75 | }
76 |
77 | function test_getJ() public {
78 | uint256 i = _getJ(_tokens, token0);
79 | assertEq(i, 0);
80 |
81 | uint256 j = _getJ(_tokens, token1);
82 | assertEq(j, 1);
83 | }
84 |
85 | function test_getJ_revertIfMissing() public {
86 | vm.expectRevert(InvalidTokens.selector);
87 | _getJ(_tokens, tokenMissing1);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/test/Well.UpdatePump.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, Call, MockPump} from "test/TestHelper.sol";
5 | import {MockEmptyFunction} from "mocks/functions/MockEmptyFunction.sol";
6 |
7 | contract WellUpdatePumpTest is TestHelper {
8 | function setUp() public {
9 | wellFunction = Call(address(new MockEmptyFunction()), "");
10 | }
11 |
12 | function test_updatePump(uint8 numPumps, bytes[8] memory pumpBytes) public {
13 | vm.assume(numPumps <= 8);
14 | for (uint256 i; i < numPumps; i++) {
15 | vm.assume(pumpBytes[i].length <= 8 * 32);
16 | }
17 |
18 | // Create `numPumps` Call structs
19 | Call[] memory pumps = new Call[](numPumps);
20 | for (uint256 i; i < numPumps; i++) {
21 | pumps[i].target = address(new MockPump());
22 | pumps[i].data = pumpBytes[i];
23 | }
24 |
25 | // Setup a Well with multiple pumps and test each
26 | // NOTE: this works because liquidity is deployed which switches
27 | // lastData from "0xATTACHED" to the `data` param which is passed during
28 | // the `update()` call. If liquidity is not added, this will fail.
29 | setupWell(2, wellFunction, pumps);
30 |
31 | // Perform an action on the Well to initialize pumps
32 | vm.prank(user);
33 | well.swapFrom(tokens[0], tokens[1], 1e18, 1, user, type(uint256).max);
34 |
35 | // During update(), MockPump sets a public storage var `lastData` equal
36 | // to Call.data.
37 | for (uint256 i; i < numPumps; i++) {
38 | assertEq(pumps[i].data, MockPump(pumps[i].target).lastData());
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/test/differential/ConstantProduct.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | EXP_PRECISION = 1e12
4 |
5 | class ConstantProduct:
6 |
7 | def __init__(self):
8 | pass
9 |
10 | def calcLPTokenUnderlying(
11 | self,
12 | lpTokenAmount,
13 | reserves,
14 | lpTokenSupply
15 | ):
16 | return [lpTokenAmount * r / lpTokenSupply for r in reserves]
17 |
18 | def calcLpTokenSupply(
19 | self,
20 | reserves
21 | ):
22 | return math.sqrt(reserves[0] * reserves[1] * EXP_PRECISION)
23 |
24 | def calcReserve(
25 | self,
26 | reserves,
27 | j,
28 | lpTokenSupply
29 | ):
30 | return (lpTokenSupply ** 2) / (reserves[0 if j == 1 else 1] * EXP_PRECISION)
31 |
32 | def calcReserveAtRatioSwap(
33 | self,
34 | reserves,
35 | j,
36 | ratios
37 | ):
38 | i = 0 if j == 1 else 1
39 | return math.sqrt((reserves[i] * reserves[j]) * (ratios[j] / ratios[i]))
40 |
41 | def calcReserveAtRatioLiquidity(
42 | self,
43 | reserves,
44 | j,
45 | ratios
46 | ):
47 | i = 0 if j == 1 else 1
48 | return reserves[i] * ratios[j] / ratios[i]
49 |
50 | def calcRate(
51 | self,
52 | reserves,
53 | i,
54 | j
55 | ):
56 | return reserves[i] / reserves[j]
--------------------------------------------------------------------------------
/test/differential/powu.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from eth_abi import encode
3 | from decimal import *
4 | getcontext().prec = 40
5 |
6 | def powuFraction(num, denom, exp):
7 | return (Decimal(num)/Decimal(denom))**Decimal(exp)
8 |
9 | def main(args):
10 | powu = powuFraction(args.numerator, args.denominator, args.exponent) * (2**128)
11 | powu_enc = encode(['int256'], [int(powu)])
12 | print("0x" + powu_enc.hex())
13 |
14 | # def test(args):
15 | # powu_fraction = powuFraction(args.numerator, args.denominator, args.exponent)
16 | # powu = powu_fraction * (2**128)
17 | # print(powu_fraction)
18 | # print('{:f}'.format(powu_fraction))
19 | # print(powu)
20 |
21 | def parse_args():
22 | parser = argparse.ArgumentParser()
23 | parser.add_argument("--numerator", "-n", type=int)
24 | parser.add_argument("--denominator", "-d", type=int)
25 | parser.add_argument("--exponent", "-e", type=int)
26 | return parser.parse_args()
27 |
28 | if __name__ == '__main__':
29 | args = parse_args()
30 | main(args)
31 | # test(args)
--------------------------------------------------------------------------------
/test/functions/ConstantProduct.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {WellFunctionHelper} from "./WellFunctionHelper.sol";
5 | import {ConstantProduct} from "src/functions/ConstantProduct.sol";
6 |
7 | contract ConstantProductTest is WellFunctionHelper {
8 | function setUp() public {
9 | _function = new ConstantProduct();
10 | _data = "";
11 | }
12 |
13 | function test_name() public {
14 | assertEq(_function.name(), "Constant Product");
15 | assertEq(_function.symbol(), "CP");
16 | }
17 |
18 | //////////// LP TOKEN SUPPLY ////////////
19 |
20 | /// @dev calcLpTokenSupply: `n` equal reserves should summate with the token supply
21 | function testLpTokenSupplySmall(uint256 n) public {
22 | n = bound(n, 2, 15);
23 | uint256[] memory reserves = new uint256[](n);
24 | for (uint256 i; i < n; ++i) {
25 | reserves[i] = 1;
26 | }
27 | assertEq(_function.calcLpTokenSupply(reserves, _data), 1 * n);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/functions/WellFunctionHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, console, stdError} from "test/TestHelper.sol";
5 | import {IMultiFlowPumpWellFunction} from "src/interfaces/IMultiFlowPumpWellFunction.sol";
6 |
7 | /// @dev Provides a base test suite for all Well functions.
8 | abstract contract WellFunctionHelper is TestHelper {
9 | IMultiFlowPumpWellFunction _function;
10 | bytes _data;
11 |
12 | /// @dev calcLpTokenSupply: 0 reserves = 0 supply
13 | /// Some Well Functions will choose to support > 2 tokens.
14 | /// Additional tokens passed in `reserves` should be ignored.
15 | function test_calcLpTokenSupply_empty(uint256 n) public {
16 | n = bound(n, 2, 15);
17 | uint256[] memory reserves = new uint256[](n);
18 | assertEq(_function.calcLpTokenSupply(reserves, _data), 0);
19 | }
20 |
21 | /// @dev require at least `n` reserves to be passed to `calcLpTokenSupply`
22 | function check_calcLpTokenSupply_minBalancesLength(uint256 n) public {
23 | for (uint256 i; i < n; ++i) {
24 | vm.expectRevert(stdError.indexOOBError); // "Index out of bounds"
25 | _function.calcLpTokenSupply(new uint256[](i), _data);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test/helpers/Users.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity >=0.7.6;
4 |
5 | import {Test} from "forge-std/Test.sol";
6 |
7 | //common utilities for forge tests
8 | contract Users is Test {
9 | bytes32 internal nextUser = keccak256(abi.encodePacked("user address"));
10 |
11 | function getNextUserAddress() external returns (address) {
12 | //bytes32 to address conversion
13 | address user = address(uint160(uint256(nextUser)));
14 | nextUser = keccak256(abi.encodePacked(nextUser));
15 | vm.deal(user, 100 ether);
16 | return user;
17 | }
18 |
19 | //create users with 100 ether balance
20 | function createUsers(uint256 userNum) external returns (address[] memory) {
21 | address[] memory users = new address[](userNum);
22 | for (uint256 i; i < userNum; i++) {
23 | address user = this.getNextUserAddress();
24 | users[i] = user;
25 | }
26 | return users;
27 | }
28 |
29 | //move block.number forward by a given number of blocks
30 | function mineBlocks(uint256 numBlocks) external {
31 | uint256 targetBlock = block.number + numBlocks;
32 | vm.roll(targetBlock);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/integration/GasMetering.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {Test, console, stdError} from "forge-std/Test.sol";
5 |
6 | contract TestGasMetering is Test {
7 | //////////////////// GAS METERING ////////////////////
8 |
9 | /// @dev invariant test: pausing gas metering should always result
10 | /// in a smaller delta.
11 | function test_invariant_pauseGasMetering() public {
12 | // Expect no change once inside paused gas metering
13 | vm.pauseGasMetering();
14 | uint256 start1 = gasleft();
15 | {
16 | for (uint256 i; i < 10; i++) {
17 | uint256 gasnow = gasleft();
18 | assertEq(gasnow, start1);
19 | }
20 | }
21 | uint256 end1 = gasleft();
22 | vm.resumeGasMetering();
23 | assertEq(start1, end1, "Gas metering invariant: gas changed while paused");
24 |
25 | uint256 start2 = gasleft();
26 | {
27 | uint256 hold2 = gasleft();
28 | for (uint256 i; i < 10; i++) {
29 | uint256 gasnow = gasleft();
30 | assertTrue(gasnow < start2);
31 | assertTrue(gasnow < hold2);
32 | hold2 = gasnow;
33 | }
34 | }
35 | uint256 end2 = gasleft();
36 |
37 | // (start1 - end1) is zero given previous assertion
38 | assertTrue((start1 - end1) < (start2 - end2), "Gas metering invariant: delta");
39 | }
40 |
41 | function test_gasleft_cost() public {
42 | uint256 start = gasleft();
43 | uint256 hold = gasleft();
44 | uint256 end = gasleft();
45 |
46 | console.log("start - end", start, end, start - end);
47 | console.log("start - hold", start, hold, start - hold);
48 | console.log("hold - end", hold, end, hold - end);
49 |
50 | assertTrue(true);
51 | }
52 |
53 | function test_pauseGasMetering_cost() public {
54 | uint256 start = gasleft();
55 | vm.pauseGasMetering();
56 | uint256 hold = gasleft();
57 | vm.resumeGasMetering();
58 | uint256 end = gasleft();
59 |
60 | console.log("pauseGasMetering cost:", start - end);
61 | console.log("resumeGasMetering cost:", hold - end);
62 |
63 | assertTrue(true);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/test/integration/interfaces/IPipeline.sol:
--------------------------------------------------------------------------------
1 | //SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {IERC20} from "test/integration/IntegrationTestHelper.sol";
5 |
6 | /**
7 | * @title IPipeline
8 | * @author Brendan
9 | * @notice Pipeline Interface – Pipeline creates a sandbox to execute any series of function calls on any series of protocols through \term{Pipe} functions.
10 | * Any assets left in Pipeline between transactions can be transferred out by any account.
11 | * Users Pipe a series of PipeCalls that each execute a function call to another protocol through Pipeline.
12 | *
13 | */
14 |
15 | // PipeCalls specify a function call to be executed by Pipeline.
16 | // Pipeline supports 2 types of PipeCalls: PipeCall and AdvancedPipeCall.
17 |
18 | // PipeCall makes a function call with a static target address and callData.
19 | struct PipeCall {
20 | address target;
21 | bytes data;
22 | }
23 |
24 | // AdvancedPipeCall makes a function call with a static target address and both static and dynamic callData.
25 | // AdvancedPipeCalls support sending Ether in calls.
26 | // [ PipeCall Type | Send Ether Flag | PipeCall Type data | Ether Value (only if flag == 1)]
27 | // [ 1 byte | 1 byte | n bytes | 0 or 32 bytes ]
28 | // See LibFunction.useClipboard for more details.
29 | struct AdvancedPipeCall {
30 | address target;
31 | bytes callData;
32 | bytes clipboard;
33 | }
34 |
35 | interface IPipeline {
36 | function pipe(PipeCall calldata p) external payable returns (bytes memory result);
37 |
38 | function multiPipe(PipeCall[] calldata pipes) external payable returns (bytes[] memory results);
39 |
40 | function advancedPipe(AdvancedPipeCall[] calldata pipes) external payable returns (bytes[] memory results);
41 | }
42 |
43 | interface IDepot {
44 | function advancedPipe(
45 | AdvancedPipeCall[] calldata pipes,
46 | uint256 value
47 | ) external payable returns (bytes[] memory results);
48 |
49 | function farm(bytes[] calldata data) external payable returns (bytes[] memory results);
50 |
51 | function transferToken(
52 | IERC20 token,
53 | address recipient,
54 | uint256 amount,
55 | From fromMode,
56 | To toMode
57 | ) external payable;
58 | }
59 |
60 | enum From {
61 | EXTERNAL,
62 | INTERNAL,
63 | EXTERNAL_INTERNAL,
64 | INTERNAL_TOLERANT
65 | }
66 |
67 | enum To {
68 | EXTERNAL,
69 | INTERNAL
70 | }
71 |
72 | interface IBeanstalk {
73 | function transferInternalTokenFrom(
74 | IERC20 token,
75 | address sender,
76 | address recipient,
77 | uint256 amount,
78 | To toMode
79 | ) external payable;
80 |
81 | function permitToken(
82 | address owner,
83 | address spender,
84 | address token,
85 | uint256 value,
86 | uint256 deadline,
87 | uint8 v,
88 | bytes32 r,
89 | bytes32 s
90 | ) external payable;
91 |
92 | function transferDeposit(
93 | address sender,
94 | address recipient,
95 | address token,
96 | uint32 season,
97 | uint256 amount
98 | ) external payable returns (uint256 bdv);
99 |
100 | function transferDeposits(
101 | address sender,
102 | address recipient,
103 | address token,
104 | uint32[] calldata seasons,
105 | uint256[] calldata amounts
106 | ) external payable returns (uint256[] memory bdvs);
107 |
108 | function permitDeposits(
109 | address owner,
110 | address spender,
111 | address[] calldata tokens,
112 | uint256[] calldata values,
113 | uint256 deadline,
114 | uint8 v,
115 | bytes32 r,
116 | bytes32 s
117 | ) external payable;
118 |
119 | function permitDeposit(
120 | address owner,
121 | address spender,
122 | address token,
123 | uint256 value,
124 | uint256 deadline,
125 | uint8 v,
126 | bytes32 r,
127 | bytes32 s
128 | ) external payable;
129 | }
130 |
--------------------------------------------------------------------------------
/test/integration/interfaces/IUniswap.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | interface IUniswapV2Router {
5 | function swapExactTokensForTokens(
6 | uint256 amountIn,
7 | uint256 amountOutMin,
8 | address[] calldata path,
9 | address to,
10 | uint256 deadline
11 | ) external returns (uint256[] memory amounts);
12 |
13 | function swapTokensForExactTokens(
14 | uint256 amountOut,
15 | uint256 amountInMax,
16 | address[] calldata path,
17 | address to,
18 | uint256 deadline
19 | ) external returns (uint256[] memory amounts);
20 |
21 | function addLiquidity(
22 | address tokenA,
23 | address tokenB,
24 | uint256 amountADesired,
25 | uint256 amountBDesired,
26 | uint256 amountAMin,
27 | uint256 amountBMin,
28 | address to,
29 | uint256 deadline
30 | ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
31 |
32 | function removeLiquidity(
33 | address tokenA,
34 | address tokenB,
35 | uint256 liquidity,
36 | uint256 amountAMin,
37 | uint256 amountBMin,
38 | address to,
39 | uint256 deadline
40 | ) external returns (uint256 amountA, uint256 amountB);
41 | }
42 |
43 | interface IUniswapV3Router {
44 | struct ExactInputSingleParams {
45 | address tokenIn;
46 | address tokenOut;
47 | uint24 fee;
48 | address recipient;
49 | uint256 deadline;
50 | uint256 amountIn;
51 | uint256 amountOutMinimum;
52 | uint160 sqrtPriceLimitX96;
53 | }
54 |
55 | /// @notice Swaps amountIn of one token for as much as possible of another token
56 | /// @param params The parameters necessary for the swap, encoded as ExactInputSingleParams in calldata
57 | /// @return amountOut The amount of the received token
58 | function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
59 |
60 | struct ExactInputParams {
61 | bytes path;
62 | address recipient;
63 | uint256 deadline;
64 | uint256 amountIn;
65 | uint256 amountOutMinimum;
66 | }
67 |
68 | /// @notice Swaps amountIn of one token for as much as possible of another along the specified path
69 | /// @param params The parameters necessary for the multi-hop swap, encoded as ExactInputParams in calldata
70 | /// @return amountOut The amount of the received token
71 | function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
72 | }
73 |
74 | interface IUniswapV2Factory {
75 | function getPair(address token0, address token1) external view returns (address);
76 | }
77 |
--------------------------------------------------------------------------------
/test/libraries/LibBytes16.t.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | *
4 | */
5 | pragma solidity ^0.8.20;
6 |
7 | import "test/TestHelper.sol";
8 |
9 | import {LibBytes16} from "src/libraries/LibBytes16.sol";
10 |
11 | contract LibBytes16Test is TestHelper {
12 | uint256 constant NUM_RESERVES_MAX = 8;
13 | bytes32 constant RESERVES_STORAGE_SLOT = bytes32(uint256(keccak256("reserves.storage.slot")) - 1);
14 |
15 | /// @dev Store fuzzed reserves, re-read and compare.
16 | function testFuzz_storeAndReadBytes16(uint256 n, bytes16[8] memory _reserves) public {
17 | n = bound(n, 0, NUM_RESERVES_MAX);
18 |
19 | // Use the first `n` reserves. Cast uint128 reserves -> uint256
20 | bytes16[] memory reserves = new bytes16[](n);
21 | for (uint256 i; i < n; i++) {
22 | reserves[i] = _reserves[i];
23 | }
24 | LibBytes16.storeBytes16(RESERVES_STORAGE_SLOT, reserves);
25 |
26 | bytes32 slot = RESERVES_STORAGE_SLOT;
27 | bytes32 test;
28 | assembly {
29 | test := sload(slot)
30 | }
31 | console.logBytes32(test);
32 | assembly {
33 | test := sload(add(slot, 32))
34 | }
35 | console.logBytes32(test);
36 |
37 | // Re-read reserves and compare
38 | bytes16[] memory reserves2 = LibBytes16.readBytes16(RESERVES_STORAGE_SLOT, n);
39 | for (uint256 i; i < reserves2.length; i++) {
40 | console.log(i);
41 | assertEq(reserves2[i], reserves[i], "ByteStorage: reserves mismatch");
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/libraries/LibContractInfo.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {TestHelper, console} from "test/TestHelper.sol";
5 | import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
6 |
7 | contract LibMathTest is TestHelper {
8 | using LibContractInfo for address;
9 |
10 | function setUp() public {
11 | setupWell(2); // setting up a well just to get some mock tokens
12 | }
13 |
14 | function test_getSymbol() public {
15 | assertEq(address(tokens[0]).getSymbol(), "TOKEN0");
16 | }
17 |
18 | function test_getName() public {
19 | assertEq(address(tokens[0]).getName(), "Token 0");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/libraries/LibLastReserveBytes.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import "test/TestHelper.sol";
5 |
6 | import {LibLastReserveBytes} from "src/libraries/LibLastReserveBytes.sol";
7 |
8 | contract LibLastReserveBytesTest is TestHelper {
9 | using LibLastReserveBytes for bytes32;
10 |
11 | uint256 constant NUM_RESERVES_MAX = 8;
12 | bytes32 constant RESERVES_STORAGE_SLOT = bytes32(uint256(keccak256("reserves.storage.slot")) - 1);
13 |
14 | /// @dev Store fuzzed reserves, re-read and compare.
15 | function testEmaFuzz_storeAndRead(
16 | uint8 n,
17 | uint40 lastTimestamp,
18 | uint256[NUM_RESERVES_MAX] memory _reserves
19 | ) public {
20 | vm.assume(n <= NUM_RESERVES_MAX);
21 |
22 | // Use the first `n` reserves. Cast uint104 reserves -> uint256
23 | uint256[] memory reserves = new uint256[](n);
24 | for (uint256 i; i < n; i++) {
25 | reserves[i] = _reserves[i];
26 | }
27 | RESERVES_STORAGE_SLOT.storeLastReserves(lastTimestamp, reserves);
28 |
29 | // Re-read reserves and compare
30 | (uint8 _n, uint40 _lastTimestamp, uint256[] memory reserves2) = RESERVES_STORAGE_SLOT.readLastReserves();
31 | uint8 __n = RESERVES_STORAGE_SLOT.readNumberOfReserves();
32 | assertEq(__n, n, "ByteStorage: n mismatch");
33 | assertEq(_n, n, "ByteStorage: n mismatch");
34 | assertEq(_lastTimestamp, lastTimestamp, "ByteStorage: lastTimestamp mismatch");
35 | for (uint256 i; i < reserves2.length; i++) {
36 | assertApproxEqRelN(reserves2[i], reserves[i], 1);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/libraries/TestABDK.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import "test/TestHelper.sol";
5 | import "src/libraries/ABDKMathQuad.sol";
6 | import "oz/utils/Strings.sol";
7 |
8 | contract ABDKTest is TestHelper {
9 | using ABDKMathQuad for uint256;
10 | using ABDKMathQuad for bytes16;
11 | using Strings for uint256;
12 |
13 | int256 constant uUINT128 = 2 ** 128;
14 |
15 | //////////////////// CORE ////////////////////
16 |
17 | /**
18 | * @dev no hysteresis: 2^(log2(a)) == a +/- 1 (due to library rounding)
19 | */
20 | function testFuzz_log2Pow2(uint256 a) public {
21 | a = bound(a, 1, type(uint256).max);
22 | uint256 b = (a.fromUInt().log_2()).pow_2().toUInt();
23 | if (a <= 1e18) {
24 | assertApproxEqAbs(a, b, 1);
25 | } else {
26 | assertApproxEqRel(a, b, 1);
27 | }
28 | }
29 |
30 | //////////////////// EXTENSIONS ////////////////////
31 |
32 | function test_powu1() public {
33 | bytes16 pu = powuFraction(9, 10, 10);
34 | uint256 puu = uint256(pu.to128x128());
35 | uint256 expected = 118_649_124_891_528_663_468_500_301_601_258_807_155;
36 | assertApproxEqRelN(puu, expected, 1, 32);
37 | }
38 |
39 | function testFuzz_powu(uint256 num, uint256 denom, uint256 exp) public {
40 | denom = bound(denom, 1, type(uint16).max);
41 | num = bound(num, 1, denom);
42 |
43 | string[] memory inputs = new string[](8);
44 | inputs[0] = "python";
45 | inputs[1] = "test/differential/powu.py";
46 | inputs[2] = "--numerator";
47 | inputs[3] = uint256(num).toString();
48 | inputs[4] = "--denominator";
49 | inputs[5] = uint256(denom).toString();
50 | inputs[6] = "--exponent";
51 | inputs[7] = uint256(exp).toString();
52 | bytes memory result = vm.ffi(inputs);
53 |
54 | bytes16 pu = powuFraction(num, denom, exp);
55 | uint256 puu = uint256(pu.to128x128());
56 | uint256 pypu = uint256(abi.decode(result, (int256)));
57 |
58 | // Rounding error starts at 5e27
59 | if (puu > 5e27) {
60 | assertApproxEqRelN(puu, pypu, 2, 28); // expecting precision to 2e-28
61 | } else {
62 | assertApproxEqAbs(puu, pypu, 1);
63 | }
64 | }
65 |
66 | /// @dev calculate (a/b)^c
67 | function powuFraction(uint256 a, uint256 b, uint256 c) public pure returns (bytes16) {
68 | return a.fromUInt().div(b.fromUInt()).powu(c);
69 | }
70 |
71 | function testFuzz_FromUIntToLog2(uint256 x) public pure {
72 | x = bound(x, 1, type(uint256).max);
73 | assertEq(ABDKMathQuad.fromUInt(x).log_2(), ABDKMathQuad.fromUIntToLog2(x));
74 | }
75 |
76 | function testFuzz_pow_2ToUInt(uint256 x) public pure {
77 | x = bound(x, 0, 255);
78 |
79 | // test the pow_2ToUInt function
80 | bytes16 _x = x.fromUInt();
81 | assertEq(ABDKMathQuad.pow_2(_x).toUInt(), ABDKMathQuad.pow_2ToUInt(_x));
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/test/pumps/Pump.Helpers.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {MultiFlowPump, ABDKMathQuad} from "src/pumps/MultiFlowPump.sol";
5 | import {simCapReserve50Percent, from18, to18} from "test/pumps/PumpHelpers.sol";
6 | import {log2, powu, UD60x18, wrap, unwrap} from "prb/math/UD60x18.sol";
7 | import {console, TestHelper} from "test/TestHelper.sol";
8 |
9 | contract PumpHelpersTest is TestHelper, MultiFlowPump {
10 | uint256[5] testCasesInput = [1, 2, 3, 4, 5];
11 | uint256[5] testCasesOutput = [1, 1, 2, 2, 3];
12 |
13 | constructor() MultiFlowPump() {}
14 |
15 | function test_getSlotForAddress() public {
16 | address addr = address(0xa755A670Aaf1FeCeF2bea56115E65e03F7722A79);
17 | bytes32 bytesAddress = _getSlotForAddress(addr);
18 |
19 | assertEq(bytesAddress, 0xa755a670aaf1fecef2bea56115e65e03f7722a79000000000000000000000000);
20 | }
21 |
22 | function test_getDeltaTimeStamp() public {
23 | vm.warp(200);
24 | uint40 providedBlock = 100;
25 | uint40 expectedDelta = 100;
26 |
27 | uint256 delta = _getDeltaTimestamp(providedBlock);
28 |
29 | assertEq(delta, expectedDelta);
30 | }
31 |
32 | function test_getSlotOffset() public {
33 | for (uint256 i; i < testCasesInput.length; i++) {
34 | assertEq(_getSlotsOffset(testCasesInput[i]), testCasesOutput[i]);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/test/pumps/Pump.Longevity.t.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | *
4 | */
5 | pragma solidity ^0.8.20;
6 |
7 | import {console, TestHelper} from "test/TestHelper.sol";
8 | import {ABDKMathQuad, MultiFlowPump} from "src/pumps/MultiFlowPump.sol";
9 | import {MockReserveWell} from "mocks/wells/MockReserveWell.sol";
10 | import {mockPumpData} from "test/pumps/PumpHelpers.sol";
11 | import {ConstantProduct2} from "src/functions/ConstantProduct2.sol";
12 |
13 | import {generateRandomUpdate, from18, to18} from "test/pumps/PumpHelpers.sol";
14 | import {log2, powu, UD60x18, wrap, unwrap} from "prb/math/UD60x18.sol";
15 | import {exp2, log2, powu, UD60x18, wrap, unwrap, uUNIT} from "prb/math/UD60x18.sol";
16 |
17 | contract PumpLongevityTest is TestHelper {
18 | using ABDKMathQuad for bytes16;
19 | using ABDKMathQuad for uint256;
20 |
21 | MultiFlowPump pump;
22 | MockReserveWell mWell;
23 | bytes data;
24 | uint256[] b = new uint256[](2);
25 |
26 | constructor() {}
27 |
28 | function setUp() public {
29 | mWell = new MockReserveWell();
30 | initUser();
31 | pump = new MultiFlowPump();
32 | data = mockPumpData();
33 | wellFunction.target = address(new ConstantProduct2());
34 | mWell.setWellFunction(wellFunction);
35 | }
36 |
37 | function testIterate() public prank(user) {
38 | bytes32 seed = bytes32(0);
39 | uint256 n = 2;
40 | uint256[] memory balances;
41 | uint40 timeStep;
42 | uint256 timestamp = block.timestamp;
43 | for (uint256 i; i < 4000; ++i) {
44 | if (i % 1000 == 0) {
45 | console.log(i);
46 | }
47 | (balances, timeStep, seed) = generateRandomUpdate(n, seed);
48 | // console.log("Time Step: ", timeStep);
49 | // for (uint256 j; j < n; ++j) {
50 | // console.log("Balance", j, balances[j]);
51 | // }
52 | increaseTime(timeStep);
53 | mWell.update(address(pump), balances, data);
54 | }
55 |
56 | // uint256[] memory lastReserves = pump.readLastReserves(address(mWell));
57 | // uint256[] memory currentReserves = pump.readInstantaneousReserves(address(mWell));
58 | // bytes16[] memory lastCumulativeReserves = pump.readLastCumulativeReserves(address(mWell));
59 |
60 | // for (uint256 i; i < n; ++i) {
61 | // console.log("Reserve", i, balances[i]);
62 | // console.log("Last Reserve", i, lastReserves[i]);
63 | // console.log("Current Reserve", i, currentReserves[i]);
64 | // console.logBytes16(lastCumulativeReserves[i]);
65 | // }
66 | uint256 deltaTimestamp = block.timestamp - timestamp;
67 | console.log("Time passed:", deltaTimestamp / 60 / 60 / 24 / 365, "years");
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/test/pumps/Pump.NotInitialized.t.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | *
4 | */
5 | pragma solidity ^0.8.20;
6 |
7 | import {console, TestHelper} from "test/TestHelper.sol";
8 | import {MultiFlowPump} from "src/pumps/MultiFlowPump.sol";
9 | import {mockPumpData} from "test/pumps/PumpHelpers.sol";
10 | import {MockReserveWell} from "mocks/wells/MockReserveWell.sol";
11 | import {IMultiFlowPumpErrors} from "src/interfaces/pumps/IMultiFlowPumpErrors.sol";
12 | import {from18} from "test/pumps/PumpHelpers.sol";
13 |
14 | contract PumpNotInitialized is TestHelper {
15 | MultiFlowPump pump;
16 | bytes data;
17 | MockReserveWell mWell;
18 | uint256[] b = new uint256[](2);
19 |
20 | function setUp() public {
21 | mWell = new MockReserveWell();
22 | initUser();
23 | pump = new MultiFlowPump();
24 | uint256[] memory reserves = new uint256[](2);
25 | mWell.setReserves(reserves);
26 | data = mockPumpData();
27 | }
28 |
29 | function test_not_initialized_last_cumulative_reserves() public {
30 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
31 | pump.readLastCumulativeReserves(address(mWell), data);
32 | }
33 |
34 | function test_not_initialized_cumulative_reserves() public {
35 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
36 | pump.readCumulativeReserves(address(mWell), data);
37 | }
38 |
39 | function test_not_initialized_last_instantaneous_reserves() public {
40 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
41 | pump.readLastInstantaneousReserves(address(mWell), data);
42 | }
43 |
44 | function test_not_initialized_instantaneous_reserves() public {
45 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
46 | pump.readInstantaneousReserves(address(mWell), data);
47 | }
48 |
49 | function test_not_initialized_last_capped_reserves() public {
50 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
51 | pump.readLastCappedReserves(address(mWell), data);
52 | }
53 |
54 | function test_not_initialized_capped_reserves() public {
55 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
56 | pump.readCappedReserves(address(mWell), data);
57 | }
58 |
59 | function test_not_initialized_twa_reserves() public {
60 | vm.expectRevert(IMultiFlowPumpErrors.NotInitialized.selector);
61 | pump.readTwaReserves(address(mWell), data, 0, data);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/test/pumps/Pump.TimeWeightedAverage.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.20;
3 |
4 | import {console, TestHelper} from "test/TestHelper.sol";
5 | import {MultiFlowPump, ABDKMathQuad} from "src/pumps/MultiFlowPump.sol";
6 | import {mockPumpData, from18, to18} from "test/pumps/PumpHelpers.sol";
7 | import {MockReserveWell} from "mocks/wells/MockReserveWell.sol";
8 | import {ConstantProduct2} from "src/functions/ConstantProduct2.sol";
9 |
10 | import {log2, powu, UD60x18, wrap, unwrap} from "prb/math/UD60x18.sol";
11 | import {exp2, log2, powu, UD60x18, wrap, unwrap, uUNIT} from "prb/math/UD60x18.sol";
12 |
13 | contract PumpTimeWeightedAverageTest is TestHelper {
14 | using ABDKMathQuad for bytes16;
15 |
16 | MultiFlowPump pump;
17 | bytes data;
18 | MockReserveWell mWell;
19 | uint256[] b = new uint256[](2);
20 |
21 | uint256 constant CAP_INTERVAL = 12;
22 |
23 | /// @dev for this test, `user` = a Well that's calling the Pump
24 | function setUp() public {
25 | mWell = new MockReserveWell();
26 | initUser();
27 | pump = new MultiFlowPump();
28 | data = mockPumpData();
29 | wellFunction.target = address(new ConstantProduct2());
30 | mWell.setWellFunction(wellFunction);
31 |
32 | // Send first update to the Pump, which will initialize it
33 | vm.prank(user);
34 | b[0] = 1e6;
35 | b[1] = 2e6;
36 | mWell.update(address(pump), b, data);
37 | mWell.update(address(pump), b, data);
38 |
39 | uint256[] memory checkReserves = mWell.getReserves();
40 | assertEq(checkReserves[0], b[0]);
41 | assertEq(checkReserves[1], b[1]);
42 | }
43 |
44 | function testTWAReserves() public prank(user) {
45 | increaseTime(12);
46 |
47 | bytes memory startCumulativeReserves = pump.readCumulativeReserves(address(mWell), data);
48 | uint256[] memory lastReserves = pump.readLastCappedReserves(address(mWell), data);
49 |
50 | assertApproxEqAbs(lastReserves[0], 1e6, 1);
51 | assertApproxEqAbs(lastReserves[1], 2e6, 1);
52 |
53 | increaseTime(120);
54 | uint256[] memory twaReserves;
55 |
56 | (twaReserves,) = pump.readTwaReserves(address(mWell), startCumulativeReserves, block.timestamp - 120, data);
57 |
58 | assertApproxEqAbs(twaReserves[0], 1e6, 1);
59 | assertApproxEqAbs(twaReserves[1], 2e6, 1);
60 |
61 | b[0] = 2e6;
62 | b[1] = 4e6;
63 | mWell.update(address(pump), b, data);
64 |
65 | increaseTime(120);
66 |
67 | (twaReserves,) = pump.readTwaReserves(address(mWell), startCumulativeReserves, block.timestamp - 240, data);
68 |
69 | assertEq(twaReserves[0], 1_414_213); // Geometric Mean of 1e6 and 2e6 is 1_414_213
70 | assertEq(twaReserves[1], 2_828_427); // Geometric mean of 2e6 and 4e6 is 2_828_427
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/test/pumps/PumpHelpers.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Unlicense
2 | pragma solidity >=0.8.0;
3 |
4 | import {ABDKMathQuad} from "src/libraries/ABDKMathQuad.sol";
5 | import {console} from "forge-std/Test.sol";
6 | import {MultiFlowPump} from "src/pumps/MultiFlowPump.sol";
7 |
8 | uint256 constant MAX_128 = 2 ** 128;
9 | uint256 constant MAX_E18 = 1e18;
10 |
11 | function from18(uint256 a) pure returns (bytes16 result) {
12 | return ABDKMathQuad.from128x128(int256((a * MAX_128) / MAX_E18));
13 | }
14 |
15 | function to18(bytes16 a) pure returns (uint256 result) {
16 | return (uint256(ABDKMathQuad.to128x128(a)) * MAX_E18) / MAX_128;
17 | }
18 |
19 | function simCapReserve50Percent(
20 | uint256 lastReserve,
21 | uint256 reserve,
22 | uint256 blocks
23 | ) view returns (uint256 cappedReserve) {
24 | uint256 limitReserve = lastReserve * 1e18;
25 |
26 | uint256 multiplier = lastReserve < reserve ? 1.5e6 : 0.5e6;
27 |
28 | uint256 tempReserve;
29 | for (uint256 i; i < blocks; ++i) {
30 | unchecked {
31 | tempReserve = (limitReserve * multiplier) / 1e6;
32 | }
33 | if (lastReserve < reserve && tempReserve < limitReserve) {
34 | limitReserve = type(uint256).max;
35 | break;
36 | }
37 | limitReserve = tempReserve;
38 | }
39 | limitReserve = limitReserve / 1e18;
40 |
41 | console.log("limitReserve", limitReserve);
42 | console.log("lastReserve", lastReserve);
43 | console.log("reserve", reserve);
44 |
45 | cappedReserve = (lastReserve < reserve && limitReserve < reserve)
46 | || (lastReserve > reserve && limitReserve > reserve) ? limitReserve : reserve;
47 | }
48 |
49 | function generateRandomUpdate(
50 | uint256 n,
51 | bytes32 seed
52 | ) pure returns (uint256[] memory balances, uint40 timeIncrease, bytes32 newSeed) {
53 | balances = new uint256[](n);
54 | seed = stepSeed(seed);
55 | timeIncrease = uint40(uint256(seed)) % 50_000_000;
56 | for (uint256 i; i < n; ++i) {
57 | seed = stepSeed(seed);
58 | balances[i] = uint256(seed) % 1e32; //
59 | }
60 | newSeed = seed;
61 | }
62 |
63 | function stepSeed(bytes32 seed) pure returns (bytes32 newSeed) {
64 | newSeed = keccak256(abi.encode(seed));
65 | }
66 |
67 | function encodePumpData(
68 | bytes16 alpha,
69 | uint256 capInterval,
70 | MultiFlowPump.CapReservesParameters memory crp
71 | ) pure returns (bytes memory data) {
72 | data = abi.encode(alpha, capInterval, crp);
73 | }
74 |
75 | function mockPumpData() pure returns (bytes memory data) {
76 | bytes16[][] memory maxRateChanges = new bytes16[][](2);
77 | maxRateChanges[0] = new bytes16[](2);
78 | maxRateChanges[1] = new bytes16[](2);
79 | maxRateChanges[0][1] = from18(0.5e18);
80 | maxRateChanges[1][0] = from18(0.5e18);
81 |
82 | data = encodePumpData(
83 | from18(0.9e18), 12, MultiFlowPump.CapReservesParameters(maxRateChanges, from18(0.5e18), from18(0.4761904762e18))
84 | );
85 | }
86 |
--------------------------------------------------------------------------------
/test/pumps/simulate.py:
--------------------------------------------------------------------------------
1 | import os
2 | import argparse
3 | from eth_abi import decode
4 | from decimal import *
5 | import pandas as pd
6 | import numpy as np
7 |
8 | def main(args):
9 | val = decode(['(uint256,uint256,uint256,uint256)[]'], bytes.fromhex(args.data[2:]))
10 | arr = np.asarray(val[0])
11 | pd.DataFrame(
12 | arr,
13 | columns=['j', 'prev', 'curr', 'capped']
14 | ).to_csv(
15 | os.path.join("test", "output", f"{args.name}.csv"),
16 | index=False
17 | )
18 | print("Done")
19 |
20 | def parse_args():
21 | parser = argparse.ArgumentParser()
22 | parser.add_argument("--data", "-d", type=str)
23 | parser.add_argument("--name", "-n", type=str)
24 | return parser.parse_args()
25 |
26 | if __name__ == '__main__':
27 | args = parse_args()
28 | main(args)
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | husky@8.0.3:
6 | version "8.0.3"
7 | resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
8 | integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
9 |
--------------------------------------------------------------------------------