├── .env.example
├── .github
├── actions
│ └── setup-repo
│ │ └── action.yml
└── workflows
│ ├── ci-deep.yml
│ └── ci.yml
├── .gitignore
├── .gitmodules
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .solhint.json
├── .solhintignore
├── .vscode
├── settings.json
└── tasks.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── contracts
├── helpers
│ ├── BaseHarvester.sol
│ ├── GenericHarvester.sol
│ └── MultiBlockHarvester.sol
├── interfaces
│ ├── IAccessControlManager.sol
│ ├── IAgToken.sol
│ ├── IDiamondCut.sol
│ ├── IDiamondEtherscan.sol
│ ├── IDiamondLoupe.sol
│ ├── IGetters.sol
│ ├── IHarvester.sol
│ ├── IManager.sol
│ ├── IPool.sol
│ ├── IRedeemer.sol
│ ├── IRewardHandler.sol
│ ├── ISetters.sol
│ ├── ISwapper.sol
│ ├── ITransmuter.sol
│ ├── ITransmuterOracle.sol
│ ├── IXEVT.sol
│ └── external
│ │ ├── IERC4626.sol
│ │ ├── chainlink
│ │ └── AggregatorV3Interface.sol
│ │ ├── coinbase
│ │ └── ICbETH.sol
│ │ ├── frax
│ │ └── ISfrxETH.sol
│ │ ├── keyring
│ │ └── IKeyringGuard.sol
│ │ ├── lido
│ │ └── IStETH.sol
│ │ ├── morpho
│ │ └── IMorphoOracle.sol
│ │ ├── permit2
│ │ └── IPermit2.sol
│ │ ├── pyth
│ │ └── IPyth.sol
│ │ └── rocketPool
│ │ └── IRETH.sol
├── savings
│ ├── BaseSavings.sol
│ ├── Savings.sol
│ ├── SavingsVest.sol
│ └── nameable
│ │ └── SavingsNameable.sol
├── transmuter
│ ├── DiamondProxy.sol
│ ├── Layout.sol
│ ├── Storage.sol
│ ├── configs
│ │ ├── FakeGnosis.sol
│ │ ├── Production.sol
│ │ ├── ProductionSidechain.sol
│ │ ├── ProductionTypes.sol
│ │ ├── ProductionUSD.sol
│ │ └── Test.sol
│ ├── facets
│ │ ├── AccessControlModifiers.sol
│ │ ├── DiamondCut.sol
│ │ ├── DiamondEtherscan.sol
│ │ ├── DiamondLoupe.sol
│ │ ├── Getters.sol
│ │ ├── Redeemer.sol
│ │ ├── RewardHandler.sol
│ │ ├── SettersGovernor.sol
│ │ ├── SettersGuardian.sol
│ │ └── Swapper.sol
│ └── libraries
│ │ ├── LibDiamond.sol
│ │ ├── LibDiamondEtherscan.sol
│ │ ├── LibGetters.sol
│ │ ├── LibHelpers.sol
│ │ ├── LibManager.sol
│ │ ├── LibOracle.sol
│ │ ├── LibSetters.sol
│ │ ├── LibStorage.sol
│ │ └── LibWhitelist.sol
└── utils
│ ├── AccessControl.sol
│ ├── Constants.sol
│ └── Errors.sol
├── foundry.toml
├── helpers
└── fork.sh
├── logo.svg
├── package.json
├── remappings.txt
├── scripts
├── AdjustYieldExposure.s.sol
├── Constants.s.sol
├── DeployGenericHarvester.s.sol
├── DeployMultiBlockHarvester.s.sol
├── DeployRedeemer.s.sol
├── DeploySavings.s.sol
├── DeploySavingsImplem.s.sol
├── DeploySavingsNoImplem.s.sol
├── DeployTransmuter.s.sol
├── DeployTransmuterSidechain.s.sol
├── DeployTransmuterWithoutFacets.s.sol
├── Helpers.s.sol
├── SetOracle.s.sol
├── SetupDeployedTransmuter.s.sol
├── UpdateOracle.s.sol
├── UpdateTransmuterFacets.s.sol
├── generated
│ ├── DummyDiamondImplementation.sol
│ └── DummyDiamondImplementationSidechain.sol
├── gnosis
│ ├── DeploySavingsGnosis.s.sol
│ ├── DeployTransmuterGnosis.s.sol
│ └── VerifyProxyEtherscanGnosis.s.sol
├── interaction
│ └── transmuter
│ │ └── Swap.s.sol
├── selectors.json
├── selectors_add.json
├── selectors_replace.json
├── test
│ ├── CheckFakeTransmuter.s.sol
│ ├── CheckTransmuter.s.sol
│ ├── CheckTransmuterUSD.s.sol
│ ├── UpdateTransmuterFacets.s.sol
│ └── UpdateTransmuterFacetsUSDATest.s.sol
├── utils
│ ├── GenerateSelectors.s.sol
│ ├── TransmuterDeploymentHelper.s.sol
│ ├── Utils.s.sol
│ └── VanityAddress.s.sol
├── vanity.json
├── vanitySavingsEUR.json
└── vanitySavingsUSD.json
├── slither.config.json
├── slither.db.json
├── slither.sh
├── test
├── Fixture.sol
├── fuzz
│ ├── BurnTest.t.sol
│ ├── GenericHarvester.t.sol
│ ├── GettersTest.t.sol
│ ├── Harvester.t.sol
│ ├── MintTest.t.sol
│ ├── MultiBlockHarvester.t.sol
│ ├── OracleTest.t.sol
│ ├── RedeemTest.t.sol
│ ├── SavingsNameableTest.t.sol
│ ├── SavingsTest.t.sol
│ ├── SavingsVestTest.t.sol
│ └── SwapTest.t.sol
├── invariants
│ ├── BasicInvariants.sol
│ ├── PathIndeInvariants.t.sol
│ └── actors
│ │ ├── Arbitrager.t.sol
│ │ ├── ArbitragerWithSplit.t.sol
│ │ ├── BaseActor.t.sol
│ │ ├── Governance.t.sol
│ │ ├── Trader.t.sol
│ │ └── TraderWithSplit.t.sol
├── mock
│ ├── MockAccessControlManager.sol
│ ├── MockChainlinkOracle.sol
│ ├── MockERC777.sol
│ ├── MockExternalOracle.sol
│ ├── MockFacets.sol
│ ├── MockKeyringGuard.sol
│ ├── MockLib.sol
│ ├── MockManager.sol
│ ├── MockMorphoOracle.sol
│ ├── MockOneInchRouter.sol
│ ├── MockProxyAdmin.sol
│ ├── MockPyth.sol
│ ├── MockReentrant.sol
│ ├── MockRouter.sol
│ ├── MockScaleDecimals.sol
│ ├── MockTokenPermit.sol
│ ├── MockTreasury.sol
│ ├── Permit2.sol
│ └── Slither.sol
├── scripts
│ └── DeployTransmuterSidechain.t.sol
├── units
│ ├── DiamondCut.t.sol
│ ├── DiamondLoupe.t.sol
│ ├── Layout.t.sol
│ ├── Libraries.t.sol
│ ├── Permit2.t.sol
│ ├── RewardHandlerTest.t.sol
│ ├── Setters.t.sol
│ ├── StablecoinCap.t.sol
│ ├── Transmuter.t.sol
│ ├── TransmuterReentrant.sol
│ └── upgrade
│ │ └── SavingsNameableUpgradeTest.t.sol
└── utils
│ ├── FunctionUtils.sol
│ ├── Helper.sol
│ └── Transmuter.sol
├── utils
└── forwardUtils.js
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | ## Add URI and BIP39 mnemonic, and etherscan API key for every network that you plan to use
2 |
3 | #ETH_NODE_URI_MAINNET=""
4 | #MNEMONIC_MAINNET=""
5 | #MAINNET_ETHERSCAN_API_KEY=""
6 |
7 | #ETH_NODE_URI_POLYGON=""
8 | #MNEMONIC_POLYGON=""
9 | #POLYGON_ETHERSCAN_API_KEY=""
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.github/actions/setup-repo/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup repo
2 | description: Runs all steps to setup the repo (install node_modules, build, etc...)
3 | inputs:
4 | registry-token:
5 | description: "PAT to access registries"
6 | runs:
7 | using: "composite"
8 | steps:
9 | - name: Get yarn cache directory path
10 | id: yarn-cache-dir-path
11 | shell: bash
12 | run: |
13 | echo "::set-output name=dir::$(yarn cache dir)"
14 | echo "::set-output name=version::$(yarn -v)"
15 |
16 | - uses: actions/setup-node@v3
17 | with:
18 | node-version: "20"
19 |
20 | - uses: actions/cache@v2
21 | id: yarn-cache
22 | with:
23 | path: |
24 | **/node_modules
25 | ${{ steps.yarn-cache-dir-path.outputs.dir }}
26 |
27 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
28 | restore-keys: |
29 | ${{ runner.os }}-yarn-
30 |
31 | - name: Install dependencies
32 | shell: bash
33 | run: echo "//npm.pkg.github.com/:_authToken=$SDK_READ_ACCESS_TOKEN" >> .npmrc && yarn install --frozen-lockfile --verbose && rm -f .npmrc
34 | env:
35 | SDK_READ_ACCESS_TOKEN: ${{ inputs.registry-token }}
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Defaults
2 | __pycache__
3 | .idea
4 | .DS_Store
5 | .deps
6 | .docs
7 | .env
8 | node_modules
9 | venv
10 | myforge
11 |
12 | # Build output
13 | /cache-hh
14 | build
15 | export
16 | **/artifacts
17 | .openzeppelin
18 | typechain
19 | slither-audit.txt
20 | slither
21 | cache
22 |
23 | # Test output
24 | coverage
25 | coverage.json
26 | lcov.info
27 | reports/
28 |
29 | # Running output
30 | gas-report.txt
31 | gasReporterOutput.json
32 | addresses.json
33 | blockchain_db
34 | yarn-error.log
35 | broadcast
36 |
37 | # deployments
38 | deployments/localhost
39 | deployments/mainnetForkRemote
40 |
41 | # bin
42 | bin
43 |
44 | # foundry
45 | /out
46 | /cache-forge
47 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/forge-std"]
2 | path = lib/forge-std
3 | url = https://github.com/foundry-rs/forge-std
4 | ignore = dirty
5 | [submodule "lib/solidity-stringutils"]
6 | path = lib/solidity-stringutils
7 | url = https://github.com/Arachnid/solidity-stringutils
8 | ignore = dirty
9 | [submodule "lib/prb-math"]
10 | path = lib/prb-math
11 | url = https://github.com/PaulRBerg/prb-math
12 | ignore = dirty
13 | [submodule "lib/openzeppelin-contracts-upgradeable"]
14 | path = lib/openzeppelin-contracts-upgradeable
15 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
16 | version = v4.7.3
17 | [submodule "lib/openzeppelin-contracts"]
18 | path = lib/openzeppelin-contracts
19 | url = https://github.com/OpenZeppelin/openzeppelin-contracts
20 | version = v4.7.3
21 | [submodule "lib/utils"]
22 | path = lib/utils
23 | url = https://github.com/AngleProtocol/utils
24 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | @angleprotocol:registry=https://npm.pkg.github.com
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | contracts/transmuter/Storage.sol
2 | contracts/transmuter/Layout.sol
3 |
4 | lib
5 | cache
6 | out
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "avoid",
3 | "jsxSingleQuote": true,
4 | "singleQuote": true,
5 | "printWidth": 120,
6 | "semi": true,
7 | "trailingComma": "all",
8 | "useTabs": false,
9 | "overrides": [
10 | {
11 | "files": "*.sol",
12 | "options": {
13 | "printWidth": 120,
14 | "singleQuote": false,
15 | "bracketSpacing": true
16 | }
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/.solhint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "solhint:recommended",
3 | "plugins": [
4 | "prettier"
5 | ],
6 | "rules": {
7 | "max-line-length": [
8 | "error",
9 | 120
10 | ],
11 | "immutable-vars-naming": "off",
12 | "avoid-call-value": "warn",
13 | "avoid-low-level-calls": "off",
14 | "avoid-tx-origin": "warn",
15 | "const-name-snakecase": "warn",
16 | "contract-name-camelcase": "warn",
17 | "imports-on-top": "warn",
18 | "prettier/prettier": "error",
19 | "ordering": "off",
20 | "max-states-count": "off",
21 | "mark-callable-contracts": "off",
22 | "no-empty-blocks": "off",
23 | "no-global-import": "off",
24 | "not-rely-on-time": "off",
25 | "compiler-version": "off",
26 | "private-vars-leading-underscore": "warn",
27 | "reentrancy": "warn",
28 | "no-inline-assembly": "off",
29 | "no-complex-fallback": "off",
30 | "reason-string": "off",
31 | "func-visibility": [
32 | "warn",
33 | {
34 | "ignoreConstructors": true
35 | }
36 | ],
37 | "explicit-types": [
38 | "error",
39 | "explicit"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/.solhintignore:
--------------------------------------------------------------------------------
1 | # Doesn't need to lint dev files
2 | lib
3 | scripts
4 | test
5 |
6 | # Doesn't need to lint build files
7 | node_modules
8 | cache-forge
9 | out
10 |
11 | # Doesn't need to lint utils
12 | contracts/transmuter/Storage.sol
13 | contracts/transmuter/Layout.sol
14 | external
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[typescript]": {
3 | "editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
4 | },
5 | "[solidity]": {
6 | "editor.defaultFormatter": "JuanBlanco.solidity"
7 | },
8 | "editor.formatOnSave": true,
9 | "workbench.colorCustomizations": {
10 | "diffEditor.insertedTextBackground": "#00bb0044",
11 | "diffEditor.removedTextBackground": "#ff000044"
12 | },
13 | "slither.solcPath": "",
14 | "slither.hiddenDetectors": [],
15 | "solidity.compileUsingRemoteVersion": "v0.8.23",
16 | "files.insertFinalNewline": true,
17 | "solidity.remappings": [
18 | "ds-test/=lib/forge-std/lib/ds-test/src/",
19 | "forge-std/=lib/forge-std/src/",
20 | "stringutils/=lib/solidity-stringutils",
21 | "contracts/=contracts/",
22 | "test/=test/",
23 | "interfaces/=contracts/interfaces/",
24 | "oz/=lib/openzeppelin-contracts/contracts/",
25 | "oz-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
26 | "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
27 | "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
28 | "mock/=test/mock/",
29 | "prb/math/=lib/prb-math/src/",
30 | "borrow/=lib/borrow-contracts/contracts",
31 | "utils/=lib/utils"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "Generate Header",
6 | "type": "shell",
7 | "command": "headers ${input:header}",
8 | "presentation": {
9 | "reveal": "never"
10 | }
11 | }
12 | ],
13 | "inputs": [
14 | {
15 | "id": "header",
16 | "description": "Header",
17 | "type": "promptString"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guidelines
2 |
3 | Thanks for your interest in contributing to the Transmuter system! Transmuter is a community built protocol and anyone is welcome to improve it.
4 |
5 | If you need to get in contact with the repository maintainers, please reach out in the [#developers channel](https://discord.gg/HcRB8QMeKU) of Angle Discord Server.
6 |
7 | ## Types of Contributing
8 |
9 | There are many ways to contribute, but here are a few if you want a place to start:
10 |
11 | 1. **Opening an issue.** Before opening an issue, please check that there is not an issue already open. If there is, feel free to comment more details, explanations, or examples within the open issue rather than duplicating it. Suggesting changes to the open development process are within the bounds of opening issues. We are always open to feedback and receptive to suggestions!
12 | 2. **Resolving an issue.** You can resolve an issue either by showing that it is not an issue or by fixing the issue with code changes, additional tests, etc. Any pull request fixing an issue should reference that issue.
13 | 3. **Reviewing open PRs.** You can provide comments, standards guidance, naming suggestions, gas optimizations, or ideas for alternative designs on any open pull request.
14 |
15 | ## Opening an Issue
16 |
17 | When opening an [issue](https://github.com/AngleProtocol/angle-transmuter/issues/new/choose), please title it with a concise problem statement and check that a similar request is not already open or in progress. Not all issues may be deemed worth resolving, so please follow through with responding to any questions or comments that others may have regarding the issue.
18 |
19 | ## Opening a Pull Request
20 |
21 | All pull requests should be opened against the `main` branch. In the pull request, please reference the issue you are fixing.
22 |
23 | Pull requests can be reviewed by community members, but to be merged they will need approval from the repository maintainers. Please understand it will take time to receive a response, although the maintainers will aim to respond and comment as soon as possible.
24 |
25 | **For larger, more substantial changes to the code, it is best to open an issue and start a discussion with the maintainers to align on the change before spending time on the development.**
26 |
27 | Finally, before opening a pull request please do the following:
28 |
29 | - Run the Foundry tests with `yarn test`
30 | - Document any new functions, structs, or interfaces following the natspec standard.
31 | - Add tests! For smaller contributions, they should be tested with unit tests, and fuzz tests where possible. For bigger contributions, they should be tested with invariant tests where possible.
32 |
33 | ## Standards
34 |
35 | All contributions must follow the below standards. Maintainers will close out PRs that do not adhere to these standards.
36 |
37 | 1. All contracts should be formatted with the prettier used in the default vscode config.
38 | 2. These contracts follow the [solidity style guide](https://docs.soliditylang.org/en/v0.8.19/style-guide.html) with one minor exception of using the `\_prependUnderscore` style naming for internal contract functions, internal top-level parameters, and function parameters with naming collisions.
39 | 3. All external facing contracts should inherit from interfaces, which specify and document its functions with natspec.
40 | 4. Picking up stale issues by other authors is fine! Please just communicate with them ahead of time and it is best practice to include co-authors in any commits.
41 | 5. Squash commits where possible to make reviews clean and efficient. PRs that are merged to main will be squashed into 1 commit.
42 |
43 | ## Code of Conduct
44 |
45 | Above all else, please be respectful of the people behind the code. Any kind of aggressive or disrespectful comments, issues, and language will be removed.
46 |
47 | Issues and PRs that are obviously spam and unhelpful to the development process or unrelated to the core code will also be closed.
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Business Source License 1.1
2 |
3 | License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
4 | "Business Source License" is a trademark of MariaDB Corporation Ab.
5 |
6 | ---
7 |
8 | Parameters
9 |
10 | Licensor: Angle Labs, Inc.
11 |
12 | Licensed Work: Transmuter Smart Contracts
13 | The Licensed Work is (c) 2023 Angle Labs
14 |
15 | Additional Use Grant: Any uses listed and defined at
16 | transmuter-license-grants.anglegovernance.eth
17 |
18 | Change Date: The earlier of 2026-06-01 or a date specified at
19 | transmuter-license-date.anglegovernance.eth
20 |
21 | Change License: GNU General Public License v2.0 or later
22 |
23 | ---
24 |
25 | Terms
26 |
27 | The Licensor hereby grants you the right to copy, modify, create derivative
28 | works, redistribute, and make non-production use of the Licensed Work. The
29 | Licensor may make an Additional Use Grant, above, permitting limited
30 | production use.
31 |
32 | Effective on the Change Date, or the fourth anniversary of the first publicly
33 | available distribution of a specific version of the Licensed Work under this
34 | License, whichever comes first, the Licensor hereby grants you rights under
35 | the terms of the Change License, and the rights granted in the paragraph
36 | above terminate.
37 |
38 | If your use of the Licensed Work does not comply with the requirements
39 | currently in effect as described in this License, you must purchase a
40 | commercial license from the Licensor, its affiliated entities, or authorized
41 | resellers, or you must refrain from using the Licensed Work.
42 |
43 | All copies of the original and modified Licensed Work, and derivative works
44 | of the Licensed Work, are subject to this License. This License applies
45 | separately for each version of the Licensed Work and the Change Date may vary
46 | for each version of the Licensed Work released by Licensor.
47 |
48 | You must conspicuously display this License on each original or modified copy
49 | of the Licensed Work. If you receive the Licensed Work in original or
50 | modified form from a third party, the terms and conditions set forth in this
51 | License apply to your use of that work.
52 |
53 | Any use of the Licensed Work in violation of this License will automatically
54 | terminate your rights under this License for the current and all other
55 | versions of the Licensed Work.
56 |
57 | This License does not grant you any right in any trademark or logo of
58 | Licensor or its affiliates (provided that you may use a trademark or logo of
59 | Licensor as expressly required by this License).
60 |
61 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
62 | AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
63 | EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
64 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
65 | TITLE.
66 |
67 | MariaDB hereby grants you permission to use this License’s text to license
68 | your works, and to refer to it using the trademark "Business Source License",
69 | as long as you comply with the Covenants of Licensor below.
70 |
71 | ---
72 |
73 | Covenants of Licensor
74 |
75 | In consideration of the right to use this License’s text and the "Business
76 | Source License" name and trademark, Licensor covenants to MariaDB, and to all
77 | other recipients of the licensed work to be provided by Licensor:
78 |
79 | 1. To specify as the Change License the GPL Version 2.0 or any later version,
80 | or a license that is compatible with GPL Version 2.0 or a later version,
81 | where "compatible" means that software provided under the Change License can
82 | be included in a program with software provided under GPL Version 2.0 or a
83 | later version. Licensor may specify additional Change Licenses without
84 | limitation.
85 |
86 | 2. To either: (a) specify an additional grant of rights to use that does not
87 | impose any additional restriction on the right granted in this License, as
88 | the Additional Use Grant; or (b) insert the text "None".
89 |
90 | 3. To specify a Change Date.
91 |
92 | 4. Not to modify this License in any other way.
93 |
94 | ---
95 |
96 | Notice
97 |
98 | The Business Source License (this document, or the "License") is not an Open
99 | Source license. However, the Licensed Work will eventually be made available
100 | under an Open Source License, as stated in this License.
101 |
--------------------------------------------------------------------------------
/contracts/interfaces/IAccessControlManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IAccessControlManager
6 | /// @author Angle Labs, Inc.
7 | interface IAccessControlManager {
8 | /// @notice Checks whether an address is governor of the Angle Protocol or not
9 | /// @param admin Address to check
10 | /// @return Whether the address has the `GOVERNOR_ROLE` or not
11 | function isGovernor(address admin) external view returns (bool);
12 |
13 | /// @notice Checks whether an address is governor or a guardian of the Angle Protocol or not
14 | /// @param admin Address to check
15 | /// @return Whether the address has the `GUARDIAN_ROLE` or not
16 | /// @dev Governance should make sure when adding a governor to also give this governor the guardian
17 | /// role by calling the `addGovernor` function
18 | function isGovernorOrGuardian(address admin) external view returns (bool);
19 | }
20 |
--------------------------------------------------------------------------------
/contracts/interfaces/IAgToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | import { IERC20 } from "oz/token/ERC20/IERC20.sol";
6 |
7 | /// @title IAgToken
8 | /// @author Angle Labs, Inc.
9 | /// @notice Interface for the stablecoins `AgToken` contracts
10 | interface IAgToken is IERC20 {
11 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
12 | MINTER ROLE ONLY FUNCTIONS
13 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
14 |
15 | /// @notice Lets a whitelisted contract mint agTokens
16 | /// @param account Address to mint to
17 | /// @param amount Amount to mint
18 | function mint(address account, uint256 amount) external;
19 |
20 | /// @notice Burns `amount` tokens from a `burner` address after being asked to by `sender`
21 | /// @param amount Amount of tokens to burn
22 | /// @param burner Address to burn from
23 | /// @param sender Address which requested the burn from `burner`
24 | /// @dev This method is to be called by a contract with the minter right after being requested
25 | /// to do so by a `sender` address willing to burn tokens from another `burner` address
26 | /// @dev The method checks the allowance between the `sender` and the `burner`
27 | function burnFrom(uint256 amount, address burner, address sender) external;
28 |
29 | /// @notice Burns `amount` tokens from a `burner` address
30 | /// @param amount Amount of tokens to burn
31 | /// @param burner Address to burn from
32 | /// @dev This method is to be called by a contract with a minter right on the AgToken after being
33 | /// requested to do so by an address willing to burn tokens from its address
34 | function burnSelf(uint256 amount, address burner) external;
35 |
36 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37 | TREASURY ONLY FUNCTIONS
38 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
39 |
40 | /// @notice Adds a minter in the contract
41 | /// @param minter Minter address to add
42 | /// @dev Zero address checks are performed directly in the `Treasury` contract
43 | function addMinter(address minter) external;
44 |
45 | /// @notice Removes a minter from the contract
46 | /// @param minter Minter address to remove
47 | /// @dev This function can also be called by a minter wishing to revoke itself
48 | function removeMinter(address minter) external;
49 |
50 | /// @notice Sets a new treasury contract
51 | /// @param _treasury New treasury address
52 | function setTreasury(address _treasury) external;
53 |
54 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55 | EXTERNAL FUNCTIONS
56 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
57 |
58 | /// @notice Checks whether an address has the right to mint agTokens
59 | /// @param minter Address for which the minting right should be checked
60 | /// @return Whether the address has the right to mint agTokens or not
61 | function isMinter(address minter) external view returns (bool);
62 |
63 | /// @notice Amount of decimals of the stablecoin
64 | function decimals() external view returns (uint8);
65 | }
66 |
--------------------------------------------------------------------------------
/contracts/interfaces/IDiamondCut.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | import "../transmuter/Storage.sol";
6 |
7 | /// @title IDiamondCut
8 | /// @author Angle Labs, Inc.
9 | /// @dev Reference: EIP-2535 Diamonds
10 | /// @dev Forked from https://github.com/mudgen/diamond-3/blob/master/contracts/interfaces/IDiamondCut.sol by mudgen
11 | interface IDiamondCut {
12 | /// @notice Add/replace/remove any number of functions and optionally execute a function with delegatecall
13 | /// @param _diamondCut Contains the facet addresses and function selectors
14 | /// @param _init The address of the contract or facet to execute _calldata
15 | /// @param _calldata A function call, including function selector and arguments, executed with delegatecall on _init
16 | function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;
17 | }
18 |
--------------------------------------------------------------------------------
/contracts/interfaces/IDiamondEtherscan.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IDiamondEtherscan
6 | /// @author Angle Labs, Inc.
7 | interface IDiamondEtherscan {
8 | /// @notice Sets a dummy implementation with the same layout at the diamond proxy contract with all its facets
9 | function setDummyImplementation(address _implementation) external;
10 |
11 | /// @notice Address of the dummy implementation used to make the DiamondProxy contract interpretable by Etherscan
12 | function implementation() external view returns (address);
13 | }
14 |
--------------------------------------------------------------------------------
/contracts/interfaces/IDiamondLoupe.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | import "../transmuter/Storage.sol";
6 |
7 | /// @notice IDiamondLoupe
8 | /// @author Angle Labs, Inc.
9 | /// @dev Reference: EIP-2535 Diamonds
10 | /// @dev Forked from https://github.com/mudgen/diamond-3/blob/master/contracts/interfaces/IDiamondLoupe.sol by mudgen
11 |
12 | interface IDiamondLoupe {
13 | /// @notice Gets all facet addresses and their four byte function selectors.
14 | /// @return facets_ Facet
15 | function facets() external view returns (Facet[] memory facets_);
16 |
17 | /// @notice Gets all the function selectors supported by a specific facet.
18 | /// @param _facet The facet address.
19 | /// @return facetFunctionSelectors_
20 | function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);
21 |
22 | /// @notice Get all the facet addresses used by a diamond.
23 | /// @return facetAddresses_
24 | function facetAddresses() external view returns (address[] memory facetAddresses_);
25 |
26 | /// @notice Gets the facet that supports the given selector.
27 | /// @dev If facet is not found return address(0).
28 | /// @param _functionSelector The function selector.
29 | /// @return facetAddress_ The facet address.
30 | function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
31 | }
32 |
--------------------------------------------------------------------------------
/contracts/interfaces/IHarvester.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | interface IHarvester {
6 | function setYieldBearingAssetData(
7 | address yieldBearingAsset,
8 | address stablecoin,
9 | uint64 targetExposure,
10 | uint64 minExposureYieldAsset,
11 | uint64 maxExposureYieldAsset,
12 | uint64 overrideExposures
13 | ) external;
14 |
15 | function updateLimitExposuresYieldAsset(address yieldBearingAsset) external;
16 |
17 | function setMaxSlippage(uint96 newMaxSlippage) external;
18 |
19 | function harvest(address yieldBearingAsset, uint256 scale, bytes calldata extraData) external;
20 | }
21 |
--------------------------------------------------------------------------------
/contracts/interfaces/IManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IManager
6 | /// @author Angle Labs, Inc.
7 | interface IManager {
8 | /// @notice Returns the amount of collateral managed by the Manager
9 | /// @return balances Balances of all the subCollaterals handled by the manager
10 | /// @dev MUST NOT revert
11 | function totalAssets() external view returns (uint256[] memory balances, uint256 totalValue);
12 |
13 | /// @notice Hook to invest `amount` of `collateral`
14 | /// @dev MUST revert if the manager cannot accept these funds
15 | /// @dev MUST have received the funds beforehand
16 | function invest(uint256 amount) external;
17 |
18 | /// @notice Sends `amount` of `collateral` to the `to` address
19 | /// @dev Called when `agToken` are burnt and during redemptions
20 | // @dev MUST revert if there are not funds enough available
21 | /// @dev MUST be callable only by the transmuter
22 | function release(address asset, address to, uint256 amount) external;
23 |
24 | /// @notice Gives the maximum amount of collateral immediately available for a transfer
25 | /// @dev Useful for integrators using `quoteIn` and `quoteOut`
26 | function maxAvailable() external view returns (uint256);
27 | }
28 |
--------------------------------------------------------------------------------
/contracts/interfaces/IPool.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | interface IPool {
6 | function deposit(uint256 assets, address lender) external returns (uint256 shares, uint256 transferInDayTimestamp);
7 |
8 | function requestRedeem(uint256 shares) external returns (uint256 assets);
9 |
10 | function convertToAssets(uint256 shares) external view returns (uint256 assets);
11 |
12 | function convertToShares(uint256 assets) external view returns (uint256 shares);
13 | }
14 |
--------------------------------------------------------------------------------
/contracts/interfaces/IRedeemer.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IRedeemer
6 | /// @author Angle Labs, Inc.
7 | interface IRedeemer {
8 | /// @notice Redeems `amount` of stablecoins from the system
9 | /// @param receiver Address which should be receiving the output tokens
10 | /// @param deadline Timestamp before which the redemption should have occured
11 | /// @param minAmountOuts Minimum amount of each token given back in the redemption to obtain
12 | /// @return tokens List of tokens returned
13 | /// @return amounts Amount given for each token in the `tokens` array
14 | function redeem(
15 | uint256 amount,
16 | address receiver,
17 | uint256 deadline,
18 | uint256[] memory minAmountOuts
19 | ) external returns (address[] memory tokens, uint256[] memory amounts);
20 |
21 | /// @notice Same as the redeem function above with the additional feature to specify a list of `forfeitTokens` for
22 | /// which the Transmuter system will not try to do a transfer to `receiver`.
23 | function redeemWithForfeit(
24 | uint256 amount,
25 | address receiver,
26 | uint256 deadline,
27 | uint256[] memory minAmountOuts,
28 | address[] memory forfeitTokens
29 | ) external returns (address[] memory tokens, uint256[] memory amounts);
30 |
31 | /// @notice Simulate the exact output that a redemption of `amount` of stablecoins would give at a given block
32 | /// @return tokens List of tokens that would be given
33 | /// @return amounts Amount that would be obtained for each token in the `tokens` array
34 | function quoteRedemptionCurve(
35 | uint256 amount
36 | ) external view returns (address[] memory tokens, uint256[] memory amounts);
37 |
38 | /// @notice Updates the normalizer variable by `amount`
39 | function updateNormalizer(uint256 amount, bool increase) external returns (uint256);
40 | }
41 |
--------------------------------------------------------------------------------
/contracts/interfaces/IRewardHandler.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IRewardHandler
6 | /// @author Angle Labs, Inc.
7 | interface IRewardHandler {
8 | /// @notice Sells some external tokens through a 1inch call
9 | /// @param minAmountOut Minimum amount of the outToken to get
10 | /// @param payload Payload to pass to 1inch
11 | /// @return amountOut Amount obtained of the outToken
12 | function sellRewards(uint256 minAmountOut, bytes memory payload) external returns (uint256 amountOut);
13 | }
14 |
--------------------------------------------------------------------------------
/contracts/interfaces/ISetters.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | import { IERC20 } from "oz/interfaces/IERC20.sol";
6 |
7 | import "../transmuter/Storage.sol";
8 |
9 | /// @title ISettersGovernor
10 | /// @author Angle Labs, Inc.
11 | interface ISettersGovernor {
12 | /// @notice Recovers `amount` of `token` from the Transmuter contract
13 | function recoverERC20(address collateral, IERC20 token, address to, uint256 amount) external;
14 |
15 | /// @notice Sets a new access control manager address
16 | function setAccessControlManager(address _newAccessControlManager) external;
17 |
18 | /// @notice Sets (or unsets) a collateral manager `collateral`
19 | function setCollateralManager(address collateral, ManagerStorage memory managerData) external;
20 |
21 | /// @notice Sets the allowance of the contract on `token` for `spender` to `amount`
22 | function changeAllowance(IERC20 token, address spender, uint256 amount) external;
23 |
24 | /// @notice Changes the trusted status for `sender` when for selling rewards or updating the normalizer
25 | function toggleTrusted(address sender, TrustedType t) external;
26 |
27 | /// @notice Changes whether a `collateral` can only be handled during burns and redemptions by whitelisted addresses
28 | /// and sets the data used to read into the whitelist
29 | function setWhitelistStatus(address collateral, uint8 whitelistStatus, bytes memory whitelistData) external;
30 |
31 | /// @notice Add `collateral` as a supported collateral in the system
32 | function addCollateral(address collateral) external;
33 |
34 | /// @notice Adjusts the amount of stablecoins issued from `collateral` by `amount`
35 | function adjustStablecoins(address collateral, uint128 amount, bool increase) external;
36 |
37 | /// @notice Revokes `collateral` from the system
38 | function revokeCollateral(address collateral) external;
39 |
40 | /// @notice Sets the `oracleConfig` used to read the value of `collateral` for the mint, burn and redemption
41 | /// operations
42 | function setOracle(address collateral, bytes memory oracleConfig) external;
43 |
44 | /// @notice Update oracle data for a given `collateral`
45 | function updateOracle(address collateral) external;
46 | }
47 |
48 | /// @title ISettersGovernor
49 | /// @author Angle Labs, Inc.
50 | interface ISettersGuardian {
51 | /// @notice Changes the pause status for mint or burn transactions for `collateral`
52 | function togglePause(address collateral, ActionType action) external;
53 |
54 | /// @notice Sets the mint or burn fees for `collateral`
55 | function setFees(address collateral, uint64[] memory xFee, int64[] memory yFee, bool mint) external;
56 |
57 | /// @notice Sets the parameters for the redemption curve
58 | function setRedemptionCurveParams(uint64[] memory xFee, int64[] memory yFee) external;
59 |
60 | /// @notice Changes the whitelist status for a collateral with `whitelistType` for an address `who`
61 | function toggleWhitelist(WhitelistType whitelistType, address who) external;
62 |
63 | /// @notice Sets the stablecoin cap that can be issued from a `collateral`
64 | function setStablecoinCap(address collateral, uint256 stablecoinCap) external;
65 | }
66 |
--------------------------------------------------------------------------------
/contracts/interfaces/ISwapper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title ISwapper
6 | /// @author Angle Labs, Inc.
7 | interface ISwapper {
8 | /// @notice Swaps (that is to say mints or burns) an exact amount of `tokenIn` for an amount of `tokenOut`
9 | /// @param amountIn Amount of `tokenIn` to bring
10 | /// @param amountOutMin Minimum amount of `tokenOut` to get: if `amountOut` is inferior to this amount, the
11 | /// function will revert
12 | /// @param tokenIn Token to bring for the swap
13 | /// @param tokenOut Token to get out of the swap
14 | /// @param to Address to which `tokenOut` must be sent
15 | /// @param deadline Timestamp before which the transaction must be executed
16 | /// @return amountOut Amount of `tokenOut` obtained through the swap
17 | function swapExactInput(
18 | uint256 amountIn,
19 | uint256 amountOutMin,
20 | address tokenIn,
21 | address tokenOut,
22 | address to,
23 | uint256 deadline
24 | ) external returns (uint256 amountOut);
25 |
26 | /// @notice Same as `swapExactInput`, but using Permit2 signatures for `tokenIn`
27 | /// @dev Can only be used to mint, hence `tokenOut` is not needed
28 | function swapExactInputWithPermit(
29 | uint256 amountIn,
30 | uint256 amountOutMin,
31 | address tokenIn,
32 | address to,
33 | uint256 deadline,
34 | bytes calldata permitData
35 | ) external returns (uint256 amountOut);
36 |
37 | /// @notice Swaps (that is to say mints or burns) an amount of `tokenIn` for an exact amount of `tokenOut`
38 | /// @param amountOut Amount of `tokenOut` to obtain from the swap
39 | /// @param amountInMax Maximum amount of `tokenIn` to bring in order to get `amountOut` of `tokenOut`
40 | /// @param tokenIn Token to bring for the swap
41 | /// @param tokenOut Token to get out of the swap
42 | /// @param to Address to which `tokenOut` must be sent
43 | /// @param deadline Timestamp before which the transaction must be executed
44 | /// @return amountIn Amount of `tokenIn` used to perform the swap
45 | function swapExactOutput(
46 | uint256 amountOut,
47 | uint256 amountInMax,
48 | address tokenIn,
49 | address tokenOut,
50 | address to,
51 | uint256 deadline
52 | ) external returns (uint256 amountIn);
53 |
54 | /// @notice Same as `swapExactOutput`, but using Permit2 signatures for `tokenIn`
55 | /// @dev Can only be used to mint, hence `tokenOut` is not needed
56 | function swapExactOutputWithPermit(
57 | uint256 amountOut,
58 | uint256 amountInMax,
59 | address tokenIn,
60 | address to,
61 | uint256 deadline,
62 | bytes calldata permitData
63 | ) external returns (uint256 amountIn);
64 |
65 | /// @notice Simulates what a call to `swapExactInput` with `amountIn` of `tokenIn` for `tokenOut` would give.
66 | /// If called right before and at the same block, the `amountOut` outputted by this function is exactly the
67 | /// amount that will be obtained with `swapExactInput`
68 | function quoteIn(uint256 amountIn, address tokenIn, address tokenOut) external view returns (uint256 amountOut);
69 |
70 | /// @notice Simulates what a call to `swapExactOutput` for `amountOut` of `tokenOut` with `tokenIn` would give.
71 | /// If called right before and at the same block, the `amountIn` outputted by this function is exactly the
72 | /// amount that will be obtained with `swapExactOutput`
73 | function quoteOut(uint256 amountOut, address tokenIn, address tokenOut) external view returns (uint256 amountIn);
74 | }
75 |
--------------------------------------------------------------------------------
/contracts/interfaces/ITransmuter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | /*
4 | * █
5 | ***** ▓▓▓
6 | * ▓▓▓▓▓▓▓
7 | * ///. ▓▓▓▓▓▓▓▓▓▓▓▓▓
8 | ***** //////// ▓▓▓▓▓▓▓
9 | * ///////////// ▓▓▓
10 | ▓▓ ////////////////// █ ▓▓
11 | ▓▓ ▓▓ /////////////////////// ▓▓ ▓▓
12 | ▓▓ ▓▓ //////////////////////////// ▓▓ ▓▓
13 | ▓▓ ▓▓ /////////▓▓▓///////▓▓▓///////// ▓▓ ▓▓
14 | ▓▓ ,////////////////////////////////////// ▓▓ ▓▓
15 | ▓▓ ////////////////////////////////////////// ▓▓
16 | ▓▓ //////////////////////▓▓▓▓/////////////////////
17 | ,////////////////////////////////////////////////////
18 | .//////////////////////////////////////////////////////////
19 | .//////////////////////////██.,//////////////////////////█
20 | .//////////////////////████..,./////////////////////██
21 | ...////////////////███████.....,.////////////////███
22 | ,.,////////////████████ ........,///////////████
23 | .,.,//////█████████ ,.......///////████
24 | ,..//████████ ........./████
25 | ..,██████ .....,███
26 | .██ ,.,█
27 |
28 |
29 |
30 | ▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓▓▓
31 | ▓▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓ ▓▓ ▓▓▓▓
32 | ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓
33 | ▓▓▓ ▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
34 | */
35 |
36 | pragma solidity >=0.5.0;
37 |
38 | import { IDiamondCut } from "./IDiamondCut.sol";
39 | import { IDiamondEtherscan } from "./IDiamondEtherscan.sol";
40 | import { IDiamondLoupe } from "./IDiamondLoupe.sol";
41 | import { IGetters } from "./IGetters.sol";
42 | import { IRedeemer } from "./IRedeemer.sol";
43 | import { IRewardHandler } from "./IRewardHandler.sol";
44 | import { ISettersGovernor, ISettersGuardian } from "./ISetters.sol";
45 | import { ISwapper } from "./ISwapper.sol";
46 |
47 | /// @title ITransmuter
48 | /// @author Angle Labs, Inc.
49 | interface ITransmuter is
50 | IDiamondCut,
51 | IDiamondEtherscan,
52 | IDiamondLoupe,
53 | IGetters,
54 | IRedeemer,
55 | IRewardHandler,
56 | ISettersGovernor,
57 | ISettersGuardian,
58 | ISwapper
59 | {
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/contracts/interfaces/ITransmuterOracle.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title ITransmuterOracle
6 | /// @author Angle Labs, Inc.
7 | interface ITransmuterOracle {
8 | /// @notice Reads the oracle value for asset to use in a redemption to compute the collateral ratio
9 | function readRedemption() external view returns (uint256);
10 |
11 | /// @notice Reads the oracle value for asset to use in a mint. It should be comprehensive of the
12 | /// deviation from the target price
13 | function readMint() external view returns (uint256);
14 |
15 | /// @notice Reads the oracle value for asset to use in a burn transaction as well as the ratio
16 | /// between the current price and the target price for the asset
17 | function readBurn() external view returns (uint256 oracleValue, uint256 ratio);
18 |
19 | /// @notice Reads the oracle value for asset
20 | function read() external view returns (uint256);
21 | }
22 |
--------------------------------------------------------------------------------
/contracts/interfaces/IXEVT.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: UNLICENSED
2 | pragma solidity ^0.8.19;
3 |
4 | interface IXEVT {
5 | function isAllowed(address) external returns (bool);
6 | }
7 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/chainlink/AggregatorV3Interface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | interface AggregatorV3Interface {
6 | function decimals() external view returns (uint8);
7 |
8 | function description() external view returns (string memory);
9 |
10 | function version() external view returns (uint256);
11 |
12 | // getRoundData and latestRoundData should both raise "No data present"
13 | // if they do not have data to report, instead of returning unset values
14 | // which could be misinterpreted as actual reported values.
15 | function getRoundData(
16 | uint80 _roundId
17 | )
18 | external
19 | view
20 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
21 |
22 | function latestRoundData()
23 | external
24 | view
25 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
26 | }
27 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/coinbase/ICbETH.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title ICbETH
6 | /// @notice Interface for the `cbETH` contract
7 | interface ICbETH {
8 | function exchangeRate() external view returns (uint256);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/frax/ISfrxETH.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title ISfrxETH
6 | /// @notice Interface for the `sfrxETH` contract
7 | interface ISfrxETH {
8 | function pricePerShare() external view returns (uint256);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/keyring/IKeyringGuard.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IKeyringGuard
6 | /// @notice Interface for the `KeyringGuard` contract
7 | interface IKeyringGuard {
8 | function isAuthorized(address from, address to) external returns (bool passed);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/lido/IStETH.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IStETH
6 | /// @notice Interface for the `StETH` contract
7 | interface IStETH {
8 | function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
9 |
10 | function submit(address) external payable returns (uint256);
11 |
12 | function getSharesByPooledEth(uint256 _ethAmount) external view returns (uint256);
13 | }
14 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/morpho/IMorphoOracle.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IMorphoOracle
6 | /// @notice Interface for the oracle contracts used within Morpho
7 | interface IMorphoOracle {
8 | function price() external view returns (uint256);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/permit2/IPermit2.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.5.0;
3 |
4 | /// @notice The token and amount details for a transfer signed in the permit transfer signature
5 | struct TokenPermissions {
6 | // ERC20 token address
7 | address token;
8 | // the maximum amount that can be spent
9 | uint256 amount;
10 | }
11 |
12 | /// @notice The signed permit message for a single token transfer
13 | struct PermitTransferFrom {
14 | TokenPermissions permitted;
15 | // a unique value for every token owner's signature to prevent signature replays
16 | uint256 nonce;
17 | // deadline on the permit signature
18 | uint256 deadline;
19 | }
20 |
21 | /// @notice Specifies the recipient address and amount for batched transfers.
22 | /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
23 | /// @dev Reverts if the requested amount is greater than the permitted signed amount.
24 | struct SignatureTransferDetails {
25 | // recipient address
26 | address to;
27 | // spender requested amount
28 | uint256 requestedAmount;
29 | }
30 |
31 | /// @title SignatureTransfer
32 | /// @notice Handles ERC20 token transfers through signature based actions
33 | /// @dev Requires user's token approval on the Permit2 contract
34 | interface IPermit2 {
35 | /// @notice Transfers a token using a signed permit message
36 | /// @dev Reverts if the requested amount is greater than the permitted signed amount
37 | /// @param permit The permit data signed over by the owner
38 | /// @param owner The owner of the tokens to transfer
39 | /// @param transferDetails The spender's requested transfer details for the permitted token
40 | /// @param signature The signature to verify
41 | function permitTransferFrom(
42 | PermitTransferFrom memory permit,
43 | SignatureTransferDetails calldata transferDetails,
44 | address owner,
45 | bytes calldata signature
46 | ) external;
47 | }
48 |
--------------------------------------------------------------------------------
/contracts/interfaces/external/rocketPool/IRETH.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | /// @title IRETH
6 | /// @notice Interface for the `rETH` contract
7 | interface IRETH {
8 | function getExchangeRate() external view returns (uint256);
9 | }
10 |
--------------------------------------------------------------------------------
/contracts/savings/BaseSavings.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | /*
4 | * █
5 | ***** ▓▓▓
6 | * ▓▓▓▓▓▓▓
7 | * ///. ▓▓▓▓▓▓▓▓▓▓▓▓▓
8 | ***** //////// ▓▓▓▓▓▓▓
9 | * ///////////// ▓▓▓
10 | ▓▓ ////////////////// █ ▓▓
11 | ▓▓ ▓▓ /////////////////////// ▓▓ ▓▓
12 | ▓▓ ▓▓ //////////////////////////// ▓▓ ▓▓
13 | ▓▓ ▓▓ /////////▓▓▓///////▓▓▓///////// ▓▓ ▓▓
14 | ▓▓ ,////////////////////////////////////// ▓▓ ▓▓
15 | ▓▓ ////////////////////////////////////////// ▓▓
16 | ▓▓ //////////////////////▓▓▓▓/////////////////////
17 | ,////////////////////////////////////////////////////
18 | .//////////////////////////////////////////////////////////
19 | .//////////////////////////██.,//////////////////////////█
20 | .//////////////////////████..,./////////////////////██
21 | ...////////////////███████.....,.////////////////███
22 | ,.,////////////████████ ........,///////////████
23 | .,.,//////█████████ ,.......///////████
24 | ,..//████████ ........./████
25 | ..,██████ .....,███
26 | .██ ,.,█
27 |
28 |
29 |
30 | ▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓▓▓
31 | ▓▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓ ▓▓ ▓▓▓▓
32 | ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓
33 | ▓▓▓ ▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
34 | */
35 |
36 | pragma solidity ^0.8.19;
37 |
38 | import "oz-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
39 | import "oz/interfaces/IERC20.sol";
40 | import "oz/token/ERC20/utils/SafeERC20.sol";
41 |
42 | import { IAgToken } from "interfaces/IAgToken.sol";
43 |
44 | import { AccessControl, IAccessControlManager } from "../utils/AccessControl.sol";
45 |
46 | import "../utils/Constants.sol";
47 | import "../utils/Errors.sol";
48 |
49 | /// @title BaseSavings
50 | /// @author Angle Labs, Inc.
51 | /// @notice Angle Savings contracts are contracts where users can deposit an `asset` and earn a yield on this asset
52 | /// when it is distributed
53 | /// @dev These contracts are functional within the Transmuter system if they have mint right on `asset` and
54 | /// if they are trusted by the Transmuter contract
55 | /// @dev Implementations assume that `asset` is safe to interact with, on which there cannot be reentrancy attacks
56 | /// @dev The ERC4626 interface does not allow users to specify a slippage protection parameter for the main entry points
57 | /// (like `deposit`, `mint`, `redeem` or `withdraw`). Even though there should be no specific sandwiching
58 | /// issue with current implementations, it is still recommended to interact with Angle Savings contracts
59 | /// through a router that can implement such a protection.
60 | abstract contract BaseSavings is ERC4626Upgradeable, AccessControl {
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/contracts/savings/nameable/SavingsNameable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "../Savings.sol";
6 |
7 | /// @title SavingsNameable
8 | /// @author Angle Labs, Inc.
9 | contract SavingsNameable is Savings {
10 | string internal __name;
11 |
12 | string internal __symbol;
13 |
14 | uint256[48] private __gapNameable;
15 |
16 | /// @inheritdoc ERC20Upgradeable
17 | function name() public view override(ERC20Upgradeable, IERC20MetadataUpgradeable) returns (string memory) {
18 | return __name;
19 | }
20 |
21 | /// @inheritdoc ERC20Upgradeable
22 | function symbol() public view override(ERC20Upgradeable, IERC20MetadataUpgradeable) returns (string memory) {
23 | return __symbol;
24 | }
25 |
26 | /// @notice Updates the name and symbol of the token
27 | function setNameAndSymbol(string memory newName, string memory newSymbol) external onlyGovernor {
28 | _setNameAndSymbol(newName, newSymbol);
29 | }
30 |
31 | function _setNameAndSymbol(string memory newName, string memory newSymbol) internal override {
32 | __name = newName;
33 | __symbol = newSymbol;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/contracts/transmuter/DiamondProxy.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { LibDiamond } from "./libraries/LibDiamond.sol";
6 | import { LibStorage as s } from "./libraries/LibStorage.sol";
7 |
8 | import "../utils/Errors.sol";
9 | import "./Storage.sol";
10 |
11 | /// @title DiamondProxy
12 | /// @author Angle Labs, Inc.
13 | /// @notice Implementation of a Diamond Proxy
14 | /// @dev Reference: EIP-2535 Diamonds
15 | /// @dev Forked from https://github.com/mudgen/diamond-3/blob/master/contracts/Diamond.sol by mudgen
16 | contract DiamondProxy {
17 | constructor(FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) payable {
18 | LibDiamond.diamondCut(_diamondCut, _init, _calldata);
19 | }
20 |
21 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
22 | FALLBACK
23 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
24 |
25 | /// @dev 1. Find the facet for the function that is called.
26 | /// @dev 2. Delegate the execution to the found facet via `delegatecall`.
27 | fallback() external payable {
28 | DiamondStorage storage ds = s.diamondStorage();
29 | // Get facet from function selector
30 | address facetAddress = ds.selectorInfo[msg.sig].facetAddress;
31 | if (facetAddress == address(0)) {
32 | revert FunctionNotFound(msg.sig);
33 | }
34 |
35 | assembly {
36 | // The pointer to the free memory slot
37 | let ptr := mload(0x40)
38 | // Copy function signature and arguments from calldata at zero position into memory at pointer position
39 | calldatacopy(ptr, 0, calldatasize())
40 | // Delegatecall method of the implementation contract returns 0 on error
41 | let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
42 | // Get the size of the last return data
43 | let size := returndatasize()
44 | // Copy the size length of bytes from return data at zero position to pointer position
45 | returndatacopy(ptr, 0, size)
46 | // Depending on the result value
47 | switch result
48 | case 0 {
49 | // End execution and revert state changes
50 | revert(ptr, size)
51 | }
52 | default {
53 | // Return data with length of size at pointers position
54 | return(ptr, size)
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/contracts/transmuter/Layout.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "../utils/Constants.sol";
6 | import { DiamondStorage, TransmuterStorage, Collateral, FacetInfo, WhitelistType } from "./Storage.sol";
7 |
8 | /// @notice Contract mimicking the overall storage layout of the transmuter system.
9 | /// @dev Not meant to be deployed or used. The goals are:
10 | /// - To ensure the storage layout is well understood by everyone
11 | /// - To force test failures if the layout is changed
12 | /// @dev
13 | /// - uint256(TRANSMUTER_STORAGE_POSITION)
14 | /// = 87725637715361972314474735372533017845526400132006062725239677556399819577533
15 | /// - uint256(DIAMOND_STORAGE_POSITION)
16 | /// = 90909012999857140622417080374671856515688564136957639390032885430481714942747
17 | /// - uint256(IMPLEMENTATION_STORAGE_POSITION)
18 | /// = 24440054405305269366569402256811496959409073762505157381672968839269610695612
19 | contract Layout {
20 | // uint256(IMPLEMENTATION_STORAGE_POSITION)
21 | uint256[24440054405305269366569402256811496959409073762505157381672968839269610695612] private __gap1;
22 | address public implementation;
23 | // uint256(TRANSMUTER_STORAGE_POSITION) - 1 - uint256(IMPLEMENTATION_STORAGE_POSITION)
24 | uint256[63285583310056702947905333115721520886117326369500905343566708717130208881920] private __gap2;
25 | address public agToken; // slot 1
26 | uint8 public isRedemptionLive; // slot 1
27 | uint8 public nonReentrant; // slot 1
28 | uint128 public normalizedStables; // slot 2
29 | uint128 public normalizer; // slot 2
30 | address[] public collateralList; // slot 3
31 | uint64[] public xRedemptionCurve; // slot 4
32 | int64[] public yRedemptionCurve; // slot 5
33 | mapping(address => Collateral) public collaterals; // slot 6
34 | mapping(address => uint256) public isTrusted; // slot 7
35 | mapping(address => uint256) public isSellerTrusted; // slot 8
36 | mapping(WhitelistType => mapping(address => uint256)) public isWhitelistedForType; // slot 9
37 | // uint256(TRANSMUTER_STORAGE_POSITION) - TransmuterStorage offset (9) - uint256(DIAMOND_STORAGE_POSITION)
38 | uint256[3183375284495168307942345002138838670162164004951576664793207874081895365205] private __gap3;
39 | bytes4[] public selectors; // slot 1
40 | mapping(bytes4 => FacetInfo) public selectorInfo; // slot 2
41 | address public accessControlManager; // slot 3
42 | }
43 |
--------------------------------------------------------------------------------
/contracts/transmuter/configs/ProductionTypes.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "interfaces/external/chainlink/AggregatorV3Interface.sol";
6 |
7 | import { LibDiamondEtherscan } from "../libraries/LibDiamondEtherscan.sol";
8 | import "../libraries/LibOracle.sol";
9 | import { LibSetters } from "../libraries/LibSetters.sol";
10 | import { LibStorage as s } from "../libraries/LibStorage.sol";
11 |
12 | import { DummyDiamondImplementation } from "../../../scripts/generated/DummyDiamondImplementation.sol";
13 |
14 | import "../../utils/Constants.sol";
15 | import "../Storage.sol" as Storage;
16 |
17 | struct CollateralSetupProd {
18 | address token;
19 | bytes oracleConfig;
20 | uint64[] xMintFee;
21 | int64[] yMintFee;
22 | uint64[] xBurnFee;
23 | int64[] yBurnFee;
24 | }
25 |
--------------------------------------------------------------------------------
/contracts/transmuter/facets/AccessControlModifiers.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { LibDiamond } from "../libraries/LibDiamond.sol";
6 | import { LibStorage as s, TransmuterStorage } from "../libraries/LibStorage.sol";
7 | import "../../utils/Errors.sol";
8 | import "../../utils/Constants.sol";
9 |
10 | /// @title AccessControlModifiers
11 | /// @author Angle Labs, Inc.
12 | contract AccessControlModifiers {
13 | /// @notice Checks whether the `msg.sender` has the governor role
14 | modifier onlyGovernor() {
15 | if (!LibDiamond.isGovernor(msg.sender)) revert NotGovernor();
16 | _;
17 | }
18 |
19 | /// @notice Checks whether the `msg.sender` has the guardian role
20 | modifier onlyGuardian() {
21 | if (!LibDiamond.isGovernorOrGuardian(msg.sender)) revert NotGovernorOrGuardian();
22 | _;
23 | }
24 |
25 | /// @notice Prevents a contract from calling itself, directly or indirectly
26 | /// @dev This implementation is an adaptation of the OpenZepellin `ReentrancyGuard` for the purpose of this
27 | /// Diamond Proxy system. The base implementation can be found here
28 | /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol
29 | modifier nonReentrant() {
30 | TransmuterStorage storage ts = s.transmuterStorage();
31 | // Reentrant protection
32 | // On the first call, `ts.statusReentrant` will be `NOT_ENTERED`
33 | if (ts.statusReentrant == ENTERED) revert ReentrantCall();
34 | // Any calls to the `nonReentrant` modifier after this point will fail
35 | ts.statusReentrant = ENTERED;
36 |
37 | _;
38 |
39 | // By storing the original value once again, a refund is triggered (see https://eips.ethereum.org/EIPS/eip-2200)
40 | ts.statusReentrant = NOT_ENTERED;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/contracts/transmuter/facets/DiamondCut.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IDiamondCut } from "interfaces/IDiamondCut.sol";
6 |
7 | import { LibDiamond } from "../libraries/LibDiamond.sol";
8 | import { AccessControlModifiers } from "./AccessControlModifiers.sol";
9 |
10 | import "../Storage.sol";
11 |
12 | /// @title DiamondCut
13 | /// @author Angle Labs, Inc.
14 | /// @dev Reference: EIP-2535 Diamonds
15 | /// @dev Forked from https://github.com/mudgen/diamond-3/blob/master/contracts/facets/DiamondCutFacet.sol by mudgen
16 | contract DiamondCut is IDiamondCut, AccessControlModifiers {
17 | /// @inheritdoc IDiamondCut
18 | function diamondCut(
19 | FacetCut[] calldata _diamondCut,
20 | address _init,
21 | bytes calldata _calldata
22 | ) external onlyGovernor {
23 | LibDiamond.diamondCut(_diamondCut, _init, _calldata);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/contracts/transmuter/facets/DiamondEtherscan.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IDiamondEtherscan } from "interfaces/IDiamondEtherscan.sol";
6 |
7 | import { LibDiamondEtherscan } from "../libraries/LibDiamondEtherscan.sol";
8 | import { AccessControlModifiers } from "./AccessControlModifiers.sol";
9 |
10 | /// @title DiamondEtherscan
11 | /// @author Forked from:
12 | /// https://github.com/zdenham/diamond-etherscan/blob/main/contracts/libraries/LibDiamondEtherscan.sol
13 | contract DiamondEtherscan is IDiamondEtherscan, AccessControlModifiers {
14 | /// @inheritdoc IDiamondEtherscan
15 | function setDummyImplementation(address _implementation) external onlyGuardian {
16 | LibDiamondEtherscan.setDummyImplementation(_implementation);
17 | }
18 |
19 | /// @inheritdoc IDiamondEtherscan
20 | function implementation() external view returns (address) {
21 | return LibDiamondEtherscan.dummyImplementation();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/transmuter/facets/RewardHandler.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IRewardHandler } from "interfaces/IRewardHandler.sol";
6 |
7 | import { IERC20 } from "oz/token/ERC20/IERC20.sol";
8 | import { SafeERC20 } from "oz/token/ERC20/utils/SafeERC20.sol";
9 |
10 | import { LibDiamond } from "../libraries/LibDiamond.sol";
11 | import { LibStorage as s } from "../libraries/LibStorage.sol";
12 |
13 | import "../../utils/Constants.sol";
14 | import "../../utils/Errors.sol";
15 | import "../Storage.sol";
16 |
17 | /// @title RewardHandler
18 | /// @author Angle Labs, Inc.
19 | contract RewardHandler is IRewardHandler {
20 | using SafeERC20 for IERC20;
21 |
22 | event RewardsSoldFor(address indexed tokenObtained, uint256 balanceUpdate);
23 |
24 | /// @notice IRewardHandler
25 | /// @dev It is impossible to sell a token that is a collateral through this function
26 | /// @dev Trusted sellers and governance only may call this function
27 | /// @dev Only governance can set which tokens can be swapped through this function by passing a prior approval
28 | /// transaction to 1inch router for the token to be swapped
29 | function sellRewards(uint256 minAmountOut, bytes memory payload) external returns (uint256 amountOut) {
30 | TransmuterStorage storage ts = s.transmuterStorage();
31 | if (!LibDiamond.isGovernorOrGuardian(msg.sender) && ts.isSellerTrusted[msg.sender] == 0) revert NotTrusted();
32 | address[] memory list = ts.collateralList;
33 | uint256 listLength = list.length;
34 | uint256[] memory balances = new uint256[](listLength);
35 | // Getting the balances of all collateral assets of the protocol to see if those do not decrease during
36 | // the swap: this is the only way to check that collateral assets have not been sold
37 | // Not checking the `subCollaterals` here as swaps should try to increase the balance of one collateral
38 | for (uint256 i; i < listLength; ++i) {
39 | balances[i] = IERC20(list[i]).balanceOf(address(this));
40 | }
41 | //solhint-disable-next-line
42 | (bool success, bytes memory result) = ONE_INCH_ROUTER.call(payload);
43 | if (!success) _revertBytes(result);
44 | amountOut = abi.decode(result, (uint256));
45 | if (amountOut < minAmountOut) revert TooSmallAmountOut();
46 | bool hasIncreased;
47 | for (uint256 i; i < listLength; ++i) {
48 | uint256 newBalance = IERC20(list[i]).balanceOf(address(this));
49 | if (newBalance < balances[i]) revert InvalidSwap();
50 | else if (newBalance > balances[i]) {
51 | hasIncreased = true;
52 | emit RewardsSoldFor(list[i], newBalance - balances[i]);
53 | }
54 | }
55 | if (!hasIncreased) revert InvalidSwap();
56 | }
57 |
58 | /// @notice Processes 1Inch revert messages
59 | function _revertBytes(bytes memory errMsg) private pure {
60 | if (errMsg.length > 0) {
61 | //solhint-disable-next-line
62 | assembly {
63 | revert(add(32, errMsg), mload(errMsg))
64 | }
65 | }
66 | revert OneInchSwapFailed();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/contracts/transmuter/facets/SettersGuardian.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { ISettersGuardian } from "interfaces/ISetters.sol";
6 |
7 | import { LibSetters } from "../libraries/LibSetters.sol";
8 | import { AccessControlModifiers } from "./AccessControlModifiers.sol";
9 |
10 | import "../Storage.sol";
11 |
12 | /// @title SettersGuardian
13 | /// @author Angle Labs, Inc.
14 | contract SettersGuardian is AccessControlModifiers, ISettersGuardian {
15 | /// @inheritdoc ISettersGuardian
16 | function togglePause(address collateral, ActionType pausedType) external onlyGuardian {
17 | LibSetters.togglePause(collateral, pausedType);
18 | }
19 |
20 | /// @inheritdoc ISettersGuardian
21 | function setFees(address collateral, uint64[] memory xFee, int64[] memory yFee, bool mint) external onlyGuardian {
22 | LibSetters.setFees(collateral, xFee, yFee, mint);
23 | }
24 |
25 | /// @inheritdoc ISettersGuardian
26 | function setRedemptionCurveParams(uint64[] memory xFee, int64[] memory yFee) external onlyGuardian {
27 | LibSetters.setRedemptionCurveParams(xFee, yFee);
28 | }
29 |
30 | /// @inheritdoc ISettersGuardian
31 | function toggleWhitelist(WhitelistType whitelistType, address who) external onlyGuardian {
32 | LibSetters.toggleWhitelist(whitelistType, who);
33 | }
34 |
35 | /// @inheritdoc ISettersGuardian
36 | function setStablecoinCap(address collateral, uint256 stablecoinCap) external onlyGuardian {
37 | LibSetters.setStablecoinCap(collateral, stablecoinCap);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/contracts/transmuter/libraries/LibDiamondEtherscan.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { LibStorage as s } from "./LibStorage.sol";
6 |
7 | /// @title LibDiamondEtherscan
8 | /// @notice Allow to verify a diamond proxy on Etherscan
9 | /// @dev Forked from https://github.com/zdenham/diamond-etherscan/blob/main/contracts/libraries/LibDiamondEtherscan.sol
10 | library LibDiamondEtherscan {
11 | event Upgraded(address indexed implementation);
12 |
13 | /// @notice Internal version of `setDummyImplementation`
14 | function setDummyImplementation(address implementationAddress) internal {
15 | s.implementationStorage().implementation = implementationAddress;
16 | emit Upgraded(implementationAddress);
17 | }
18 |
19 | /// @notice Internal version of `implementation`
20 | function dummyImplementation() internal view returns (address) {
21 | return s.implementationStorage().implementation;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/transmuter/libraries/LibHelpers.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { Math } from "oz/utils/math/Math.sol";
6 |
7 | import "../Storage.sol";
8 |
9 | /// @title LibHelpers
10 | /// @author Angle Labs, Inc.
11 | library LibHelpers {
12 | /// @notice Rebases the units of `amount` from `fromDecimals` to `toDecimals`
13 | function convertDecimalTo(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
14 | if (fromDecimals > toDecimals) return amount / 10 ** (fromDecimals - toDecimals);
15 | else if (fromDecimals < toDecimals) return amount * 10 ** (toDecimals - fromDecimals);
16 | else return amount;
17 | }
18 |
19 | /// @notice Checks whether a `token` is in a list `tokens` and returns the index of the token in the list
20 | /// or -1 in the other case
21 | function checkList(address token, address[] memory tokens) internal pure returns (int256) {
22 | uint256 tokensLength = tokens.length;
23 | for (uint256 i; i < tokensLength; ++i) {
24 | if (token == tokens[i]) return int256(i);
25 | }
26 | return -1;
27 | }
28 |
29 | /// @notice Searches a sorted `array` and returns the first index that contains a value strictly greater
30 | /// (or lower if increasingArray is false) to `element` minus 1
31 | /// @dev If no such index exists (i.e. all values in the array are strictly lesser/greater than `element`),
32 | /// either array length minus 1, or 0 are returned
33 | /// @dev The time complexity of the search is O(log n).
34 | /// @dev Inspired from OpenZeppelin Contracts v4.4.1:
35 | /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Arrays.sol
36 | /// @dev Modified by Angle Labs to support `uint64`, monotonous arrays and exclusive upper bounds
37 | function findLowerBound(
38 | bool increasingArray,
39 | uint64[] memory array,
40 | uint64 normalizerArray,
41 | uint64 element
42 | ) internal pure returns (uint256) {
43 | if (array.length == 0) {
44 | return 0;
45 | }
46 | uint256 low = 1;
47 | uint256 high = array.length;
48 |
49 | if (
50 | (increasingArray && array[high - 1] * normalizerArray <= element) ||
51 | (!increasingArray && array[high - 1] * normalizerArray >= element)
52 | ) return high - 1;
53 |
54 | while (low < high) {
55 | uint256 mid = Math.average(low, high);
56 |
57 | // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
58 | // because Math.average rounds down (it does integer division with truncation).
59 | if (increasingArray ? array[mid] * normalizerArray > element : array[mid] * normalizerArray < element) {
60 | high = mid;
61 | } else {
62 | low = mid + 1;
63 | }
64 | }
65 |
66 | // At this point `low` is the exclusive upper bound.
67 | // `low - 1` is the inclusive lower bound.
68 | return low - 1;
69 | }
70 |
71 | /// @notice Evaluates for `x` a piecewise linear function defined with the breaking points in the arrays
72 | /// `xArray` and `yArray`
73 | /// @dev The values in the `xArray` must be increasing
74 | function piecewiseLinear(uint64 x, uint64[] memory xArray, int64[] memory yArray) internal pure returns (int64) {
75 | uint256 indexLowerBound = findLowerBound(true, xArray, 1, x);
76 | if (indexLowerBound == 0 && x < xArray[0]) return yArray[0];
77 | else if (indexLowerBound == xArray.length - 1) return yArray[xArray.length - 1];
78 | return
79 | yArray[indexLowerBound] +
80 | ((yArray[indexLowerBound + 1] - yArray[indexLowerBound]) * int64(x - xArray[indexLowerBound])) /
81 | int64(xArray[indexLowerBound + 1] - xArray[indexLowerBound]);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/contracts/transmuter/libraries/LibManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IManager } from "interfaces/IManager.sol";
6 |
7 | import "../Storage.sol";
8 |
9 | /// @title LibManager
10 | /// @author Angle Labs, Inc.
11 | /// @dev Managed collateral assets may be handled through external smart contracts or directly through this library
12 | /// @dev There is no implementation at this point for a managed collateral handled through this library, and
13 | /// a new specific `ManagerType` would need to be added in this case
14 | library LibManager {
15 | /// @notice Checks to which address managed funds must be transferred
16 | function transferRecipient(bytes memory config) internal view returns (address recipient) {
17 | (ManagerType managerType, bytes memory data) = parseManagerConfig(config);
18 | recipient = address(this);
19 | if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (address));
20 | }
21 |
22 | /// @notice Performs a transfer of `token` for a collateral that is managed to a `to` address
23 | /// @dev `token` may not be the actual collateral itself, as some collaterals have subcollaterals associated
24 | /// with it
25 | /// @dev Eventually pulls funds from strategies
26 | function release(address token, address to, uint256 amount, bytes memory config) internal {
27 | (ManagerType managerType, bytes memory data) = parseManagerConfig(config);
28 | if (managerType == ManagerType.EXTERNAL) abi.decode(data, (IManager)).release(token, to, amount);
29 | }
30 |
31 | /// @notice Gets the balances of all the tokens controlled through `managerData`
32 | /// @return balances An array of size `subCollaterals` with current balances of all subCollaterals
33 | /// including the one corresponding to the `managerData` given
34 | /// @return totalValue The value of all the `subCollaterals` in `collateral`
35 | /// @dev `subCollaterals` must always have as first token (index 0) the collateral itself
36 | function totalAssets(bytes memory config) internal view returns (uint256[] memory balances, uint256 totalValue) {
37 | (ManagerType managerType, bytes memory data) = parseManagerConfig(config);
38 | if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (IManager)).totalAssets();
39 | }
40 |
41 | /// @notice Calls a hook if needed after new funds have been transfered to a manager
42 | function invest(uint256 amount, bytes memory config) internal {
43 | (ManagerType managerType, bytes memory data) = parseManagerConfig(config);
44 | if (managerType == ManagerType.EXTERNAL) abi.decode(data, (IManager)).invest(amount);
45 | }
46 |
47 | /// @notice Returns available underlying tokens, for instance if liquidity is fully used and
48 | /// not withdrawable the function will return 0
49 | function maxAvailable(bytes memory config) internal view returns (uint256 available) {
50 | (ManagerType managerType, bytes memory data) = parseManagerConfig(config);
51 | if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (IManager)).maxAvailable();
52 | }
53 |
54 | /// @notice Decodes the `managerData` associated to a collateral
55 | function parseManagerConfig(
56 | bytes memory config
57 | ) internal pure returns (ManagerType managerType, bytes memory data) {
58 | (managerType, data) = abi.decode(config, (ManagerType, bytes));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/contracts/transmuter/libraries/LibStorage.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "../../utils/Constants.sol";
6 | import { DiamondStorage, ImplementationStorage, TransmuterStorage } from "../Storage.sol";
7 |
8 | /// @title LibStorage
9 | /// @author Angle Labs, Inc.
10 | library LibStorage {
11 | /// @notice Returns the storage struct stored at the `DIAMOND_STORAGE_POSITION` slot
12 | /// @dev This struct handles the logic of the different facets used in the diamond proxy
13 | function diamondStorage() internal pure returns (DiamondStorage storage ds) {
14 | bytes32 position = DIAMOND_STORAGE_POSITION;
15 | assembly {
16 | ds.slot := position
17 | }
18 | }
19 |
20 | /// @notice Returns the storage struct stored at the `TRANSMUTER_STORAGE_POSITION` slot
21 | /// @dev This struct handles the particular logic of the Transmuter system
22 | function transmuterStorage() internal pure returns (TransmuterStorage storage ts) {
23 | bytes32 position = TRANSMUTER_STORAGE_POSITION;
24 | assembly {
25 | ts.slot := position
26 | }
27 | }
28 |
29 | /// @notice Returns the storage struct stored at the `IMPLEMENTATION_STORAGE_POSITION` slot
30 | /// @dev This struct handles the logic for making the contract easily usable on Etherscan
31 | function implementationStorage() internal pure returns (ImplementationStorage storage ims) {
32 | bytes32 position = IMPLEMENTATION_STORAGE_POSITION;
33 | assembly {
34 | ims.slot := position
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/contracts/transmuter/libraries/LibWhitelist.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IKeyringGuard } from "interfaces/external/keyring/IKeyringGuard.sol";
6 |
7 | import { LibStorage as s } from "./LibStorage.sol";
8 |
9 | import "../../utils/Errors.sol";
10 | import "../Storage.sol";
11 |
12 | /// @title LibWhitelist
13 | /// @author Angle Labs, Inc.
14 | library LibWhitelist {
15 | /// @notice Checks whether `sender` is whitelisted for a collateral with `whitelistData`
16 | function checkWhitelist(bytes memory whitelistData, address sender) internal returns (bool) {
17 | (WhitelistType whitelistType, bytes memory data) = abi.decode(whitelistData, (WhitelistType, bytes));
18 | if (s.transmuterStorage().isWhitelistedForType[whitelistType][sender] > 0) return true;
19 | if (data.length != 0) {
20 | if (whitelistType == WhitelistType.BACKED) {
21 | address keyringGuard = abi.decode(data, (address));
22 | if (keyringGuard != address(0)) return IKeyringGuard(keyringGuard).isAuthorized(address(this), sender);
23 | }
24 | }
25 | return false;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/contracts/utils/AccessControl.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IAccessControlManager } from "interfaces/IAccessControlManager.sol";
6 |
7 | import "../utils/Errors.sol";
8 |
9 | /// @title AccessControl
10 | /// @author Angle Labs, Inc.
11 | contract AccessControl {
12 | /// @notice `accessControlManager` used to check roles
13 | IAccessControlManager public accessControlManager;
14 |
15 | uint256[49] private __gapAccessControl;
16 |
17 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 | MODIFIERS
19 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
20 |
21 | /// @notice Checks whether the `msg.sender` has the governor role
22 | modifier onlyGovernor() {
23 | if (!accessControlManager.isGovernor(msg.sender)) revert NotGovernor();
24 | _;
25 | }
26 |
27 | /// @notice Checks whether the `msg.sender` has the guardian role
28 | modifier onlyGuardian() {
29 | if (!accessControlManager.isGovernorOrGuardian(msg.sender)) revert NotGovernorOrGuardian();
30 | _;
31 | }
32 |
33 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
34 | FUNCTIONS
35 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
36 |
37 | /// @notice Checks whether `admin` has the governor role
38 | function isGovernor(address admin) external view returns (bool) {
39 | return accessControlManager.isGovernor(admin);
40 | }
41 |
42 | /// @notice Checks whether `admin` has the guardian role
43 | function isGovernorOrGuardian(address admin) external view returns (bool) {
44 | return accessControlManager.isGovernorOrGuardian(admin);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/contracts/utils/Constants.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity >=0.5.0;
4 |
5 | import { ICbETH } from "interfaces/external/coinbase/ICbETH.sol";
6 | import { ISfrxETH } from "interfaces/external/frax/ISfrxETH.sol";
7 | import { IStETH } from "interfaces/external/lido/IStETH.sol";
8 | import { IRETH } from "interfaces/external/rocketPool/IRETH.sol";
9 |
10 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 | STORAGE SLOTS
12 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
13 |
14 | /// @dev Storage position of `DiamondStorage` structure
15 | /// @dev Equals `keccak256("diamond.standard.diamond.storage") - 1`
16 | bytes32 constant DIAMOND_STORAGE_POSITION = 0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b;
17 |
18 | /// @dev Storage position of `TransmuterStorage` structure
19 | /// @dev Equals `keccak256("diamond.standard.transmuter.storage") - 1`
20 | bytes32 constant TRANSMUTER_STORAGE_POSITION = 0xc1f2f38dde3351ac0a64934139e816326caa800303a1235dc53707d0de05d8bd;
21 |
22 | /// @dev Storage position of `ImplementationStorage` structure
23 | /// @dev Equals `keccak256("eip1967.proxy.implementation") - 1`
24 | bytes32 constant IMPLEMENTATION_STORAGE_POSITION = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
25 |
26 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
27 | MATHS
28 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
29 |
30 | uint256 constant BASE_6 = 1e6;
31 | uint256 constant BASE_8 = 1e8;
32 | uint256 constant BASE_9 = 1e9;
33 | uint256 constant BASE_12 = 1e12;
34 | uint256 constant BPS = 1e14;
35 | uint256 constant BASE_18 = 1e18;
36 | uint256 constant HALF_BASE_27 = 1e27 / 2;
37 | uint256 constant BASE_27 = 1e27;
38 | uint256 constant BASE_36 = 1e36;
39 | uint256 constant MAX_BURN_FEE = 999_000_000;
40 | uint256 constant MAX_MINT_FEE = BASE_12 - 1;
41 |
42 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
43 | REENTRANT
44 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
45 |
46 | // The values being non-zero value makes deployment a bit more expensive,
47 | // but in exchange the refund on every call to nonReentrant will be lower in
48 | // amount. Since refunds are capped to a percentage of the total
49 | // transaction's gas, it is best to keep them low in cases like this one, to
50 | // increase the likelihood of the full refund coming into effect.
51 | uint8 constant NOT_ENTERED = 1;
52 | uint8 constant ENTERED = 2;
53 |
54 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55 | COMMON ADDRESSES
56 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
57 |
58 | address constant PERMIT_2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
59 | address constant ONE_INCH_ROUTER = 0x1111111254EEB25477B68fb85Ed929f73A960582;
60 | address constant AGEUR = 0x1a7e4e63778B4f12a199C062f3eFdD288afCBce8;
61 | ICbETH constant CBETH = ICbETH(0xBe9895146f7AF43049ca1c1AE358B0541Ea49704);
62 | IRETH constant RETH = IRETH(0xae78736Cd615f374D3085123A210448E74Fc6393);
63 | IStETH constant STETH = IStETH(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
64 | ISfrxETH constant SFRXETH = ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);
65 | address constant XEVT = 0x3Ee320c9F73a84D1717557af00695A34b26d1F1d;
66 | address constant USDM = 0x59D9356E565Ab3A36dD77763Fc0d87fEaf85508C;
67 | address constant STEAK_USDC = 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB;
68 | address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
69 | address constant EURC = 0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c;
70 |
71 | address constant ONEINCH_ROUTER = 0x111111125421cA6dc452d289314280a0f8842A65;
72 |
--------------------------------------------------------------------------------
/contracts/utils/Errors.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | error AlreadyAdded();
6 | error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector);
7 | error CannotAddSelectorsToZeroAddress(bytes4[] _selectors);
8 | error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector);
9 | error CannotRemoveImmutableFunction(bytes4 _selector);
10 | error CannotReplaceFunctionsFromFacetWithZeroAddress(bytes4[] _selectors);
11 | error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector);
12 | error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector);
13 | error CannotReplaceImmutableFunction(bytes4 _selector);
14 | error ContractHasNoCode();
15 | error FunctionNotFound(bytes4 _functionSelector);
16 | error IncorrectFacetCutAction(uint8 _action);
17 | error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata);
18 | error InvalidChainlinkRate();
19 | error InvalidLengths();
20 | error InvalidNegativeFees();
21 | error InvalidOracleType();
22 | error InvalidParam();
23 | error InvalidParams();
24 | error InvalidRate();
25 | error InvalidSwap();
26 | error InvalidTokens();
27 | error ManagerHasAssets();
28 | error NoSelectorsProvidedForFacetForCut(address _facetAddress);
29 | error NotAllowed();
30 | error NotCollateral();
31 | error NotGovernor();
32 | error NotGovernorOrGuardian();
33 | error NotTrusted();
34 | error NotTrustedOrGuardian();
35 | error NotWhitelisted();
36 | error OneInchSwapFailed();
37 | error OracleUpdateFailed();
38 | error Paused();
39 | error ReentrantCall();
40 | error RemoveFacetAddressMustBeZeroAddress(address _facetAddress);
41 | error TooBigAmountIn();
42 | error TooLate();
43 | error TooSmallAmountOut();
44 | error ZeroAddress();
45 | error ZeroAmount();
46 | error SwapError();
47 | error SlippageTooHigh();
48 | error InsufficientFunds();
49 |
--------------------------------------------------------------------------------
/foundry.toml:
--------------------------------------------------------------------------------
1 | [profile.default]
2 | src = 'contract'
3 | out = 'out'
4 | test = 'test'
5 | libs = ['node_modules', 'lib']
6 | script = 'scripts'
7 | cache_path = 'cache-forge'
8 | gas_reports = ["*"]
9 | via_ir = true
10 | sizes = true
11 | optimizer = true
12 | optimizer_runs=1000
13 | solc_version = '0.8.26'
14 | ffi = true
15 | fs_permissions = [
16 | { access = "read-write", path = "./scripts/selectors.json"},
17 | { access = "read-write", path = "./scripts/selectors_replace.json"},
18 | { access = "read-write", path = "./scripts/selectors_add.json"},
19 | { access = "read-write", path = "./scripts/vanity.json"}
20 | ]
21 | memory_limit = 1000043554432
22 |
23 | [fuzz]
24 | runs = 10000
25 |
26 | [invariant]
27 | runs = 8
28 | depth = 256
29 |
30 | [rpc_endpoints]
31 | arbitrum = "${ETH_NODE_URI_ARBITRUM}"
32 | gnosis = "${ETH_NODE_URI_GNOSIS}"
33 | mainnet = "${ETH_NODE_URI_MAINNET}"
34 | optimism = "${ETH_NODE_URI_OPTIMISM}"
35 | polygon = "${ETH_NODE_URI_POLYGON}"
36 | fork = "${ETH_NODE_URI_FORK}"
37 | avalanche = "${ETH_NODE_URI_AVALANCHE}"
38 | celo = "${ETH_NODE_URI_CELO}"
39 | polygonzkevm = "${ETH_NODE_URI_POLYGONZKEVM}"
40 | bsc = "${ETH_NODE_URI_BSC}"
41 | base = "${ETH_NODE_URI_BASE}"
42 | linea = "${ETH_NODE_URI_LINEA}"
43 |
44 | [etherscan]
45 | arbitrum = { key = "${ARBITRUM_ETHERSCAN_API_KEY}" }
46 | gnosis = { key = "${GNOSIS_ETHERSCAN_API_KEY}" , url = "https://api.gnosisscan.io/api"}
47 | mainnet = { key = "${MAINNET_ETHERSCAN_API_KEY}" }
48 | optimism = { key = "${OPTIMISM_ETHERSCAN_API_KEY}" }
49 | polygon = { key = "${POLYGON_ETHERSCAN_API_KEY}" }
50 | avalanche = { key = "${AVALANCHE_ETHERSCAN_API_KEY}" }
51 | celo = { key = "${CELO_ETHERSCAN_API_KEY}", url = "https://api.celoscan.io/api" }
52 | base = { key = "${BASE_ETHERSCAN_API_KEY}", url = "https://api.basescan.org/api" }
53 | polygon-zkevm = { key = "${POLYGONZKEVM_ETHERSCAN_API_KEY}", url = "https://api-zkevm.polygonscan.com/api" }
54 | bsc = { key = "${BSC_ETHERSCAN_API_KEY}"}
55 | linea = { key = "${LINEA_ETHERSCAN_API_KEY}"}
56 |
57 | [profile.dev]
58 | via_ir = false
59 | src = 'contracts'
60 | gas_reports = ["*"]
61 |
62 | [profile.dev.fuzz]
63 | runs = 200000
64 |
65 | [profile.dev.invariant]
66 | runs = 100
67 | depth = 30
68 | fail_on_revert = false
69 |
70 | [profile.ci]
71 | optimizer = true
72 | src = 'contracts'
73 | via_ir = false
74 | gas_reports = ["*"]
75 |
76 | [profile.ci.fuzz]
77 | runs = 1000
78 |
79 | [profile.ci.invariant]
80 | runs = 10
81 | depth = 30
82 | fail_on_revert = false
83 |
--------------------------------------------------------------------------------
/helpers/fork.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | source lib/utils/helpers/common.sh
4 |
5 | function main {
6 | if [ ! -f .env ]; then
7 | echo ".env not found!"
8 | exit 1
9 | fi
10 | source .env
11 |
12 | echo "Which chain would you like to fork ?"
13 | echo "- 1: Ethereum Mainnet"
14 | echo "- 2: Arbitrum"
15 | echo "- 3: Polygon"
16 | echo "- 4: Gnosis"
17 | echo "- 5: Avalanche"
18 | echo "- 6: Base"
19 | echo "- 7: Binance Smart Chain"
20 | echo "- 8: Celo"
21 | echo "- 9: Polygon ZkEvm"
22 | echo "- 10: Optimism"
23 | echo "- 11: Linea"
24 |
25 | read option
26 |
27 | uri=$(chain_to_uri $option)
28 | if [ -z "$uri" ]; then
29 | echo "Unknown network"
30 | exit 1
31 | fi
32 |
33 | echo "What block do you want to fork ? (Can leave empty for instant)"
34 |
35 | read block
36 |
37 | if [ -z "$block" ]; then
38 | echo "Forking $uri"
39 | anvil --fork-url $uri
40 | else
41 | echo "Forking $uri at block $block"
42 | anvil --fork-url $uri --fork-block-number $block
43 | fi
44 | }
45 |
46 | main
47 |
--------------------------------------------------------------------------------
/logo.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angle-transmuter",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "ci:coverage": "forge coverage --report lcov --no-match-path 'test/scripts/*' && yarn lcov:clean",
7 | "coverage": "FOUNDRY_PROFILE=dev forge coverage --report lcov && yarn lcov:clean && yarn lcov:generate-html",
8 | "compile": "forge build",
9 | "compile:dev": "FOUNDRY_PROFILE=dev forge build",
10 | "deploy": "FOUNDRY_PROFILE=dev forge script --skip test --broadcast --verify --slow -vvvv --rpc-url",
11 | "deploy:fork": "FOUNDRY_PROFILE=dev forge script --skip test --slow --fork-url fork --broadcast -vvvv",
12 | "generate": "FOUNDRY_PROFILE=dev forge script scripts/utils/GenerateSelectors.s.sol",
13 | "deploy:check": "FOUNDRY_PROFILE=dev forge script --fork-url fork scripts/test/CheckTransmuter.s.sol",
14 | "gas": "FOUNDRY_PROFILE=dev yarn test --gas-report",
15 | "fork": "bash helpers/fork.sh",
16 | "run": "docker run -it --rm -v $(pwd):/app -w /app ghcr.io/foundry-rs/foundry sh",
17 | "script:fork": "forge script --skip test --fork-url fork --broadcast -vvvv",
18 | "test:unit": "forge test -vvvv --gas-report --match-path \"test/units/**/*.sol\"",
19 | "test:invariant": "forge test -vvv --gas-report --match-path \"test/invariants/**/*.sol\"",
20 | "test:fuzz": "forge test -vvv --gas-report --match-path \"test/fuzz/**/*.sol\"",
21 | "slither": "chmod +x ./slither.sh && ./slither.sh",
22 | "test": "forge test -vvv",
23 | "lcov:clean": "lcov --remove lcov.info -o lcov.info 'test/**' 'scripts/**' 'contracts/transmuter/configs/**' 'contracts/utils/**'",
24 | "lcov:generate-html": "genhtml lcov.info --output=coverage",
25 | "size": "forge build --skip test --sizes",
26 | "size:dev": "FOUNDRY_PROFILE=dev forge build --skip test --sizes",
27 | "prettier": "prettier --write '**/*.sol'",
28 | "lint": "yarn lint:check --fix",
29 | "lint:check": "solhint --max-warnings 20 \"**/*.sol\"",
30 | "vanity": "forge script --skip test --slow -vvvv --rpc-url mainnet ./scripts/utils/VanityAddress.s.sol",
31 | "verify:agEUR": "forge verify-contract --chain-id 100 --num-of-optimizations 1000 --watch --constructor-args $(cast abi-encode 'constructor(string memory,string memory,uint8)' 'Mock-AgEUR' 'Mock-AgEUR' 18) --compiler-version v0.8.19+commit.7dd6d40 --etherscan-api-key HQU42G9VWZ6KFNYTYKE6VFB7V48KMJ69HS 0x5fE0E497Ac676d8bA78598FC8016EBC1E6cE14a3 lib/borrow-contracts/contracts/mock/MockTokenPermit:MockTokenPermit.sol",
32 | "verify:stUSD": "forge verify-contract --num-of-optimizations 1000 --watch --constructor-args 0000000000000000000000000000000000ffe8b47b3e2130213b802212439497000000000000000000000000fda462548ce04282f4b6d6619823a7c64fdc018500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000 --compiler-version v0.8.19+commit.7dd6d404 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776 lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy --chain",
33 | "verify:stEUR": "forge verify-contract --num-of-optimizations 1000 --watch --constructor-args 0000000000000000000000000000000000ffe8b47b3e2130213b802212439497000000000000000000000000fda462548ce04282f4b6d6619823a7c64fdc018500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000 --compiler-version v0.8.19+commit.7dd6d404 0x004626A008B1aCdC4c74ab51644093b155e59A23 lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy --chain"
34 | },
35 | "keywords": [],
36 | "author": "Angle Labs, Inc.",
37 | "license": "BUSL-1.1",
38 | "bugs": {
39 | "url": "https://github.com/AngleProtocol/angle-transmuter/issues"
40 | },
41 | "devDependencies": {
42 | "prettier": "^2.0.0",
43 | "prettier-plugin-solidity": "^1.1.3",
44 | "solhint": "^3.5.1",
45 | "solhint-plugin-prettier": "^0.0.5"
46 | },
47 | "dependencies": {
48 | "@angleprotocol/sdk": "^2.9.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/remappings.txt:
--------------------------------------------------------------------------------
1 | ds-test/=lib/forge-std/lib/ds-test/src/
2 | forge-std/=lib/forge-std/src/
3 | stringutils/=lib/solidity-stringutils
4 | contracts/=contracts/
5 | test/=test/
6 | interfaces/=contracts/interfaces/
7 | oz/=lib/openzeppelin-contracts/contracts/
8 | oz-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
9 | @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
10 | @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
11 | mock/=test/mock/
12 | prb/math/=lib/prb-math/src/
13 | utils/=lib/utils
14 |
--------------------------------------------------------------------------------
/scripts/AdjustYieldExposure.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "stringutils/strings.sol";
8 | import "./Constants.s.sol";
9 |
10 | import { GenericHarvester, SwapType } from "contracts/helpers/GenericHarvester.sol";
11 |
12 | contract AdjustYieldExposure is Utils {
13 | function run() external {
14 | uint256 deployerPrivateKey = vm.envUint("KEEPER_PRIVATE_KEY");
15 | address deployer = vm.addr(deployerPrivateKey);
16 | console.log("Address: %s", deployer);
17 | console.log(deployer.balance);
18 | vm.startBroadcast(deployerPrivateKey);
19 |
20 | GenericHarvester rebalancer = GenericHarvester(0x22604C0E5633A9810E01c9cb469B23Eee17AC411);
21 | rebalancer.adjustYieldExposure(
22 | 1300000 * 1 ether,
23 | 0,
24 | USDC,
25 | STEAK_USDC,
26 | 1200000 * 1 ether,
27 | SwapType.VAULT,
28 | new bytes(0)
29 | );
30 |
31 | vm.stopBroadcast();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/scripts/Constants.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { MAX_MINT_FEE, MAX_BURN_FEE, BASE_6, BASE_8, BPS } from "contracts/utils/Constants.sol";
5 | import "utils/src/Constants.sol";
6 |
7 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8 | MAINNET CONSTANTS
9 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
10 |
11 | uint256 constant CHAIN_SOURCE = CHAIN_ETHEREUM;
12 |
13 | address constant DEPLOYER = 0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185;
14 | address constant KEEPER = 0xcC617C6f9725eACC993ac626C7efC6B96476916E;
15 | address constant NEW_DEPLOYER = 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701;
16 | address constant NEW_KEEPER = 0xa9bbbDDe822789F123667044443dc7001fb43C01;
17 |
18 | address constant EUROC = 0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c;
19 | address constant EUROE = 0x820802Fa8a99901F52e39acD21177b0BE6EE2974;
20 | address constant EURE = 0x3231Cb76718CDeF2155FC47b5286d82e6eDA273f;
21 | address constant BC3M = 0x2F123cF3F37CE3328CC9B5b8415f9EC5109b45e7;
22 | address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
23 | address constant BERNX = 0x3f95AA88dDbB7D9D484aa3D482bf0a80009c52c9;
24 | address constant STEAK_USDC = 0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB;
25 | address constant BIB01 = 0xCA30c93B02514f86d5C86a6e375E3A330B435Fb5;
26 | address constant USDM = 0x59D9356E565Ab3A36dD77763Fc0d87fEaf85508C;
27 |
28 | address constant ONEINCH_ROUTER = 0x111111125421cA6dc452d289314280a0f8842A65;
29 |
30 | // EUROC
31 | uint128 constant FIREWALL_BURN_RATIO_EUROC = uint128(0);
32 | uint128 constant USER_PROTECTION_EUROC = uint128(10 * BPS);
33 |
34 | // BC3M
35 | uint128 constant FIREWALL_BURN_RATIO_BC3M = uint128(10 * BPS);
36 | uint128 constant USER_PROTECTION_BC3M = uint128(0);
37 |
38 | // ERNX
39 | uint128 constant FIREWALL_BURN_RATIO_ERNX = uint128(20 * BPS);
40 | uint128 constant USER_PROTECTION_ERNX = uint128(0);
41 |
42 | uint128 constant FIREWALL_BURN_RATIO_USDC = uint128(0);
43 | uint128 constant USER_PROTECTION_USDC = uint128(5 * BPS);
44 |
45 | uint128 constant FIREWALL_BURN_RATIO_STEAK_USDC = uint128(5 * BPS);
46 | uint128 constant USER_PROTECTION_STEAK_USDC = uint128(0);
47 |
48 | uint128 constant FIREWALL_BURN_RATIO_IB01 = uint128(20 * BPS);
49 | uint128 constant USER_PROTECTION_IB01 = uint128(0);
50 |
51 | uint32 constant HEARTBEAT = uint32(7 days);
52 |
53 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
54 | FACET ADDRESSES
55 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
56 |
57 | address constant DIAMOND_CUT_FACET = 0x53B7d70013dEC21A97F216e80eEFCF45F25c2900;
58 | address constant DIAMOND_ETHERSCAN_FACET = 0xFa94Cd9d711de75695693c877BecA5473462Cf12;
59 | address constant DIAMOND_LOUPE_FACET = 0x65Ddeedf8e68f26D787B678E28Af13fde0249967;
60 | address constant GETTERS_FACET = 0xd1b575ED715e4630340BfdC4fB8A37dF3383C84a;
61 | address constant REWARD_HANDLER_FACET = 0x770756e43b9ac742538850003791deF3020211F3;
62 | address constant SETTERS_GOVERNOR_FACET = 0x1F37F93c6aA7d987AE04786145d3066EAb8EEB43;
63 | address constant SETTERS_GUARDIAN_FACET = 0xdda8f002925a0DfB151c0EaCb48d7136ce6a999F;
64 | address constant SWAPPER_FACET = 0x06c33a0C80C3970cbeDDE641C7A6419d703D93d7;
65 | address constant REDEEMER_FACET = 0x1e45b65CdD3712fEf0024d063d6574A609985E59;
66 |
67 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
68 | SAVINGS IMPLEM
69 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
70 |
71 | address constant SAVINGS_IMPLEM = 0xfD2cCc920d498db30FBE9c13D5705aE2C72670F9;
72 |
--------------------------------------------------------------------------------
/scripts/DeployGenericHarvester.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { GenericHarvester } from "contracts/helpers/GenericHarvester.sol";
7 | import { IAccessControlManager } from "contracts/utils/AccessControl.sol";
8 | import { IAgToken } from "contracts/interfaces/IAgToken.sol";
9 | import { ITransmuter } from "contracts/interfaces/ITransmuter.sol";
10 | import { IERC3156FlashLender } from "oz/interfaces/IERC3156FlashLender.sol";
11 | import "./Constants.s.sol";
12 |
13 | contract DeployGenericHarvester is Utils {
14 | function run() external {
15 | uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
16 | vm.startBroadcast(deployerPrivateKey);
17 |
18 | address deployer = vm.addr(deployerPrivateKey);
19 | console.log("Deployer address: ", deployer);
20 | uint96 maxSlippage = 0.3e7; // 0.3%
21 | IERC3156FlashLender flashloan = IERC3156FlashLender(_chainToContract(CHAIN_SOURCE, ContractType.FlashLoan));
22 | IAgToken agToken = IAgToken(_chainToContract(CHAIN_SOURCE, ContractType.AgUSD));
23 | ITransmuter transmuter = ITransmuter(_chainToContract(CHAIN_SOURCE, ContractType.TransmuterAgUSD));
24 | IAccessControlManager accessControlManager = transmuter.accessControlManager();
25 |
26 | GenericHarvester harvester = new GenericHarvester(
27 | maxSlippage,
28 | ONEINCH_ROUTER,
29 | ONEINCH_ROUTER,
30 | agToken,
31 | transmuter,
32 | accessControlManager,
33 | flashloan
34 | );
35 | console.log("HarvesterVault deployed at: ", address(harvester));
36 |
37 | vm.stopBroadcast();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/scripts/DeployMultiBlockHarvester.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { MultiBlockHarvester } from "contracts/helpers/MultiBlockHarvester.sol";
7 | import { IAccessControlManager } from "contracts/utils/AccessControl.sol";
8 | import { IAgToken } from "contracts/interfaces/IAgToken.sol";
9 | import { ITransmuter } from "contracts/interfaces/ITransmuter.sol";
10 | import "./Constants.s.sol";
11 |
12 | contract DeployMultiBlockHarvester is Utils {
13 | function run() external {
14 | uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
15 | vm.startBroadcast(deployerPrivateKey);
16 |
17 | address deployer = vm.addr(deployerPrivateKey);
18 | console.log("Deployer address: ", deployer);
19 | uint96 maxSlippage = 0.3e7; // 0.3%
20 | address agToken = _chainToContract(CHAIN_SOURCE, ContractType.AgEUR);
21 | address transmuter = _chainToContract(CHAIN_SOURCE, ContractType.TransmuterAgEUR);
22 | IAccessControlManager accessControlManager = ITransmuter(transmuter).accessControlManager();
23 |
24 | MultiBlockHarvester harvester = new MultiBlockHarvester(
25 | maxSlippage,
26 | accessControlManager,
27 | IAgToken(agToken),
28 | ITransmuter(transmuter)
29 | );
30 | console.log("HarvesterVault deployed at: ", address(harvester));
31 |
32 | vm.stopBroadcast();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/scripts/DeployRedeemer.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "./Constants.s.sol";
8 |
9 | import "contracts/utils/Errors.sol" as Errors;
10 | import "contracts/transmuter/Storage.sol" as Storage;
11 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
12 | import { ITransmuter } from "interfaces/ITransmuter.sol";
13 |
14 | contract DeployRedeemer is Utils {
15 | using stdJson for string;
16 |
17 | string[] facetNames;
18 | address[] facetAddressList;
19 |
20 | ITransmuter transmuter = ITransmuter(0x00253582b2a3FE112feEC532221d9708c64cEFAb);
21 | address oldRedeemer = 0x8E669F6eF8485694196F32d568BA4Ac268b9FE8f;
22 |
23 | function run() external {
24 | // vm.startPrank(_chainToContract(CHAIN_SOURCE, ContractType.GovernorMultisig);
25 |
26 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_FORK"), 0);
27 | address deployer = vm.addr(deployerPrivateKey);
28 | console.log("Address: %s", deployer);
29 | vm.startBroadcast(deployerPrivateKey);
30 |
31 | facetNames.push("Redeemer");
32 | facetAddressList.push(address(new Redeemer()));
33 |
34 | string memory json = vm.readFile(JSON_SELECTOR_PATH);
35 | // Build appropriate payload
36 | uint256 n = facetNames.length;
37 | Storage.FacetCut[] memory cut = new Storage.FacetCut[](n);
38 | Storage.FacetCut[] memory removeCut = new Storage.FacetCut[](n);
39 | for (uint256 i = 0; i < n; ++i) {
40 | // Get Selectors from json
41 | bytes4[] memory selectors = _arrayBytes32ToBytes4(
42 | json.readBytes32Array(string.concat("$.", facetNames[i]))
43 | );
44 | cut[i] = Storage.FacetCut({
45 | facetAddress: facetAddressList[i],
46 | action: Storage.FacetCutAction.Add,
47 | functionSelectors: selectors
48 | });
49 |
50 | removeCut[i] = Storage.FacetCut({
51 | facetAddress: address(0),
52 | action: Storage.FacetCutAction.Remove,
53 | functionSelectors: selectors
54 | });
55 | }
56 |
57 | console.log("Redeemer deployed at: %s", facetAddressList[0]);
58 |
59 | vm.stopBroadcast();
60 |
61 | // // if fork
62 | // bytes memory callData;
63 | // transmuter.diamondCut(removeCut, address(0), callData);
64 | // transmuter.diamondCut(cut, address(0), callData);
65 | // vm.stopPrank();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/scripts/DeploySavingsImplem.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import "utils/src/Constants.sol";
6 | import { console } from "forge-std/console.sol";
7 | import { stdJson } from "forge-std/StdJson.sol";
8 | import "stringutils/strings.sol";
9 | import { SavingsNameable } from "contracts/savings/nameable/SavingsNameable.sol";
10 | import { IAccessControlManager } from "contracts/utils/AccessControl.sol";
11 | import "oz/interfaces/IERC20.sol";
12 | import "oz-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
13 |
14 | import { ImmutableCreate2Factory } from "./utils/TransmuterDeploymentHelper.s.sol";
15 |
16 | contract DeploySavingsImplem is Utils {
17 | using stdJson for string;
18 | using strings for *;
19 |
20 | function run() external {
21 | uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
22 | ImmutableCreate2Factory create2Factory = ImmutableCreate2Factory(IMMUTABLE_CREATE2_FACTORY_ADDRESS);
23 | bytes32 salt = 0xa9ddd91249dfdd450e81e1c56ab60e1a62651701000000000000000000438ec0;
24 |
25 | vm.startBroadcast(deployerPrivateKey);
26 |
27 | address deployer = vm.addr(deployerPrivateKey);
28 | console.log("Deployer address: ", deployer);
29 |
30 | bytes memory emptyData;
31 | bytes memory initCode = abi.encodePacked(
32 | type(SavingsNameable).creationCode,
33 | abi.encode(IMMUTABLE_CREATE2_FACTORY_ADDRESS, deployer, emptyData)
34 | );
35 | address computedAddress = create2Factory.findCreate2Address(salt, initCode);
36 | console.log("Supposed to deploy: %s", computedAddress);
37 | if (computedAddress != 0x2C28Bd22aB59341892e85aD76d159d127c4B03FA) revert();
38 |
39 | address saving = create2Factory.safeCreate2(salt, initCode);
40 | console.log("Savings implementation deployed at: ", address(saving));
41 |
42 | vm.stopBroadcast();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/scripts/DeploySavingsNoImplem.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "stringutils/strings.sol";
8 | import { CHAIN_SOURCE } from "./Constants.s.sol";
9 | import { Savings } from "contracts/savings/Savings.sol";
10 | import { IAccessControlManager } from "contracts/utils/AccessControl.sol";
11 | import "./Constants.s.sol";
12 | import "oz/interfaces/IERC20.sol";
13 | import "oz-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
14 | import { TransparentUpgradeableProxy } from "oz/proxy/transparent/TransparentUpgradeableProxy.sol";
15 |
16 | import { ImmutableCreate2Factory } from "./utils/TransmuterDeploymentHelper.s.sol";
17 |
18 | import { MockTreasury } from "test/mock/MockTreasury.sol";
19 |
20 | /// @dev To deploy on a different chain, just replace the chainId and be sure the sdk has the required addresses
21 | contract DeploySavingsNoImplem is Utils {
22 | using stdJson for string;
23 | using strings for *;
24 |
25 | function run() external {
26 | // TODO: make sure that deployer has a 1 stablecoin (=1e18) balance
27 | // TODO: change the chainId
28 | uint256 chainId = CHAIN_SOURCE;
29 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_MAINNET"), "m/44'/60'/0'/0/", 0);
30 | ImmutableCreate2Factory create2Factory = ImmutableCreate2Factory(IMMUTABLE_CREATE2_FACTORY_ADDRESS);
31 | string memory jsonVanity = vm.readFile(JSON_VANITY_PATH);
32 | bytes32 salt = jsonVanity.readBytes32(string.concat("$.", "salt"));
33 | vm.startBroadcast(deployerPrivateKey);
34 |
35 | address deployer = vm.addr(deployerPrivateKey);
36 | console.log("Deployer address: ", deployer);
37 | // No need to deploy the implementation here
38 | // TODO: update addresses based on deployment
39 | // address agToken = _chainToContract(chainId, ContractType.AgUSD);
40 | address agToken = _chainToContract(chainId, ContractType.AgEUR);
41 | address accessControlManager = _chainToContract(chainId, ContractType.CoreBorrow);
42 | address treasury = _chainToContract(chainId, ContractType.TreasuryAgUSD);
43 |
44 | // Then deploying the proxy.
45 | // To maintain chain consistency, we deploy with the deployer as a proxyAdmin before transferring
46 | // to another address
47 | // We use a contract that is widely deployed across many chains as an implementation to make it resilient
48 | // to possible implementation changes
49 |
50 | bytes memory emptyData;
51 | bytes memory initCode = abi.encodePacked(
52 | type(TransparentUpgradeableProxy).creationCode,
53 | abi.encode(IMMUTABLE_CREATE2_FACTORY_ADDRESS, deployer, emptyData)
54 | );
55 | console.log("Proxy bytecode");
56 | console.logBytes(initCode);
57 | console.logBytes(abi.encode(IMMUTABLE_CREATE2_FACTORY_ADDRESS, deployer, emptyData));
58 | console.log("");
59 |
60 | address computedAddress = create2Factory.findCreate2Address(salt, initCode);
61 | console.log("Supposed to deploy: %s", computedAddress);
62 | if (computedAddress != 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776) revert();
63 | address saving = create2Factory.safeCreate2(salt, initCode);
64 | console.log("Savings deployed at: ", address(saving));
65 | TransparentUpgradeableProxy(payable(saving)).upgradeTo(address(SAVINGS_IMPLEM));
66 | TransparentUpgradeableProxy(payable(saving)).changeAdmin(_chainToContract(chainId, ContractType.ProxyAdmin));
67 | IERC20MetadataUpgradeable(agToken).approve(address(saving), 1e18);
68 | Savings(saving).initialize(
69 | IAccessControlManager(accessControlManager),
70 | IERC20MetadataUpgradeable(agToken),
71 | "Staked agUSD",
72 | "stUSD",
73 | 1
74 | );
75 |
76 | MockTreasury(treasury).addMinter(saving);
77 |
78 | vm.stopBroadcast();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/scripts/DeployTransmuter.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import { TransmuterDeploymentHelper } from "./utils/TransmuterDeploymentHelper.s.sol";
6 | import { console } from "forge-std/console.sol";
7 | import { stdJson } from "forge-std/StdJson.sol";
8 | import "stringutils/strings.sol";
9 | import "./Constants.s.sol";
10 |
11 | import { CollateralSetupProd, Production } from "contracts/transmuter/configs/Production.sol";
12 | import "contracts/transmuter/Storage.sol" as Storage;
13 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
14 | import { DiamondEtherscan } from "contracts/transmuter/facets/DiamondEtherscan.sol";
15 | import { DiamondLoupe } from "contracts/transmuter/facets/DiamondLoupe.sol";
16 | import { DiamondProxy } from "contracts/transmuter/DiamondProxy.sol";
17 | import { Getters } from "contracts/transmuter/facets/Getters.sol";
18 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
19 | import { RewardHandler } from "contracts/transmuter/facets/RewardHandler.sol";
20 | import { SettersGovernor } from "contracts/transmuter/facets/SettersGovernor.sol";
21 | import { SettersGuardian } from "contracts/transmuter/facets/SettersGuardian.sol";
22 | import { DummyDiamondImplementation } from "./generated/DummyDiamondImplementation.sol";
23 | import { Swapper } from "contracts/transmuter/facets/Swapper.sol";
24 | import { ITransmuter } from "interfaces/ITransmuter.sol";
25 |
26 | contract DeployTransmuter is TransmuterDeploymentHelper {
27 | function run() external {
28 | // TODO: make sure that selectors are well generated `yarn generate` before running this script
29 | // Here the `selectors.json` file is normally up to date
30 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_FORK"), "m/44'/60'/0'/0/", 0);
31 | address deployer = vm.addr(deployerPrivateKey);
32 | console.log("Address: %s", deployer);
33 | console.log(deployer.balance);
34 | vm.startBroadcast(deployerPrivateKey);
35 |
36 | // Config
37 | config = address(new Production());
38 |
39 | address dummyImplementation = address(new DummyDiamondImplementation());
40 | ITransmuter transmuter = _deployTransmuter(
41 | config,
42 | abi.encodeWithSelector(
43 | Production.initialize.selector,
44 | _chainToContract(CHAIN_SOURCE, ContractType.CoreBorrow),
45 | _chainToContract(CHAIN_SOURCE, ContractType.AgEUR),
46 | dummyImplementation
47 | )
48 | );
49 |
50 | console.log("Transmuter deployed at: %s", address(transmuter));
51 | vm.stopBroadcast();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/scripts/DeployTransmuterSidechain.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import { TransmuterDeploymentHelper } from "./utils/TransmuterDeploymentHelper.s.sol";
6 | import { console } from "forge-std/console.sol";
7 | import { stdJson } from "forge-std/StdJson.sol";
8 | import "stringutils/strings.sol";
9 | import "./Constants.s.sol";
10 |
11 | import { CollateralSetupProd, ProductionSidechain } from "contracts/transmuter/configs/ProductionSidechain.sol";
12 | import "contracts/transmuter/Storage.sol" as Storage;
13 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
14 | import { DiamondEtherscan } from "contracts/transmuter/facets/DiamondEtherscan.sol";
15 | import { DiamondLoupe } from "contracts/transmuter/facets/DiamondLoupe.sol";
16 | import { DiamondProxy } from "contracts/transmuter/DiamondProxy.sol";
17 | import { Getters } from "contracts/transmuter/facets/Getters.sol";
18 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
19 | import { RewardHandler } from "contracts/transmuter/facets/RewardHandler.sol";
20 | import { SettersGovernor } from "contracts/transmuter/facets/SettersGovernor.sol";
21 | import { SettersGuardian } from "contracts/transmuter/facets/SettersGuardian.sol";
22 | import { DummyDiamondImplementation } from "./generated/DummyDiamondImplementationSidechain.sol";
23 | import { Swapper } from "contracts/transmuter/facets/Swapper.sol";
24 | import { ITransmuter } from "interfaces/ITransmuter.sol";
25 | import { Helpers } from "./Helpers.s.sol";
26 |
27 | contract DeployTransmuterSidechain is TransmuterDeploymentHelper, Helpers {
28 | function run() external {
29 | // TODO: make sure that selectors are well generated `yarn generate` before running this script
30 | // Here the `selectors.json` file is normally up to date
31 | uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
32 | vm.startBroadcast(deployerPrivateKey);
33 |
34 | // TODO
35 | uint256 chain = CHAIN_BASE;
36 | uint256 hardCap = 2_000_000 ether;
37 | address core = _chainToContract(chain, ContractType.CoreBorrow);
38 | address agToken = _chainToContract(chain, ContractType.AgEUR);
39 | address liquidStablecoin;
40 | address[] memory oracleLiquidStablecoin;
41 | uint8[] memory oracleIsMultiplied;
42 | {
43 | StablecoinType fiat = StablecoinType.EUR;
44 | (liquidStablecoin, oracleLiquidStablecoin, oracleIsMultiplied) = _chainToLiquidStablecoinAndOracle(
45 | chain,
46 | fiat
47 | );
48 | }
49 |
50 | // Config
51 | config = address(new ProductionSidechain());
52 |
53 | address dummyImplementation = address(new DummyDiamondImplementation());
54 |
55 | ITransmuter transmuter = _deployTransmuter(
56 | config,
57 | abi.encodeWithSelector(
58 | ProductionSidechain.initialize.selector,
59 | core,
60 | agToken,
61 | liquidStablecoin,
62 | oracleLiquidStablecoin,
63 | oracleIsMultiplied,
64 | hardCap,
65 | dummyImplementation
66 | )
67 | );
68 |
69 | console.log("Transmuter on chain %s deployed at: %s", chain, address(transmuter));
70 | vm.stopBroadcast();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/scripts/DeployTransmuterWithoutFacets.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "./utils/Utils.s.sol";
5 | import { TransmuterDeploymentHelper } from "./utils/TransmuterDeploymentHelper.s.sol";
6 | import { console } from "forge-std/console.sol";
7 | import { stdJson } from "forge-std/StdJson.sol";
8 | import "stringutils/strings.sol";
9 | import "./Constants.s.sol";
10 |
11 | import { CollateralSetupProd, ProductionUSD } from "contracts/transmuter/configs/ProductionUSD.sol";
12 | import "contracts/transmuter/Storage.sol" as Storage;
13 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
14 | import { DiamondEtherscan } from "contracts/transmuter/facets/DiamondEtherscan.sol";
15 | import { DiamondLoupe } from "contracts/transmuter/facets/DiamondLoupe.sol";
16 | import { DiamondProxy } from "contracts/transmuter/DiamondProxy.sol";
17 | import { Getters } from "contracts/transmuter/facets/Getters.sol";
18 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
19 | import { RewardHandler } from "contracts/transmuter/facets/RewardHandler.sol";
20 | import { SettersGovernor } from "contracts/transmuter/facets/SettersGovernor.sol";
21 | import { SettersGuardian } from "contracts/transmuter/facets/SettersGuardian.sol";
22 | import { DummyDiamondImplementation } from "./generated/DummyDiamondImplementation.sol";
23 | import { Swapper } from "contracts/transmuter/facets/Swapper.sol";
24 | import { ITransmuter } from "interfaces/ITransmuter.sol";
25 |
26 | import { MockTreasury } from "test/mock/MockTreasury.sol";
27 |
28 | contract DeployTransmuterWithoutFacets is TransmuterDeploymentHelper {
29 | function run() external {
30 | // TODO: make sure that selectors are well generated `yarn generate` before running this script
31 | // Here the `selectors.json` file is normally up to date
32 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_MAINNET"), "m/44'/60'/0'/0/", 0);
33 | address deployer = vm.addr(deployerPrivateKey);
34 | console.log("Address: %s", deployer);
35 | console.log(deployer.balance);
36 | vm.startBroadcast(deployerPrivateKey);
37 |
38 | // TODO change before actual deployment and replace with actual addresses inherited from other
39 | // deployment
40 |
41 | address agToken = 0x0000206329b97DB379d5E1Bf586BbDB969C63274;
42 | address treasury = 0xf8588520E760BB0b3bDD62Ecb25186A28b0830ee;
43 | address accessControlManager = 0x3fc5a1bd4d0A435c55374208A6A81535A1923039;
44 |
45 | config = address(new ProductionUSD()); // Config
46 | // Already deployed
47 | address dummyImplementation = 0x5d34839A3d4051f630D36e26698d53c58DD39072;
48 | ITransmuter transmuter = _deployTransmuterWithoutFacets(
49 | config,
50 | abi.encodeWithSelector(
51 | ProductionUSD.initialize.selector,
52 | accessControlManager,
53 | agToken,
54 | dummyImplementation
55 | )
56 | );
57 |
58 | console.log("Transmuter deployed at: %s", address(transmuter));
59 |
60 | MockTreasury(treasury).addMinter(address(transmuter));
61 | vm.stopBroadcast();
62 | // TODO: test minting afterwards
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/scripts/SetOracle.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "./utils/Utils.s.sol";
5 | import { TransmuterDeploymentHelper } from "./utils/TransmuterDeploymentHelper.s.sol";
6 | import { console } from "forge-std/console.sol";
7 | import { stdJson } from "forge-std/StdJson.sol";
8 | import "stringutils/strings.sol";
9 | import "./Constants.s.sol";
10 |
11 | import { CollateralSetupProd, Production } from "contracts/transmuter/configs/Production.sol";
12 | import "contracts/transmuter/Storage.sol" as Storage;
13 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
14 | import { DiamondEtherscan } from "contracts/transmuter/facets/DiamondEtherscan.sol";
15 | import { DiamondLoupe } from "contracts/transmuter/facets/DiamondLoupe.sol";
16 | import { DiamondProxy } from "contracts/transmuter/DiamondProxy.sol";
17 | import { Getters } from "contracts/transmuter/facets/Getters.sol";
18 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
19 | import { RewardHandler } from "contracts/transmuter/facets/RewardHandler.sol";
20 | import { SettersGovernor } from "contracts/transmuter/facets/SettersGovernor.sol";
21 | import { SettersGuardian } from "contracts/transmuter/facets/SettersGuardian.sol";
22 | import { DummyDiamondImplementation } from "./generated/DummyDiamondImplementation.sol";
23 | import { Swapper } from "contracts/transmuter/facets/Swapper.sol";
24 | import { ITransmuter } from "interfaces/ITransmuter.sol";
25 |
26 | contract SetOracle is Utils {
27 | function run() external {
28 | bytes memory oracleConfig;
29 | {
30 | // Pyth oracle for EUROC
31 | bytes32[] memory feedIds = new bytes32[](2);
32 | uint32[] memory stalePeriods = new uint32[](2);
33 | uint8[] memory isMultiplied = new uint8[](2);
34 | // pyth address
35 | address pyth = 0x4305FB66699C3B2702D4d05CF36551390A4c69C6;
36 | // EUROC/USD
37 | feedIds[0] = 0x76fa85158bf14ede77087fe3ae472f66213f6ea2f5b411cb2de472794990fa5c;
38 | // USD/EUR
39 | feedIds[1] = 0xa995d00bb36a63cef7fd2c287dc105fc8f3d93779f062f09551b0af3e81ec30b;
40 | stalePeriods[0] = 14 days;
41 | stalePeriods[1] = 14 days;
42 | isMultiplied[0] = 1;
43 | isMultiplied[1] = 0;
44 | Storage.OracleQuoteType quoteType = Storage.OracleQuoteType.UNIT;
45 | bytes memory readData = abi.encode(pyth, feedIds, stalePeriods, isMultiplied, quoteType);
46 | bytes memory targetData;
47 | oracleConfig = abi.encode(Storage.OracleReadType.PYTH, Storage.OracleReadType.STABLE, readData, targetData);
48 | console.logBytes(oracleConfig);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/scripts/UpdateOracle.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "stringutils/strings.sol";
8 | import "./Constants.s.sol";
9 |
10 | import "contracts/transmuter/Storage.sol" as Storage;
11 | import { ITransmuter } from "interfaces/ITransmuter.sol";
12 | import "interfaces/external/chainlink/AggregatorV3Interface.sol";
13 | import "interfaces/external/IERC4626.sol";
14 |
15 | import { CollateralSetupProd } from "contracts/transmuter/configs/ProductionTypes.sol";
16 |
17 | contract UpdateOracle is Utils {
18 | function run() external {
19 | uint256 deployerPrivateKey = vm.envUint("KEEPER_PRIVATE_KEY");
20 | address deployer = vm.addr(deployerPrivateKey);
21 | console.log("Address: %s", deployer);
22 | console.log(deployer.balance);
23 | vm.startBroadcast(deployerPrivateKey);
24 |
25 | ITransmuter usdaTransmuter = ITransmuter(0x222222fD79264BBE280b4986F6FEfBC3524d0137);
26 | console.log(address(usdaTransmuter));
27 | usdaTransmuter.updateOracle(0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB);
28 |
29 | /*
30 | ITransmuter euraTransmuter = ITransmuter(0x00253582b2a3FE112feEC532221d9708c64cEFAb);
31 | // euraTransmuter.updateOracle(0x3f95AA88dDbB7D9D484aa3D482bf0a80009c52c9);
32 | euraTransmuter.updateOracle(0x2F123cF3F37CE3328CC9B5b8415f9EC5109b45e7);
33 | */
34 | vm.stopBroadcast();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/scripts/UpdateTransmuterFacets.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "./utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "stringutils/strings.sol";
8 | import "./Constants.s.sol";
9 | import { Helpers } from "./Helpers.s.sol";
10 |
11 | import "contracts/utils/Errors.sol" as Errors;
12 | import "contracts/transmuter/Storage.sol" as Storage;
13 | import { Getters } from "contracts/transmuter/facets/Getters.sol";
14 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
15 | import { SettersGovernor } from "contracts/transmuter/facets/SettersGovernor.sol";
16 | import { SettersGuardian } from "contracts/transmuter/facets/SettersGuardian.sol";
17 | import { Swapper } from "contracts/transmuter/facets/Swapper.sol";
18 | import "contracts/transmuter/libraries/LibHelpers.sol";
19 | import { ITransmuter } from "interfaces/ITransmuter.sol";
20 | import "utils/src/Constants.sol";
21 | import { IERC20 } from "oz/interfaces/IERC20.sol";
22 |
23 | contract UpdateTransmuterFacets is Helpers {
24 | string[] replaceFacetNames;
25 | address[] facetAddressList;
26 |
27 | ITransmuter transmuter;
28 | IERC20 agEUR;
29 | address governor;
30 | bytes public oracleConfigEUROC;
31 | bytes public oracleConfigBC3M;
32 |
33 | function run() external {
34 | // TODO: make sure that selectors are well generated `yarn generate` before running this script
35 | // Here the `selectors.json` file is normally up to date
36 | uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
37 | vm.startBroadcast(deployerPrivateKey);
38 |
39 | Storage.FacetCut[] memory replaceCut;
40 | Storage.FacetCut[] memory addCut;
41 |
42 | replaceFacetNames.push("Getters");
43 | facetAddressList.push(address(new Getters()));
44 | console.log("Getters deployed at: ", facetAddressList[facetAddressList.length - 1]);
45 |
46 | replaceFacetNames.push("Redeemer");
47 | facetAddressList.push(address(new Redeemer()));
48 | console.log("Redeemer deployed at: ", facetAddressList[facetAddressList.length - 1]);
49 |
50 | replaceFacetNames.push("SettersGovernor");
51 | facetAddressList.push(address(new SettersGovernor()));
52 | console.log("SettersGovernor deployed at: ", facetAddressList[facetAddressList.length - 1]);
53 |
54 | replaceFacetNames.push("Swapper");
55 | facetAddressList.push(address(new Swapper()));
56 | console.log("Swapper deployed at: ", facetAddressList[facetAddressList.length - 1]);
57 |
58 | // TODO Governance should pass tx to upgrade them from the `angle-governance` repo
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/scripts/gnosis/DeploySavingsGnosis.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "../utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "stringutils/strings.sol";
8 | import { Savings } from "contracts/savings/Savings.sol";
9 | import { AccessControl, IAccessControlManager } from "contracts/utils/AccessControl.sol";
10 | import { MockTokenPermit } from "test/mock/MockTokenPermit.sol";
11 | import "oz/interfaces/IERC20.sol";
12 | import "oz-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
13 | import "oz/proxy/transparent/ProxyAdmin.sol";
14 |
15 | contract DeploySavingsGnosis is Utils {
16 | using strings for *;
17 | using stdJson for string;
18 |
19 | // the allowance address was obtained by running the script and check the deployed address of the savings proxy
20 | address constant SAVING_ADDRESS = 0x9De6Efe3454F8EFF8C8C8d1314CD019AF2432e59;
21 | ProxyAdmin proxy;
22 |
23 | function run() external {
24 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_GNOSIS"), 0);
25 | vm.startBroadcast(deployerPrivateKey);
26 |
27 | address deployer = vm.addr(deployerPrivateKey);
28 | console.log("address: %s", deployer);
29 |
30 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
31 | DEPLOY
32 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
33 | // Deploy fakes Core borrow, agEUR, and collaterals
34 |
35 | proxy = new ProxyAdmin();
36 | console.log("Proxy deployed at: %s", address(proxy));
37 |
38 | IAccessControlManager coreBorrow = IAccessControlManager(0xBDbdF128368De1cf6a3Aa37f67Bc19405c96f49F);
39 | IERC20MetadataUpgradeable agEUR = IERC20MetadataUpgradeable(0x5fE0E497Ac676d8bA78598FC8016EBC1E6cE14a3);
40 |
41 | MockTokenPermit(address(agEUR)).mint(address(deployer), 1e18);
42 | MockTokenPermit(address(agEUR)).setAllowance(address(deployer), SAVING_ADDRESS);
43 |
44 | Savings savingsImpl = new Savings();
45 | Savings saving = Savings(
46 | _deployUpgradeable(
47 | address(savingsImpl),
48 | address(proxy),
49 | abi.encodeWithSelector(Savings.initialize.selector, coreBorrow, agEUR, "Mock-sagEUR", "Mock-sagEUR", 1)
50 | )
51 | );
52 | console.log("Savings deployed at: %s", address(saving));
53 |
54 | vm.stopBroadcast();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/scripts/gnosis/DeployTransmuterGnosis.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "../utils/Utils.s.sol";
5 | import { TransmuterDeploymentHelper } from "../utils/TransmuterDeploymentHelper.s.sol";
6 | import { console } from "forge-std/console.sol";
7 | import { stdJson } from "forge-std/StdJson.sol";
8 | import "stringutils/strings.sol";
9 | import { CollateralSetupProd, FakeGnosis } from "contracts/transmuter/configs/FakeGnosis.sol";
10 | import "contracts/transmuter/Storage.sol" as Storage;
11 | import { ITransmuter } from "interfaces/ITransmuter.sol";
12 | import { MockTokenPermit } from "test/mock/MockTokenPermit.sol";
13 | import { MockAccessControlManager } from "test/mock/MockAccessControlManager.sol";
14 | import { DummyDiamondImplementation } from "../generated/DummyDiamondImplementation.sol";
15 | import { MockChainlinkOracle } from "test/mock/MockChainlinkOracle.sol";
16 |
17 | contract DeployTransmuterGnosis is TransmuterDeploymentHelper {
18 | using strings for *;
19 | using stdJson for string;
20 |
21 | function run() external {
22 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_GNOSIS"), 0);
23 | vm.startBroadcast(deployerPrivateKey);
24 |
25 | address deployer = vm.addr(deployerPrivateKey);
26 | console.log("address: %s", deployer);
27 |
28 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 | DEPLOY
30 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
31 | // Deploy fakes Core borrow, agEUR, and collaterals
32 |
33 | MockAccessControlManager coreBorrow = new MockAccessControlManager();
34 | console.log("CoreBorrow deployed at: %s", address(coreBorrow));
35 | coreBorrow.toggleGovernor(deployer);
36 |
37 | MockTokenPermit agEUR = new MockTokenPermit("Mock-AgEUR", "Mock-AgEUR", 18);
38 | console.log("AgEUR deployed at: %s", address(agEUR));
39 |
40 | address[] memory _collateralAddresses = new address[](3);
41 | address[] memory _oracleAddresses = new address[](3);
42 | for (uint256 i; i < 3; i++) {
43 | MockTokenPermit collateral = new MockTokenPermit(
44 | string(abi.encodePacked("MockCollat", vm.toString(i))),
45 | string(abi.encodePacked("MockCollat", vm.toString(i))),
46 | uint8(6 * (i + 1))
47 | );
48 | _collateralAddresses[i] = address(collateral);
49 | console.log("Collateral %i deployed at: %s", i, address(collateral));
50 |
51 | MockChainlinkOracle oracle = new MockChainlinkOracle();
52 | console.log("oracle deployed at: %s", address(oracle));
53 | oracle.setLatestAnswer(1e8);
54 | _oracleAddresses[i] = address(oracle);
55 | }
56 |
57 | // Config
58 | config = address(new FakeGnosis());
59 | ITransmuter transmuter = _deployTransmuter(
60 | config,
61 | abi.encodeWithSelector(
62 | FakeGnosis.initialize.selector,
63 | coreBorrow,
64 | agEUR,
65 | _collateralAddresses,
66 | _oracleAddresses
67 | )
68 | );
69 |
70 | console.log("Transmuter deployed at: %s", address(transmuter));
71 |
72 | // Redeem
73 | uint64[] memory xRedeemFee = new uint64[](1);
74 | int64[] memory yRedeemFee = new int64[](1);
75 | xRedeemFee[0] = 0;
76 | yRedeemFee[0] = 1e9;
77 | transmuter.setRedemptionCurveParams(xRedeemFee, yRedeemFee);
78 | transmuter.togglePause(address(0x0), Storage.ActionType.Redeem);
79 |
80 | vm.stopBroadcast();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/scripts/gnosis/VerifyProxyEtherscanGnosis.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "../utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import { stdJson } from "forge-std/StdJson.sol";
7 | import "stringutils/strings.sol";
8 | import "contracts/utils/Errors.sol" as Errors;
9 | import "contracts/transmuter/Storage.sol" as Storage;
10 | import { DiamondEtherscan } from "contracts/transmuter/facets/DiamondEtherscan.sol";
11 | import { DummyDiamondImplementation } from "../generated/DummyDiamondImplementation.sol";
12 | import { ITransmuter } from "interfaces/ITransmuter.sol";
13 |
14 | contract VerifyProxyEtherscanGnosis is Utils {
15 | using strings for *;
16 | using stdJson for string;
17 |
18 | ITransmuter transmuter;
19 | DiamondEtherscan etherscanFacet;
20 | string[] facetNames;
21 | address[] facetAddressList;
22 |
23 | function run() external {
24 | uint256 deployerPrivateKey = vm.deriveKey(vm.envString("MNEMONIC_GNOSIS"), 0);
25 | vm.startBroadcast(deployerPrivateKey);
26 |
27 | address deployer = vm.addr(deployerPrivateKey);
28 | console.log("address: %s", deployer);
29 |
30 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
31 | DEPLOY
32 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
33 |
34 | transmuter = ITransmuter(0x4A44f77978Daa3E92Eb3D97210bd11645cF935Ab);
35 |
36 | // deploy dummy implementation
37 | DummyDiamondImplementation dummyImpl = new DummyDiamondImplementation();
38 | //DummyDiamondImplementation dummyImpl = DummyDiamondImplementation(0x8911084eF979Ac1B02D6d9AAbfAD86927C5b1589);
39 | _deployDiamondEtherscan();
40 | transmuter.setDummyImplementation(address(dummyImpl));
41 |
42 | vm.stopBroadcast();
43 | }
44 |
45 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46 | HELPERS
47 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
48 |
49 | // @dev Deploys diamond and connects facets
50 | function _deployDiamondEtherscan() internal {
51 | // Deploy every facet
52 | facetNames.push("DiamondEtherscan");
53 | etherscanFacet = new DiamondEtherscan();
54 | // etherscanFacet = DiamondEtherscan(0xC492fBAe68cE6C5E14C7ed5cd8a59babD5c90e4C);
55 | facetAddressList.push(address(etherscanFacet));
56 |
57 | string memory json = vm.readFile(JSON_SELECTOR_PATH);
58 |
59 | // Build appropriate payload
60 | uint256 n = facetNames.length;
61 | Storage.FacetCut[] memory cut = new Storage.FacetCut[](n);
62 | for (uint256 i = 0; i < n; ++i) {
63 | bytes4[] memory selectors = _arrayBytes32ToBytes4(
64 | json.readBytes32Array(string.concat("$.", facetNames[i]))
65 | );
66 | cut[i] = Storage.FacetCut({
67 | facetAddress: facetAddressList[i],
68 | action: Storage.FacetCutAction.Add,
69 | functionSelectors: selectors
70 | });
71 | }
72 |
73 | // add facet
74 | transmuter.diamondCut(cut, address(0), hex"");
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/scripts/interaction/transmuter/Swap.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "../../utils/Utils.s.sol";
5 | import { console } from "forge-std/console.sol";
6 | import "stringutils/strings.sol";
7 | import "../../Constants.s.sol";
8 |
9 | import "contracts/transmuter/Storage.sol" as Storage;
10 | import { ITransmuter } from "interfaces/ITransmuter.sol";
11 | import { Helpers } from "../../Helpers.s.sol";
12 | import { IERC20 } from "oz/token/ERC20/IERC20.sol";
13 | import { IERC20Metadata } from "oz/token/ERC20/extensions/IERC20Metadata.sol";
14 |
15 | contract SwapTransmuter is Utils, Helpers {
16 | ITransmuter public transmuter;
17 | address public tokenIn;
18 | uint256 public decimalsIn;
19 | address public tokenOut;
20 | uint256 public decimalsOut;
21 | uint256 public amount;
22 |
23 | function run() external {
24 | // TODO: make sure that selectors are well generated `yarn generate` before running this script
25 | // Here the `selectors.json` file is normally up to date
26 | uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
27 | address deployer = vm.addr(deployerPrivateKey);
28 | vm.startBroadcast(deployerPrivateKey);
29 |
30 | // TODO
31 | {
32 | uint256 chain = CHAIN_GNOSIS;
33 | StablecoinType fiat = StablecoinType.USD;
34 | address agToken = _chainToContract(chain, ContractType.AgUSD);
35 | (address liquidStablecoin, , ) = _chainToLiquidStablecoinAndOracle(chain, fiat);
36 | transmuter = ITransmuter(_chainToContract(chain, ContractType.TransmuterAgUSD));
37 | tokenIn = agToken;
38 | decimalsIn = IERC20Metadata(agToken).decimals();
39 | tokenOut = liquidStablecoin;
40 | decimalsOut = IERC20Metadata(liquidStablecoin).decimals();
41 | amount = 1.2375 ether;
42 | }
43 | // TODO END
44 |
45 | uint256 minAmountOut = (((amount * 10 ** decimalsOut) / 10 ** decimalsIn) * 99) / 100;
46 |
47 | IERC20(tokenIn).approve(address(transmuter), amount);
48 | transmuter.swapExactInput(amount, minAmountOut, tokenIn, tokenOut, deployer, 0);
49 | vm.stopBroadcast();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/scripts/selectors.json:
--------------------------------------------------------------------------------
1 | {
2 | "DiamondCut": [
3 | "0x1f931c1c00000000000000000000000000000000000000000000000000000000"
4 | ],
5 | "DiamondEtherscan": [
6 | "0x5c60da1b00000000000000000000000000000000000000000000000000000000",
7 | "0xc39aa07d00000000000000000000000000000000000000000000000000000000"
8 | ],
9 | "DiamondLoupe": [
10 | "0xcdffacc600000000000000000000000000000000000000000000000000000000",
11 | "0x52ef6b2c00000000000000000000000000000000000000000000000000000000",
12 | "0xadfca15e00000000000000000000000000000000000000000000000000000000",
13 | "0x7a0ed62700000000000000000000000000000000000000000000000000000000"
14 | ],
15 | "Getters": [
16 | "0xb4a0bdf300000000000000000000000000000000000000000000000000000000",
17 | "0xee565a6300000000000000000000000000000000000000000000000000000000",
18 | "0x847da7be00000000000000000000000000000000000000000000000000000000",
19 | "0xeb7aac5f00000000000000000000000000000000000000000000000000000000",
20 | "0x3335221000000000000000000000000000000000000000000000000000000000",
21 | "0xb718136100000000000000000000000000000000000000000000000000000000",
22 | "0xb85780bc00000000000000000000000000000000000000000000000000000000",
23 | "0xcd377c5300000000000000000000000000000000000000000000000000000000",
24 | "0x782513bd00000000000000000000000000000000000000000000000000000000",
25 | "0x94e35d9e00000000000000000000000000000000000000000000000000000000",
26 | "0x4ea3e34300000000000000000000000000000000000000000000000000000000",
27 | "0x10d3d22e00000000000000000000000000000000000000000000000000000000",
28 | "0x38c269eb00000000000000000000000000000000000000000000000000000000",
29 | "0xadc9d1f700000000000000000000000000000000000000000000000000000000",
30 | "0x31da6b1300000000000000000000000000000000000000000000000000000000",
31 | "0x8db9653f00000000000000000000000000000000000000000000000000000000",
32 | "0x0d12662700000000000000000000000000000000000000000000000000000000",
33 | "0x96d6487900000000000000000000000000000000000000000000000000000000",
34 | "0xfe7d0c5400000000000000000000000000000000000000000000000000000000",
35 | "0x77dc342900000000000000000000000000000000000000000000000000000000",
36 | "0xf9839d8900000000000000000000000000000000000000000000000000000000",
37 | "0xa52aefd400000000000000000000000000000000000000000000000000000000",
38 | "0x99eeca4900000000000000000000000000000000000000000000000000000000"
39 | ],
40 | "Redeemer": [
41 | "0xd703a0cd00000000000000000000000000000000000000000000000000000000",
42 | "0x815822c100000000000000000000000000000000000000000000000000000000",
43 | "0x2e7639bc00000000000000000000000000000000000000000000000000000000",
44 | "0xfd7daaf800000000000000000000000000000000000000000000000000000000"
45 | ],
46 | "RewardHandler": [
47 | "0x05b4193400000000000000000000000000000000000000000000000000000000"
48 | ],
49 | "SettersGovernor": [
50 | "0xf0d2d5a800000000000000000000000000000000000000000000000000000000",
51 | "0xc1cdee7e00000000000000000000000000000000000000000000000000000000",
52 | "0x87c8ab7a00000000000000000000000000000000000000000000000000000000",
53 | "0x5c3eebda00000000000000000000000000000000000000000000000000000000",
54 | "0x1f0ec8ee00000000000000000000000000000000000000000000000000000000",
55 | "0x0e32cb8600000000000000000000000000000000000000000000000000000000",
56 | "0x81ee2deb00000000000000000000000000000000000000000000000000000000",
57 | "0xb13b084700000000000000000000000000000000000000000000000000000000",
58 | "0x1b0c718200000000000000000000000000000000000000000000000000000000",
59 | "0x7c0343a100000000000000000000000000000000000000000000000000000000",
60 | "0x1cb44dfc00000000000000000000000000000000000000000000000000000000"
61 | ],
62 | "SettersGuardian": [
63 | "0x629feb6200000000000000000000000000000000000000000000000000000000",
64 | "0x4eec47b900000000000000000000000000000000000000000000000000000000",
65 | "0x603b432700000000000000000000000000000000000000000000000000000000",
66 | "0xa9e6a1a400000000000000000000000000000000000000000000000000000000",
67 | "0xb607d09900000000000000000000000000000000000000000000000000000000"
68 | ],
69 | "Swapper": [
70 | "0x4583aea600000000000000000000000000000000000000000000000000000000",
71 | "0x9525f3ab00000000000000000000000000000000000000000000000000000000",
72 | "0x3b6a1fe000000000000000000000000000000000000000000000000000000000",
73 | "0xd92c6cb200000000000000000000000000000000000000000000000000000000",
74 | "0xb92567fa00000000000000000000000000000000000000000000000000000000",
75 | "0xc10a628700000000000000000000000000000000000000000000000000000000"
76 | ],
77 | "useless": ""
78 | }
--------------------------------------------------------------------------------
/scripts/selectors_add.json:
--------------------------------------------------------------------------------
1 | {
2 | "SettersGovernor": [
3 | "0x1cb44dfc00000000000000000000000000000000000000000000000000000000"
4 | ],
5 | "SettersGuardian": [
6 | "0x603b432700000000000000000000000000000000000000000000000000000000"
7 | ],
8 | "useless": ""
9 | }
10 |
--------------------------------------------------------------------------------
/scripts/selectors_replace.json:
--------------------------------------------------------------------------------
1 | {
2 | "DiamondCut": [
3 | "0x1f931c1c00000000000000000000000000000000000000000000000000000000"
4 | ],
5 | "DiamondEtherscan": [
6 | "0x5c60da1b00000000000000000000000000000000000000000000000000000000",
7 | "0xc39aa07d00000000000000000000000000000000000000000000000000000000"
8 | ],
9 | "DiamondLoupe": [
10 | "0xcdffacc600000000000000000000000000000000000000000000000000000000",
11 | "0x52ef6b2c00000000000000000000000000000000000000000000000000000000",
12 | "0xadfca15e00000000000000000000000000000000000000000000000000000000",
13 | "0x7a0ed62700000000000000000000000000000000000000000000000000000000"
14 | ],
15 | "Getters": [
16 | "0xb4a0bdf300000000000000000000000000000000000000000000000000000000",
17 | "0xee565a6300000000000000000000000000000000000000000000000000000000",
18 | "0x847da7be00000000000000000000000000000000000000000000000000000000",
19 | "0xeb7aac5f00000000000000000000000000000000000000000000000000000000",
20 | "0x3335221000000000000000000000000000000000000000000000000000000000",
21 | "0xb718136100000000000000000000000000000000000000000000000000000000",
22 | "0xb85780bc00000000000000000000000000000000000000000000000000000000",
23 | "0xcd377c5300000000000000000000000000000000000000000000000000000000",
24 | "0x782513bd00000000000000000000000000000000000000000000000000000000",
25 | "0x94e35d9e00000000000000000000000000000000000000000000000000000000",
26 | "0x4ea3e34300000000000000000000000000000000000000000000000000000000",
27 | "0x10d3d22e00000000000000000000000000000000000000000000000000000000",
28 | "0x38c269eb00000000000000000000000000000000000000000000000000000000",
29 | "0xadc9d1f700000000000000000000000000000000000000000000000000000000",
30 | "0x8db9653f00000000000000000000000000000000000000000000000000000000",
31 | "0x0d12662700000000000000000000000000000000000000000000000000000000",
32 | "0x96d6487900000000000000000000000000000000000000000000000000000000",
33 | "0xfe7d0c5400000000000000000000000000000000000000000000000000000000",
34 | "0x77dc342900000000000000000000000000000000000000000000000000000000",
35 | "0xf9839d8900000000000000000000000000000000000000000000000000000000",
36 | "0xa52aefd400000000000000000000000000000000000000000000000000000000",
37 | "0x99eeca4900000000000000000000000000000000000000000000000000000000"
38 | ],
39 | "Redeemer": [
40 | "0xd703a0cd00000000000000000000000000000000000000000000000000000000",
41 | "0x815822c100000000000000000000000000000000000000000000000000000000",
42 | "0x2e7639bc00000000000000000000000000000000000000000000000000000000",
43 | "0xfd7daaf800000000000000000000000000000000000000000000000000000000"
44 | ],
45 | "RewardHandler": [
46 | "0x05b4193400000000000000000000000000000000000000000000000000000000"
47 | ],
48 | "SettersGovernor": [
49 | "0xf0d2d5a800000000000000000000000000000000000000000000000000000000",
50 | "0xc1cdee7e00000000000000000000000000000000000000000000000000000000",
51 | "0x87c8ab7a00000000000000000000000000000000000000000000000000000000",
52 | "0x5c3eebda00000000000000000000000000000000000000000000000000000000",
53 | "0x1f0ec8ee00000000000000000000000000000000000000000000000000000000",
54 | "0x0e32cb8600000000000000000000000000000000000000000000000000000000",
55 | "0x81ee2deb00000000000000000000000000000000000000000000000000000000",
56 | "0xb13b084700000000000000000000000000000000000000000000000000000000",
57 | "0x1b0c718200000000000000000000000000000000000000000000000000000000",
58 | "0x7c0343a100000000000000000000000000000000000000000000000000000000"
59 | ],
60 | "SettersGuardian": [
61 | "0x629feb6200000000000000000000000000000000000000000000000000000000",
62 | "0x4eec47b900000000000000000000000000000000000000000000000000000000",
63 | "0xa9e6a1a400000000000000000000000000000000000000000000000000000000",
64 | "0xb607d09900000000000000000000000000000000000000000000000000000000"
65 | ],
66 | "Swapper": [
67 | "0x4583aea600000000000000000000000000000000000000000000000000000000",
68 | "0x9525f3ab00000000000000000000000000000000000000000000000000000000",
69 | "0x3b6a1fe000000000000000000000000000000000000000000000000000000000",
70 | "0xd92c6cb200000000000000000000000000000000000000000000000000000000",
71 | "0xb92567fa00000000000000000000000000000000000000000000000000000000",
72 | "0xc10a628700000000000000000000000000000000000000000000000000000000"
73 | ],
74 | "useless": ""
75 | }
--------------------------------------------------------------------------------
/scripts/utils/GenerateSelectors.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { Utils } from "./Utils.s.sol";
5 | import "stringutils/strings.sol";
6 |
7 | contract GenerateSelectors is Utils {
8 | using strings for *;
9 |
10 | address public config;
11 | string[] facetNames;
12 |
13 | function run() external {
14 | facetNames.push("DiamondCut");
15 | facetNames.push("DiamondLoupe");
16 | facetNames.push("Getters");
17 | facetNames.push("Redeemer");
18 | facetNames.push("RewardHandler");
19 | facetNames.push("SettersGovernor");
20 | facetNames.push("SettersGuardian");
21 | facetNames.push("Swapper");
22 | facetNames.push("DiamondEtherscan");
23 |
24 | string memory json = "";
25 | for (uint256 i = 0; i < facetNames.length; ++i) {
26 | bytes4[] memory selectors = _generateSelectors(facetNames[i]);
27 | vm.serializeBytes32(json, facetNames[i], _arrayBytes4ToBytes32(selectors));
28 | }
29 | string memory finalJson = vm.serializeString(json, "useless", "");
30 | vm.writeJson(finalJson, JSON_SELECTOR_PATH);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/utils/Utils.s.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import "forge-std/Script.sol";
5 | import { StdAssertions } from "forge-std/Test.sol";
6 | import "stringutils/strings.sol";
7 |
8 | import { CommonUtils } from "utils/src/CommonUtils.sol";
9 | import { ContractType } from "utils/src/Constants.sol";
10 |
11 | contract Utils is Script, CommonUtils {
12 | using strings for *;
13 |
14 | string constant JSON_SELECTOR_PATH = "./scripts/selectors.json";
15 | string constant JSON_SELECTOR_PATH_REPLACE = "./scripts/selectors_replace.json";
16 | string constant JSON_SELECTOR_PATH_ADD = "./scripts/selectors_add.json";
17 | string constant JSON_VANITY_PATH = "./scripts/vanity.json";
18 |
19 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 | HELPERS
21 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
22 |
23 | function _bytes4ToBytes32(bytes4 _in) internal pure returns (bytes32 out) {
24 | assembly {
25 | out := _in
26 | }
27 | }
28 |
29 | function _arrayBytes4ToBytes32(bytes4[] memory _in) internal pure returns (bytes32[] memory out) {
30 | out = new bytes32[](_in.length);
31 | for (uint256 i = 0; i < _in.length; ++i) {
32 | out[i] = _bytes4ToBytes32(_in[i]);
33 | }
34 | }
35 |
36 | function _arrayBytes32ToBytes4(bytes32[] memory _in) internal pure returns (bytes4[] memory out) {
37 | out = new bytes4[](_in.length);
38 | for (uint256 i = 0; i < _in.length; ++i) {
39 | out[i] = bytes4(_in[i]);
40 | }
41 | }
42 |
43 | function consoleLogBytes4Array(bytes4[] memory _in) internal view {
44 | for (uint256 i = 0; i < _in.length; ++i) {
45 | console.logBytes4(_in[i]);
46 | }
47 | }
48 | }
49 |
50 | contract AssertUtils is StdAssertions {
51 | function _assertArrayUint64(uint64[] memory _a, uint64[] memory _b) internal {
52 | assertEq(_a.length, _b.length);
53 | for (uint256 i = 0; i < _a.length; ++i) {
54 | assertEq(_a[i], _b[i]);
55 | }
56 | }
57 |
58 | function _assertArrayInt64(int64[] memory _a, int64[] memory _b) internal {
59 | assertEq(_a.length, _b.length);
60 | for (uint256 i = 0; i < _a.length; ++i) {
61 | assertEq(_a[i], _b[i]);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/scripts/vanity.json:
--------------------------------------------------------------------------------
1 | {"init":431738,"salt":"0xa9ddd91249dfdd450e81e1c56ab60e1a6265170100000000000000000006967a"}
--------------------------------------------------------------------------------
/slither.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "detectors_to_exclude": "naming-convention,solc-version",
3 | "filter_paths": "(lib|test|external|scripts)",
4 | "solc_remaps": [
5 | "ds-test/=lib/forge-std/lib/ds-test/src/",
6 | "forge-std/=lib/forge-std/src/",
7 | "stringutils/=lib/solidity-stringutils",
8 | "contracts/=contracts/",
9 | "test/=test/",
10 | "interfaces/=contracts/interfaces/",
11 | "oz/=lib/openzeppelin-contracts/contracts/",
12 | "oz-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
13 | "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
14 | "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
15 | "mock/=test/mock/",
16 | "prb/math/=lib/prb-math/src/",
17 | "borrow/=lib/borrow-contracts/contracts"
18 | ]
19 | }
--------------------------------------------------------------------------------
/slither.sh:
--------------------------------------------------------------------------------
1 | # Use git grep to find all files containing '0.8.19'
2 | files=$(git grep -l '0.8.19')
3 |
4 | # Loop over files and replace '0.8.19' with '0.8.18'
5 | for file in $files
6 | do
7 | sed -i '' 's/0.8.19/0.8.18/g' "$file"
8 | done
9 |
10 | # # # Ignore test files
11 | # find "test/" -type f -name "*.sol" | while read file; do
12 | # # Replace '.sol' extension with '.txt'
13 | # mv "$file" "${file%.sol}.txt"
14 | # done
15 |
16 | # mv "test/mock/MockLib.txt" "test/mock/MockLib.sol"
17 |
18 | # Function to handle error
19 | reset() {
20 | # find "test/" -type f -name "*.txt" | while read file; do
21 | # # Replace '.sol' extension with '.txt'
22 | # mv "$file" "${file%.txt}.sol"
23 | # done
24 |
25 | files=$(git grep -l '0.8.18')
26 | for file in $files
27 | do
28 | sed -i '' 's/0.8.18/0.8.19/g' "$file"
29 | done
30 | }
31 |
32 | trap 'reset' ERR
33 |
34 | # pip3 install slither-analyzer
35 | # pip3 install solc-select
36 | # solc-select install 0.8.18
37 | # solc-select use 0.8.18
38 | FOUNDRY_PROFILE=dev forge build --skip test
39 |
40 | slither test/mock/Slither.sol --show-ignored-findings --foundry-ignore-compile
41 |
42 | reset
--------------------------------------------------------------------------------
/test/invariants/actors/BaseActor.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.19;
3 |
4 | import { IERC20 } from "oz/token/ERC20/IERC20.sol";
5 | import { IERC20Metadata } from "oz/token/ERC20/extensions/IERC20Metadata.sol";
6 | //solhint-disable
7 |
8 | import { AggregatorV3Interface } from "interfaces/external/chainlink/AggregatorV3Interface.sol";
9 | import { IAgToken } from "interfaces/IAgToken.sol";
10 | import { ITransmuter } from "interfaces/ITransmuter.sol";
11 | import { Test, stdMath, StdStorage, stdStorage } from "forge-std/Test.sol";
12 | import "contracts/utils/Constants.sol";
13 | import "contracts/utils/Errors.sol";
14 |
15 | struct TestStorage {
16 | address tokenIn;
17 | address tokenOut;
18 | uint256 amountIn;
19 | uint256 amountOut;
20 | uint256 amountInSplit1;
21 | uint256 amountOutSplit1;
22 | uint256 amountInSplit2;
23 | uint256 amountOutSplit2;
24 | }
25 |
26 | contract BaseActor is Test {
27 | address public constant sweeper = address(uint160(uint256(keccak256(abi.encodePacked("sweeper")))));
28 | uint256 internal _maxAmountWithoutDecimals = 10 ** 15;
29 | // making this value smaller worsen rounding and make test harder to pass.
30 | // Trade off between bullet proof against all oracles and all interactions
31 | uint256 internal _minOracleValue = 10 ** 3; // 10**(-6)
32 | uint256 internal _minWallet = 10 ** 18; // in base 18
33 | uint256 internal _maxWallet = 10 ** (18 + 12); // in base 18
34 | int64 internal _minBurnFee = -int64(int256(BASE_9 / 2));
35 |
36 | mapping(bytes32 => uint256) public calls;
37 | address[] public actors;
38 | uint256 public nbrActor;
39 | address internal _currentActor;
40 |
41 | IAgToken agToken;
42 | ITransmuter internal _transmuter;
43 | ITransmuter internal _transmuterSplit;
44 | address[] internal _collaterals;
45 | AggregatorV3Interface[] internal _oracles;
46 | uint256[] internal _maxTokenAmount;
47 |
48 | modifier countCall(bytes32 key) {
49 | calls[key]++;
50 | _;
51 | }
52 |
53 | modifier useActor(uint256 actorIndexSeed) {
54 | _currentActor = actors[bound(actorIndexSeed, 0, actors.length - 1)];
55 | vm.startPrank(_currentActor);
56 | _;
57 | vm.stopPrank();
58 | }
59 |
60 | constructor(
61 | uint256 _nbrActor,
62 | string memory actorType,
63 | ITransmuter transmuter,
64 | ITransmuter transmuterSplit,
65 | address[] memory collaterals,
66 | AggregatorV3Interface[] memory oracles
67 | ) {
68 | for (uint256 i; i < _nbrActor; ++i) {
69 | address actor = address(uint160(uint256(keccak256(abi.encodePacked("actor", actorType, i)))));
70 | actors.push(actor);
71 | }
72 | nbrActor = _nbrActor;
73 | _transmuter = transmuter;
74 | _transmuterSplit = transmuterSplit;
75 | agToken = IAgToken(transmuter.agToken());
76 | _collaterals = collaterals;
77 | _oracles = oracles;
78 | _maxTokenAmount.push(_maxAmountWithoutDecimals * 10 ** IERC20Metadata(_collaterals[0]).decimals());
79 | _maxTokenAmount.push(_maxAmountWithoutDecimals * 10 ** IERC20Metadata(_collaterals[1]).decimals());
80 | _maxTokenAmount.push(_maxAmountWithoutDecimals * 10 ** IERC20Metadata(_collaterals[2]).decimals());
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/test/mock/MockAccessControlManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | contract MockAccessControlManager {
6 | mapping(address => bool) public governors;
7 | mapping(address => bool) public guardians;
8 |
9 | function isGovernor(address admin) external view returns (bool) {
10 | return governors[admin];
11 | }
12 |
13 | function isGovernorOrGuardian(address admin) external view returns (bool) {
14 | return guardians[admin] || governors[admin];
15 | }
16 |
17 | function toggleGovernor(address admin) external {
18 | governors[admin] = !governors[admin];
19 | }
20 |
21 | function toggleGuardian(address admin) external {
22 | guardians[admin] = !guardians[admin];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/mock/MockChainlinkOracle.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | interface MockAggregatorV3Interface {
6 | function decimals() external view returns (uint8);
7 |
8 | function description() external view returns (string memory);
9 |
10 | function version() external view returns (uint256);
11 |
12 | function getRoundData(
13 | uint80 _roundId
14 | )
15 | external
16 | view
17 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
18 |
19 | function latestRoundData()
20 | external
21 | view
22 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
23 | }
24 |
25 | contract MockChainlinkOracle is MockAggregatorV3Interface {
26 | uint80 public roundId = 0;
27 | uint8 public keyDecimals = 0;
28 |
29 | struct Entry {
30 | uint80 roundId;
31 | int256 answer;
32 | uint256 startedAt;
33 | uint256 updatedAt;
34 | uint80 answeredInRound;
35 | }
36 |
37 | mapping(uint256 => Entry) public entries;
38 |
39 | bool public latestRoundDataShouldRevert;
40 |
41 | string public desc;
42 |
43 | constructor() {}
44 |
45 | // Mock setup function
46 | function setLatestAnswer(int256 answer) external {
47 | roundId++;
48 | entries[roundId] = Entry({
49 | roundId: roundId,
50 | answer: answer,
51 | startedAt: block.timestamp,
52 | updatedAt: block.timestamp,
53 | answeredInRound: roundId
54 | });
55 | }
56 |
57 | function setLatestAnswer(int256 answer, uint256 timestamp) external {
58 | roundId++;
59 | entries[roundId] = Entry({
60 | roundId: roundId,
61 | answer: answer,
62 | startedAt: timestamp,
63 | updatedAt: timestamp,
64 | answeredInRound: roundId
65 | });
66 | }
67 |
68 | function setLatestAnswerWithRound(int256 answer, uint256 timestamp, uint80 _roundId) external {
69 | roundId = _roundId;
70 | entries[roundId] = Entry({
71 | roundId: roundId,
72 | answer: answer,
73 | startedAt: timestamp,
74 | updatedAt: timestamp,
75 | answeredInRound: roundId
76 | });
77 | }
78 |
79 | function setLatestAnswerRevert(int256 answer, uint256 timestamp) external {
80 | roundId++;
81 | entries[roundId] = Entry({
82 | roundId: roundId,
83 | answer: answer,
84 | startedAt: timestamp,
85 | updatedAt: timestamp,
86 | answeredInRound: roundId - 1
87 | });
88 | }
89 |
90 | function setLatestRoundDataShouldRevert(bool _shouldRevert) external {
91 | latestRoundDataShouldRevert = _shouldRevert;
92 | }
93 |
94 | function setDecimals(uint8 _decimals) external {
95 | keyDecimals = _decimals;
96 | }
97 |
98 | function setDescritpion(string memory _desc) external {
99 | desc = _desc;
100 | }
101 |
102 | function description() external view override returns (string memory) {
103 | return desc;
104 | }
105 |
106 | function version() external view override returns (uint256) {
107 | roundId;
108 | return 0;
109 | }
110 |
111 | function latestRoundData() external view override returns (uint80, int256, uint256, uint256, uint80) {
112 | if (latestRoundDataShouldRevert) {
113 | revert("latestRoundData reverted");
114 | }
115 | return getRoundData(uint80(roundId));
116 | }
117 |
118 | function decimals() external view override returns (uint8) {
119 | return keyDecimals;
120 | }
121 |
122 | function getRoundData(uint80 _roundId) public view override returns (uint80, int256, uint256, uint256, uint80) {
123 | Entry memory entry = entries[_roundId];
124 | // Emulate a Chainlink aggregator
125 | require(entry.updatedAt > 0, "No data present");
126 | return (entry.roundId, entry.answer, entry.startedAt, entry.updatedAt, entry.answeredInRound);
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/test/mock/MockERC777.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "oz/token/ERC777/ERC777.sol";
6 |
7 | contract MockERC777 is ERC777 {
8 | event Minting(address indexed _to, address indexed _minter, uint256 _amount);
9 |
10 | event Burning(address indexed _from, address indexed _burner, uint256 _amount);
11 |
12 | constructor(string memory name_, string memory symbol_, uint8 decimal_) ERC777(name_, symbol_, new address[](0)) {}
13 |
14 | function mint(address account, uint256 amount) external {
15 | _mint(account, amount, "", "");
16 | emit Minting(account, msg.sender, amount);
17 | }
18 |
19 | function burn(address account, uint256 amount) public {
20 | _burn(account, amount, "", "");
21 | emit Burning(account, msg.sender, amount);
22 | }
23 |
24 | function setAllowance(address from, address to) public {
25 | _approve(from, to, type(uint256).max);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/mock/MockExternalOracle.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { AggregatorV3Interface } from "contracts/interfaces/external/chainlink/AggregatorV3Interface.sol";
6 | import { ITransmuterOracle } from "contracts/interfaces/ITransmuterOracle.sol";
7 |
8 | contract MockExternalOracle is ITransmuterOracle {
9 | AggregatorV3Interface feed;
10 |
11 | constructor(AggregatorV3Interface _feed) {
12 | feed = _feed;
13 | }
14 |
15 | function readRedemption() external view returns (uint256) {
16 | (, int256 ratio, , , ) = feed.latestRoundData();
17 | return uint256(ratio) * 1e12;
18 | }
19 |
20 | function readMint() external view returns (uint256) {
21 | (, int256 ratio, , , ) = feed.latestRoundData();
22 | return uint256(ratio) * 1e12;
23 | }
24 |
25 | function readBurn() external view returns (uint256 oracleValue, uint256 deviation) {
26 | (, int256 ratio, , , ) = feed.latestRoundData();
27 | return (uint256(ratio) * 1e12, 1e18);
28 | }
29 |
30 | function read() external view returns (uint256) {
31 | (, int256 ratio, , , ) = feed.latestRoundData();
32 | return uint256(ratio) * 1e12;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/mock/MockKeyringGuard.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | contract MockKeyringGuard {
6 | mapping(address => bool) public authorized;
7 |
8 | function isAuthorized(address, address to) external view returns (bool passed) {
9 | return authorized[to];
10 | }
11 |
12 | function setAuthorized(address to, bool status) external {
13 | authorized[to] = status;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/mock/MockLib.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { LibHelpers } from "../../contracts/transmuter/libraries/LibHelpers.sol";
6 | import { LibManager } from "../../contracts/transmuter/libraries/LibManager.sol";
7 | import { LibStorage } from "../../contracts/transmuter/libraries/LibStorage.sol";
8 | import { ImplementationStorage } from "../../contracts/transmuter/Storage.sol";
9 |
10 | contract MockLib {
11 | function convertDecimalTo(uint256 amount, uint8 fromDecimals, uint8 toDecimals) external pure returns (uint256) {
12 | return LibHelpers.convertDecimalTo(amount, fromDecimals, toDecimals);
13 | }
14 |
15 | function checkList(address token, address[] memory tokens) external pure returns (int256) {
16 | return LibHelpers.checkList(token, tokens);
17 | }
18 |
19 | function findLowerBound(
20 | bool increasingArray,
21 | uint64[] memory array,
22 | uint64 normalizerArray,
23 | uint64 element
24 | ) external pure returns (uint256) {
25 | return LibHelpers.findLowerBound(increasingArray, array, normalizerArray, element);
26 | }
27 |
28 | function piecewiseLinear(uint64 x, uint64[] memory xArray, int64[] memory yArray) external pure returns (int64) {
29 | return LibHelpers.piecewiseLinear(x, xArray, yArray);
30 | }
31 |
32 | function implementationStorage() external pure returns (ImplementationStorage memory) {
33 | return LibStorage.implementationStorage();
34 | }
35 |
36 | function transferRecipient(bytes memory config) external view returns (address) {
37 | return LibManager.transferRecipient(config);
38 | }
39 |
40 | function totalAssets(bytes memory config) external view returns (uint256[] memory balances, uint256 totalValue) {
41 | return LibManager.totalAssets(config);
42 | }
43 |
44 | function invest(uint256 amount, bytes memory config) external {
45 | LibManager.invest(amount, config);
46 | }
47 |
48 | function maxAvailable(bytes memory config) external view returns (uint256 available) {
49 | return LibManager.maxAvailable(config);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/mock/MockManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "oz/token/ERC20/IERC20.sol";
6 | import "oz/token/ERC20/utils/SafeERC20.sol";
7 |
8 | import "interfaces/IManager.sol";
9 |
10 | import { LibHelpers } from "../../contracts/transmuter/libraries/LibHelpers.sol";
11 | import { LibOracle, AggregatorV3Interface } from "../../contracts/transmuter/libraries/LibOracle.sol";
12 |
13 | import "../../contracts/utils/Constants.sol";
14 | import "../../contracts/utils/Errors.sol";
15 |
16 | contract MockManager is IManager {
17 | address public collateral;
18 | IERC20[] public subCollaterals;
19 | bytes public config;
20 | mapping(address => bool) public governors;
21 | mapping(address => bool) public guardians;
22 |
23 | constructor(address _collateral) {
24 | collateral = _collateral;
25 | }
26 |
27 | function setSubCollaterals(IERC20[] memory _subCollaterals, bytes memory _managerConfig) external {
28 | subCollaterals = _subCollaterals;
29 | config = _managerConfig;
30 | }
31 |
32 | function release(address token, address to, uint256 amount) external {
33 | bool found;
34 | for (uint256 i; i < subCollaterals.length; ++i) {
35 | if (token == address(subCollaterals[i])) {
36 | found = true;
37 | break;
38 | }
39 | }
40 | if (!found) revert NotCollateral();
41 | IERC20(token).transfer(to, amount);
42 | }
43 |
44 | function invest(uint256 amount) external {}
45 |
46 | function withdrawAndTransfer(address token, address to, uint256 amount) external {
47 | bool found;
48 | for (uint256 i; i < subCollaterals.length; ++i) {
49 | if (token == address(subCollaterals[i])) {
50 | found = true;
51 | break;
52 | }
53 | }
54 | if (!found) revert NotCollateral();
55 | IERC20(token).transfer(to, amount);
56 | }
57 |
58 | function pullAll() external {
59 | for (uint256 i; i < subCollaterals.length; ++i) {
60 | subCollaterals[i].transfer(msg.sender, subCollaterals[i].balanceOf(address(this)));
61 | }
62 | }
63 |
64 | /// @notice Gets the balances of all the tokens controlled be the manager contract
65 | /// @return balances An array of size `subCollaterals` with current balances
66 | /// @return totalValue The sum of the balances corrected by an oracle
67 | function totalAssets() external view returns (uint256[] memory balances, uint256 totalValue) {
68 | uint256 nbrCollaterals = subCollaterals.length;
69 | balances = new uint256[](nbrCollaterals);
70 | if (nbrCollaterals == 0) return (balances, totalValue);
71 | if (config.length == 0) {
72 | for (uint256 i = 0; i < nbrCollaterals; ++i) {
73 | balances[i] = subCollaterals[i].balanceOf(address(this));
74 | totalValue += balances[i];
75 | }
76 | return (balances, totalValue);
77 | }
78 | (
79 | uint8[] memory tokenDecimals,
80 | AggregatorV3Interface[] memory oracles,
81 | uint32[] memory stalePeriods,
82 | uint8[] memory oracleIsMultiplied,
83 | uint8[] memory chainlinkDecimals
84 | ) = abi.decode(config, (uint8[], AggregatorV3Interface[], uint32[], uint8[], uint8[]));
85 |
86 | for (uint256 i = 0; i < nbrCollaterals; ++i) {
87 | balances[i] = subCollaterals[i].balanceOf(address(this));
88 | if (i > 0) {
89 | totalValue += LibOracle.readChainlinkFeed(
90 | LibHelpers.convertDecimalTo(balances[i], tokenDecimals[i], tokenDecimals[0]),
91 | oracles[i - 1],
92 | oracleIsMultiplied[i - 1],
93 | chainlinkDecimals[i - 1],
94 | stalePeriods[i - 1]
95 | );
96 | } else totalValue += balances[i];
97 | }
98 | }
99 |
100 | /// @notice Gives the maximum amount of collateral immediately available for a transfer
101 | function maxAvailable() external view returns (uint256) {
102 | return IERC20(collateral).balanceOf(address(this));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/test/mock/MockMorphoOracle.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IMorphoOracle } from "contracts/interfaces/external/morpho/IMorphoOracle.sol";
6 |
7 | contract MockMorphoOracle is IMorphoOracle {
8 | uint256 value;
9 |
10 | constructor(uint256 _value) {
11 | value = _value;
12 | }
13 |
14 | function price() external view returns (uint256) {
15 | return value;
16 | }
17 |
18 | function setValue(uint256 _value) external {
19 | value = _value;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/mock/MockOneInchRouter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "oz/token/ERC20/IERC20.sol";
6 | import "oz/token/ERC20/utils/SafeERC20.sol";
7 |
8 | import "../../contracts/utils/Constants.sol";
9 | import "../../contracts/utils/Errors.sol";
10 |
11 | contract MockOneInchRouter {
12 | using SafeERC20 for IERC20;
13 | bool public setRevert;
14 | bool public setRevertWithMessage;
15 |
16 | function swap(uint256 amountIn, uint256 amountOut, address tokenIn, address tokenOut) external returns (uint256) {
17 | if (setRevert) require(false);
18 | if (setRevertWithMessage) revert("wrong swap");
19 | IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
20 | IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
21 | return amountOut;
22 | }
23 |
24 | function setRevertStatuses(bool _setRevert, bool _setRevertWithMessage) external {
25 | setRevert = _setRevert;
26 | setRevertWithMessage = _setRevertWithMessage;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test/mock/MockPyth.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "interfaces/external/pyth/IPyth.sol";
6 |
7 | contract MockPyth {
8 | int64 public price;
9 | int32 public expo;
10 |
11 | function getPriceNoOlderThan(bytes32, uint) external view returns (PythStructs.Price memory) {
12 | return PythStructs.Price({ price: price, conf: 0, expo: expo, publishTime: block.timestamp });
13 | }
14 |
15 | function setParams(int64 _price, int32 _expo) external {
16 | price = _price;
17 | expo = _expo;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/mock/MockReentrant.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { ITransmuter, Transmuter } from "../utils/Transmuter.sol";
6 | import { IERC20 } from "oz/interfaces/IERC20.sol";
7 | import { IERC1820Registry } from "oz/utils/introspection/IERC1820Registry.sol";
8 |
9 | contract ReentrantRedeemGetCollateralRatio {
10 | bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");
11 | bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
12 |
13 | ITransmuter transmuter;
14 | IERC1820Registry registry;
15 |
16 | constructor(ITransmuter _transmuter, IERC1820Registry _registry) {
17 | transmuter = _transmuter;
18 | registry = _registry;
19 | }
20 |
21 | function testERC777Reentrancy(uint256 redeemAmount) public {
22 | uint256[] memory minAmountOuts;
23 | (, uint256[] memory quoteAmounts) = transmuter.quoteRedemptionCurve(redeemAmount);
24 | minAmountOuts = new uint256[](quoteAmounts.length);
25 | transmuter.redeem(redeemAmount, address(this), block.timestamp * 2, minAmountOuts);
26 | }
27 |
28 | function setInterfaceImplementer() public {
29 | // tokensReceived Hook
30 | // The token contract MUST call the tokensReceived hook of the recipient if the recipient registers an ERC777TokensRecipient implementation via ERC-1820.
31 | registry.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
32 | }
33 |
34 | function tokensReceived(address, address from, address, uint256, bytes calldata, bytes calldata) external view {
35 | // reenter here
36 | if (from != address(0)) {
37 | // It should revert here
38 | transmuter.getCollateralRatio();
39 | }
40 | }
41 |
42 | receive() external payable {}
43 | }
44 |
45 | contract ReentrantRedeemSwap {
46 | bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");
47 | bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
48 |
49 | ITransmuter transmuter;
50 | IERC1820Registry registry;
51 | IERC20 agToken;
52 | IERC20 collateral;
53 |
54 | constructor(ITransmuter _transmuter, IERC1820Registry _registry, IERC20 _agToken, IERC20 _collateral) {
55 | transmuter = _transmuter;
56 | registry = _registry;
57 | agToken = _agToken;
58 | collateral = _collateral;
59 | }
60 |
61 | function testERC777Reentrancy(uint256 redeemAmount) public {
62 | uint256[] memory minAmountOuts;
63 | (, uint256[] memory quoteAmounts) = transmuter.quoteRedemptionCurve(redeemAmount);
64 | minAmountOuts = new uint256[](quoteAmounts.length);
65 | transmuter.redeem(redeemAmount, address(this), block.timestamp * 2, minAmountOuts);
66 | }
67 |
68 | function setInterfaceImplementer() public {
69 | // tokensReceived Hook
70 | // The token contract MUST call the tokensReceived hook of the recipient if the recipient registers an ERC777TokensRecipient implementation via ERC-1820.
71 | registry.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
72 | }
73 |
74 | function tokensReceived(address, address from, address, uint256, bytes calldata, bytes calldata) external {
75 | // reenter here
76 | if (from != address(0)) {
77 | // It should revert here
78 | transmuter.swapExactInput(
79 | 1e18,
80 | 0,
81 | address(collateral),
82 | address(agToken),
83 | address(this),
84 | block.timestamp * 2
85 | );
86 | }
87 | }
88 |
89 | receive() external payable {}
90 | }
91 |
--------------------------------------------------------------------------------
/test/mock/MockRouter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Unlicensed
2 | pragma solidity ^0.8.19;
3 |
4 | import { SafeERC20, IERC20 } from "oz/token/ERC20/utils/SafeERC20.sol";
5 |
6 | contract MockRouter {
7 | using SafeERC20 for IERC20;
8 |
9 | function swap(uint256 amountIn, address tokenIn, uint256 amountOut, address tokenOut) external {
10 | IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
11 | IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/mock/MockScaleDecimals.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Unlicensed
2 | pragma solidity ^0.8.19;
3 |
4 | import "contracts/helpers/BaseHarvester.sol";
5 |
6 | contract MockScaleDecimals is BaseHarvester {
7 | constructor(
8 | uint96 initialMaxSlippage,
9 | IAccessControlManager definitiveAccessControlManager,
10 | IAgToken definitiveAgToken,
11 | ITransmuter definitiveTransmuter
12 | ) BaseHarvester(initialMaxSlippage, definitiveAccessControlManager, definitiveAgToken, definitiveTransmuter) {}
13 |
14 | function harvest(address yieldBearingAsset, uint256 scale, bytes calldata extraData) external override {}
15 |
16 | function scaleDecimals(
17 | uint256 decimalsTokenIn,
18 | uint256 decimalsTokenOut,
19 | uint256 amountIn,
20 | bool assetIn
21 | ) external pure returns (uint256) {
22 | return _scaleAmountBasedOnDecimals(decimalsTokenIn, decimalsTokenOut, amountIn, assetIn);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/mock/MockTokenPermit.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.7;
4 |
5 | import "oz/token/ERC20/IERC20.sol";
6 | import "oz/token/ERC20/extensions/draft-ERC20Permit.sol";
7 | import "oz/token/ERC20/utils/SafeERC20.sol";
8 |
9 | contract MockTokenPermit is ERC20Permit {
10 | using SafeERC20 for IERC20;
11 | event Minting(address indexed _to, address indexed _minter, uint256 _amount);
12 |
13 | event Burning(address indexed _from, address indexed _burner, uint256 _amount);
14 |
15 | uint8 internal _decimal;
16 | mapping(address => bool) public minters;
17 | address public treasury;
18 | uint256 public fees;
19 |
20 | bool public reverts;
21 |
22 | constructor(string memory name_, string memory symbol_, uint8 decimal_) ERC20Permit(name_) ERC20(name_, symbol_) {
23 | _decimal = decimal_;
24 | }
25 |
26 | function decimals() public view override returns (uint8) {
27 | return _decimal;
28 | }
29 |
30 | function mint(address account, uint256 amount) external {
31 | _mint(account, amount);
32 | emit Minting(account, msg.sender, amount);
33 | }
34 |
35 | function burn(address account, uint256 amount) public {
36 | _burn(account, amount);
37 | emit Burning(account, msg.sender, amount);
38 | }
39 |
40 | function setAllowance(address from, address to) public {
41 | _approve(from, to, type(uint256).max);
42 | }
43 |
44 | function burnSelf(uint256 amount, address account) public {
45 | _burn(account, amount);
46 | emit Burning(account, msg.sender, amount);
47 | }
48 |
49 | function addMinter(address minter) public {
50 | minters[minter] = true;
51 | }
52 |
53 | function removeMinter(address minter) public {
54 | minters[minter] = false;
55 | }
56 |
57 | function setTreasury(address _treasury) public {
58 | treasury = _treasury;
59 | }
60 |
61 | function setFees(uint256 _fees) public {
62 | fees = _fees;
63 | }
64 |
65 | function recoverERC20(IERC20 token, address to, uint256 amount) external {
66 | token.safeTransfer(to, amount);
67 | }
68 |
69 | function swapIn(address bridgeToken, uint256 amount, address to) external returns (uint256) {
70 | require(!reverts);
71 |
72 | IERC20(bridgeToken).safeTransferFrom(msg.sender, address(this), amount);
73 | uint256 canonicalOut = amount;
74 | canonicalOut -= (canonicalOut * fees) / 10 ** 9;
75 | _mint(to, canonicalOut);
76 | return canonicalOut;
77 | }
78 |
79 | function swapOut(address bridgeToken, uint256 amount, address to) external returns (uint256) {
80 | require(!reverts);
81 | _burn(msg.sender, amount);
82 | uint256 bridgeOut = amount;
83 | bridgeOut -= (bridgeOut * fees) / 10 ** 9;
84 | IERC20(bridgeToken).safeTransfer(to, bridgeOut);
85 | return bridgeOut;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/test/mock/MockTreasury.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.12;
4 |
5 | interface IStablecoin {
6 | function addMinter(address minter) external;
7 | }
8 |
9 | contract MockTreasury {
10 | uint256 public counter;
11 |
12 | function addMinter(address) external {
13 | counter += 1;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/mock/Slither.sol:
--------------------------------------------------------------------------------
1 | // // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.17;
3 |
4 | import "contracts/savings/Savings.sol";
5 | import "contracts/savings/SavingsVest.sol";
6 | import "contracts/transmuter/facets/Swapper.sol";
7 | import "contracts/transmuter/facets/Getters.sol";
8 | import "contracts/transmuter/facets/Redeemer.sol";
9 | import "contracts/transmuter/facets/RewardHandler.sol";
10 | import "contracts/transmuter/facets/SettersGovernor.sol";
11 | import "contracts/transmuter/facets/SettersGuardian.sol";
12 | import "contracts/transmuter/facets/DiamondCut.sol";
13 | import "contracts/transmuter/DiamondProxy.sol";
14 | import "contracts/transmuter/Storage.sol";
15 |
16 | // Workaround to have only 1 file to run slither on
17 | contract Mock {
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/test/units/DiamondLoupe.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IMockFacet, MockPureFacet } from "mock/MockFacets.sol";
6 |
7 | import "contracts/transmuter/Storage.sol";
8 | import { Test } from "contracts/transmuter/configs/Test.sol";
9 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
10 | import "contracts/utils/Constants.sol";
11 |
12 | import { Fixture } from "../Fixture.sol";
13 |
14 | contract Test_DiamondLoupe is Fixture {
15 | address pureFacet = address(new MockPureFacet());
16 |
17 | function test_Facets() public {
18 | Facet[] memory facets = transmuter.facets();
19 |
20 | // DiamondCut, DiamondLoupe, Getters, Redeemer, RewardHandler, SettersGovernor, SettersGuardian , Swapper
21 | for (uint256 i; i < facetNames.length; ++i) {
22 | assertEq(facets[i].facetAddress, address(facetAddressList[i])); // Check address
23 |
24 | bytes4[] memory selectors = _generateSelectors(facetNames[i]);
25 | assertEq(facets[i].functionSelectors.length, selectors.length); // Check selectors length
26 |
27 | for (uint256 j; j < selectors.length; ++j) {
28 | assertEq(facets[i].functionSelectors[j], selectors[j]); // Check all selectors are present
29 | }
30 | }
31 | }
32 |
33 | function test_FacetFunctionSelectors() public {
34 | // Create a facet added in 2 phases to test the robustness
35 | bytes4[] memory selectors = _generateSelectors("IMockFacet");
36 |
37 | bytes4[] memory auxSelectors = new bytes4[](1);
38 | auxSelectors[0] = selectors[0];
39 |
40 | FacetCut[] memory facetCut = new FacetCut[](1);
41 | facetCut[0] = FacetCut({
42 | facetAddress: address(pureFacet),
43 | action: FacetCutAction.Add,
44 | functionSelectors: auxSelectors
45 | });
46 |
47 | hoax(governor);
48 | transmuter.diamondCut(facetCut, address(0), ""); // Deploy only the first selector
49 |
50 | bytes4[] memory fetchedSelectors = transmuter.facetFunctionSelectors(pureFacet);
51 |
52 | assertEq(fetchedSelectors.length, 1); // Check only the first selector is accessible
53 | assertEq(fetchedSelectors[0], selectors[0]);
54 |
55 | auxSelectors[0] = selectors[1];
56 | facetCut[0] = FacetCut({
57 | facetAddress: address(pureFacet),
58 | action: FacetCutAction.Add,
59 | functionSelectors: auxSelectors
60 | });
61 |
62 | hoax(governor);
63 | transmuter.diamondCut(facetCut, address(0), ""); // Deploy the second selector
64 |
65 | fetchedSelectors = transmuter.facetFunctionSelectors(pureFacet);
66 |
67 | assertEq(fetchedSelectors.length, 2); // Check only the first selector is accessible
68 | assertEq(fetchedSelectors[0], selectors[0]);
69 | assertEq(fetchedSelectors[1], selectors[1]);
70 | }
71 |
72 | function test_FacetAddresses() public {
73 | address[] memory facetAddresses = transmuter.facetAddresses();
74 | for (uint256 i; i < facetAddresses.length; ++i) {
75 | assertEq(facetAddresses[i], facetAddressList[i]);
76 | }
77 | }
78 |
79 | function test_FacetAddress() public {
80 | for (uint256 i; i < facetNames.length; ++i) {
81 | bytes4[] memory selectors = _generateSelectors(facetNames[i]);
82 |
83 | for (uint256 j; j < selectors.length; ++j) {
84 | assertEq(transmuter.facetAddress(selectors[j]), facetAddressList[i]); // Check all selectors are present
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/test/units/Layout.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IAgToken } from "interfaces/IAgToken.sol";
6 |
7 | import { console } from "forge-std/console.sol";
8 |
9 | import { IMockFacet, MockPureFacet } from "mock/MockFacets.sol";
10 |
11 | import { Layout } from "contracts/transmuter/Layout.sol";
12 | import "contracts/transmuter/Storage.sol";
13 | import { Test } from "contracts/transmuter/configs/Test.sol";
14 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
15 | import "contracts/utils/Constants.sol";
16 |
17 | import { Fixture } from "../Fixture.sol";
18 |
19 | contract Test_Layout is Fixture {
20 | Layout layout;
21 |
22 | function setUp() public override {
23 | super.setUp();
24 | layout = Layout(address(transmuter));
25 | }
26 |
27 | function test_Layout() public {
28 | address agToken = address(transmuter.agToken());
29 | uint8 isRedemptionLive = transmuter.isPaused(address(0), ActionType.Redeem) ? 0 : 1;
30 | uint256 stablecoinsIssued = transmuter.getTotalIssued();
31 | address[] memory collateralList = transmuter.getCollateralList();
32 | (uint64[] memory xRedemptionCurve, int64[] memory yRedemptionCurve) = transmuter.getRedemptionFees();
33 | Collateral memory collateral = transmuter.getCollateralInfo(collateralList[0]);
34 | hoax(governor);
35 | transmuter.toggleTrusted(alice, TrustedType.Updater);
36 | hoax(governor);
37 | transmuter.toggleTrusted(alice, TrustedType.Seller);
38 | address accessControlManager = address(transmuter.accessControlManager());
39 | hoax(governor);
40 | transmuter.setDummyImplementation(address(alice));
41 | address implementation = transmuter.implementation();
42 |
43 | _etch();
44 |
45 | assertEq(layout.agToken(), agToken);
46 | assertEq(layout.isRedemptionLive(), isRedemptionLive);
47 | assertEq((layout.normalizedStables() * layout.normalizer()) / BASE_27, stablecoinsIssued);
48 | for (uint256 i; i < collateralList.length; i++) {
49 | assertEq(layout.collateralList(i), collateralList[i]);
50 | }
51 | for (uint256 i; i < xRedemptionCurve.length; i++) {
52 | assertEq(layout.xRedemptionCurve(i), xRedemptionCurve[i]);
53 | }
54 | for (uint256 i; i < yRedemptionCurve.length; i++) {
55 | assertEq(layout.yRedemptionCurve(i), yRedemptionCurve[i]);
56 | }
57 | (
58 | uint8 isManaged,
59 | uint8 isMintLive,
60 | uint8 isBurnLive,
61 | uint8 decimals,
62 | uint8 onlyWhitelisted,
63 | uint216 normalizedStables,
64 | bytes memory oracleConfig,
65 | bytes memory whitelistData,
66 | ,
67 |
68 | ) = layout.collaterals(collateralList[0]);
69 |
70 | assertEq(isManaged, collateral.isManaged);
71 | assertEq(isMintLive, collateral.isMintLive);
72 | assertEq(isBurnLive, collateral.isBurnLive);
73 | assertEq(decimals, collateral.decimals);
74 | assertEq(onlyWhitelisted, collateral.onlyWhitelisted);
75 | assertEq(normalizedStables, collateral.normalizedStables);
76 | assertEq(oracleConfig, collateral.oracleConfig);
77 | assertEq(whitelistData, collateral.whitelistData);
78 | assertEq(layout.isTrusted(alice), 1);
79 | assertEq(layout.isSellerTrusted(alice), 1);
80 | assertEq(layout.isTrusted(bob), 0);
81 | assertEq(layout.isSellerTrusted(bob), 0);
82 |
83 | bytes4[] memory selectors = _generateSelectors("ITransmuter");
84 | for (uint i = 0; i < selectors.length; ++i) {
85 | (address facetAddress, uint16 selectorPosition) = layout.selectorInfo(selectors[i]);
86 | assertNotEq(facetAddress, address(0));
87 | assertEq(layout.selectors(selectorPosition), selectors[i]);
88 | }
89 |
90 | assertEq(layout.accessControlManager(), accessControlManager);
91 | assertEq(layout.implementation(), implementation);
92 | }
93 |
94 | /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
95 | INTERNAL
96 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
97 |
98 | function _etch() internal {
99 | Layout tempLayout = new Layout();
100 | vm.etch(address(layout), address(tempLayout).code);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/test/units/StablecoinCap.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IERC20 } from "oz/interfaces/IERC20.sol";
6 |
7 | import { IAccessControlManager } from "interfaces/IAccessControlManager.sol";
8 | import { IAgToken } from "interfaces/IAgToken.sol";
9 | import { AggregatorV3Interface } from "interfaces/external/chainlink/AggregatorV3Interface.sol";
10 |
11 | import { MockAccessControlManager } from "mock/MockAccessControlManager.sol";
12 | import { MockChainlinkOracle } from "mock/MockChainlinkOracle.sol";
13 | import { MockTokenPermit } from "mock/MockTokenPermit.sol";
14 |
15 | import { Test } from "contracts/transmuter/configs/Test.sol";
16 | import { LibGetters } from "contracts/transmuter/libraries/LibGetters.sol";
17 | import "contracts/transmuter/Storage.sol";
18 | import "contracts/utils/Constants.sol";
19 | import "contracts/utils/Errors.sol" as Errors;
20 |
21 | import { Fixture } from "../Fixture.sol";
22 |
23 | contract StablecoinCapTest is Fixture {
24 | function test_GetStablecoinCap_Init_Success() public {
25 | assertEq(transmuter.getStablecoinCap(address(eurA)), type(uint256).max);
26 | assertEq(transmuter.getStablecoinCap(address(eurB)), type(uint256).max);
27 | assertEq(transmuter.getStablecoinCap(address(eurY)), type(uint256).max);
28 | }
29 |
30 | function test_RevertWhen_SetStablecoinCap_TooLargeMint() public {
31 | uint256 amount = 2 ether;
32 | uint256 stablecoinCap = 1 ether;
33 | address collateral = address(eurA);
34 |
35 | vm.prank(governor);
36 | transmuter.setStablecoinCap(collateral, stablecoinCap);
37 |
38 | deal(collateral, bob, amount);
39 | startHoax(bob);
40 | IERC20(collateral).approve(address(transmuter), amount);
41 | vm.expectRevert(Errors.InvalidSwap.selector);
42 | startHoax(bob);
43 | transmuter.swapExactOutput(amount, type(uint256).max, collateral, address(agToken), bob, block.timestamp * 2);
44 | }
45 |
46 | function test_RevertWhen_SetStablecoinCap_SlightlyLargeMint() public {
47 | uint256 amount = 1.0000000000001 ether;
48 | uint256 stablecoinCap = 1 ether;
49 | address collateral = address(eurA);
50 |
51 | vm.prank(governor);
52 | transmuter.setStablecoinCap(collateral, stablecoinCap);
53 |
54 | deal(collateral, bob, amount);
55 | startHoax(bob);
56 | IERC20(collateral).approve(address(transmuter), amount);
57 | vm.expectRevert(Errors.InvalidSwap.selector);
58 | startHoax(bob);
59 | transmuter.swapExactOutput(amount, type(uint256).max, collateral, address(agToken), bob, block.timestamp * 2);
60 | }
61 |
62 | function test_SetStablecoinCap_Success() public {
63 | uint256 amount = 0.99 ether;
64 | uint256 stablecoinCap = 1 ether;
65 | address collateral = address(eurA);
66 |
67 | vm.prank(governor);
68 | transmuter.setStablecoinCap(collateral, stablecoinCap);
69 |
70 | deal(collateral, bob, amount);
71 | startHoax(bob);
72 | IERC20(collateral).approve(address(transmuter), amount);
73 | startHoax(bob);
74 | transmuter.swapExactOutput(amount, type(uint256).max, collateral, address(agToken), bob, block.timestamp * 2);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/test/units/Transmuter.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { IERC20 } from "oz/interfaces/IERC20.sol";
6 |
7 | import { IAccessControlManager } from "interfaces/IAccessControlManager.sol";
8 | import { IAgToken } from "interfaces/IAgToken.sol";
9 | import { AggregatorV3Interface } from "interfaces/external/chainlink/AggregatorV3Interface.sol";
10 |
11 | import { MockAccessControlManager } from "mock/MockAccessControlManager.sol";
12 | import { MockChainlinkOracle } from "mock/MockChainlinkOracle.sol";
13 | import { MockTokenPermit } from "mock/MockTokenPermit.sol";
14 |
15 | import { Test } from "contracts/transmuter/configs/Test.sol";
16 | import { LibGetters } from "contracts/transmuter/libraries/LibGetters.sol";
17 | import "contracts/transmuter/Storage.sol";
18 | import "contracts/utils/Constants.sol";
19 | import "contracts/utils/Errors.sol";
20 |
21 | import { Fixture } from "../Fixture.sol";
22 |
23 | contract TestTransmuter is Fixture {
24 | function test_FacetsHaveCorrectSelectors() public {
25 | for (uint i = 0; i < facetAddressList.length; ++i) {
26 | bytes4[] memory fromLoupeFacet = transmuter.facetFunctionSelectors(facetAddressList[i]);
27 | bytes4[] memory fromGenSelectors = _generateSelectors(facetNames[i]);
28 | assertTrue(sameMembers(fromLoupeFacet, fromGenSelectors));
29 | }
30 | }
31 |
32 | function test_SelectorsAssociatedWithCorrectFacet() public {
33 | for (uint i = 0; i < facetAddressList.length; ++i) {
34 | bytes4[] memory fromGenSelectors = _generateSelectors(facetNames[i]);
35 | for (uint j = 0; j < fromGenSelectors.length; j++) {
36 | assertEq(facetAddressList[i], transmuter.facetAddress(fromGenSelectors[j]));
37 | }
38 | }
39 | }
40 |
41 | function test_InterfaceCorrectlyImplemented() public {
42 | bytes4[] memory selectors = _generateSelectors("ITransmuter");
43 | for (uint i = 0; i < selectors.length; ++i) {
44 | assertEq(transmuter.isValidSelector(selectors[i]), true);
45 | }
46 | }
47 |
48 | // Checks that all implemented selectors are in the interface
49 | function test_OnlyInterfaceIsImplemented() public {
50 | bytes4[] memory interfaceSelectors = _generateSelectors("ITransmuter");
51 |
52 | Facet[] memory facets = transmuter.facets();
53 |
54 | for (uint256 i; i < facetNames.length; ++i) {
55 | for (uint256 j; j < facets[i].functionSelectors.length; ++j) {
56 | bool found = false;
57 | for (uint256 k; k < interfaceSelectors.length; ++k) {
58 | if (facets[i].functionSelectors[j] == interfaceSelectors[k]) {
59 | found = true;
60 | break;
61 | }
62 | }
63 | assert(found);
64 | }
65 | }
66 | }
67 |
68 | function test_QuoteInScenario() public {
69 | uint256 quote = (transmuter.quoteIn(BASE_6, address(eurA), address(agToken)));
70 | assertEq(quote, BASE_27 / (BASE_9 + BASE_9 / 99));
71 | }
72 |
73 | function test_SimpleSwapInScenario() public {
74 | deal(address(eurA), alice, BASE_6);
75 |
76 | startHoax(alice);
77 | eurA.approve(address(transmuter), BASE_6);
78 | transmuter.swapExactInput(BASE_6, 0, address(eurA), address(agToken), alice, block.timestamp + 1 hours);
79 |
80 | assertEq(agToken.balanceOf(alice), BASE_27 / (BASE_9 + BASE_9 / 99));
81 | }
82 |
83 | function test_QuoteCollateralRatio() public {
84 | transmuter.getCollateralRatio();
85 | assertEq(uint256(0), uint256(0));
86 | }
87 |
88 | function test_QuoteCollateralRatioDirectCall() public {
89 | LibGetters.getCollateralRatio();
90 | assertEq(uint256(0), uint256(0));
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/test/units/upgrade/SavingsNameableUpgradeTest.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: UNLICENSED
2 | pragma solidity ^0.8.19;
3 |
4 | import "../../utils/Helper.sol";
5 | import { Test } from "forge-std/Test.sol";
6 | import "utils/src/Constants.sol";
7 | import { SavingsNameable } from "contracts/savings/nameable/SavingsNameable.sol";
8 | import { ProxyAdmin } from "oz/proxy/transparent/ProxyAdmin.sol";
9 | import { IERC20Metadata } from "oz/interfaces/IERC20Metadata.sol";
10 | import { TransparentUpgradeableProxy } from "oz/proxy/transparent/TransparentUpgradeableProxy.sol";
11 |
12 | contract SavingsNameableUpgradeTest is Test, Helper {
13 | uint256 constant CHAIN = CHAIN_ETHEREUM;
14 | string constant CHAIN_NAME = "mainnet";
15 |
16 | address public savings;
17 | address public savingsImpl;
18 |
19 | uint208 public rate;
20 | uint40 public lastUpdate;
21 | uint8 public paused;
22 | uint256 public maxRate;
23 | uint256 public totalSupply;
24 | uint256 public totalAssets;
25 | uint256 public previewDeposit;
26 | uint256 public previewMint;
27 | uint256 public previewWithdraw;
28 | uint256 public previewRedeem;
29 | address public governor;
30 | address public guardian;
31 | ProxyAdmin public proxyAdmin;
32 |
33 | function setUp() public {
34 | vm.createSelectFork(CHAIN_NAME);
35 |
36 | savings = _chainToContract(CHAIN, ContractType.StUSD);
37 |
38 | if (CHAIN == CHAIN_BASE || CHAIN == CHAIN_POLYGONZKEVM)
39 | proxyAdmin = ProxyAdmin(_chainToContract(CHAIN, ContractType.ProxyAdminGuardian));
40 | else proxyAdmin = ProxyAdmin(_chainToContract(CHAIN, ContractType.ProxyAdmin));
41 | governor = _chainToContract(CHAIN, ContractType.GovernorMultisig);
42 | guardian = _chainToContract(CHAIN, ContractType.GuardianMultisig);
43 |
44 | // TODO: to be removed when chainToContract works
45 | // savings = 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776;
46 | savings = 0x004626A008B1aCdC4c74ab51644093b155e59A23;
47 | proxyAdmin = ProxyAdmin(0x1D941EF0D3Bba4ad67DBfBCeE5262F4CEE53A32b);
48 | governor = 0xdC4e6DFe07EFCa50a197DF15D9200883eF4Eb1c8;
49 | guardian = 0x0C2553e4B9dFA9f83b1A6D3EAB96c4bAaB42d430;
50 |
51 | assertEq(IERC20Metadata(savings).name(), "Staked EURA");
52 | assertEq(IERC20Metadata(savings).symbol(), "stEUR");
53 | rate = SavingsNameable(savings).rate();
54 | lastUpdate = SavingsNameable(savings).lastUpdate();
55 | paused = SavingsNameable(savings).paused();
56 | maxRate = SavingsNameable(savings).maxRate();
57 | totalSupply = SavingsNameable(savings).totalSupply();
58 | totalAssets = SavingsNameable(savings).totalAssets();
59 | previewDeposit = SavingsNameable(savings).previewDeposit(BASE_18);
60 | previewMint = SavingsNameable(savings).previewMint(BASE_18);
61 | previewWithdraw = SavingsNameable(savings).previewWithdraw(BASE_18);
62 | previewRedeem = SavingsNameable(savings).previewRedeem(BASE_18);
63 |
64 | // savingsImpl = address(new SavingsNameable());
65 | savingsImpl = 0x2C28Bd22aB59341892e85aD76d159d127c4B03FA;
66 | }
67 |
68 | function _upgradeContract(string memory name, string memory symbol) internal {
69 | vm.prank(governor, governor);
70 | proxyAdmin.upgrade(TransparentUpgradeableProxy(payable(savings)), savingsImpl);
71 | vm.prank(governor, governor);
72 | SavingsNameable(savings).setNameAndSymbol(name, symbol);
73 | }
74 |
75 | function test_UpdatedValues() public {
76 | _upgradeContract("Staked USDA", "stUSD");
77 | assertEq(IERC20Metadata(savings).name(), "Staked USDA");
78 | assertEq(IERC20Metadata(savings).symbol(), "stUSD");
79 | assertEq(SavingsNameable(savings).previewRedeem(BASE_18), previewRedeem);
80 | assertEq(SavingsNameable(savings).previewWithdraw(BASE_18), previewWithdraw);
81 | assertEq(SavingsNameable(savings).previewMint(BASE_18), previewMint);
82 | assertEq(SavingsNameable(savings).previewDeposit(BASE_18), previewDeposit);
83 | assertEq(SavingsNameable(savings).totalAssets(), totalAssets);
84 | assertEq(SavingsNameable(savings).totalSupply(), totalSupply);
85 | assertEq(SavingsNameable(savings).maxRate(), maxRate);
86 | assertEq(SavingsNameable(savings).paused(), paused);
87 | assertEq(SavingsNameable(savings).lastUpdate(), lastUpdate);
88 | assertEq(SavingsNameable(savings).rate(), rate);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/test/utils/FunctionUtils.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { StdUtils } from "forge-std/Test.sol";
6 | import "contracts/utils/Constants.sol";
7 |
8 | /// @title FunctionUtils
9 | /// @author Angle Labs, Inc.
10 | contract FunctionUtils is StdUtils {
11 | function _convertDecimalTo(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
12 | if (fromDecimals > toDecimals) return amount / 10 ** (fromDecimals - toDecimals);
13 | else if (fromDecimals < toDecimals) return amount * 10 ** (toDecimals - fromDecimals);
14 | else return amount;
15 | }
16 |
17 | function _generateCurves(
18 | uint64[10] memory thresholds,
19 | int64[10] memory intercepts,
20 | bool increasing,
21 | bool mint,
22 | int256 minFee,
23 | int256 maxFee
24 | ) internal pure returns (uint64[] memory postThres, int64[] memory postIntercep) {
25 | thresholds[0] = increasing ? 0 : uint64(BASE_9);
26 | intercepts[0] = int64(bound(int256(intercepts[0]), minFee, maxFee));
27 | uint256 nbrInflexion = 1;
28 | for (uint256 i = 1; i < thresholds.length; ++i) {
29 | thresholds[i] = increasing
30 | ? uint64(bound(thresholds[i], thresholds[i - 1] + 1, BASE_9 - 1))
31 | : uint64(bound(thresholds[i], 0, thresholds[i - 1] - 1));
32 | intercepts[i] = !(!increasing && i == 1)
33 | ? int64(bound(int256(intercepts[i]), intercepts[i - 1], maxFee))
34 | : intercepts[i - 1]; // Because the first degment of a burnFees should be constant
35 | if (
36 | // For the mint functions we hardcoded BASE_12 as the maximum fees, after that it
37 | // is considered as 100% fees
38 | (mint &&
39 | (int256(BASE_9) <= int256(intercepts[i]) ||
40 | int256(BASE_18) / (int256(BASE_9) - int256(intercepts[i])) - int256(BASE_9) >=
41 | int256(BASE_12) - 1)) ||
42 | intercepts[i] == int256(BASE_9) ||
43 | (increasing && thresholds[i] == BASE_9 - 1) ||
44 | (!increasing && thresholds[i] == 0)
45 | ) {
46 | nbrInflexion = i + 1;
47 | break;
48 | }
49 | }
50 | if (mint) {
51 | for (uint256 i = 0; i < thresholds.length; ++i) {
52 | intercepts[i] = (int256(BASE_9) > int256(intercepts[i])) &&
53 | (int256(BASE_18) / (int256(BASE_9) - int256(intercepts[i])) - int256(BASE_9) < int256(BASE_12))
54 | ? int64(int256(BASE_18) / (int256(BASE_9) - int256(intercepts[i])) - int256(BASE_9))
55 | : int64(int256(BASE_12)) - 1;
56 | }
57 | }
58 | postThres = new uint64[](nbrInflexion);
59 | postIntercep = new int64[](nbrInflexion);
60 | for (uint256 i; i < nbrInflexion; ++i) {
61 | postThres[i] = thresholds[i];
62 | postIntercep[i] = intercepts[i];
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/test/utils/Helper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: UNLICENSED
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import "interfaces/IDiamondLoupe.sol";
6 |
7 | import { Test, stdError } from "forge-std/Test.sol";
8 | import { CommonUtils } from "utils/src/CommonUtils.sol";
9 |
10 | import "stringutils/strings.sol";
11 |
12 | /******************************************************************************\
13 | * Authors: Timo Neumann
14 | * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
15 | * Helper functions for the translation from the jest tests in the original repo
16 | * to solidity tests.
17 | /******************************************************************************/
18 |
19 | abstract contract Helper is Test, CommonUtils {
20 | using strings for *;
21 |
22 | // helper to remove index from bytes4[] array
23 | function removeElement(uint index, bytes4[] memory array) public pure returns (bytes4[] memory) {
24 | bytes4[] memory newarray = new bytes4[](array.length - 1);
25 | uint j = 0;
26 | for (uint i = 0; i < array.length; ++i) {
27 | if (i != index) {
28 | newarray[j] = array[i];
29 | j += 1;
30 | }
31 | }
32 | return newarray;
33 | }
34 |
35 | // helper to remove value from bytes4[] array
36 | function removeElement(bytes4 el, bytes4[] memory array) public pure returns (bytes4[] memory) {
37 | for (uint i = 0; i < array.length; ++i) {
38 | if (array[i] == el) {
39 | return removeElement(i, array);
40 | }
41 | }
42 | return array;
43 | }
44 |
45 | function containsElement(bytes4[] memory array, bytes4 el) public pure returns (bool) {
46 | for (uint i = 0; i < array.length; ++i) {
47 | if (array[i] == el) {
48 | return true;
49 | }
50 | }
51 |
52 | return false;
53 | }
54 |
55 | function containsElement(address[] memory array, address el) public pure returns (bool) {
56 | for (uint i = 0; i < array.length; ++i) {
57 | if (array[i] == el) {
58 | return true;
59 | }
60 | }
61 |
62 | return false;
63 | }
64 |
65 | function sameMembers(bytes4[] memory array1, bytes4[] memory array2) public pure returns (bool) {
66 | if (array1.length != array2.length) {
67 | return false;
68 | }
69 | for (uint i = 0; i < array1.length; ++i) {
70 | if (containsElement(array1, array2[i])) {
71 | return true;
72 | }
73 | }
74 |
75 | return false;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/test/utils/Transmuter.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity ^0.8.19;
4 |
5 | import { ITransmuter } from "interfaces/ITransmuter.sol";
6 |
7 | import { DiamondProxy } from "contracts/transmuter/DiamondProxy.sol";
8 | import "contracts/transmuter/Storage.sol";
9 | import { DiamondCut } from "contracts/transmuter/facets/DiamondCut.sol";
10 | import { DiamondEtherscan } from "contracts/transmuter/facets/DiamondEtherscan.sol";
11 | import { DiamondLoupe } from "contracts/transmuter/facets/DiamondLoupe.sol";
12 | import { Getters } from "contracts/transmuter/facets/Getters.sol";
13 | import { Redeemer } from "contracts/transmuter/facets/Redeemer.sol";
14 | import { RewardHandler } from "contracts/transmuter/facets/RewardHandler.sol";
15 | import { SettersGovernor } from "contracts/transmuter/facets/SettersGovernor.sol";
16 | import { SettersGuardian } from "contracts/transmuter/facets/SettersGuardian.sol";
17 | import { Swapper } from "contracts/transmuter/facets/Swapper.sol";
18 | import "contracts/utils/Errors.sol";
19 | import { DummyDiamondImplementation } from "../../scripts/generated/DummyDiamondImplementation.sol";
20 |
21 | import "./Helper.sol";
22 |
23 | abstract contract Transmuter is Helper {
24 | // Diamond
25 | ITransmuter transmuter;
26 |
27 | string[] facetNames;
28 | address[] facetAddressList;
29 |
30 | // @dev Deploys diamond and connects facets
31 | function deployTransmuter(address _init, bytes memory _calldata) public virtual {
32 | // Deploy every facet
33 | facetNames.push("DiamondCut");
34 | facetAddressList.push(address(new DiamondCut()));
35 |
36 | facetNames.push("DiamondEtherscan");
37 | facetAddressList.push(address(new DiamondEtherscan()));
38 |
39 | facetNames.push("DiamondLoupe");
40 | facetAddressList.push(address(new DiamondLoupe()));
41 |
42 | facetNames.push("Getters");
43 | facetAddressList.push(address(new Getters()));
44 |
45 | facetNames.push("Redeemer");
46 | facetAddressList.push(address(new Redeemer()));
47 |
48 | facetNames.push("RewardHandler");
49 | facetAddressList.push(address(new RewardHandler()));
50 |
51 | facetNames.push("SettersGovernor");
52 | facetAddressList.push(address(new SettersGovernor()));
53 |
54 | facetNames.push("SettersGuardian");
55 | facetAddressList.push(address(new SettersGuardian()));
56 |
57 | facetNames.push("Swapper");
58 | facetAddressList.push(address(new Swapper()));
59 |
60 | // Build appropriate payload
61 | uint256 n = facetNames.length;
62 | FacetCut[] memory cut = new FacetCut[](n);
63 | for (uint256 i = 0; i < n; ++i) {
64 | cut[i] = FacetCut({
65 | facetAddress: facetAddressList[i],
66 | action: FacetCutAction.Add,
67 | functionSelectors: _generateSelectors(facetNames[i])
68 | });
69 | }
70 |
71 | // Deploy diamond
72 | transmuter = ITransmuter(address(new DiamondProxy(cut, _init, _calldata)));
73 | }
74 |
75 | // @dev Deploys diamond and connects facets
76 | function deployReplicaTransmuter(
77 | address _init,
78 | bytes memory _calldata
79 | ) public virtual returns (ITransmuter _transmuter) {
80 | // Build appropriate payload
81 | uint256 n = facetNames.length;
82 | FacetCut[] memory cut = new FacetCut[](n);
83 | for (uint256 i = 0; i < n; ++i) {
84 | cut[i] = FacetCut({
85 | facetAddress: facetAddressList[i],
86 | action: FacetCutAction.Add,
87 | functionSelectors: _generateSelectors(facetNames[i])
88 | });
89 | }
90 |
91 | // Deploy diamond
92 | _transmuter = ITransmuter(address(new DiamondProxy(cut, _init, _calldata)));
93 |
94 | return _transmuter;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/utils/forwardUtils.js:
--------------------------------------------------------------------------------
1 | const { exec } = require("child_process");
2 |
3 | if (process.argv.length < 3) {
4 | console.error('Please provide a chain input as an argument.');
5 | process.exit(1);
6 | }
7 |
8 | const command = process.argv[2];
9 | const extraArgs = process.argv.slice(3).join(' ');
10 |
11 | exec(`node lib/utils/utils/${command}.js ${extraArgs}`, (error, stdout, stderr) => {
12 | if (error) {
13 | console.log(error);
14 | process.exit(1);
15 | }
16 | if (stderr) {
17 | console.log(stderr);
18 | process.exit(1);
19 | }
20 | console.log(stdout);
21 | });
22 |
--------------------------------------------------------------------------------