├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── .husky ├── pre-commit └── pre-push ├── .npmignore ├── .solcover.js ├── .solhint.json ├── Makefile ├── README.md ├── cloc-report.bash ├── contracts ├── Balancer.sol ├── tOFT │ ├── BaseTOFT.sol │ ├── BaseTOFTTokenMsgType.sol │ ├── TOFT.sol │ ├── TOFTVault.sol │ ├── extensions │ │ └── TOFTHelper.sol │ ├── libraries │ │ ├── RevertMsgDecoder.sol │ │ └── TOFTMsgCodec.sol │ ├── mTOFT.sol │ └── modules │ │ ├── BaseTOFTReceiver.sol │ │ ├── ModuleManager.sol │ │ ├── TOFTGenericReceiverModule.sol │ │ ├── TOFTMarketReceiverModule.sol │ │ ├── TOFTOptionsReceiverModule.sol │ │ ├── TOFTReceiver.sol │ │ ├── TOFTSender.sol │ │ └── mTOFTReceiver.sol ├── tapioca-periph-file-loader.sol └── util │ └── ERC4494.sol ├── foundry.toml ├── hardhat.config.ts ├── hardhat.export.ts ├── hardhat.tasks.ts ├── lib └── forge-std │ ├── .gitattributes │ ├── .github │ └── workflows │ │ ├── ci.yml │ │ └── sync.yml │ ├── .gitignore │ ├── .gitmodules │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── foundry.toml │ ├── lib │ └── ds-test │ │ ├── .github │ │ └── workflows │ │ │ └── build.yml │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── default.nix │ │ ├── demo │ │ └── demo.sol │ │ ├── package.json │ │ └── src │ │ ├── test.sol │ │ └── test.t.sol │ ├── package.json │ ├── scripts │ └── vm.py │ ├── src │ ├── Base.sol │ ├── Script.sol │ ├── StdAssertions.sol │ ├── StdChains.sol │ ├── StdCheats.sol │ ├── StdError.sol │ ├── StdInvariant.sol │ ├── StdJson.sol │ ├── StdMath.sol │ ├── StdStorage.sol │ ├── StdStyle.sol │ ├── StdUtils.sol │ ├── Test.sol │ ├── Vm.sol │ ├── console.sol │ ├── console2.sol │ ├── interfaces │ │ ├── IERC1155.sol │ │ ├── IERC165.sol │ │ ├── IERC20.sol │ │ ├── IERC4626.sol │ │ ├── IERC721.sol │ │ └── IMulticall3.sol │ ├── mocks │ │ ├── MockERC20.sol │ │ └── MockERC721.sol │ └── safeconsole.sol │ └── test │ ├── StdAssertions.t.sol │ ├── StdChains.t.sol │ ├── StdCheats.t.sol │ ├── StdError.t.sol │ ├── StdMath.t.sol │ ├── StdStorage.t.sol │ ├── StdStyle.t.sol │ ├── StdUtils.t.sol │ ├── Vm.t.sol │ ├── compilation │ ├── CompilationScript.sol │ ├── CompilationScriptBase.sol │ ├── CompilationTest.sol │ └── CompilationTestBase.sol │ ├── fixtures │ └── broadcast.log.json │ └── mocks │ ├── MockERC20.t.sol │ └── MockERC721.t.sol ├── local.db.json ├── package.json ├── script └── Counter.s.sol ├── scripts └── utils.ts ├── tasks ├── constants.ts ├── deploy │ ├── 1-deployPostLbp__task.ts │ ├── 2-deployFinal__task.ts │ ├── DEPLOY_CONFIG.ts │ ├── deprecated │ │ ├── deployBalancer.ts │ │ └── deployTOFT.ts │ ├── testnet │ │ └── deployTOFTMinter.ts │ ├── toftDeployerUtils.ts │ └── toftDeployer__task.ts ├── deployBuilds │ ├── buildBalancer.ts │ ├── buildExtExec.ts │ ├── buildMTOFT.ts │ ├── buildTOFT.ts │ ├── buildTOFTGenericReceiverModule.ts │ ├── buildTOFTMarketReceiverModule.ts │ ├── buildTOFTOptionsReceiverModule.ts │ ├── buildTOFTReceiverModule.ts │ ├── buildTOFTSenderModule.ts │ ├── buildToftHelper.ts │ └── buildToftVault.ts ├── exec │ ├── balancer │ │ ├── 01-balancer-toggleSwapEth.ts │ │ ├── 02-balancer-emergencySaveTokens.ts │ │ ├── 03-balancer-initConnectedOFT.ts │ │ ├── 04-balancer-addRebalanceAmount.ts │ │ ├── 05-balancer-retryRevert.ts │ │ ├── 06-balancer-instantRedeemLocal.ts │ │ ├── 07-balancer-redeemLocal.ts │ │ ├── 08-balancer-redeemRemote.ts │ │ └── balancerInnitConnectedOft__task.ts │ ├── saveBlockNumber.ts │ └── toft │ │ ├── 09-mOft-updateConnectedChain.ts │ │ ├── 10-mOft-updateBalancerState.ts │ │ ├── 11-oft-rescueEth.ts │ │ ├── 12-oft-setStargateRouter.ts │ │ └── 13-setCluster.ts ├── scopes │ ├── balancerScope.ts │ ├── deployScope.ts │ └── toftScope.ts ├── utils.ts └── views │ └── listDeploy.ts ├── test ├── Balancer.t.sol ├── ERC20Mock.sol ├── ERC721Mock.sol ├── LZSetup │ ├── TestHelper.sol │ └── mocks │ │ ├── ERC20Mock.sol │ │ ├── ExecutorFeeLibMock.sol │ │ ├── OFTAdapterMock.sol │ │ ├── OFTComposerMock.sol │ │ ├── OFTInspectorMock.sol │ │ ├── OFTMock.sol │ │ ├── OptionsHelper.sol │ │ ├── SendUln302Mock.sol │ │ └── SimpleMessageLibMock.sol ├── MagnetarMock.sol ├── MarketHelperMock.sol ├── SingularityMock.sol ├── StargateRouterMock.sol ├── TOFT.t.sol ├── TOFTMock.sol ├── TOFTTestHelper.t.sol ├── TapiocaOptionsBrokerMock.sol ├── TestUtils.t.sol └── btt │ ├── Base_Test.t.sol │ ├── mocks │ ├── ERC20Mock_test.sol │ └── FeeGetterMock_test.sol │ ├── unit │ ├── concrete │ │ └── OFT │ │ │ ├── OFT_constructor.t.sol │ │ │ ├── OFT_constructor.tree │ │ │ ├── OFT_setters.t.sol │ │ │ ├── OFT_setters.tree │ │ │ ├── OFT_wrapUnwrap.t.sol │ │ │ └── OFT_wrapUnwrap.tree │ └── shared │ │ └── OFT_Unit_Shared.t.sol │ └── utils │ ├── Constants.sol │ ├── Events.sol │ ├── Types.sol │ └── Utils.sol ├── test_hardhat ├── fixtures.ts └── test.utils.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | # Description: Example of .env file. 2 | # The env file should be renamed to `name_of_chain.env`. The file needs to be in the `.env/` folder. 3 | # dotenv loading can not load `export` env vars for some reasons 4 | 5 | # Global Config 6 | PRIVATE_KEY= # Private key of the deployer. Needs be prefixed with 0x 7 | ALCHEMY_API_KEY= # Alchemy API key 8 | export RPC_URL= 9 | export CHAIN_NAME= # Name of the chain 10 | 11 | # LZ Config 12 | export ENDPOINT= # Endpoint address of the chain 13 | export CHAIN_ID= # Chain ID 14 | export LZ_CHAIN_ID= # LZ Chain ID 15 | export ENVIRONMENT= # Environment (mainnet, testnet) 16 | 17 | # Deployment 18 | export TAPIOCA_DEPLOYER= # TapiocaDeployer address on the target chain 19 | export MULTICALL3=0xcA11bde05977b3631167028862bE2a173976CA11 # Multicall3 address on the target chain 20 | export SCAN_API_KEY= # Arbitrum Sepolia API key -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage 5 | gitsub_tapioca-sdk 6 | tapioca-periph 7 | docs 8 | deployments 9 | typechain -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es2021: true, 5 | mocha: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:prettier/recommended', 11 | ], 12 | parser: '@typescript-eslint/parser', 13 | plugins: ['@typescript-eslint', 'prettier'], 14 | root: true, 15 | parserOptions: { 16 | ecmaVersion: 12, 17 | }, 18 | rules: { 19 | '@typescript-eslint/no-explicit-any': ['off'], 20 | '@typescript-eslint/no-non-null-assertion': ['off'], 21 | 'prettier/prettier': [ 22 | 'error', 23 | { 24 | trailingComma: 'all', 25 | singleQuote: true, 26 | printWidth: 80, 27 | endOfLine: 'auto', 28 | useTabs: false, 29 | tabWidth: 4, 30 | }, 31 | ], 32 | 'comma-dangle': [2, 'always-multiline'], 33 | semi: ['error', 'always'], 34 | 'comma-spacing': ['error', { before: false, after: true }], 35 | quotes: ['error', 'single'], 36 | indent: 'off', 37 | 'key-spacing': ['error', { afterColon: true }], 38 | 'no-multi-spaces': ['error'], 39 | 'no-multiple-empty-lines': ['error', { max: 2 }], 40 | '@typescript-eslint/ban-ts-comment': 'off', 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Run Forge build 26 | run: | 27 | forge --version 28 | forge build --sizes 29 | id: build 30 | 31 | - name: Run Forge tests 32 | run: | 33 | forge test -vvv 34 | id: test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | package-lock.json 7 | docs 8 | .VSCodeCounter 9 | 10 | #Hardhat & forge files 11 | gen 12 | 13 | #Others 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "gitmodule/tapioca-sdk"] 5 | path = gitmodule/tapioca-sdk 6 | url = https://github.com/Tapioca-DAO/tapioca-sdk 7 | [submodule "lib/tap-yieldbox"] 8 | path = lib/tap-yieldbox 9 | url = https://github.com/Tapioca-DAO/tap-yieldbox 10 | branch = fix-dependencies 11 | [submodule "lib/tap-utils"] 12 | path = lib/tap-utils 13 | url = https://github.com/Tapioca-DAO/tap-utils 14 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | hardhat.config.ts 2 | scripts 3 | test 4 | front -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ['mocks','OFT20'], 3 | configureYulOptimizer: true, 4 | } 5 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": ["off"] 5 | } 6 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: dipslayBalances hardhatCompile buildNightlySDK 2 | all: hardhatCompile 3 | 4 | 5 | # Networks to deploy to 6 | NETWORKS = arbitrum_sepolia optimism_sepolia 7 | 8 | BALANCE_OF_COMMAND = hh utils balanceOf 9 | dipslayBalances: 10 | @echo "Displaying balances for " && hh utils currentAccount --network arbitrum_sepolia 11 | @$(foreach var,$(NETWORKS), echo "${var}": && $(BALANCE_OF_COMMAND) --network $(var) ; ) 12 | 13 | #DEPLOY_STACK_COMMAND = hh deploys stack 14 | #deploy: 15 | # @$(foreach var,$(NETWORKS), echo "Deploying to ${var}": && $(DEPLOY_STACK_COMMAND) --network $(var) ; ) 16 | 17 | hardhatCompile: 18 | @hh compile --network localhost 19 | 20 | buildNightlySDK: 21 | @cd gitmodule/tapioca-sdk && yarn build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | All rights are reserved and the Tapioca codebase is not Open Source or Free. You cannot modify or redistribute this code without explicit written permission from the copyright holder (Tapioca Foundation & BoringCrypto [where applicable]). 2 | 3 | # TapiocaZ 🍹 🤙 4 | 5 | Tapioca harnessing LayerZero omni-chain infrastructure 🤯 6 | 7 | - [TapiocaWrapper](docs/TapiocaWrapper.md) Handle the deployment of `TOFT` contracts and execution of its `onlyOwner` functions. 8 | - [TapiocaOFT](docs/TapiocaOFT.md) `OFT20`, Layer-Zero superset of `ERC20`. Handles the `wrap` and `unwrap` of a desired `ERC20` 9 | 10 | 11 | 12 | ## Usage 13 | 14 | To install Foundry: 15 | 16 | ```sh 17 | curl -L https://foundry.paradigm.xyz | bash 18 | ``` 19 | 20 | This will download foundryup. To start Foundry, run: 21 | 22 | ```sh 23 | foundryup 24 | ``` 25 | 26 | To clone the repo: 27 | 28 | ```sh 29 | git clone https://github.com/Tapioca-DAO/TapiocaZ.git && cd TapiocaZ 30 | ``` 31 | 32 | To install as a forge library: 33 | 34 | ```sh 35 | forge install Tapioca-DAO/TapiocaZ 36 | ``` 37 | 38 | To install as a submodule: 39 | 40 | ```sh 41 | git submodule add https://github.com/Tapioca-DAO/TapiocaZ.git 42 | ``` 43 | 44 | ## Install 45 | 46 | To install this repository: 47 | 48 | ```bash 49 | forge install 50 | yarn 51 | ``` -------------------------------------------------------------------------------- /cloc-report.bash: -------------------------------------------------------------------------------- 1 | cloc ./contracts/**/*.sol --exclude-content ".*(Test).*" --hide-rate --by-file --csv --quiet > cloc-report.csv -------------------------------------------------------------------------------- /contracts/tOFT/BaseTOFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // External 5 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | 9 | // Tapioca 10 | import {BaseTapiocaOmnichainEngine} from "tap-utils/tapiocaOmnichainEngine/BaseTapiocaOmnichainEngine.sol"; 11 | import {TOFTInitStruct, IToftVault} from "tap-utils/interfaces/oft/ITOFT.sol"; 12 | import {PearlmitHandler} from "tap-utils/pearlmit/PearlmitHandler.sol"; 13 | import {IYieldBox} from "tap-utils/interfaces/yieldbox/IYieldBox.sol"; 14 | import {ICluster} from "tap-utils/interfaces/periph/ICluster.sol"; 15 | import {BaseTOFTTokenMsgType} from "./BaseTOFTTokenMsgType.sol"; 16 | import {ModuleManager} from "./modules/ModuleManager.sol"; 17 | 18 | /* 19 | 20 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 21 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 22 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 23 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 24 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 25 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 26 | 27 | */ 28 | 29 | /** 30 | * @title BaseTOFT 31 | * @author TapiocaDAO 32 | * @notice Base TOFT contract for LZ V2 33 | */ 34 | abstract contract BaseTOFT is 35 | ModuleManager, 36 | PearlmitHandler, 37 | BaseTapiocaOmnichainEngine, 38 | BaseTOFTTokenMsgType, 39 | Pausable 40 | { 41 | using SafeERC20 for IERC20; 42 | 43 | IYieldBox public immutable yieldBox; 44 | IToftVault public immutable vault; 45 | uint256 public immutable hostEid; 46 | address public immutable erc20; 47 | 48 | error TOFT_AllowanceNotValid(); 49 | error TOFT_NotValid(); 50 | error TOFT_VaultWrongERC20(); 51 | error TOFT_VaultWrongOwner(); 52 | error TOFT_NotAuthorized(); 53 | 54 | constructor(TOFTInitStruct memory _data) 55 | BaseTapiocaOmnichainEngine( 56 | _data.name, 57 | _data.symbol, 58 | _data.endpoint, 59 | _data.delegate, 60 | _data.extExec, 61 | _data.pearlmit, 62 | ICluster(_data.cluster) 63 | ) 64 | { 65 | yieldBox = IYieldBox(_data.yieldBox); 66 | hostEid = _data.hostEid; 67 | erc20 = _data.erc20; 68 | 69 | _transferOwnership(_data.delegate); 70 | 71 | vault = IToftVault(_data.vault); 72 | 73 | if (address(vault._token()) != erc20) revert TOFT_VaultWrongERC20(); 74 | } 75 | 76 | // *********************** // 77 | // *** OWNER FUNCTIONS *** // 78 | // *********************** // 79 | /** 80 | * @notice Un/Pauses this contract. 81 | */ 82 | function setPause(bool _pauseState) external { 83 | if (!getCluster().hasRole(msg.sender, keccak256("PAUSABLE")) && msg.sender != owner()) revert TOFT_NotAuthorized(); 84 | if (_pauseState) { 85 | _pause(); 86 | } else { 87 | _unpause(); 88 | } 89 | } 90 | 91 | // ************************** // 92 | // *** INTERNAL FUNCTIONS *** // 93 | // ************************** // 94 | 95 | function _wrap(address _fromAddress, address _toAddress, uint256 _amount, uint256 _feeAmount) internal virtual { 96 | // Check internal allowance only if not the same address 97 | if (_toAddress != _fromAddress) { 98 | if (allowance(_fromAddress, msg.sender) < _amount) { 99 | revert TOFT_AllowanceNotValid(); 100 | } 101 | _spendAllowance(_fromAddress, msg.sender, _amount); 102 | } 103 | if (_amount == 0) revert TOFT_NotValid(); 104 | // IERC20(erc20).safeTransferFrom(_fromAddress, address(vault), _amount); 105 | bool isErr = pearlmit.transferFromERC20(_fromAddress, address(vault), erc20, _amount); 106 | if (isErr) revert TOFT_NotValid(); 107 | _mint(_toAddress, _amount - _feeAmount); 108 | } 109 | 110 | function _wrapNative(address _toAddress, uint256 _amount, uint256 _feeAmount) internal virtual { 111 | vault.depositNative{value: _amount - _feeAmount}(); 112 | _mint(_toAddress, _amount - _feeAmount); 113 | } 114 | 115 | function _unwrap(address _from, address _toAddress, uint256 _amount) internal virtual { 116 | _burn(_from, _amount); 117 | vault.withdraw(_toAddress, _amount); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /contracts/tOFT/BaseTOFTTokenMsgType.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | /* 5 | 6 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 7 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 8 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 9 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 10 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 11 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 12 | 13 | */ 14 | 15 | abstract contract BaseTOFTTokenMsgType { 16 | uint16 internal constant MSG_MARKET_REMOVE_COLLATERAL = 800; // Use for remove collateral from a market available on another chain 17 | uint16 internal constant MSG_YB_SEND_SGL_BORROW = 801; // Use fror send to YB and/or borrow from a market available on another chain 18 | uint16 internal constant MSG_TAP_EXERCISE = 802; // Use for exercise options on tOB available on another chain 19 | uint16 internal constant MSG_SEND_PARAMS = 803; // Use for perform a normal OFT send but with a custom payload 20 | uint16 internal constant MSG_LOCK_AND_PARTICIPATE = 804; // Use for `magnetar.mintFromBBAndSendForLending` step 3 call 21 | uint16 internal constant MSG_LEVERAGE_UP = 805; // Use for leveraging up on a market availabl eon another chain 22 | } 23 | -------------------------------------------------------------------------------- /contracts/tOFT/TOFTVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // External 5 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 8 | 9 | /* 10 | 11 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 12 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 13 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 14 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 15 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 16 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 17 | 18 | */ 19 | 20 | /** 21 | * @title TOFTVault 22 | * @author TapiocaDAO 23 | * @notice Holds TOFT funds 24 | */ 25 | contract TOFTVault is Ownable { 26 | using SafeERC20 for IERC20; 27 | 28 | address public _token; 29 | bool private _isNative; 30 | uint256 private _fees; 31 | 32 | error NotValid(); 33 | error ZeroAmount(); 34 | error Failed(); 35 | error FeesAmountNotRight(); 36 | error AmountNotRight(); 37 | error OwnerSet(); 38 | 39 | constructor(address token_) { 40 | _token = token_; 41 | _isNative = token_ == address(0); 42 | 43 | _transferOwnership(address(0)); 44 | } 45 | 46 | /// ===================== 47 | /// View 48 | /// ===================== 49 | /// @notice returns total active supply including fees 50 | function viewTotalSupply() external view returns (uint256) { 51 | return viewSupply() + viewFees(); 52 | } 53 | 54 | /// @notice returns total active supply 55 | /// @dev fees are not taken into account 56 | function viewSupply() public view returns (uint256) { 57 | if (_isNative) { 58 | return address(this).balance - _fees; 59 | } 60 | return IERC20(_token).balanceOf(address(this)) - _fees; 61 | } 62 | 63 | /// @notice returns fees amount 64 | function viewFees() public view returns (uint256) { 65 | return _fees; 66 | } 67 | 68 | /// ===================== 69 | /// Owner 70 | /// ===================== 71 | 72 | /// @dev Intended to be called once by the TOFT contract 73 | function claimOwnership() external { 74 | if (owner() != address(0)) revert OwnerSet(); 75 | _transferOwnership(msg.sender); 76 | } 77 | 78 | /// @notice register fees for mTOFT 79 | function registerFees(uint256 amount) external payable onlyOwner { 80 | if (msg.value > 0 && msg.value != amount) revert FeesAmountNotRight(); 81 | _fees += amount; 82 | } 83 | 84 | /// @notice transfers fees out of the vault 85 | /// @dev the receiver is usually the Balancer.sol contract 86 | /// @param to receiver 87 | /// @param amount the extracted amount 88 | function transferFees(address to, uint256 amount) external onlyOwner { 89 | if (amount > _fees) revert FeesAmountNotRight(); 90 | _fees -= amount; 91 | _withdraw(to, amount); 92 | } 93 | 94 | /// @notice deposit native gas to vault 95 | function depositNative() external payable onlyOwner { 96 | if (!_isNative) revert NotValid(); 97 | if (msg.value == 0) revert ZeroAmount(); 98 | } 99 | 100 | /// @notice extracts from vault 101 | /// @param to receiver 102 | /// @param amount the extracted amount 103 | function withdraw(address to, uint256 amount) external onlyOwner { 104 | _withdraw(to, amount); 105 | } 106 | 107 | /// ===================== 108 | /// Private 109 | /// ===================== 110 | function _withdraw(address to, uint256 amount) private { 111 | if (amount > viewSupply()) revert AmountNotRight(); 112 | if (_isNative) { 113 | (bool success,) = to.call{value: amount}(""); 114 | if (!success) revert Failed(); 115 | } else { 116 | IERC20(_token).safeTransfer(to, amount); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /contracts/tOFT/extensions/TOFTHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // Tapioca 5 | import { 6 | ITOFT, 7 | MarketBorrowMsg, 8 | MarketRemoveCollateralMsg, 9 | SendParamsMsg, 10 | ExerciseOptionsMsg, 11 | LeverageUpActionMsg 12 | } from "tap-utils/interfaces/oft/ITOFT.sol"; 13 | import { 14 | TapiocaOmnichainEngineHelper, 15 | PrepareLzCallData, 16 | PrepareLzCallReturn, 17 | ComposeMsgData 18 | } from "tap-utils/tapiocaOmnichainEngine/extension/TapiocaOmnichainEngineHelper.sol"; 19 | import { 20 | YieldBoxApproveAllMsg, 21 | MarketPermitActionMsg, 22 | YieldBoxApproveAssetMsg 23 | } from "tap-utils/interfaces/periph/ITapiocaOmnichainEngine.sol"; 24 | import { LockAndParticipateData } from "tap-utils/interfaces/periph/IMagnetar.sol"; 25 | import {TOFTMsgCodec} from "contracts/tOFT/libraries/TOFTMsgCodec.sol"; 26 | import {BaseTOFTTokenMsgType} from "../BaseTOFTTokenMsgType.sol"; 27 | 28 | /* 29 | 30 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 31 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 32 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 33 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 34 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 35 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 36 | 37 | */ 38 | 39 | contract TOFTHelper is TapiocaOmnichainEngineHelper, BaseTOFTTokenMsgType { 40 | /// ======================= 41 | /// Builder functions 42 | /// ======================= 43 | /** 44 | * @notice Encodes the message for the PT_LOCK_AND_PARTICIPATE operation. 45 | * 46 | */ 47 | function buildLockAndParticipateMsg(LockAndParticipateData calldata _msg) public pure returns (bytes memory) { 48 | return TOFTMsgCodec.buildLockAndParticipateMsg(_msg); 49 | } 50 | 51 | /** 52 | * @notice Encodes the message for the PT_SEND_PARAMS operation. 53 | * 54 | */ 55 | function buildLeverageUpMsg(LeverageUpActionMsg calldata _msg) public pure returns (bytes memory) { 56 | return TOFTMsgCodec.buildLeverageUpMsg(_msg); 57 | } 58 | /** 59 | * @notice Encodes the message for the exercise options operation. 60 | * 61 | */ 62 | 63 | function buildExerciseOptionMsg(ExerciseOptionsMsg calldata _msg) public pure returns (bytes memory) { 64 | return TOFTMsgCodec.buildExerciseOptionsMsg(_msg); 65 | } 66 | 67 | /** 68 | * @notice Encodes the message for the PT_SEND_PARAMS operation. 69 | * 70 | */ 71 | function buildSendWithParamsMsg(SendParamsMsg calldata _msg) public pure returns (bytes memory) { 72 | return TOFTMsgCodec.buildSendParamsMsg(_msg); 73 | } 74 | 75 | /** 76 | * @notice Encodes the message for the PT_YB_SEND_SGL_BORROW operation. 77 | * 78 | */ 79 | function buildMarketRemoveCollateralMsg(MarketRemoveCollateralMsg calldata _marketMsg) 80 | public 81 | pure 82 | returns (bytes memory) 83 | { 84 | return TOFTMsgCodec.buildMarketRemoveCollateralMsg(_marketMsg); 85 | } 86 | 87 | /** 88 | * @notice Encodes the message for the PT_YB_SEND_SGL_BORROW operation. 89 | * 90 | */ 91 | function buildMarketBorrowMsg(MarketBorrowMsg calldata _marketBorrowMsg) public pure returns (bytes memory) { 92 | return TOFTMsgCodec.buildMarketBorrow(_marketBorrowMsg); 93 | } 94 | 95 | /** 96 | * @dev Sanitizes the message type to match one of the Tapioca supported ones. 97 | * @param _msgType The message type, custom ones with `PT_` as a prefix. 98 | */ 99 | function _sanitizeMsgTypeExtended(uint16 _msgType) internal pure override returns (bool) { 100 | if ( 101 | _msgType == MSG_MARKET_REMOVE_COLLATERAL || _msgType == MSG_YB_SEND_SGL_BORROW 102 | || _msgType == MSG_TAP_EXERCISE || _msgType == MSG_SEND_PARAMS || _msgType == MSG_LEVERAGE_UP || _msgType == MSG_LOCK_AND_PARTICIPATE 103 | ) { 104 | return true; 105 | } 106 | return false; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /contracts/tOFT/libraries/RevertMsgDecoder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.22; 3 | 4 | /* 5 | 6 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 7 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 8 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 9 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 10 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 11 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 12 | 13 | */ 14 | 15 | library RevertMsgDecoder { 16 | /** 17 | * @notice Return the revert message from an external call. 18 | * @param _returnData The return data from the external call. 19 | */ 20 | function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) { 21 | if (_returnData.length > 1000) { 22 | return "RevertMsgDecoder: reason too long"; 23 | } 24 | 25 | // If the _res length is less than 68, then the transaction failed silently (without a revert message) 26 | if (_returnData.length < 68) return "RevertMsgDecoder: no data"; 27 | // solhint-disable-next-line no-inline-assembly 28 | assembly { 29 | // Slice the sighash. 30 | _returnData := add(_returnData, 0x04) 31 | } 32 | return abi.decode(_returnData, (string)); // All that remains is the revert string 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/tOFT/libraries/TOFTMsgCodec.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | 3 | pragma solidity ^0.8.22; 4 | 5 | // Tapioca 6 | import { 7 | ITOFT, 8 | MarketRemoveCollateralMsg, 9 | MarketBorrowMsg, 10 | ExerciseOptionsMsg, 11 | SendParamsMsg, 12 | LeverageUpActionMsg 13 | } from "tap-utils/interfaces/oft/ITOFT.sol"; 14 | import { 15 | LockAndParticipateData 16 | } from "tap-utils/interfaces/periph/IMagnetar.sol"; 17 | import {ITOFT} from "tap-utils/interfaces/oft/ITOFT.sol"; 18 | 19 | /* 20 | 21 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 22 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 23 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 24 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 25 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 26 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 27 | 28 | */ 29 | 30 | library TOFTMsgCodec { 31 | // *************************************** 32 | // * Encoding & Decoding TOFT messages * 33 | // *************************************** 34 | 35 | /** 36 | * @notice Encodes the message for the `TOFTMarketReceiverModule.marketBorrowReceiver()` operation. 37 | */ 38 | function buildMarketBorrow(MarketBorrowMsg memory _marketBorrowMsg) internal pure returns (bytes memory) { 39 | return abi.encode(_marketBorrowMsg); 40 | } 41 | 42 | /** 43 | * @notice Decodes an encoded message for the `TOFTMarketReceiverModule.marketBorrowReceiver()` operation. 44 | */ 45 | function decodeMarketBorrowMsg(bytes memory _msg) internal pure returns (MarketBorrowMsg memory marketBorrowMsg_) { 46 | return abi.decode(_msg, (MarketBorrowMsg)); 47 | } 48 | 49 | /** 50 | * @notice Encodes the message for the `TOFTMarketReceiverModule.marketRemoveCollateralReceiver()` operation. 51 | */ 52 | function buildMarketRemoveCollateralMsg(MarketRemoveCollateralMsg memory _marketMsg) 53 | internal 54 | pure 55 | returns (bytes memory) 56 | { 57 | return abi.encode(_marketMsg); 58 | } 59 | 60 | /** 61 | * @notice Decodes an encoded message for the `TOFTMarketReceiverModule.marketRemoveCollateralReceiver()` operation. 62 | */ 63 | function decodeMarketRemoveCollateralMsg(bytes memory _msg) 64 | internal 65 | pure 66 | returns (MarketRemoveCollateralMsg memory marketMsg_) 67 | { 68 | return abi.decode(_msg, (MarketRemoveCollateralMsg)); 69 | } 70 | 71 | /** 72 | * @notice Encodes the message for the `TOFTOptionsReceiverModule.exerciseOptionsReceiver()` operation. 73 | */ 74 | function buildExerciseOptionsMsg(ExerciseOptionsMsg memory _marketMsg) internal pure returns (bytes memory) { 75 | return abi.encode(_marketMsg); 76 | } 77 | 78 | /** 79 | * @notice Decodes an encoded message for the `TOFTOptionsReceiverModule.exerciseOptionsReceiver()` operation. 80 | */ 81 | function decodeExerciseOptionsMsg(bytes memory _msg) internal pure returns (ExerciseOptionsMsg memory marketMsg_) { 82 | return abi.decode(_msg, (ExerciseOptionsMsg)); 83 | } 84 | 85 | /** 86 | * @notice Encodes the message for the `TOFTReceiver._receiveWithParams()` operation. 87 | */ 88 | function buildSendParamsMsg(SendParamsMsg memory _msg) internal pure returns (bytes memory) { 89 | return abi.encode(_msg); 90 | } 91 | 92 | /** 93 | * @notice Decodes an encoded message for the `TOFTReceiver._receiveWithParams()` operation. 94 | */ 95 | function decodeSendParamsMsg(bytes memory _msg) internal pure returns (SendParamsMsg memory sendMsg_) { 96 | return abi.decode(_msg, (SendParamsMsg)); 97 | } 98 | 99 | /** 100 | * @notice Encodes the message for the `TOFTOptionsReceiverModule.lockAndParticipateReceiver()` operation. 101 | */ 102 | function buildLockAndParticipateMsg(LockAndParticipateData memory _marketMsg) 103 | internal 104 | pure 105 | returns (bytes memory) 106 | { 107 | return abi.encode(_marketMsg); 108 | } 109 | 110 | /** 111 | * @notice Decodes an encoded message for the `TOFTOptionsReceiverModule.lockAndParticipateReceiver()` operation. 112 | */ 113 | function decodeLockAndParticipateMsg(bytes memory _msg) 114 | internal 115 | pure 116 | returns (LockAndParticipateData memory marketMsg_) 117 | { 118 | return abi.decode(_msg, (LockAndParticipateData)); 119 | } 120 | 121 | /** 122 | * @notice Encodes the message for the `TOFTMarketReceiverModule.leverageUpReceiver()` operation. 123 | */ 124 | function buildLeverageUpMsg(LeverageUpActionMsg memory _marketMsg) internal pure returns (bytes memory) { 125 | return abi.encode(_marketMsg); 126 | } 127 | 128 | /** 129 | * @notice Decodes an encoded message for the `TOFTMarketReceiverModule.leverageUpReceiver()` operation. 130 | */ 131 | function decodeLeverageUpMsg(bytes memory _msg) internal pure returns (LeverageUpActionMsg memory marketMsg_) { 132 | return abi.decode(_msg, (LeverageUpActionMsg)); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /contracts/tOFT/modules/BaseTOFTReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // LZ 5 | import {OFTMsgCodec} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/libs/OFTMsgCodec.sol"; 6 | import {OFTCore} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFTCore.sol"; 7 | import {Origin} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol"; 8 | import {OFT} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFT.sol"; 9 | 10 | // External 11 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 12 | import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 13 | 14 | // Tapioca 15 | import {ITOFT, TOFTInitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 16 | import { 17 | YieldBoxApproveAllMsg, 18 | MarketPermitActionMsg, 19 | YieldBoxApproveAssetMsg 20 | } from "tap-utils/interfaces/periph/ITapiocaOmnichainEngine.sol"; 21 | import {TapiocaOmnichainReceiver} from "tap-utils/tapiocaOmnichainEngine/TapiocaOmnichainReceiver.sol"; 22 | import {TOFTGenericReceiverModule} from "./TOFTGenericReceiverModule.sol"; 23 | import {TOFTOptionsReceiverModule} from "./TOFTOptionsReceiverModule.sol"; 24 | import {TOFTMarketReceiverModule} from "./TOFTMarketReceiverModule.sol"; 25 | import {BaseTOFT} from "../BaseTOFT.sol"; 26 | 27 | /* 28 | 29 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 30 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 31 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 32 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 33 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 34 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 35 | 36 | */ 37 | 38 | abstract contract BaseTOFTReceiver is BaseTOFT, TapiocaOmnichainReceiver, ReentrancyGuard { 39 | using OFTMsgCodec for bytes; 40 | using OFTMsgCodec for bytes32; 41 | using SafeERC20 for IERC20; 42 | 43 | error InvalidApprovalTarget(address _target); 44 | 45 | constructor(TOFTInitStruct memory _data) BaseTOFT(_data) {} 46 | 47 | /** 48 | * @inheritdoc TapiocaOmnichainReceiver 49 | */ 50 | function _lzReceive( 51 | Origin calldata _origin, 52 | bytes32 _guid, 53 | bytes calldata _message, 54 | address _executor, /*_executor*/ // @dev unused in the default implementation. 55 | bytes calldata _extraData /*_extraData*/ // @dev unused in the default implementation. 56 | ) internal virtual override(OFTCore, TapiocaOmnichainReceiver) { 57 | TapiocaOmnichainReceiver._lzReceive(_origin, _guid, _message, _executor, _extraData); 58 | } 59 | 60 | /** 61 | * @inheritdoc TapiocaOmnichainReceiver 62 | */ 63 | function _toeComposeReceiver(uint16 _msgType, address _srcChainSender, bytes memory _toeComposeMsg) 64 | internal 65 | override 66 | nonReentrant 67 | returns (bool success) 68 | { 69 | if (_msgType == MSG_YB_SEND_SGL_BORROW) { 70 | _executeModule( 71 | uint8(ITOFT.Module.TOFTMarketReceiver), 72 | abi.encodeWithSelector( 73 | TOFTMarketReceiverModule.marketBorrowReceiver.selector, _srcChainSender, _toeComposeMsg 74 | ), 75 | false 76 | ); 77 | } else if (_msgType == MSG_MARKET_REMOVE_COLLATERAL) { 78 | _executeModule( 79 | uint8(ITOFT.Module.TOFTMarketReceiver), 80 | abi.encodeWithSelector( 81 | TOFTMarketReceiverModule.marketRemoveCollateralReceiver.selector, _srcChainSender, _toeComposeMsg 82 | ), 83 | false 84 | ); 85 | } else if (_msgType == MSG_TAP_EXERCISE) { 86 | _executeModule( 87 | uint8(ITOFT.Module.TOFTOptionsReceiver), 88 | abi.encodeWithSelector( 89 | TOFTOptionsReceiverModule.exerciseOptionsReceiver.selector, _srcChainSender, _toeComposeMsg 90 | ), 91 | false 92 | ); 93 | } else if (_msgType == MSG_SEND_PARAMS) { 94 | _executeModule( 95 | uint8(ITOFT.Module.TOFTGenericReceiver), 96 | abi.encodeWithSelector( 97 | TOFTGenericReceiverModule.receiveWithParamsReceiver.selector, _srcChainSender, _toeComposeMsg 98 | ), 99 | false 100 | ); 101 | } else if (_msgType == MSG_LOCK_AND_PARTICIPATE) { 102 | _executeModule( 103 | uint8(ITOFT.Module.TOFTOptionsReceiver), 104 | abi.encodeWithSelector( 105 | TOFTOptionsReceiverModule.lockAndParticipateReceiver.selector, _srcChainSender, _toeComposeMsg 106 | ), 107 | false 108 | ); 109 | } else { 110 | return _toftCustomComposeReceiver(_msgType, _srcChainSender, _toeComposeMsg); 111 | } 112 | return true; 113 | } 114 | 115 | function _toftCustomComposeReceiver(uint16 _msgType, address _srcChainSender, bytes memory _toeComposeMsg) 116 | internal 117 | virtual 118 | returns (bool success); 119 | 120 | // ********************* // 121 | // ***** RECEIVERS ***** // 122 | // ********************* // 123 | function _sanitizeTarget(address target) internal view { 124 | if (!getCluster().isWhitelisted(0, target)) { 125 | revert InvalidApprovalTarget(target); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /contracts/tOFT/modules/ModuleManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | import {RevertMsgDecoder} from "../libraries/RevertMsgDecoder.sol"; 5 | 6 | /* 7 | 8 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 9 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 10 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 11 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 12 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 13 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 14 | 15 | */ 16 | 17 | /** 18 | * @title ModuleManager 19 | * @author TapiocaDAO 20 | * @notice Help to modularize a contract. 21 | * 22 | * @dev !!!!!!!! IMPORTANT !!!!!!!! 23 | * `_executeModule()` will sometimes return random/garbage data on the `(success, returnData) = module.delegatecall(_data)` call. 24 | * Occurrence happens when calling a non view function on the main contract, that target a view function on the module. 25 | * The module function is a view function that deals with `byte memory` slicing. Problem might be related to how Solidity handles memory bytes in 32 bytes chunks. 26 | */ 27 | abstract contract ModuleManager { 28 | /// @notice returns whitelisted modules 29 | mapping(uint8 module => address moduleAddress) internal _moduleAddresses; 30 | 31 | error ModuleManager__ModuleNotAuthorized(); 32 | 33 | /** 34 | * @notice Sets a module to the whitelist. 35 | * @param _module The module to add. 36 | * @param _moduleAddress The module address. 37 | */ 38 | function _setModule(uint8 _module, address _moduleAddress) internal { 39 | _moduleAddresses[_module] = _moduleAddress; 40 | } 41 | 42 | /** 43 | * @dev Returns the module address, if whitelisted. 44 | * @param _module The module we wants to execute. 45 | */ 46 | function _extractModule(uint8 _module) internal view returns (address) { 47 | address module = _moduleAddresses[_module]; 48 | if (module == address(0)) revert ModuleManager__ModuleNotAuthorized(); 49 | 50 | return module; 51 | } 52 | 53 | /** 54 | * @notice Execute an call to a given module. 55 | * 56 | * @param _module The module to execute. 57 | * @param _data The data to execute. 58 | * @param _forwardRevert If true, forward the revert message from the module. 59 | * 60 | * @return returnData The return data from the module execution, if any. 61 | */ 62 | function _executeModule(uint8 _module, bytes memory _data, bool _forwardRevert) 63 | internal 64 | returns (bytes memory returnData) 65 | { 66 | bool success = true; 67 | address module = _extractModule(_module); 68 | 69 | (success, returnData) = module.delegatecall(_data); 70 | if (!success && !_forwardRevert) { 71 | revert(RevertMsgDecoder._getRevertMsg(returnData)); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /contracts/tOFT/modules/TOFTGenericReceiverModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // External 5 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; 7 | 8 | //LZ 9 | import {IMessagingChannel} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol"; 10 | 11 | // Tapioca 12 | import {ITOFT, TOFTInitStruct, SendParamsMsg} from "tap-utils/interfaces/oft/ITOFT.sol"; 13 | import {TOFTMsgCodec} from "../libraries/TOFTMsgCodec.sol"; 14 | import {BaseTOFT} from "../BaseTOFT.sol"; 15 | 16 | /* 17 | 18 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 19 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 20 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 21 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 22 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 23 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 24 | 25 | */ 26 | 27 | /** 28 | * @title TOFTGenericReceiverModule 29 | * @author TapiocaDAO 30 | * @notice TOFT Generic module 31 | */ 32 | contract TOFTGenericReceiverModule is BaseTOFT { 33 | using SafeERC20 for IERC20; 34 | using SafeCast for uint256; 35 | 36 | error TOFTGenericReceiverModule_NotAuthorized(address invalidAddress); 37 | error TOFTGenericReceiverModule_TransferFailed(); 38 | error TOFTGenericReceiverModule_AmountMismatch(); 39 | error TOFTGenericReceiverModule_OnlyHostChain(); 40 | 41 | event WithParamsReceived(uint256 amount, address receiver, address srcChainSender); 42 | 43 | constructor(TOFTInitStruct memory _data) BaseTOFT(_data) {} 44 | 45 | /** 46 | * @notice Unwrap and sends underlying to `receiver`. 47 | * 48 | * @param srcChainSender The address of the sender on the source chain. 49 | * @param _data The call data containing info about the operation. 50 | * - receiver::address: Underlying tokens receiver. 51 | * - unwrap::bool: Unwrap TOFT. 52 | * - amount::uint256: Amount to unwrap. 53 | */ 54 | function receiveWithParamsReceiver(address srcChainSender, bytes memory _data) public payable { 55 | SendParamsMsg memory msg_ = TOFTMsgCodec.decodeSendParamsMsg(_data); 56 | 57 | /** 58 | * @dev validate data 59 | */ 60 | msg_ = _validateReceiveWithParams(msg_); 61 | 62 | /** 63 | * @dev executes unwrap or revert 64 | */ 65 | _unwrapInReceiveWithParams(msg_, srcChainSender); 66 | 67 | emit WithParamsReceived(msg_.amount, msg_.receiver, srcChainSender); 68 | } 69 | 70 | function _validateReceiveWithParams(SendParamsMsg memory msg_) private view returns (SendParamsMsg memory) { 71 | msg_.amount = _toLD(msg_.amount.toUint64()); 72 | return msg_; 73 | } 74 | 75 | function _unwrapInReceiveWithParams(SendParamsMsg memory msg_, address srcChainSender) private { 76 | if (msg_.unwrap) { 77 | /// @dev xChain owner needs to have approved dst srcChain `sendPacket()` msg.sender in a previous composedMsg. Or be the same address. 78 | _internalTransferWithAllowance(msg_.receiver, srcChainSender, msg_.amount); 79 | 80 | if (IMessagingChannel(endpoint).eid() != hostEid) revert TOFTGenericReceiverModule_OnlyHostChain(); 81 | _unwrap(address(this), msg_.receiver, msg_.amount); 82 | } else { 83 | if (msg.value > 0) revert TOFTGenericReceiverModule_AmountMismatch(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /contracts/tOFT/modules/TOFTReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // LZ 5 | import {OFT} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFT.sol"; 6 | 7 | // Tapioca 8 | import {TOFTInitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 9 | import {BaseTOFTReceiver} from "./BaseTOFTReceiver.sol"; 10 | 11 | /* 12 | 13 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 14 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 15 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 16 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 17 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 18 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 19 | 20 | */ 21 | 22 | contract TOFTReceiver is BaseTOFTReceiver { 23 | constructor(TOFTInitStruct memory _data) BaseTOFTReceiver(_data) {} 24 | 25 | function _toftCustomComposeReceiver(uint16, address, bytes memory) internal pure override returns (bool success) { 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/tOFT/modules/TOFTSender.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // Tapioca 5 | import {TapiocaOmnichainSender} from "tap-utils/tapiocaOmnichainEngine/TapiocaOmnichainSender.sol"; 6 | import {ITOFT, TOFTInitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 7 | import {BaseTOFT} from "../BaseTOFT.sol"; 8 | 9 | /* 10 | 11 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 12 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 13 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 14 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 15 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 16 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 17 | 18 | */ 19 | 20 | contract TOFTSender is BaseTOFT, TapiocaOmnichainSender { 21 | constructor(TOFTInitStruct memory _data) BaseTOFT(_data) {} 22 | } 23 | -------------------------------------------------------------------------------- /contracts/tOFT/modules/mTOFTReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // Tapioca 5 | import {TOFTInitStruct, LeverageUpActionMsg, ITOFT} from "tap-utils/interfaces/oft/ITOFT.sol"; 6 | import {TOFTOptionsReceiverModule} from "./TOFTOptionsReceiverModule.sol"; 7 | import {TOFTMarketReceiverModule} from "./TOFTMarketReceiverModule.sol"; 8 | import {BaseTOFTReceiver} from "./BaseTOFTReceiver.sol"; 9 | 10 | /* 11 | 12 | ████████╗ █████╗ ██████╗ ██╗ ██████╗ ██████╗ █████╗ 13 | ╚══██╔══╝██╔══██╗██╔══██╗██║██╔═══██╗██╔════╝██╔══██╗ 14 | ██║ ███████║██████╔╝██║██║ ██║██║ ███████║ 15 | ██║ ██╔══██║██╔═══╝ ██║██║ ██║██║ ██╔══██║ 16 | ██║ ██║ ██║██║ ██║╚██████╔╝╚██████╗██║ ██║ 17 | ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ 18 | 19 | */ 20 | 21 | contract mTOFTReceiver is BaseTOFTReceiver { 22 | constructor(TOFTInitStruct memory _data) BaseTOFTReceiver(_data) {} 23 | 24 | function _toftCustomComposeReceiver(uint16 _msgType, address _srcChainSender, bytes memory _toeComposeMsg) 25 | internal 26 | override 27 | returns (bool success) 28 | { 29 | if (_msgType == MSG_LEVERAGE_UP) { 30 | _executeModule( 31 | uint8(ITOFT.Module.TOFTMarketReceiver), 32 | abi.encodeWithSelector( 33 | TOFTMarketReceiverModule.leverageUpReceiver.selector, _srcChainSender, _toeComposeMsg 34 | ), 35 | false 36 | ); 37 | return true; 38 | } 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/tapioca-periph-file-loader.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | /// THIS FILE IS USED TO LOAD THE TAPIOCA PERIPH CONTRACTS 5 | /// Comment the imports for faster compilation 6 | 7 | import {PearlmitHandler} from "tap-utils/pearlmit/PearlmitHandler.sol"; 8 | import {Pearlmit} from "tap-utils/pearlmit/Pearlmit.sol"; 9 | -------------------------------------------------------------------------------- /contracts/util/ERC4494.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | 3 | pragma solidity 0.8.22; 4 | 5 | import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; 6 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 7 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 8 | import "@openzeppelin/contracts/utils/Counters.sol"; 9 | 10 | /** 11 | * Modification of the OpenZeppelin ERC20Permit contract to support ERC721 tokens. 12 | * OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol). 13 | * 14 | * @dev Implementation of the ERC-4494 Permit extension allowing approvals to be made via signatures, as defined in 15 | * https://eips.ethereum.org/EIPS/eip-4494[EIP-4494]. 16 | * 17 | * Adds the {permit} method, which can be used to change an account's ERC721 allowance (see {IERC721-allowance}) by 18 | * presenting a message signed by the account. By not relying on `{IERC721-approve}`, the token holder account doesn't 19 | * need to send a transaction, and thus is not required to hold Ether at all. 20 | */ 21 | abstract contract ERC721Permit is ERC721, EIP712 { 22 | using Counters for Counters.Counter; 23 | 24 | mapping(uint256 => Counters.Counter) private _nonces; 25 | 26 | // solhint-disable-next-line var-name-mixedcase 27 | bytes32 private constant _PERMIT_TYPEHASH = 28 | keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"); 29 | /** 30 | * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. 31 | * However, to ensure consistency with the upgradeable transpiler, we will continue 32 | * to reserve a slot. 33 | * @custom:oz-renamed-from _PERMIT_TYPEHASH 34 | */ 35 | // solhint-disable-next-line var-name-mixedcase 36 | bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; 37 | 38 | /** 39 | * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. 40 | * 41 | * It's a good idea to use the same `name` that is defined as the ERC721 token name. 42 | */ 43 | constructor(string memory name) EIP712(name, "1") {} 44 | 45 | function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual { 46 | require(block.timestamp <= deadline, "ERC721Permit: expired deadline"); 47 | 48 | address owner = ownerOf(tokenId); 49 | bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, spender, tokenId, _useNonce(tokenId), deadline)); 50 | 51 | bytes32 hash = _hashTypedDataV4(structHash); 52 | 53 | address signer = ECDSA.recover(hash, v, r, s); 54 | require(signer == owner, "ERC721Permit: invalid signature"); 55 | 56 | _approve(spender, tokenId); 57 | } 58 | 59 | /** 60 | * @dev See {IERC20Permit-nonces}. 61 | */ 62 | function nonces(uint256 _tokenId) public view virtual returns (uint256) { 63 | return _nonces[_tokenId].current(); 64 | } 65 | 66 | /** 67 | * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. 68 | */ 69 | // solhint-disable-next-line func-name-mixedcase 70 | function DOMAIN_SEPARATOR() external view returns (bytes32) { 71 | return _domainSeparatorV4(); 72 | } 73 | 74 | /** 75 | * @dev "Consume a nonce": return the current value and increment. 76 | * 77 | */ 78 | function _useNonce(uint256 _tokenId) internal virtual returns (uint256 current) { 79 | Counters.Counter storage nonce = _nonces[_tokenId]; 80 | current = nonce.current(); 81 | nonce.increment(); 82 | } 83 | 84 | function _afterTokenTransfer( 85 | address from, 86 | address to, 87 | uint256 firstTokenId, 88 | uint256 batchSize 89 | ) internal virtual override { 90 | _useNonce(firstTokenId); 91 | super._afterTokenTransfer(from, to, firstTokenId, batchSize); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'contracts' 3 | out = 'gen/out' 4 | libs = ['node_modules', 'lib'] 5 | test = 'test' 6 | cache_path = 'gen/cache_forge' 7 | 8 | solc_version='0.8.22' 9 | evm_version='paris' 10 | optimizer = true 11 | optimizer_runs = 30 12 | 13 | 14 | remappings = [ 15 | 'forge-deploy/=node_modules/forge-deploy/contracts/', 16 | "solidity-bytes-utils/=node_modules/@layerzerolabs/solidity-bytes-utils/", 17 | "forge-std/=lib/forge-std/src/", 18 | "tapioca-sdk/=gitmodule/tapioca-sdk/contracts/", 19 | "tap-utils/=lib/tap-utils/contracts/", 20 | "yieldbox/=lib/tap-yieldbox/contracts/", 21 | "permitc/=lib/tap-utils/lib/permitc/src/", # Needs to be init in the periph repo 22 | ] 23 | 24 | # forge-dpeloy 25 | fs_permissions = [ 26 | { access = "read-write", path = "./deployments"}, 27 | { access = "read", path = "./out"} 28 | ] 29 | 30 | [etherscan] 31 | arbitrum-sepolia = { key = "${ARBITRUM_SEPOLIA_API_KEY}", url = "https://api-sepolia.arbiscan.io/api"} -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import 'tsconfig-paths/register'; // Important. Used to resolve paths in tsconfig.json 2 | import conf from './hardhat.export'; 3 | import './hardhat.tasks'; 4 | 5 | export default conf; 6 | -------------------------------------------------------------------------------- /hardhat.export.ts: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import '@nomiclabs/hardhat-ethers'; 3 | import '@nomicfoundation/hardhat-verify'; 4 | import '@typechain/hardhat'; 5 | 6 | import '@nomicfoundation/hardhat-chai-matchers'; 7 | import '@nomicfoundation/hardhat-foundry'; 8 | import '@primitivefi/hardhat-dodoc'; 9 | import 'hardhat-contract-sizer'; 10 | import 'hardhat-tracer'; 11 | import { HardhatUserConfig } from 'hardhat/config'; 12 | import { HttpNetworkConfig, HttpNetworkUserConfig } from 'hardhat/types'; 13 | import fs from 'fs'; 14 | 15 | // Utils 16 | import { TAPIOCA_PROJECTS_NAME } from '@tapioca-sdk/api/config'; 17 | import { SDK, loadEnv } from 'tapioca-sdk'; 18 | import 'tapioca-sdk'; // Use directly the un-compiled code, no need to wait for the tarball to be published. 19 | 20 | declare global { 21 | // eslint-disable-next-line @typescript-eslint/no-namespace 22 | namespace NodeJS { 23 | interface ProcessEnv { 24 | ALCHEMY_API_KEY: string; 25 | ENV: string; 26 | } 27 | } 28 | } 29 | 30 | // Load the env vars from the .env/.env file. the file name is the same as the network in hh `--network arbitrum_sepolia` 31 | loadEnv(); 32 | 33 | // TODO refactor all of that in the SDK? 34 | type TNetwork = ReturnType< 35 | typeof SDK.API.utils.getSupportedChains 36 | >[number]['name']; 37 | const supportedChains = SDK.API.utils.getSupportedChains().reduce( 38 | (sdkChains, chain) => ({ 39 | ...sdkChains, 40 | [chain.name]: { 41 | accounts: 42 | process.env.PRIVATE_KEY !== undefined 43 | ? [process.env.PRIVATE_KEY] 44 | : [], 45 | live: true, 46 | url: chain.rpc.replace('', process.env.ALCHEMY_API_KEY), 47 | gasMultiplier: chain.tags[0] === 'testnet' ? 2 : 1, 48 | chainId: Number(chain.chainId), 49 | tags: [...chain.tags], 50 | }, 51 | }), 52 | {} as { [key in TNetwork]: HttpNetworkConfig }, 53 | ); 54 | 55 | const config: HardhatUserConfig & { dodoc: any } = { 56 | SDK: { project: TAPIOCA_PROJECTS_NAME.TapiocaZ }, 57 | solidity: { 58 | compilers: [ 59 | { 60 | version: '0.8.22', 61 | settings: { 62 | evmVersion: 'paris', // Latest before Shanghai 63 | optimizer: { 64 | enabled: true, 65 | runs: 9999, 66 | }, 67 | }, 68 | }, 69 | ], 70 | overrides: { 71 | 'contracts/tOFT/mTOFT.sol': { 72 | version: '0.8.22', 73 | settings: { 74 | evmVersion: 'paris', // Latest before Shanghai 75 | optimizer: { 76 | enabled: true, 77 | runs: 20, 78 | }, 79 | }, 80 | }, 81 | 'contracts/tOFT/modules/TOFTOptionsReceiverModule.sol': { 82 | version: '0.8.22', 83 | settings: { 84 | evmVersion: 'paris', // Latest before Shanghai 85 | optimizer: { 86 | enabled: true, 87 | runs: 5000, 88 | }, 89 | }, 90 | }, 91 | 'contracts/tOFT/modules/TOFTMarketReceiverModule.sol': { 92 | version: '0.8.22', 93 | settings: { 94 | evmVersion: 'paris', // Latest before Shanghai 95 | optimizer: { 96 | enabled: true, 97 | runs: 5000, 98 | }, 99 | }, 100 | }, 101 | }, 102 | }, 103 | paths: { 104 | artifacts: './gen/artifacts', 105 | cache: './gen/cache', 106 | tests: './test_hardhat', 107 | }, 108 | dodoc: { 109 | runOnCompile: false, 110 | freshOutput: false, 111 | outputDir: 'gen/docs', 112 | }, 113 | typechain: { 114 | outDir: 'gen/typechain', 115 | target: 'ethers-v5', 116 | }, 117 | defaultNetwork: 'hardhat', 118 | networks: { 119 | hardhat: { 120 | allowUnlimitedContractSize: true, 121 | accounts: { 122 | count: 5, 123 | }, 124 | }, 125 | ...supportedChains, 126 | }, 127 | etherscan: { 128 | apiKey: { 129 | sepolia: process.env.SCAN_API_KEY ?? '', 130 | arbitrumSepolia: process.env.SCAN_API_KEY ?? '', 131 | optimismSepolia: process.env.SCAN_API_KEY ?? '', 132 | avalancheFujiTestnet: process.env.SCAN_API_KEY ?? '', 133 | bscTestnet: process.env.SCAN_API_KEY ?? '', 134 | polygonMumbai: process.env.SCAN_API_KEY ?? '', 135 | ftmTestnet: process.env.SCAN_API_KEY ?? '', 136 | }, 137 | customChains: [ 138 | { 139 | network: 'arbitrumSepolia', 140 | chainId: 421614, 141 | urls: { 142 | apiURL: 'https://api-sepolia.arbiscan.io/api', 143 | browserURL: 'https://sepolia.arbiscan.io/', 144 | }, 145 | }, 146 | { 147 | network: 'optimismSepolia', 148 | chainId: 11155420, 149 | urls: { 150 | apiURL: 'https://api-sepolia-optimistic.etherscan.io/', 151 | browserURL: 'https://sepolia-optimism.etherscan.io/', 152 | }, 153 | }, 154 | ], 155 | }, 156 | }; 157 | 158 | export default config; 159 | -------------------------------------------------------------------------------- /hardhat.tasks.ts: -------------------------------------------------------------------------------- 1 | import '@nomiclabs/hardhat-ethers'; 2 | 3 | import './tasks/scopes/balancerScope'; 4 | import './tasks/scopes/toftScope'; 5 | import './tasks/scopes/deployScope'; 6 | -------------------------------------------------------------------------------- /lib/forge-std/.gitattributes: -------------------------------------------------------------------------------- 1 | src/Vm.sol linguist-generated 2 | -------------------------------------------------------------------------------- /lib/forge-std/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Install Foundry 17 | uses: onbjerg/foundry-toolchain@v1 18 | with: 19 | version: nightly 20 | 21 | - name: Print forge version 22 | run: forge --version 23 | 24 | # Backwards compatibility checks: 25 | # - the oldest and newest version of each supported minor version 26 | # - versions with specific issues 27 | - name: Check compatibility with latest 28 | if: always() 29 | run: | 30 | output=$(forge build --skip test) 31 | 32 | if echo "$output" | grep -q "Warning"; then 33 | echo "$output" 34 | exit 1 35 | fi 36 | 37 | - name: Check compatibility with 0.8.0 38 | if: always() 39 | run: | 40 | output=$(forge build --skip test --use solc:0.8.0) 41 | 42 | if echo "$output" | grep -q "Warning"; then 43 | echo "$output" 44 | exit 1 45 | fi 46 | 47 | - name: Check compatibility with 0.7.6 48 | if: always() 49 | run: | 50 | output=$(forge build --skip test --use solc:0.7.6) 51 | 52 | if echo "$output" | grep -q "Warning"; then 53 | echo "$output" 54 | exit 1 55 | fi 56 | 57 | - name: Check compatibility with 0.7.0 58 | if: always() 59 | run: | 60 | output=$(forge build --skip test --use solc:0.7.0) 61 | 62 | if echo "$output" | grep -q "Warning"; then 63 | echo "$output" 64 | exit 1 65 | fi 66 | 67 | - name: Check compatibility with 0.6.12 68 | if: always() 69 | run: | 70 | output=$(forge build --skip test --use solc:0.6.12) 71 | 72 | if echo "$output" | grep -q "Warning"; then 73 | echo "$output" 74 | exit 1 75 | fi 76 | 77 | - name: Check compatibility with 0.6.2 78 | if: always() 79 | run: | 80 | output=$(forge build --skip test --use solc:0.6.2) 81 | 82 | if echo "$output" | grep -q "Warning"; then 83 | echo "$output" 84 | exit 1 85 | fi 86 | 87 | # via-ir compilation time checks. 88 | - name: Measure compilation time of Test with 0.8.17 --via-ir 89 | if: always() 90 | run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir 91 | 92 | - name: Measure compilation time of TestBase with 0.8.17 --via-ir 93 | if: always() 94 | run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir 95 | 96 | - name: Measure compilation time of Script with 0.8.17 --via-ir 97 | if: always() 98 | run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir 99 | 100 | - name: Measure compilation time of ScriptBase with 0.8.17 --via-ir 101 | if: always() 102 | run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir 103 | 104 | test: 105 | runs-on: ubuntu-latest 106 | steps: 107 | - uses: actions/checkout@v3 108 | 109 | - name: Install Foundry 110 | uses: onbjerg/foundry-toolchain@v1 111 | with: 112 | version: nightly 113 | 114 | - name: Print forge version 115 | run: forge --version 116 | 117 | - name: Run tests 118 | run: forge test -vvv 119 | 120 | fmt: 121 | runs-on: ubuntu-latest 122 | steps: 123 | - uses: actions/checkout@v3 124 | 125 | - name: Install Foundry 126 | uses: onbjerg/foundry-toolchain@v1 127 | with: 128 | version: nightly 129 | 130 | - name: Print forge version 131 | run: forge --version 132 | 133 | - name: Check formatting 134 | run: forge fmt --check 135 | -------------------------------------------------------------------------------- /lib/forge-std/.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync Release Branch 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | 8 | jobs: 9 | sync-release-branch: 10 | runs-on: ubuntu-latest 11 | if: startsWith(github.event.release.tag_name, 'v1') 12 | steps: 13 | - name: Check out the repo 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | ref: v1 18 | 19 | - name: Configure Git 20 | run: | 21 | git config user.name github-actions[bot] 22 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 23 | 24 | - name: Sync Release Branch 25 | run: | 26 | git fetch --tags 27 | git checkout v1 28 | git reset --hard ${GITHUB_REF} 29 | git push --force 30 | -------------------------------------------------------------------------------- /lib/forge-std/.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | out/ 3 | .vscode 4 | .idea 5 | -------------------------------------------------------------------------------- /lib/forge-std/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/ds-test"] 2 | path = lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | -------------------------------------------------------------------------------- /lib/forge-std/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright Contributors to Forge Standard Library 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE.R 26 | -------------------------------------------------------------------------------- /lib/forge-std/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | fs_permissions = [{ access = "read-write", path = "./"}] 3 | 4 | [rpc_endpoints] 5 | # The RPC URLs are modified versions of the default for testing initialization. 6 | mainnet = "https://mainnet.infura.io/v3/b1d3925804e74152b316ca7da97060d3" # Different API key. 7 | optimism_goerli = "https://goerli.optimism.io/" # Adds a trailing slash. 8 | arbitrum_one_goerli = "https://goerli-rollup.arbitrum.io/rpc/" # Adds a trailing slash. 9 | needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" 10 | 11 | [fmt] 12 | # These are all the `forge fmt` defaults. 13 | line_length = 120 14 | tab_width = 4 15 | bracket_spacing = false 16 | int_types = 'long' 17 | multiline_func_header = 'attributes_first' 18 | quote_style = 'double' 19 | number_underscore = 'preserve' 20 | single_line_statement_blocks = 'preserve' 21 | ignore = ["src/console.sol", "src/console2.sol"] -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: cachix/install-nix-action@v20 11 | with: 12 | nix_path: nixpkgs=channel:nixos-unstable 13 | extra_nix_config: | 14 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 15 | 16 | - name: setup dapp binary cache 17 | uses: cachix/cachix-action@v12 18 | with: 19 | name: dapp 20 | 21 | - name: install dapptools 22 | run: nix profile install github:dapphub/dapptools#dapp --accept-flake-config 23 | 24 | - name: install foundry 25 | uses: foundry-rs/foundry-toolchain@v1 26 | 27 | - name: test with solc-0.5.17 28 | run: dapp --use solc-0.5.17 test -v 29 | 30 | - name: test with solc-0.6.11 31 | run: dapp --use solc-0.6.11 test -v 32 | 33 | - name: test with solc-0.7.6 34 | run: dapp --use solc-0.7.6 test -v 35 | 36 | - name: test with solc-0.8.18 37 | run: dapp --use solc-0.8.18 test -v 38 | 39 | - name: Run tests with foundry 40 | run: forge test -vvv 41 | 42 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/.gitignore: -------------------------------------------------------------------------------- 1 | /.dapple 2 | /build 3 | /out 4 | /cache/ 5 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/Makefile: -------------------------------------------------------------------------------- 1 | all:; dapp build 2 | 3 | test: 4 | -dapp --use solc:0.4.23 build 5 | -dapp --use solc:0.4.26 build 6 | -dapp --use solc:0.5.17 build 7 | -dapp --use solc:0.6.12 build 8 | -dapp --use solc:0.7.5 build 9 | 10 | demo: 11 | DAPP_SRC=demo dapp --use solc:0.7.5 build 12 | -hevm dapp-test --verbose 3 13 | 14 | .PHONY: test demo 15 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/default.nix: -------------------------------------------------------------------------------- 1 | { solidityPackage, dappsys }: solidityPackage { 2 | name = "ds-test"; 3 | src = ./src; 4 | } 5 | -------------------------------------------------------------------------------- /lib/forge-std/lib/ds-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ds-test", 3 | "version": "1.0.0", 4 | "description": "Assertions, equality checks and other test helpers ", 5 | "bugs": "https://github.com/dapphub/ds-test/issues", 6 | "license": "GPL-3.0", 7 | "author": "Contributors to ds-test", 8 | "files": [ 9 | "src/*" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dapphub/ds-test.git" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/forge-std/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forge-std", 3 | "version": "1.7.6", 4 | "description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.", 5 | "homepage": "https://book.getfoundry.sh/forge/forge-std", 6 | "bugs": "https://github.com/foundry-rs/forge-std/issues", 7 | "license": "(Apache-2.0 OR MIT)", 8 | "author": "Contributors to Forge Standard Library", 9 | "files": [ 10 | "src/**/*" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/foundry-rs/forge-std.git" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/forge-std/src/Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | import {StdStorage} from "./StdStorage.sol"; 5 | import {Vm, VmSafe} from "./Vm.sol"; 6 | 7 | abstract contract CommonBase { 8 | // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. 9 | address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); 10 | // console.sol and console2.sol work by executing a staticcall to this address. 11 | address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; 12 | // Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. 13 | address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; 14 | // Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. 15 | address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller")))); 16 | // Address of the test contract, deployed by the DEFAULT_SENDER. 17 | address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; 18 | // Deterministic deployment address of the Multicall3 contract. 19 | address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11; 20 | // The order of the secp256k1 curve. 21 | uint256 internal constant SECP256K1_ORDER = 22 | 115792089237316195423570985008687907852837564279074904382605163141518161494337; 23 | 24 | uint256 internal constant UINT256_MAX = 25 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 26 | 27 | Vm internal constant vm = Vm(VM_ADDRESS); 28 | StdStorage internal stdstore; 29 | } 30 | 31 | abstract contract TestBase is CommonBase {} 32 | 33 | abstract contract ScriptBase is CommonBase { 34 | VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); 35 | } 36 | -------------------------------------------------------------------------------- /lib/forge-std/src/Script.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | // 💬 ABOUT 5 | // Forge Std's default Script. 6 | 7 | // 🧩 MODULES 8 | import {console} from "./console.sol"; 9 | import {console2} from "./console2.sol"; 10 | import {safeconsole} from "./safeconsole.sol"; 11 | import {StdChains} from "./StdChains.sol"; 12 | import {StdCheatsSafe} from "./StdCheats.sol"; 13 | import {stdJson} from "./StdJson.sol"; 14 | import {stdMath} from "./StdMath.sol"; 15 | import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; 16 | import {StdStyle} from "./StdStyle.sol"; 17 | import {StdUtils} from "./StdUtils.sol"; 18 | import {VmSafe} from "./Vm.sol"; 19 | 20 | // 📦 BOILERPLATE 21 | import {ScriptBase} from "./Base.sol"; 22 | 23 | // ⭐️ SCRIPT 24 | abstract contract Script is ScriptBase, StdChains, StdCheatsSafe, StdUtils { 25 | // Note: IS_SCRIPT() must return true. 26 | bool public IS_SCRIPT = true; 27 | } 28 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdError.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test 3 | pragma solidity >=0.6.2 <0.9.0; 4 | 5 | library stdError { 6 | bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); 7 | bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); 8 | bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); 9 | bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); 10 | bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); 11 | bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); 12 | bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); 13 | bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); 14 | bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); 15 | } 16 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdInvariant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | abstract contract StdInvariant { 7 | struct FuzzSelector { 8 | address addr; 9 | bytes4[] selectors; 10 | } 11 | 12 | struct FuzzInterface { 13 | address addr; 14 | string[] artifacts; 15 | } 16 | 17 | address[] private _excludedContracts; 18 | address[] private _excludedSenders; 19 | address[] private _targetedContracts; 20 | address[] private _targetedSenders; 21 | 22 | string[] private _excludedArtifacts; 23 | string[] private _targetedArtifacts; 24 | 25 | FuzzSelector[] private _targetedArtifactSelectors; 26 | FuzzSelector[] private _targetedSelectors; 27 | 28 | FuzzInterface[] private _targetedInterfaces; 29 | 30 | // Functions for users: 31 | // These are intended to be called in tests. 32 | 33 | function excludeContract(address newExcludedContract_) internal { 34 | _excludedContracts.push(newExcludedContract_); 35 | } 36 | 37 | function excludeSender(address newExcludedSender_) internal { 38 | _excludedSenders.push(newExcludedSender_); 39 | } 40 | 41 | function excludeArtifact(string memory newExcludedArtifact_) internal { 42 | _excludedArtifacts.push(newExcludedArtifact_); 43 | } 44 | 45 | function targetArtifact(string memory newTargetedArtifact_) internal { 46 | _targetedArtifacts.push(newTargetedArtifact_); 47 | } 48 | 49 | function targetArtifactSelector(FuzzSelector memory newTargetedArtifactSelector_) internal { 50 | _targetedArtifactSelectors.push(newTargetedArtifactSelector_); 51 | } 52 | 53 | function targetContract(address newTargetedContract_) internal { 54 | _targetedContracts.push(newTargetedContract_); 55 | } 56 | 57 | function targetSelector(FuzzSelector memory newTargetedSelector_) internal { 58 | _targetedSelectors.push(newTargetedSelector_); 59 | } 60 | 61 | function targetSender(address newTargetedSender_) internal { 62 | _targetedSenders.push(newTargetedSender_); 63 | } 64 | 65 | function targetInterface(FuzzInterface memory newTargetedInterface_) internal { 66 | _targetedInterfaces.push(newTargetedInterface_); 67 | } 68 | 69 | // Functions for forge: 70 | // These are called by forge to run invariant tests and don't need to be called in tests. 71 | 72 | function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) { 73 | excludedArtifacts_ = _excludedArtifacts; 74 | } 75 | 76 | function excludeContracts() public view returns (address[] memory excludedContracts_) { 77 | excludedContracts_ = _excludedContracts; 78 | } 79 | 80 | function excludeSenders() public view returns (address[] memory excludedSenders_) { 81 | excludedSenders_ = _excludedSenders; 82 | } 83 | 84 | function targetArtifacts() public view returns (string[] memory targetedArtifacts_) { 85 | targetedArtifacts_ = _targetedArtifacts; 86 | } 87 | 88 | function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) { 89 | targetedArtifactSelectors_ = _targetedArtifactSelectors; 90 | } 91 | 92 | function targetContracts() public view returns (address[] memory targetedContracts_) { 93 | targetedContracts_ = _targetedContracts; 94 | } 95 | 96 | function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) { 97 | targetedSelectors_ = _targetedSelectors; 98 | } 99 | 100 | function targetSenders() public view returns (address[] memory targetedSenders_) { 101 | targetedSenders_ = _targetedSenders; 102 | } 103 | 104 | function targetInterfaces() public view returns (FuzzInterface[] memory targetedInterfaces_) { 105 | targetedInterfaces_ = _targetedInterfaces; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/forge-std/src/StdMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | library stdMath { 5 | int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; 6 | 7 | function abs(int256 a) internal pure returns (uint256) { 8 | // Required or it will fail when `a = type(int256).min` 9 | if (a == INT256_MIN) { 10 | return 57896044618658097711785492504343953926634992332820282019728792003956564819968; 11 | } 12 | 13 | return uint256(a > 0 ? a : -a); 14 | } 15 | 16 | function delta(uint256 a, uint256 b) internal pure returns (uint256) { 17 | return a > b ? a - b : b - a; 18 | } 19 | 20 | function delta(int256 a, int256 b) internal pure returns (uint256) { 21 | // a and b are of the same sign 22 | // this works thanks to two's complement, the left-most bit is the sign bit 23 | if ((a ^ b) > -1) { 24 | return delta(abs(a), abs(b)); 25 | } 26 | 27 | // a and b are of opposite signs 28 | return abs(a) + abs(b); 29 | } 30 | 31 | function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { 32 | uint256 absDelta = delta(a, b); 33 | 34 | return absDelta * 1e18 / b; 35 | } 36 | 37 | function percentDelta(int256 a, int256 b) internal pure returns (uint256) { 38 | uint256 absDelta = delta(a, b); 39 | uint256 absB = abs(b); 40 | 41 | return absDelta * 1e18 / absB; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/forge-std/src/Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | // 💬 ABOUT 7 | // Forge Std's default Test. 8 | 9 | // 🧩 MODULES 10 | import {console} from "./console.sol"; 11 | import {console2} from "./console2.sol"; 12 | import {safeconsole} from "./safeconsole.sol"; 13 | import {StdAssertions} from "./StdAssertions.sol"; 14 | import {StdChains} from "./StdChains.sol"; 15 | import {StdCheats} from "./StdCheats.sol"; 16 | import {stdError} from "./StdError.sol"; 17 | import {StdInvariant} from "./StdInvariant.sol"; 18 | import {stdJson} from "./StdJson.sol"; 19 | import {stdMath} from "./StdMath.sol"; 20 | import {StdStorage, stdStorage} from "./StdStorage.sol"; 21 | import {StdStyle} from "./StdStyle.sol"; 22 | import {StdUtils} from "./StdUtils.sol"; 23 | import {Vm} from "./Vm.sol"; 24 | 25 | // 📦 BOILERPLATE 26 | import {TestBase} from "./Base.sol"; 27 | import {DSTest} from "ds-test/test.sol"; 28 | 29 | // ⭐️ TEST 30 | abstract contract Test is TestBase, DSTest, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils { 31 | // Note: IS_TEST() must return true. 32 | // Note: Must have failure system, https://github.com/dapphub/ds-test/blob/cd98eff28324bfac652e63a239a60632a761790b/src/test.sol#L39-L76. 33 | } 34 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | interface IERC165 { 5 | /// @notice Query if a contract implements an interface 6 | /// @param interfaceID The interface identifier, as specified in ERC-165 7 | /// @dev Interface identification is specified in ERC-165. This function 8 | /// uses less than 30,000 gas. 9 | /// @return `true` if the contract implements `interfaceID` and 10 | /// `interfaceID` is not 0xffffffff, `false` otherwise 11 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | /// @dev Interface of the ERC20 standard as defined in the EIP. 5 | /// @dev This includes the optional name, symbol, and decimals metadata. 6 | interface IERC20 { 7 | /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`). 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | 10 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value` 11 | /// is the new allowance. 12 | event Approval(address indexed owner, address indexed spender, uint256 value); 13 | 14 | /// @notice Returns the amount of tokens in existence. 15 | function totalSupply() external view returns (uint256); 16 | 17 | /// @notice Returns the amount of tokens owned by `account`. 18 | function balanceOf(address account) external view returns (uint256); 19 | 20 | /// @notice Moves `amount` tokens from the caller's account to `to`. 21 | function transfer(address to, uint256 amount) external returns (bool); 22 | 23 | /// @notice Returns the remaining number of tokens that `spender` is allowed 24 | /// to spend on behalf of `owner` 25 | function allowance(address owner, address spender) external view returns (uint256); 26 | 27 | /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens. 28 | /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 29 | function approve(address spender, uint256 amount) external returns (bool); 30 | 31 | /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism. 32 | /// `amount` is then deducted from the caller's allowance. 33 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 34 | 35 | /// @notice Returns the name of the token. 36 | function name() external view returns (string memory); 37 | 38 | /// @notice Returns the symbol of the token. 39 | function symbol() external view returns (string memory); 40 | 41 | /// @notice Returns the decimals places of the token. 42 | function decimals() external view returns (uint8); 43 | } 44 | -------------------------------------------------------------------------------- /lib/forge-std/src/interfaces/IMulticall3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IMulticall3 { 7 | struct Call { 8 | address target; 9 | bytes callData; 10 | } 11 | 12 | struct Call3 { 13 | address target; 14 | bool allowFailure; 15 | bytes callData; 16 | } 17 | 18 | struct Call3Value { 19 | address target; 20 | bool allowFailure; 21 | uint256 value; 22 | bytes callData; 23 | } 24 | 25 | struct Result { 26 | bool success; 27 | bytes returnData; 28 | } 29 | 30 | function aggregate(Call[] calldata calls) 31 | external 32 | payable 33 | returns (uint256 blockNumber, bytes[] memory returnData); 34 | 35 | function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData); 36 | 37 | function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData); 38 | 39 | function blockAndAggregate(Call[] calldata calls) 40 | external 41 | payable 42 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 43 | 44 | function getBasefee() external view returns (uint256 basefee); 45 | 46 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 47 | 48 | function getBlockNumber() external view returns (uint256 blockNumber); 49 | 50 | function getChainId() external view returns (uint256 chainid); 51 | 52 | function getCurrentBlockCoinbase() external view returns (address coinbase); 53 | 54 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 55 | 56 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 57 | 58 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 59 | 60 | function getEthBalance(address addr) external view returns (uint256 balance); 61 | 62 | function getLastBlockHash() external view returns (bytes32 blockHash); 63 | 64 | function tryAggregate(bool requireSuccess, Call[] calldata calls) 65 | external 66 | payable 67 | returns (Result[] memory returnData); 68 | 69 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) 70 | external 71 | payable 72 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 73 | } 74 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdError.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "../src/StdError.sol"; 5 | import "../src/Test.sol"; 6 | 7 | contract StdErrorsTest is Test { 8 | ErrorsTest test; 9 | 10 | function setUp() public { 11 | test = new ErrorsTest(); 12 | } 13 | 14 | function test_ExpectAssertion() public { 15 | vm.expectRevert(stdError.assertionError); 16 | test.assertionError(); 17 | } 18 | 19 | function test_ExpectArithmetic() public { 20 | vm.expectRevert(stdError.arithmeticError); 21 | test.arithmeticError(10); 22 | } 23 | 24 | function test_ExpectDiv() public { 25 | vm.expectRevert(stdError.divisionError); 26 | test.divError(0); 27 | } 28 | 29 | function test_ExpectMod() public { 30 | vm.expectRevert(stdError.divisionError); 31 | test.modError(0); 32 | } 33 | 34 | function test_ExpectEnum() public { 35 | vm.expectRevert(stdError.enumConversionError); 36 | test.enumConversion(1); 37 | } 38 | 39 | function test_ExpectEncodeStg() public { 40 | vm.expectRevert(stdError.encodeStorageError); 41 | test.encodeStgError(); 42 | } 43 | 44 | function test_ExpectPop() public { 45 | vm.expectRevert(stdError.popError); 46 | test.pop(); 47 | } 48 | 49 | function test_ExpectOOB() public { 50 | vm.expectRevert(stdError.indexOOBError); 51 | test.indexOOBError(1); 52 | } 53 | 54 | function test_ExpectMem() public { 55 | vm.expectRevert(stdError.memOverflowError); 56 | test.mem(); 57 | } 58 | 59 | function test_ExpectIntern() public { 60 | vm.expectRevert(stdError.zeroVarError); 61 | test.intern(); 62 | } 63 | } 64 | 65 | contract ErrorsTest { 66 | enum T { 67 | T1 68 | } 69 | 70 | uint256[] public someArr; 71 | bytes someBytes; 72 | 73 | function assertionError() public pure { 74 | assert(false); 75 | } 76 | 77 | function arithmeticError(uint256 a) public pure { 78 | a -= 100; 79 | } 80 | 81 | function divError(uint256 a) public pure { 82 | 100 / a; 83 | } 84 | 85 | function modError(uint256 a) public pure { 86 | 100 % a; 87 | } 88 | 89 | function enumConversion(uint256 a) public pure { 90 | T(a); 91 | } 92 | 93 | function encodeStgError() public { 94 | /// @solidity memory-safe-assembly 95 | assembly { 96 | sstore(someBytes.slot, 1) 97 | } 98 | keccak256(someBytes); 99 | } 100 | 101 | function pop() public { 102 | someArr.pop(); 103 | } 104 | 105 | function indexOOBError(uint256 a) public pure { 106 | uint256[] memory t = new uint256[](0); 107 | t[a]; 108 | } 109 | 110 | function mem() public pure { 111 | uint256 l = 2 ** 256 / 32; 112 | new uint256[](l); 113 | } 114 | 115 | function intern() public returns (uint256) { 116 | function(uint256) internal returns (uint256) x; 117 | x(2); 118 | return 7; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/forge-std/test/StdStyle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "../src/Test.sol"; 5 | 6 | contract StdStyleTest is Test { 7 | function test_StyleColor() public pure { 8 | console2.log(StdStyle.red("StdStyle.red String Test")); 9 | console2.log(StdStyle.red(uint256(10e18))); 10 | console2.log(StdStyle.red(int256(-10e18))); 11 | console2.log(StdStyle.red(true)); 12 | console2.log(StdStyle.red(address(0))); 13 | console2.log(StdStyle.redBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 14 | console2.log(StdStyle.redBytes32("StdStyle.redBytes32")); 15 | console2.log(StdStyle.green("StdStyle.green String Test")); 16 | console2.log(StdStyle.green(uint256(10e18))); 17 | console2.log(StdStyle.green(int256(-10e18))); 18 | console2.log(StdStyle.green(true)); 19 | console2.log(StdStyle.green(address(0))); 20 | console2.log(StdStyle.greenBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 21 | console2.log(StdStyle.greenBytes32("StdStyle.greenBytes32")); 22 | console2.log(StdStyle.yellow("StdStyle.yellow String Test")); 23 | console2.log(StdStyle.yellow(uint256(10e18))); 24 | console2.log(StdStyle.yellow(int256(-10e18))); 25 | console2.log(StdStyle.yellow(true)); 26 | console2.log(StdStyle.yellow(address(0))); 27 | console2.log(StdStyle.yellowBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 28 | console2.log(StdStyle.yellowBytes32("StdStyle.yellowBytes32")); 29 | console2.log(StdStyle.blue("StdStyle.blue String Test")); 30 | console2.log(StdStyle.blue(uint256(10e18))); 31 | console2.log(StdStyle.blue(int256(-10e18))); 32 | console2.log(StdStyle.blue(true)); 33 | console2.log(StdStyle.blue(address(0))); 34 | console2.log(StdStyle.blueBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 35 | console2.log(StdStyle.blueBytes32("StdStyle.blueBytes32")); 36 | console2.log(StdStyle.magenta("StdStyle.magenta String Test")); 37 | console2.log(StdStyle.magenta(uint256(10e18))); 38 | console2.log(StdStyle.magenta(int256(-10e18))); 39 | console2.log(StdStyle.magenta(true)); 40 | console2.log(StdStyle.magenta(address(0))); 41 | console2.log(StdStyle.magentaBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 42 | console2.log(StdStyle.magentaBytes32("StdStyle.magentaBytes32")); 43 | console2.log(StdStyle.cyan("StdStyle.cyan String Test")); 44 | console2.log(StdStyle.cyan(uint256(10e18))); 45 | console2.log(StdStyle.cyan(int256(-10e18))); 46 | console2.log(StdStyle.cyan(true)); 47 | console2.log(StdStyle.cyan(address(0))); 48 | console2.log(StdStyle.cyanBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 49 | console2.log(StdStyle.cyanBytes32("StdStyle.cyanBytes32")); 50 | } 51 | 52 | function test_StyleFontWeight() public pure { 53 | console2.log(StdStyle.bold("StdStyle.bold String Test")); 54 | console2.log(StdStyle.bold(uint256(10e18))); 55 | console2.log(StdStyle.bold(int256(-10e18))); 56 | console2.log(StdStyle.bold(address(0))); 57 | console2.log(StdStyle.bold(true)); 58 | console2.log(StdStyle.boldBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 59 | console2.log(StdStyle.boldBytes32("StdStyle.boldBytes32")); 60 | console2.log(StdStyle.dim("StdStyle.dim String Test")); 61 | console2.log(StdStyle.dim(uint256(10e18))); 62 | console2.log(StdStyle.dim(int256(-10e18))); 63 | console2.log(StdStyle.dim(address(0))); 64 | console2.log(StdStyle.dim(true)); 65 | console2.log(StdStyle.dimBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 66 | console2.log(StdStyle.dimBytes32("StdStyle.dimBytes32")); 67 | console2.log(StdStyle.italic("StdStyle.italic String Test")); 68 | console2.log(StdStyle.italic(uint256(10e18))); 69 | console2.log(StdStyle.italic(int256(-10e18))); 70 | console2.log(StdStyle.italic(address(0))); 71 | console2.log(StdStyle.italic(true)); 72 | console2.log(StdStyle.italicBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 73 | console2.log(StdStyle.italicBytes32("StdStyle.italicBytes32")); 74 | console2.log(StdStyle.underline("StdStyle.underline String Test")); 75 | console2.log(StdStyle.underline(uint256(10e18))); 76 | console2.log(StdStyle.underline(int256(-10e18))); 77 | console2.log(StdStyle.underline(address(0))); 78 | console2.log(StdStyle.underline(true)); 79 | console2.log(StdStyle.underlineBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 80 | console2.log(StdStyle.underlineBytes32("StdStyle.underlineBytes32")); 81 | console2.log(StdStyle.inverse("StdStyle.inverse String Test")); 82 | console2.log(StdStyle.inverse(uint256(10e18))); 83 | console2.log(StdStyle.inverse(int256(-10e18))); 84 | console2.log(StdStyle.inverse(address(0))); 85 | console2.log(StdStyle.inverse(true)); 86 | console2.log(StdStyle.inverseBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D")); 87 | console2.log(StdStyle.inverseBytes32("StdStyle.inverseBytes32")); 88 | } 89 | 90 | function test_StyleCombined() public pure { 91 | console2.log(StdStyle.red(StdStyle.bold("Red Bold String Test"))); 92 | console2.log(StdStyle.green(StdStyle.dim(uint256(10e18)))); 93 | console2.log(StdStyle.yellow(StdStyle.italic(int256(-10e18)))); 94 | console2.log(StdStyle.blue(StdStyle.underline(address(0)))); 95 | console2.log(StdStyle.magenta(StdStyle.inverse(true))); 96 | } 97 | 98 | function test_StyleCustom() public pure { 99 | console2.log(h1("Custom Style 1")); 100 | console2.log(h2("Custom Style 2")); 101 | } 102 | 103 | function h1(string memory a) private pure returns (string memory) { 104 | return StdStyle.cyan(StdStyle.inverse(StdStyle.bold(a))); 105 | } 106 | 107 | function h2(string memory a) private pure returns (string memory) { 108 | return StdStyle.magenta(StdStyle.bold(StdStyle.underline(a))); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/forge-std/test/Vm.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import {Test} from "../src/Test.sol"; 5 | import {Vm, VmSafe} from "../src/Vm.sol"; 6 | 7 | contract VmTest is Test { 8 | // This test ensures that functions are never accidentally removed from a Vm interface, or 9 | // inadvertently moved between Vm and VmSafe. This test must be updated each time a function is 10 | // added to or removed from Vm or VmSafe. 11 | function test_interfaceId() public { 12 | assertEq(type(VmSafe).interfaceId, bytes4(0x01ec102d), "VmSafe"); 13 | assertEq(type(Vm).interfaceId, bytes4(0xa63eed6b), "Vm"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationScript.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScript is Script {} 11 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationScriptBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Script.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationScriptBase is ScriptBase {} 11 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTest is Test {} 11 | -------------------------------------------------------------------------------- /lib/forge-std/test/compilation/CompilationTestBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../../src/Test.sol"; 7 | 8 | // The purpose of this contract is to benchmark compilation time to avoid accidentally introducing 9 | // a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 10 | contract CompilationTestBase is TestBase {} 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tapiocaz", 3 | "scripts": { 4 | "clean": "npx hardhat --config hardhat.export.ts clean", 5 | "compile": "npx hardhat --config hardhat.export.ts compile", 6 | "typechain": "npx hardhat --config hardhat.export.ts typechain", 7 | "coverage": "npm run clean && npm run typechain && hh --config hardhat.export.ts coverage", 8 | "gen:sdk": "npx hardhat exportSDK --network", 9 | "deploy": "npx hardhat deploy --network", 10 | "deploy:tapiocaWrapper": "hardhat deploy --tags TapiocaWrapper --network", 11 | "deploy:tapiocaOft": "hardhat deploy --tags TapiocaOFT --network", 12 | "deploy:rebalancing": "hardhat deploy --tags Rebalancing --network", 13 | "contracts_size": "yarn compile && NODE_ENV=hardhat hardhat size-contracts", 14 | "list": "npx hardhat listDeploy --network", 15 | "erc20:mock": "npx hardhat run ./scripts/deployERC20.ts --network", 16 | "size": "npx hardhat size-contracts", 17 | "test": "npx hardhat test", 18 | "prepare": "husky install", 19 | "lint:ts": "eslint . --fix", 20 | "lint:sol": "prettier --write \"./contracts/**/*.sol\" && solhint \"./contracts/**/*.sol\" --fix", 21 | "lint": "npm run lint:ts && npm run lint:sol" 22 | }, 23 | "devDependencies": { 24 | "@boringcrypto/boring-solidity": "https://github.com/boringcrypto/BoringSolidity.git", 25 | "@ethersproject/abi": "^5.7.0", 26 | "@ethersproject/providers": "^5.7.0", 27 | "@layerzerolabs/lz-evm-messagelib-v2": "2.3.6", 28 | "@layerzerolabs/lz-evm-oapp-v2": "2.3.6", 29 | "@layerzerolabs/lz-evm-protocol-v2": "2.3.6", 30 | "@layerzerolabs/lz-evm-v1-0.7": "2.3.6", 31 | "@layerzerolabs/solidity-bytes-utils": "^0.8.0", 32 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.3", 33 | "@nomicfoundation/hardhat-foundry": "^1.1.1", 34 | "@nomicfoundation/hardhat-network-helpers": "^1.0.4", 35 | "@nomiclabs/hardhat-ethers": "^2.1.1", 36 | "@openzeppelin/contracts": "^4.9.5", 37 | "@openzeppelin/contracts-upgradeable": "^4.8.1", 38 | "@rari-capital/solmate": "^6.4.0", 39 | "@swc/core": "^1.6.3", 40 | "@typechain/ethers-v5": "^10.1.0", 41 | "@typechain/hardhat": "^6.1.2", 42 | "@types/chai": "^4.2.22", 43 | "@types/lodash": "^4.14.185", 44 | "@types/mocha": "^9.0.0", 45 | "@types/node": "^16.11.11", 46 | "@typescript-eslint/eslint-plugin": "^5.11.0", 47 | "@typescript-eslint/parser": "^5.11.0", 48 | "chai": "^4.3.6", 49 | "dotenv": "^10.0.0", 50 | "eslint": "^7.32.0", 51 | "eslint-config-prettier": "^8.3.0", 52 | "eslint-plugin-prettier": "^4.0.0", 53 | "ethers": "^5.7.0", 54 | "hardhat": "^2.13.0", 55 | "hardhat-contract-sizer": "^2.5.1", 56 | "hardhat-deploy": "^0.11.45", 57 | "hardhat-gas-reporter": "^1.0.8", 58 | "hardhat-tracer": "^2.5.0", 59 | "http-server": "^14.0.0", 60 | "husky": "^8.0.3", 61 | "mocha": "^9.1.3", 62 | "prb-math": "^2.4.1", 63 | "prettier": "^2.5.1", 64 | "prettier-plugin-solidity": "^1.0.0-beta.19", 65 | "solc": "^0.8.4", 66 | "solhint": "^3.4.1", 67 | "solidity-coverage": "^0.8.1", 68 | "ts-node": "^10.7.0", 69 | "tsconfig-paths": "^4.2.0", 70 | "typechain": "^8.1.0", 71 | "typescript": "^4.4.3", 72 | "write-json-file": "4.3.0" 73 | }, 74 | "dependencies": { 75 | "@nomicfoundation/hardhat-verify": "^2.0.8", 76 | "@primitivefi/hardhat-dodoc": "^0.2.3", 77 | "@types/inquirer": "^9.0.3", 78 | "@types/uuid": "^9.0.1", 79 | "inquirer": "^8.2.5", 80 | "load-json-file": "^7.0.1", 81 | "lodash": "^4.17.21", 82 | "tapioca-sdk": "^1.8.26", 83 | "uuid": "^9.0.0" 84 | }, 85 | "resolutions": { 86 | "@ethersproject/providers": "npm:ethersproject-providers-arbitrum-hotfix@5.7.10" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /script/Counter.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {Script, console} from "forge-std/Script.sol"; 5 | 6 | contract CounterScript is Script { 7 | function setUp() public {} 8 | 9 | function run() public { 10 | vm.broadcast(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/utils.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; 2 | import { BigNumberish, BytesLike, ethers, Signature, Wallet } from 'ethers'; 3 | import { splitSignature } from 'ethers/lib/utils'; 4 | import { existsSync, link, readFileSync, writeFileSync } from 'fs'; 5 | import { Deployment } from 'hardhat-deploy/types'; 6 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 7 | import _ from 'lodash'; 8 | import SDK from 'tapioca-sdk'; 9 | import { 10 | LZEndpointMock__factory, 11 | YieldBoxMock__factory, 12 | } from '@tapioca-sdk/typechain/tapioca-mocks'; 13 | import config from '../hardhat.export'; 14 | 15 | export const BN = (n: any) => ethers.BigNumber.from(n); 16 | export const generateSalt = () => ethers.utils.randomBytes(32); 17 | 18 | export const useNetwork = async ( 19 | hre: HardhatRuntimeEnvironment, 20 | network: string, 21 | ) => { 22 | const pk = process.env.PRIVATE_KEY; 23 | if (pk === undefined) throw new Error('[-] PRIVATE_KEY not set'); 24 | const info: any = config.networks?.[network]; 25 | if (!info) 26 | throw new Error(`[-] Hardhat network config not found for ${network} `); 27 | 28 | const provider = new hre.ethers.providers.JsonRpcProvider( 29 | { url: info.url }, 30 | { chainId: info.chainId, name: `rpc-${info.chainId}` }, 31 | ); 32 | 33 | return new hre.ethers.Wallet(pk, provider); 34 | }; 35 | 36 | export const useUtils = ( 37 | hre: HardhatRuntimeEnvironment, 38 | signer: SignerWithAddress, 39 | ) => { 40 | const newEOA = () => 41 | new hre.ethers.Wallet( 42 | hre.ethers.Wallet.createRandom().privateKey, 43 | hre.ethers.provider, 44 | ); 45 | 46 | const deployYieldBoxMock = async () => { 47 | const YieldBoxMock = new YieldBoxMock__factory(signer); 48 | return await YieldBoxMock.deploy(); 49 | }; 50 | 51 | return { 52 | newEOA, 53 | deployYieldBoxMock, 54 | }; 55 | }; 56 | 57 | export async function getERC20PermitSignature( 58 | wallet: Wallet | SignerWithAddress, 59 | token: ERC20Permit, 60 | spender: string, 61 | value: BigNumberish = ethers.constants.MaxUint256, 62 | deadline = ethers.constants.MaxUint256, 63 | permitConfig?: { 64 | nonce?: BigNumberish; 65 | name?: string; 66 | chainId?: number; 67 | version?: string; 68 | }, 69 | ): Promise { 70 | const [nonce, name, version, chainId] = await Promise.all([ 71 | permitConfig?.nonce ?? token.nonces(wallet.address), 72 | permitConfig?.name ?? token.name(), 73 | permitConfig?.version ?? '1', 74 | permitConfig?.chainId ?? wallet.getChainId(), 75 | ]); 76 | 77 | return splitSignature( 78 | await wallet._signTypedData( 79 | { 80 | name, 81 | version, 82 | chainId, 83 | verifyingContract: token.address, 84 | }, 85 | { 86 | Permit: [ 87 | { 88 | name: 'owner', 89 | type: 'address', 90 | }, 91 | { 92 | name: 'spender', 93 | type: 'address', 94 | }, 95 | { 96 | name: 'value', 97 | type: 'uint256', 98 | }, 99 | { 100 | name: 'nonce', 101 | type: 'uint256', 102 | }, 103 | { 104 | name: 'deadline', 105 | type: 'uint256', 106 | }, 107 | ], 108 | }, 109 | { 110 | owner: wallet.address, 111 | spender, 112 | value, 113 | nonce, 114 | deadline, 115 | }, 116 | ), 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /tasks/constants.ts: -------------------------------------------------------------------------------- 1 | // TODO fixme use the SDK EChainID 2 | enum EChainID { 3 | // Mainnets 4 | MAINNET = '1', 5 | BSC = '56', 6 | AVALANCHE = '43114', 7 | POLYGON = '137', 8 | FANTOM = '250', 9 | ARBITRUM = '42161', 10 | OPTIMISM = '10', 11 | HARMONY = '1666600000', 12 | // Testnets 13 | GOERLI = '5', 14 | BSC_TESTNET = '97', 15 | FUJI_AVALANCHE = '43113', 16 | MUMBAI_POLYGON = '80001', 17 | FANTOM_TESTNET = '4002', 18 | ARBITRUM_GOERLI = '421613', 19 | OPTIMISM_GOERLI = '420', 20 | HARMONY_TESTNET = '1666700000', 21 | } 22 | 23 | // TODO - add all chains (Eth, Arb, OP, and their testnets), double check the values 24 | export const STARGATE_ROUTERS: { 25 | [key in EChainID]?: { 26 | stargateChainId: string; 27 | router: string; 28 | routerETH: string; 29 | }; 30 | } = { 31 | // Mainnet 32 | [EChainID.MAINNET]: { 33 | stargateChainId: '101', 34 | routerETH: '0x150f94b44927f078737562f0fcf3c95c01cc2376', 35 | router: '0x8731d54E9D02c286767d56ac03e8037C07e01e98', 36 | }, 37 | [EChainID.ARBITRUM]: { 38 | stargateChainId: '110', 39 | routerETH: '0xbf22f0f184bCcbeA268dF387a49fF5238dD23E40', 40 | router: '0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614', 41 | }, 42 | [EChainID.BSC]: { 43 | stargateChainId: '102', 44 | routerETH: '0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8', 45 | router: '0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8', 46 | }, 47 | [EChainID.AVALANCHE]: { 48 | stargateChainId: '106', 49 | routerETH: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd', 50 | router: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd', 51 | }, 52 | [EChainID.POLYGON]: { 53 | stargateChainId: '109', 54 | routerETH: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd', 55 | router: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd', 56 | }, 57 | [EChainID.OPTIMISM]: { 58 | stargateChainId: '111', 59 | routerETH: '0xB49c4e680174E331CB0A7fF3Ab58afC9738d5F8b', 60 | router: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b', 61 | }, 62 | [EChainID.FANTOM]: { 63 | stargateChainId: '112', 64 | routerETH: '0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6', 65 | router: '0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6', 66 | }, 67 | // Testnet 68 | [EChainID.GOERLI]: { 69 | stargateChainId: '10121', 70 | routerETH: '0xdb19Ad528F4649692B92586828346beF9e4a3532', 71 | router: '0x7612aE2a34E5A363E137De748801FB4c86499152', 72 | }, 73 | [EChainID.ARBITRUM_GOERLI]: { 74 | stargateChainId: '10143', 75 | routerETH: '0x7612aE2a34E5A363E137De748801FB4c86499152', 76 | router: '0xb850873f4c993Ac2405A1AdD71F6ca5D4d4d6b4f', 77 | }, 78 | [EChainID.FUJI_AVALANCHE]: { 79 | stargateChainId: '10106', 80 | routerETH: '0x7612aE2a34E5A363E137De748801FB4c86499152', 81 | router: '0xb850873f4c993Ac2405A1AdD71F6ca5D4d4d6b4f', 82 | }, 83 | [EChainID.MUMBAI_POLYGON]: { 84 | stargateChainId: '10109', 85 | routerETH: '0x7612aE2a34E5A363E137De748801FB4c86499152', 86 | router: '0xb850873f4c993Ac2405A1AdD71F6ca5D4d4d6b4f', 87 | }, 88 | }; 89 | -------------------------------------------------------------------------------- /tasks/deploy/2-deployFinal__task.ts: -------------------------------------------------------------------------------- 1 | import * as TAPIOCA_BAR_CONFIG from '@tapioca-bar/config'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { 4 | loadGlobalContract, 5 | loadLocalContract, 6 | setLzPeer__task, 7 | } from 'tapioca-sdk'; 8 | import { TTapiocaDeployerVmPass } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 9 | import { DEPLOYMENT_NAMES } from './DEPLOY_CONFIG'; 10 | import { TToftDeployerTaskArgs, VMAddToft } from './toftDeployer__task'; 11 | import { TAPIOCA_PROJECTS_NAME } from '@tapioca-sdk/api/config'; 12 | import { findGlobalDeployment } from '@tapioca-sdk/api/db'; 13 | 14 | /** 15 | * @notice Should be called after Bar post lbp side chain deployment 16 | * @notice Should be called on Mainnet as main chain an Arbitrum as side chain 17 | * 18 | * Deploys: Arb, Eth 19 | * - Tapioca OFT SGL DAI Market 20 | * 21 | * Post deploy: Arb, Eth 22 | * - LZPeer link the SGL DAI Market OFT xChain 23 | */ 24 | export const deployFinal__task = async ( 25 | _taskArgs: TToftDeployerTaskArgs & { sdaiMarketChainName: string }, 26 | hre: HardhatRuntimeEnvironment, 27 | ) => { 28 | await hre.SDK.DeployerVM.tapiocaDeployTask( 29 | _taskArgs, 30 | { 31 | hre, 32 | // // Static simulation needs to be false, constructor relies on external call. We're using 0x00 replacement with DeployerVM, which creates a false positive for static simulation. 33 | staticSimulation: false, 34 | // bytecodeSizeLimit: 80_000, 35 | // overrideOptions: { 36 | // gasLimit: 10_000_000, 37 | // }, 38 | }, 39 | tapiocaDeployTask, 40 | tapiocaPostDeployTask, 41 | ); 42 | }; 43 | 44 | async function tapiocaPostDeployTask( 45 | params: TTapiocaDeployerVmPass<{ sdaiMarketChainName: string }>, 46 | ) { 47 | const { hre, taskArgs, chainInfo } = params; 48 | const { tag } = taskArgs; 49 | 50 | // await setLzPeer__task( 51 | // { tag, targetName: DEPLOYMENT_NAMES.T_SGL_SDAI_MARKET }, 52 | // hre, 53 | // ); 54 | } 55 | 56 | async function tapiocaDeployTask( 57 | params: TTapiocaDeployerVmPass<{ sdaiMarketChainName: string }>, 58 | ) { 59 | const { 60 | hre, 61 | VM, 62 | tapiocaMulticallAddr, 63 | taskArgs, 64 | isTestnet, 65 | chainInfo, 66 | isHostChain, 67 | isSideChain, 68 | } = params; 69 | const { tag, sdaiMarketChainName } = taskArgs; 70 | 71 | /** 72 | *SGL GLP Market OFT on Host chain 73 | */ 74 | if (isHostChain) { 75 | const sglGlpMarket = loadGlobalContract( 76 | hre, 77 | TAPIOCA_PROJECTS_NAME.TapiocaBar, 78 | chainInfo.chainId, 79 | TAPIOCA_BAR_CONFIG.DEPLOYMENT_NAMES.SGL_S_GLP_MARKET, 80 | tag, 81 | ).address; 82 | 83 | const VMAddToftWithArgs = async (args: TToftDeployerTaskArgs) => 84 | await VMAddToft({ 85 | chainInfo, 86 | hre, 87 | isTestnet, 88 | tapiocaMulticallAddr, 89 | VM, 90 | isHostChain, 91 | isSideChain, 92 | taskArgs: args, 93 | }); 94 | 95 | await VMAddToftWithArgs({ 96 | ...taskArgs, 97 | target: 'toft', 98 | deploymentName: DEPLOYMENT_NAMES.T_SGL_GLP_MARKET, 99 | erc20: sglGlpMarket, 100 | name: 'Tapioca OFT SGL GLP Market', 101 | symbol: DEPLOYMENT_NAMES.T_SGL_GLP_MARKET, 102 | noModuleDeploy: false, 103 | hostEid: chainInfo.lzChainId, 104 | }); 105 | } 106 | 107 | /** 108 | * sDaiMarketChain from Side chain 109 | */ 110 | const sdaiMarketChain = hre.SDK.utils.getChainBy( 111 | 'name', 112 | sdaiMarketChainName, 113 | ); 114 | 115 | // const sDaiSglMarket = loadGlobalContract( 116 | // hre, 117 | // TAPIOCA_PROJECTS_NAME.TapiocaBar, 118 | // sdaiMarketChain.chainId, 119 | // TAPIOCA_BAR_CONFIG.DEPLOYMENT_NAMES.SGL_S_DAI_MARKET, 120 | // tag, 121 | // ).address; 122 | 123 | const VMAddToftWithArgs = async (args: TToftDeployerTaskArgs) => 124 | await VMAddToft({ 125 | chainInfo, 126 | hre, 127 | isTestnet, 128 | tapiocaMulticallAddr, 129 | VM, 130 | isHostChain, 131 | isSideChain, 132 | taskArgs: args, 133 | }); 134 | 135 | // await VMAddToftWithArgs({ 136 | // ...taskArgs, 137 | // target: 'toft', 138 | // deploymentName: DEPLOYMENT_NAMES.T_SGL_SDAI_MARKET, 139 | // erc20: sDaiSglMarket, 140 | // name: 'Tapioca OFT SGL DAI Market', 141 | // symbol: DEPLOYMENT_NAMES.T_SGL_SDAI_MARKET, 142 | // noModuleDeploy: false, 143 | // hostEid: sdaiMarketChain.lzChainId, 144 | // }); 145 | } 146 | -------------------------------------------------------------------------------- /tasks/deploy/DEPLOY_CONFIG.ts: -------------------------------------------------------------------------------- 1 | import { EChainID } from '@tapioca-sdk/api/config'; 2 | 3 | // Name of the contract deployments to be used in the deployment scripts and saved in the deployments file 4 | export const DEPLOYMENT_NAMES = { 5 | // MODULES 6 | TOFT_EXT_EXEC: 'TOFT_EXT_EXEC', 7 | TOFT_SENDER_MODULE: 'TOFT_SENDER_MODULE', 8 | TOFT_RECEIVER_MODULE: 'TOFT_RECEIVER_MODULE', 9 | TOFT_MARKET_RECEIVER_MODULE: 'TOFT_MARKET_RECEIVER_MODULE', 10 | TOFT_OPTIONS_RECEIVER_MODULE: 'TOFT_OPTIONS_RECEIVER_MODULE', 11 | TOFT_GENERIC_RECEIVER_MODULE: 'TOFT_GENERIC_RECEIVER_MODULE', 12 | TOFT_VAULT: 'TOFT_VAULT', 13 | TOFT_BALANCER: 'TOFT_BALANCER', 14 | TOFT_HELPER: 'TOFT_HELPER', 15 | // Meta Market TOFT 16 | T_SGL_SDAI_MARKET: 'T_SGL_SDAI_MARKET', 17 | T_SGL_GLP_MARKET: 'T_SGL_GLP_MARKET', 18 | // tETH 19 | tETH: 'tETH', 20 | // mTOFT 21 | mtETH: 'mtETH', 22 | // tOFT BB 23 | tWSTETH: 'tWSTETH', 24 | tRETH: 'tRETH', 25 | // tOFT SGL 26 | tsDAI: 'tsDAI', 27 | tsGLP: 'tsGLP', 28 | }; 29 | 30 | type TPostLbp = { 31 | [key in EChainID]?: { 32 | WETH: string; 33 | wstETH: string; 34 | reth: string; 35 | sDAI: string; 36 | sGLP: string; 37 | }; 38 | }; 39 | 40 | const POST_LBP: TPostLbp = { 41 | [EChainID.ARBITRUM]: { 42 | WETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', 43 | wstETH: '0x5979D7b546E38E414F7E9822514be443A4800529', 44 | reth: '0xEC70Dcb4A1EFa46b8F2D97C310C9c4790ba5ffA8', 45 | sDAI: '', 46 | sGLP: '0x5402B5F40310bDED796c7D0F3FF6683f5C0cFfdf', 47 | }, 48 | [EChainID.MAINNET]: { 49 | WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 50 | wstETH: '', 51 | reth: '', 52 | sDAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // It's dai not sDai 53 | sGLP: '', 54 | }, 55 | [EChainID.ARBITRUM_SEPOLIA]: { 56 | WETH: '0x2EAe4fbc552fE35C1D3Df2B546032409bb0E431E', 57 | wstETH: '0x568567fb156cDf083FA9e4A672fB462aA49673e9', 58 | reth: '0x518746126A545cC7F31EeD92bF2b04eb99eD103B', 59 | sDAI: '', 60 | sGLP: '0x1B460E311753fDB46451EF3d11d7B9eE5542b369', 61 | }, 62 | [EChainID.SEPOLIA]: { 63 | WETH: '0xD8a79b479b0c47675E3882A1DAA494b6775CE227', 64 | wstETH: '', 65 | reth: '', 66 | sDAI: '0xC6EA2075314a58cf74DE8430b24714E600A21Dd8', 67 | sGLP: '', 68 | }, 69 | [EChainID.OPTIMISM_SEPOLIA]: { 70 | WETH: '0x4fB538Ed1a085200bD08F66083B72c0bfEb29112', 71 | wstETH: '', 72 | reth: '', 73 | sDAI: '0x37359B8bfbFAE28E513EE31a2A94A9Ec60668d90', 74 | sGLP: '', 75 | }, 76 | [EChainID.FUJI_AVALANCHE]: { 77 | WETH: '0x4404EF158716dfad1c2BEffE9c7c8Fa261684544', 78 | wstETH: '', 79 | reth: '', 80 | sDAI: '0xed18DBCb2810E4178c23668794198C81B0668b23', 81 | sGLP: '', 82 | }, 83 | }; 84 | POST_LBP['31337' as EChainID] = POST_LBP[EChainID.ARBITRUM]; // Copy from Arbitrum 85 | 86 | type TMisc = { 87 | [key in EChainID]?: { 88 | STARGATE_ROUTER_ETH: string; 89 | STARGATE_ROUTER: string; 90 | STARGATE_FACTORY: string; 91 | }; 92 | }; 93 | const MISC: TMisc = { 94 | [EChainID.MAINNET]: { 95 | STARGATE_ROUTER_ETH: '0xb1b2eeF380f21747944f46d28f683cD1FBB4d03c', 96 | STARGATE_ROUTER: '0x8731d54E9D02c286767d56ac03e8037C07e01e98', 97 | STARGATE_FACTORY: '0x06D538690AF257Da524f25D0CD52fD85b1c2173E', 98 | }, 99 | [EChainID.ARBITRUM]: { 100 | STARGATE_ROUTER_ETH: '0xb1b2eeF380f21747944f46d28f683cD1FBB4d03c', 101 | STARGATE_ROUTER: '0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614', 102 | STARGATE_FACTORY: '0x55bDb4164D28FBaF0898e0eF14a589ac09Ac9970', 103 | }, 104 | [EChainID.ARBITRUM_SEPOLIA]: { 105 | STARGATE_ROUTER_ETH: '0x771A4f8a880b499A40c8fF53c7925798E0f2E594', // Change to mock? 106 | STARGATE_ROUTER: '0x2a4C2F5ffB0E0F2dcB3f9EBBd442B8F77ECDB9Cc', 107 | STARGATE_FACTORY: '0x7eEB77fFD369Da207b34FAcD202698dc733192a5', 108 | }, 109 | [EChainID.SEPOLIA]: { 110 | STARGATE_ROUTER_ETH: '0x676Fa8D37B948236aAcE03A0b34fc0Bc37FABA8D', // Change to mock? 111 | STARGATE_ROUTER: '0x2836045A50744FB50D3d04a9C8D18aD7B5012102', 112 | STARGATE_FACTORY: '0xA296710670e16BA7791E919ddB3704c61f366118', 113 | }, 114 | [EChainID.OPTIMISM_SEPOLIA]: { 115 | STARGATE_ROUTER_ETH: '0xA251Af9e97aadE0F0E650525Ad531a7a534c335E', // Change to mock? 116 | STARGATE_ROUTER: '0xa2dfFdDc372C6aeC3a8e79aAfa3953e8Bc956D63', 117 | STARGATE_FACTORY: '0xDb6E40E8fACF1a76866ff067D57539c8EE1bfC16', 118 | }, 119 | // Wrong ones, copied from OpSep 120 | [EChainID.FUJI_AVALANCHE]: { 121 | STARGATE_ROUTER_ETH: '0xA251Af9e97aadE0F0E650525Ad531a7a534c335E', // Change to mock? 122 | STARGATE_ROUTER: '0xa2dfFdDc372C6aeC3a8e79aAfa3953e8Bc956D63', 123 | STARGATE_FACTORY: '0xDb6E40E8fACF1a76866ff067D57539c8EE1bfC16', 124 | }, 125 | }; 126 | 127 | export const DEPLOY_CONFIG = { 128 | POST_LBP, 129 | MISC, 130 | }; 131 | -------------------------------------------------------------------------------- /tasks/deploy/deprecated/deployBalancer.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { STARGATE_ROUTERS } from '../../constants'; 3 | import { loadVM } from '../../utils'; 4 | 5 | export const deployBalancer__task = async ( 6 | taskArgs: { overwrite?: boolean }, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | console.log('[+] Deploying Balancer...'); 10 | 11 | const { overwrite } = taskArgs; 12 | 13 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 14 | 15 | const signer = (await hre.ethers.getSigners())[0]; 16 | const chainId = String(hre.network.config.chainId); 17 | 18 | // Check if already deployed 19 | const prevDeployment = hre.SDK.db.getLocalDeployment( 20 | chainId, 21 | 'Balancer', 22 | tag, 23 | ); 24 | if (prevDeployment && !overwrite) { 25 | console.log( 26 | `[-] Balancer already deployed on ${hre.network.name} at ${prevDeployment.address}`, 27 | ); 28 | return; 29 | } 30 | 31 | // Check if stargate router exists 32 | const stargateObj = 33 | STARGATE_ROUTERS[chainId as keyof typeof STARGATE_ROUTERS]; 34 | if (!stargateObj) { 35 | throw new Error(`[-] No stargate router found for chainId ${chainId}`); 36 | } 37 | 38 | const balancer = await hre.ethers.getContractFactory('Balancer'); 39 | 40 | const deployerVM = await loadVM(hre, tag); 41 | 42 | deployerVM.add({ 43 | contract: balancer, 44 | args: [stargateObj.routerETH, stargateObj.router, signer.address], 45 | deploymentName: 'Balancer', 46 | }); 47 | await deployerVM.execute(3); 48 | deployerVM.save(); 49 | await deployerVM.verify(); 50 | }; 51 | -------------------------------------------------------------------------------- /tasks/deploy/testnet/deployTOFTMinter.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import inquirer from 'inquirer'; 3 | import { TOFTMinter } from '../../../typechain'; 4 | 5 | export const askForChains = async (hre: HardhatRuntimeEnvironment) => { 6 | const supportedChains = hre.SDK.utils.getSupportedChains(); 7 | const choices = supportedChains.map((e) => e.name); 8 | 9 | const { chains }: { chains: typeof choices } = await inquirer.prompt({ 10 | type: 'checkbox', 11 | name: 'chains', 12 | message: 'Choose chains to deploy to', 13 | choices, 14 | }); 15 | 16 | return supportedChains.filter((e) => chains.includes(e.name)); 17 | }; 18 | export const deployTOFTMinter__task = async ( 19 | taskArgs: { overwrite?: boolean }, 20 | hre: HardhatRuntimeEnvironment, 21 | ) => { 22 | console.log('[+] Deploying TOFTMinter [+]'); 23 | 24 | const tempConf = hre.config.SDK.project; 25 | hre.config.SDK.project = 'generic'; 26 | 27 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 28 | const { overwrite } = taskArgs; 29 | 30 | const chains = await askForChains(hre); 31 | 32 | for (const chain of chains) { 33 | console.log(`[+] Deploying on ${chain.name}`); 34 | const toftMinterFactory = await hre.ethers.getContractFactory( 35 | 'TOFTMinter', 36 | ); 37 | const signer = await hre.SDK.hardhatUtils.useNetwork(hre, chain.name); 38 | 39 | const OFTs = hre.SDK.db 40 | .loadLocalDeployment(tag, chain.chainId) 41 | .filter((e) => e.meta['isToftHost']); 42 | 43 | // Deploy TOFTMinter for each TOFT on the chain 44 | for (const OFT of OFTs) { 45 | console.log(`\t+for ${OFT.name}`); 46 | 47 | // Check if already deployed 48 | const prevDeployment = hre.SDK.db.getLocalDeployment( 49 | chain.chainId, 50 | `TOFTMinter_${OFT.name}`, 51 | tag, 52 | ); 53 | if (prevDeployment && !overwrite) { 54 | console.log( 55 | `[-] TOFTMinter already deployed on ${chain.name} at ${prevDeployment.address}, skipping`, 56 | ); 57 | continue; 58 | } 59 | 60 | const toftMinter = await toftMinterFactory 61 | .connect(signer) 62 | .deploy(OFT.address); 63 | await toftMinter.deployed(); 64 | 65 | // Save deployment 66 | hre.SDK.db.saveLocally({ 67 | [chain.chainId]: [ 68 | { 69 | name: `TOFTMinter_${OFT.name}`, 70 | address: toftMinter.address, 71 | meta: { 72 | isTOFTMinter: true, 73 | args: [OFT.address], 74 | }, 75 | }, 76 | ], 77 | }); 78 | // Verify if same chain 79 | if (String(chain.chainId) === String(hre.network.config.chainId)) { 80 | console.log('\t\t+Verifying TOFTMinter'); 81 | await hre.run('verify:verify', { 82 | address: toftMinter.address, 83 | constructorArguments: [OFT.address], 84 | noCompile: true, 85 | }); 86 | } 87 | } 88 | } 89 | 90 | console.log('[+] Done 🤝 [+]'); 91 | 92 | hre.config.SDK.project = tempConf; 93 | }; 94 | -------------------------------------------------------------------------------- /tasks/deploy/toftDeployer__task.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { 3 | TTapiocaDeployTaskArgs, 4 | TTapiocaDeployerVmPass, 5 | } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 6 | import { buildMTOFT } from 'tasks/deployBuilds/buildMTOFT'; 7 | import { buildTOFT } from 'tasks/deployBuilds/buildTOFT'; 8 | import { buildToftVault } from 'tasks/deployBuilds/buildToftVault'; 9 | import { DEPLOYMENT_NAMES, DEPLOY_CONFIG } from './DEPLOY_CONFIG'; 10 | import { 11 | VMAddToftModule, 12 | getInitStruct, 13 | getModuleStruct, 14 | } from './toftDeployerUtils'; 15 | 16 | export type TToftDeployerTaskArgs = TTapiocaDeployTaskArgs & { 17 | erc20: string; 18 | target: 'toft' | 'mtoft'; 19 | hostEid: string | number; 20 | deploymentName: string; 21 | name: string; 22 | symbol: string; 23 | noModuleDeploy?: boolean; 24 | }; 25 | export const toftDeployer__task = async ( 26 | _taskArgs: TToftDeployerTaskArgs, 27 | hre: HardhatRuntimeEnvironment, 28 | ) => { 29 | await hre.SDK.DeployerVM.tapiocaDeployTask(_taskArgs, { hre }, VMAddToft); 30 | }; 31 | 32 | export async function VMAddToft( 33 | params: TTapiocaDeployerVmPass, 34 | ) { 35 | const { hre, VM, tapiocaMulticallAddr, taskArgs, isTestnet, chainInfo } = 36 | params; 37 | const { 38 | tag, 39 | deploymentName, 40 | erc20, 41 | name, 42 | symbol, 43 | target, 44 | noModuleDeploy, 45 | hostEid, 46 | } = taskArgs; 47 | const owner = tapiocaMulticallAddr; 48 | 49 | const vaultDeploymentName = `${DEPLOYMENT_NAMES.TOFT_VAULT}/${deploymentName}`; 50 | VM.add(await buildToftVault(hre, vaultDeploymentName, [erc20])); 51 | if (!noModuleDeploy) { 52 | await VMAddToftModule({ 53 | hre, 54 | VM, 55 | owner, 56 | vaultDeploymentName, 57 | erc20, 58 | tag, 59 | }); 60 | } 61 | 62 | const [initStruct, dependsOnInitStruct] = await getInitStruct({ 63 | hre, 64 | tag, 65 | owner, 66 | erc20, 67 | hostEid, 68 | name, 69 | symbol, 70 | vaultDeploymentName, 71 | isTestnet, 72 | chainInfo, 73 | }); 74 | const [moduleStruct, dependsOnModuleStruct] = getModuleStruct({ hre }); 75 | if (target === 'toft') { 76 | VM.add( 77 | await buildTOFT( 78 | hre, 79 | deploymentName, 80 | [initStruct, moduleStruct], 81 | [...dependsOnInitStruct, ...dependsOnModuleStruct], 82 | ), 83 | ); 84 | } else if (target === 'mtoft') { 85 | VM.add( 86 | await buildMTOFT( 87 | hre, 88 | deploymentName, 89 | [ 90 | initStruct, 91 | moduleStruct, 92 | DEPLOY_CONFIG.MISC[chainInfo.chainId]!.STARGATE_ROUTER, 93 | ], 94 | [...dependsOnInitStruct, ...dependsOnModuleStruct], 95 | ), 96 | ); 97 | } else { 98 | throw new Error('[-] Invalid target. Use "toft" or "mtoft"'); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildBalancer.ts: -------------------------------------------------------------------------------- 1 | import { Balancer__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildBalancer = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | args: Parameters, 9 | ): Promise> => { 10 | return { 11 | contract: await hre.ethers.getContractFactory('Balancer'), 12 | deploymentName, 13 | args, 14 | dependsOn: [], 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildExtExec.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 3 | import { IDependentOn } from '@tapioca-sdk/ethers/hardhat/DeployerVM'; 4 | import { TapiocaOmnichainExtExec__factory } from '@typechain/index'; 5 | 6 | export const buildExtExec = async ( 7 | hre: HardhatRuntimeEnvironment, 8 | deploymentName: string, 9 | args: Parameters, 10 | dependsOn: IDependentOn[], 11 | ): Promise> => { 12 | return { 13 | contract: await hre.ethers.getContractFactory( 14 | 'TapiocaOmnichainExtExec', 15 | ), 16 | deploymentName, 17 | args, 18 | dependsOn, 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildMTOFT.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 3 | import { IDependentOn } from '@tapioca-sdk/ethers/hardhat/DeployerVM'; 4 | import { MTOFT__factory } from '@typechain/index'; 5 | 6 | export const buildMTOFT = async ( 7 | hre: HardhatRuntimeEnvironment, 8 | deploymentName: string, 9 | args: Parameters, 10 | dependsOn: IDependentOn[], 11 | ): Promise> => { 12 | return { 13 | contract: new MTOFT__factory(hre.ethers.provider.getSigner(0)), 14 | deploymentName, 15 | args, 16 | dependsOn, 17 | meta: { 18 | mtoft: true, 19 | }, 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildTOFT.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 3 | import { IDependentOn } from '@tapioca-sdk/ethers/hardhat/DeployerVM'; 4 | import { TOFT__factory } from '@typechain/index'; 5 | 6 | export const buildTOFT = async ( 7 | hre: HardhatRuntimeEnvironment, 8 | deploymentName: string, 9 | args: Parameters, 10 | dependsOn: IDependentOn[], 11 | ): Promise> => { 12 | return { 13 | contract: await hre.ethers.getContractFactory('TOFT'), 14 | deploymentName, 15 | args, 16 | dependsOn, 17 | meta: { 18 | toft: true, 19 | }, 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildTOFTGenericReceiverModule.ts: -------------------------------------------------------------------------------- 1 | import { IDependentOn } from '@tapioca-sdk/ethers/hardhat/DeployerVM'; 2 | import { TOFTGenericReceiverModule__factory } from '@typechain/index'; 3 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 4 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 5 | 6 | export const buildTOFTGenericReceiverModule = async ( 7 | hre: HardhatRuntimeEnvironment, 8 | deploymentName: string, 9 | args: Parameters, 10 | dependsOn: IDependentOn[], 11 | ): Promise> => { 12 | return { 13 | contract: await hre.ethers.getContractFactory( 14 | 'TOFTGenericReceiverModule', 15 | ), 16 | deploymentName, 17 | args, 18 | dependsOn, 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildTOFTMarketReceiverModule.ts: -------------------------------------------------------------------------------- 1 | import { TOFTMarketReceiverModule__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildTOFTMarketReceiverModule = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | args: Parameters, 9 | ): Promise> => { 10 | return { 11 | contract: await hre.ethers.getContractFactory( 12 | 'TOFTMarketReceiverModule', 13 | ), 14 | deploymentName, 15 | args, 16 | dependsOn: [], 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildTOFTOptionsReceiverModule.ts: -------------------------------------------------------------------------------- 1 | import { TOFTOptionsReceiverModule__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildTOFTOptionsReceiverModule = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | args: Parameters, 9 | ): Promise> => { 10 | return { 11 | contract: await hre.ethers.getContractFactory( 12 | 'TOFTOptionsReceiverModule', 13 | ), 14 | deploymentName, 15 | args, 16 | dependsOn: [], 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildTOFTReceiverModule.ts: -------------------------------------------------------------------------------- 1 | import { TOFTReceiver__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildTOFTReceiverModule = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | args: Parameters, 9 | ): Promise> => { 10 | return { 11 | contract: await hre.ethers.getContractFactory('TOFTReceiver'), 12 | deploymentName, 13 | args, 14 | dependsOn: [], 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildTOFTSenderModule.ts: -------------------------------------------------------------------------------- 1 | import { TOFTSender__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildTOFTSenderModule = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | args: Parameters, 9 | ): Promise> => { 10 | return { 11 | contract: await hre.ethers.getContractFactory('TOFTSender'), 12 | deploymentName, 13 | args, 14 | dependsOn: [], 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildToftHelper.ts: -------------------------------------------------------------------------------- 1 | import { TOFTHelper__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildToftHelper = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | ): Promise> => { 9 | return { 10 | contract: await hre.ethers.getContractFactory('TOFTHelper'), 11 | deploymentName, 12 | args: [], 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /tasks/deployBuilds/buildToftVault.ts: -------------------------------------------------------------------------------- 1 | import { TOFTVault__factory } from '@typechain/index'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { IDeployerVMAdd } from 'tapioca-sdk/dist/ethers/hardhat/DeployerVM'; 4 | 5 | export const buildToftVault = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | deploymentName: string, 8 | args: Parameters, 9 | ): Promise> => { 10 | return { 11 | contract: await hre.ethers.getContractFactory('TOFTVault'), 12 | deploymentName, 13 | args, 14 | dependsOn: [], 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /tasks/exec/balancer/01-balancer-toggleSwapEth.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const toggleSwapEth__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | const { status } = await inquirer.prompt({ 20 | type: 'confirm', 21 | name: 'status', 22 | message: 'Enable?', 23 | }); 24 | await (await balancer.setSwapEth(status)).wait(3); 25 | }; 26 | -------------------------------------------------------------------------------- /tasks/exec/balancer/02-balancer-emergencySaveTokens.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const emergencySaveTokens__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | const { token } = await inquirer.prompt({ 20 | type: 'input', 21 | name: 'token', 22 | message: 'Token address', 23 | default: hre.ethers.constants.AddressZero, 24 | }); 25 | 26 | const { amount } = await inquirer.prompt({ 27 | type: 'input', 28 | name: 'amount', 29 | message: 'Token amount', 30 | default: hre.ethers.constants.AddressZero, 31 | }); 32 | 33 | await (await balancer.emergencySaveTokens(token, amount)).wait(3); 34 | }; 35 | -------------------------------------------------------------------------------- /tasks/exec/balancer/03-balancer-initConnectedOFT.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const initConnectedOFT__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | 20 | const { srcOft } = await inquirer.prompt({ 21 | type: 'input', 22 | name: 'srcOft', 23 | message: 'Source tOFT', 24 | default: hre.ethers.constants.AddressZero, 25 | }); 26 | 27 | const { dstOft } = await inquirer.prompt({ 28 | type: 'input', 29 | name: 'dstOft', 30 | message: 'Destination tOFT', 31 | default: hre.ethers.constants.AddressZero, 32 | }); 33 | 34 | const { dstChainId } = await inquirer.prompt({ 35 | type: 'input', 36 | name: 'dstChainId', 37 | message: 'Destination tOFT LZ chain id', 38 | default: 0, 39 | }); 40 | 41 | const { ercData } = await inquirer.prompt({ 42 | type: 'input', 43 | name: 'ercData', 44 | message: 'ERC20 data', 45 | default: '0x', 46 | }); 47 | 48 | await ( 49 | await balancer.initConnectedOFT(srcOft, dstChainId, dstOft, ercData) 50 | ).wait(3); 51 | }; 52 | -------------------------------------------------------------------------------- /tasks/exec/balancer/04-balancer-addRebalanceAmount.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const addRebalanceAmount__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | 20 | const { srcOft } = await inquirer.prompt({ 21 | type: 'input', 22 | name: 'srcOft', 23 | message: 'Source tOFT', 24 | default: hre.ethers.constants.AddressZero, 25 | }); 26 | 27 | const { dstChainId } = await inquirer.prompt({ 28 | type: 'input', 29 | name: 'dstChainId', 30 | message: 'Destination tOFT LZ chain id', 31 | default: 0, 32 | }); 33 | 34 | const { amount } = await inquirer.prompt({ 35 | type: 'input', 36 | name: 'amount', 37 | message: 'Rebalance amount', 38 | default: 0, 39 | }); 40 | 41 | await ( 42 | await balancer.addRebalanceAmount(srcOft, dstChainId, amount) 43 | ).wait(3); 44 | }; 45 | -------------------------------------------------------------------------------- /tasks/exec/balancer/05-balancer-retryRevert.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const retryRevertOnBalancer__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | const { srcChainId } = await inquirer.prompt({ 20 | type: 'input', 21 | name: 'srcChainId', 22 | message: 'Source tOFT LZ chain id', 23 | default: 0, 24 | }); 25 | 26 | const { srcAddress } = await inquirer.prompt({ 27 | type: 'input', 28 | name: 'srcAddress', 29 | message: 'Source tOFT', 30 | default: hre.ethers.constants.AddressZero, 31 | }); 32 | 33 | const { nonce } = await inquirer.prompt({ 34 | type: 'input', 35 | name: 'nonce', 36 | message: 'Nonce', 37 | default: 0, 38 | }); 39 | 40 | await (await balancer.retryRevert(srcChainId, srcAddress, nonce)).wait(3); 41 | }; 42 | -------------------------------------------------------------------------------- /tasks/exec/balancer/06-balancer-instantRedeemLocal.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const instantRedeemLocalOnBalancer__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | const { srcPoolId } = await inquirer.prompt({ 20 | type: 'input', 21 | name: 'srcPoolId', 22 | message: 'Source LZ pool id', 23 | default: 0, 24 | }); 25 | 26 | const { amountLP } = await inquirer.prompt({ 27 | type: 'input', 28 | name: 'amountLP', 29 | message: 'LZ amount LP', 30 | default: 0, 31 | }); 32 | 33 | const { to } = await inquirer.prompt({ 34 | type: 'input', 35 | name: 'to', 36 | message: 'Receiver', 37 | default: hre.ethers.constants.AddressZero, 38 | }); 39 | 40 | await (await balancer.instantRedeemLocal(srcPoolId, amountLP, to)).wait(3); 41 | }; 42 | -------------------------------------------------------------------------------- /tasks/exec/balancer/07-balancer-redeemLocal.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const redeemLocalOnBalancer__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | 20 | const { dstChainId } = await inquirer.prompt({ 21 | type: 'input', 22 | name: 'dstChainId', 23 | message: 'Destination LZ chain id', 24 | default: 0, 25 | }); 26 | 27 | const { srcPoolId } = await inquirer.prompt({ 28 | type: 'input', 29 | name: 'srcPoolId', 30 | message: 'Source LZ pool id', 31 | default: 0, 32 | }); 33 | 34 | const { dstPoolId } = await inquirer.prompt({ 35 | type: 'input', 36 | name: 'dstPoolId', 37 | message: 'Destination LZ pool id', 38 | default: 0, 39 | }); 40 | 41 | const { refundAddress } = await inquirer.prompt({ 42 | type: 'input', 43 | name: 'refundAddress', 44 | message: 'Refund address', 45 | default: hre.ethers.constants.AddressZero, 46 | }); 47 | 48 | const { amountLP } = await inquirer.prompt({ 49 | type: 'input', 50 | name: 'amountLP', 51 | message: 'LZ amount LP', 52 | default: 0, 53 | }); 54 | 55 | const { to } = await inquirer.prompt({ 56 | type: 'input', 57 | name: 'to', 58 | message: 'Receiver', 59 | default: hre.ethers.constants.AddressZero, 60 | }); 61 | 62 | const { dstGasForCall } = await inquirer.prompt({ 63 | type: 'input', 64 | name: 'dstGasForCall', 65 | message: 'Destination gas', 66 | default: 0, 67 | }); 68 | 69 | const { dstNativeAmount } = await inquirer.prompt({ 70 | type: 'input', 71 | name: 'dstNativeAmount', 72 | message: 'Destination native amount', 73 | default: 0, 74 | }); 75 | 76 | const { dstNativeAddr } = await inquirer.prompt({ 77 | type: 'input', 78 | name: 'dstNativeAddr', 79 | message: 'Destination native address', 80 | default: '0x', 81 | }); 82 | 83 | await ( 84 | await balancer.redeemLocal( 85 | dstChainId, 86 | srcPoolId, 87 | dstPoolId, 88 | refundAddress, 89 | amountLP, 90 | to, 91 | { dstGasForCall, dstNativeAddr, dstNativeAmount }, 92 | ) 93 | ).wait(3); 94 | }; 95 | -------------------------------------------------------------------------------- /tasks/exec/balancer/08-balancer-redeemRemote.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const redeemRemoteOnBalancer__task = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tag = await hre.SDK.hardhatUtils.askForTag(hre, 'local'); 10 | const dep = await hre.SDK.hardhatUtils.getLocalContract( 11 | hre, 12 | 'Balancer', 13 | tag, 14 | ); 15 | const balancer = await hre.ethers.getContractAt( 16 | 'Balancer', 17 | dep.contract.address, 18 | ); 19 | 20 | const { dstChainId } = await inquirer.prompt({ 21 | type: 'input', 22 | name: 'dstChainId', 23 | message: 'Destination LZ chain id', 24 | default: 0, 25 | }); 26 | 27 | const { srcPoolId } = await inquirer.prompt({ 28 | type: 'input', 29 | name: 'srcPoolId', 30 | message: 'Source LZ pool id', 31 | default: 0, 32 | }); 33 | 34 | const { dstPoolId } = await inquirer.prompt({ 35 | type: 'input', 36 | name: 'dstPoolId', 37 | message: 'Destination LZ pool id', 38 | default: 0, 39 | }); 40 | 41 | const { refundAddress } = await inquirer.prompt({ 42 | type: 'input', 43 | name: 'refundAddress', 44 | message: 'Refund address', 45 | default: hre.ethers.constants.AddressZero, 46 | }); 47 | 48 | const { amountLP } = await inquirer.prompt({ 49 | type: 'input', 50 | name: 'amountLP', 51 | message: 'LZ amount LP', 52 | default: 0, 53 | }); 54 | 55 | const { minAmountLD } = await inquirer.prompt({ 56 | type: 'input', 57 | name: 'minAmountLD', 58 | message: 'Min amount on destination', 59 | default: 0, 60 | }); 61 | 62 | const { to } = await inquirer.prompt({ 63 | type: 'input', 64 | name: 'to', 65 | message: 'Receiver', 66 | default: hre.ethers.constants.AddressZero, 67 | }); 68 | 69 | const { dstGasForCall } = await inquirer.prompt({ 70 | type: 'input', 71 | name: 'dstGasForCall', 72 | message: 'Destination gas', 73 | default: 0, 74 | }); 75 | 76 | const { dstNativeAmount } = await inquirer.prompt({ 77 | type: 'input', 78 | name: 'dstNativeAmount', 79 | message: 'Destination native amount', 80 | default: 0, 81 | }); 82 | 83 | const { dstNativeAddr } = await inquirer.prompt({ 84 | type: 'input', 85 | name: 'dstNativeAddr', 86 | message: 'Destination native address', 87 | default: '0x', 88 | }); 89 | 90 | await ( 91 | await balancer.redeemRemote( 92 | dstChainId, 93 | srcPoolId, 94 | dstPoolId, 95 | refundAddress, 96 | amountLP, 97 | minAmountLD, 98 | to, 99 | { dstGasForCall, dstNativeAddr, dstNativeAmount }, 100 | ) 101 | ).wait(3); 102 | }; 103 | -------------------------------------------------------------------------------- /tasks/exec/balancer/balancerInnitConnectedOft__task.ts: -------------------------------------------------------------------------------- 1 | import { getChainBy } from '@tapioca-sdk/api/utils'; 2 | import { TTapiocaDeployTaskArgs } from '@tapioca-sdk/ethers/hardhat/DeployerVM'; 3 | import { TapiocaMulticall } from '@tapioca-sdk/typechain/tapioca-periphery'; 4 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 5 | import { loadLocalContract, loadLocalContractOnAllChains } from 'tapioca-sdk'; 6 | import { TContractWithChainInfo } from 'tapioca-sdk/dist/ethers/utils'; 7 | 8 | import { DEPLOYMENT_NAMES } from 'tasks/deploy/DEPLOY_CONFIG'; 9 | 10 | // Does not support ERC20s, only gas token mTOFT 11 | export const balancerInnitConnectedOft__task = async ( 12 | _taskArgs: TTapiocaDeployTaskArgs & { 13 | targetName: string; 14 | }, 15 | hre: HardhatRuntimeEnvironment, 16 | ) => { 17 | console.log( 18 | `[+] Setting Balancer connected OFT for ${_taskArgs.targetName}...`, 19 | ); 20 | const chain = hre.SDK.utils.getChainBy('chainId', hre.SDK.eChainId); 21 | const { targetName, tag } = _taskArgs; 22 | const isTestnet = !!chain.tags.find((e) => e === 'testnet'); 23 | const deployments = loadLocalContractOnAllChains( 24 | hre, 25 | targetName, 26 | tag, 27 | isTestnet, 28 | ); 29 | 30 | if (deployments.length === 0) { 31 | throw new Error( 32 | `[-] No deployment found for contract ${targetName} on tag ${tag}`, 33 | ); 34 | } 35 | 36 | for (const targetDep of deployments) { 37 | const targetChain = getChainBy('chainId', targetDep.chainInfo.chainId); 38 | await hre.SDK.hardhatUtils.useNetwork(hre, targetChain.name); // Need to switch network to the target chain 39 | const VM = hre.SDK.DeployerVM.loadVM({ hre, tag }); // Need to load the VM for every chainID to get the right multicall instance 40 | 41 | console.log(`\t[+] Setting connected OFT on ${targetChain.name}...`); 42 | const calls = await populateCalls(hre, tag, targetDep, deployments); 43 | await VM.executeMulticall(calls); 44 | } 45 | 46 | // Switch back to the original network 47 | console.log(`[+] connected OFT setting for ${targetName} done!`); 48 | await hre.SDK.hardhatUtils.useNetwork(hre, chain.name); 49 | }; 50 | 51 | async function populateCalls( 52 | hre: HardhatRuntimeEnvironment, 53 | tag: string, 54 | targetDep: TContractWithChainInfo, 55 | deployments: TContractWithChainInfo[], 56 | ) { 57 | const balancer = await hre.ethers.getContractAt( 58 | 'Balancer', 59 | loadLocalContract( 60 | hre, 61 | hre.SDK.eChainId, 62 | DEPLOYMENT_NAMES.TOFT_BALANCER, 63 | tag, 64 | ).address, 65 | ); 66 | 67 | const calls: TapiocaMulticall.CallStruct[] = []; 68 | const peers = deployments.filter( 69 | (e) => targetDep.chainInfo.chainId !== e.chainInfo.chainId, 70 | ); // Filter out the target chain 71 | for (const peer of peers) { 72 | console.log(`\t\t[+] Setting peer for ${peer.chainInfo.name}...`); 73 | calls.push({ 74 | target: balancer.address, 75 | callData: balancer.interface.encodeFunctionData( 76 | 'initConnectedOFT', 77 | [ 78 | targetDep.deployment.address, 79 | peer.chainInfo.lzChainId, 80 | peer.deployment.address, 81 | '', 82 | // Add _bytes memory _ercData to support ERC20 83 | ], 84 | ), 85 | allowFailure: false, 86 | }); 87 | } 88 | return calls; 89 | } 90 | -------------------------------------------------------------------------------- /tasks/exec/saveBlockNumber.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | 4 | export const saveBlockNumber__task = async ( 5 | // eslint-disable-next-line @typescript-eslint/ban-types 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | console.log('Retrieving latest block'); 10 | const latestBlock = await hre.ethers.provider.getBlock('latest'); 11 | console.log('Saving latest block'); 12 | const dep = hre.SDK.db.buildLocalDeployment({ 13 | chainId: String(hre.network.config.chainId), 14 | contracts: [ 15 | { 16 | name: 'NonContract-BlockDetails', 17 | address: hre.ethers.constants.AddressZero, 18 | meta: { 19 | blockNumber: latestBlock.number, 20 | nonce: latestBlock.nonce, 21 | timestamp: latestBlock.timestamp, 22 | }, 23 | }, 24 | ], 25 | }); 26 | hre.SDK.db.saveGlobally(dep, 'non-contracts', 'default'); 27 | console.log('Done'); 28 | }; 29 | -------------------------------------------------------------------------------- /tasks/exec/toft/09-mOft-updateConnectedChain.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const updateConnectedChain__task = async ( 6 | args: { address: string }, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const mTOFT = await hre.ethers.getContractAt('mTOFT', args.address); 10 | 11 | const { chain } = await inquirer.prompt({ 12 | type: 'input', 13 | name: 'chain', 14 | message: 'LZ chain id', 15 | default: hre.ethers.constants.AddressZero, 16 | }); 17 | 18 | await (await mTOFT.setConnectedChain(chain)).wait(3); 19 | }; 20 | -------------------------------------------------------------------------------- /tasks/exec/toft/10-mOft-updateBalancerState.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const updateBalancerState__task = async ( 6 | args: { address: string }, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const { balancer } = await inquirer.prompt({ 10 | type: 'input', 11 | name: 'balancer', 12 | message: 'Balancer address', 13 | default: hre.ethers.constants.AddressZero, 14 | }); 15 | 16 | const { status } = await inquirer.prompt({ 17 | type: 'confirm', 18 | name: 'status', 19 | message: 'Enable?', 20 | }); 21 | 22 | const mTOFT = await hre.ethers.getContractAt('mTOFT', args.address); 23 | await (await mTOFT.updateBalancerState(balancer, status)).wait(3); 24 | }; 25 | -------------------------------------------------------------------------------- /tasks/exec/toft/11-oft-rescueEth.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const rescueEthFromOft__task = async ( 6 | args: { address: string; oft: boolean }, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tOFT = args.oft 10 | ? await hre.ethers.getContractAt('TOFT', args.address) 11 | : await hre.ethers.getContractAt('mTOFT', args.address); 12 | 13 | const { to } = await inquirer.prompt({ 14 | type: 'input', 15 | name: 'to', 16 | message: 'Receiver address', 17 | default: hre.ethers.constants.AddressZero, 18 | }); 19 | const { amount } = await inquirer.prompt({ 20 | type: 'input', 21 | name: 'amount', 22 | message: 'Receiver address', 23 | default: hre.ethers.constants.AddressZero, 24 | }); 25 | 26 | await (await tOFT.rescueEth(amount, to)).wait(3); 27 | }; 28 | -------------------------------------------------------------------------------- /tasks/exec/toft/12-oft-setStargateRouter.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import inquirer from 'inquirer'; 4 | 5 | export const setStargateRouterOnOft__task = async ( 6 | args: { address: string }, 7 | hre: HardhatRuntimeEnvironment, 8 | ) => { 9 | const tOFT = await hre.ethers.getContractAt('mTOFT', args.address); 10 | 11 | const { router } = await inquirer.prompt({ 12 | type: 'input', 13 | name: 'router', 14 | message: 'Stargate router address', 15 | default: hre.ethers.constants.AddressZero, 16 | }); 17 | await (await tOFT.setStargateRouter(router)).wait(3); 18 | }; 19 | -------------------------------------------------------------------------------- /tasks/exec/toft/13-setCluster.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | 3 | export const setCluster__task = async ( 4 | args: { address: string; cluster: string; oft: boolean }, 5 | hre: HardhatRuntimeEnvironment, 6 | ) => { 7 | const tOFT = args.oft 8 | ? await hre.ethers.getContractAt('TOFT', args.address) 9 | : await hre.ethers.getContractAt('mTOFT', args.address); 10 | await (await tOFT.setCluster(args.cluster)).wait(3); 11 | }; 12 | -------------------------------------------------------------------------------- /tasks/scopes/balancerScope.ts: -------------------------------------------------------------------------------- 1 | import '@nomiclabs/hardhat-ethers'; 2 | import { glob } from 'glob'; 3 | import { scope } from 'hardhat/config'; 4 | 5 | import { toggleSwapEth__task } from '../exec/balancer/01-balancer-toggleSwapEth'; 6 | import { emergencySaveTokens__task } from '../exec/balancer/02-balancer-emergencySaveTokens'; 7 | import { initConnectedOFT__task } from '../exec/balancer/03-balancer-initConnectedOFT'; 8 | import { addRebalanceAmount__task } from '../exec/balancer/04-balancer-addRebalanceAmount'; 9 | import { retryRevertOnBalancer__task } from '../exec/balancer/05-balancer-retryRevert'; 10 | import { instantRedeemLocalOnBalancer__task } from '../exec/balancer/06-balancer-instantRedeemLocal'; 11 | import { redeemLocalOnBalancer__task } from '../exec/balancer/07-balancer-redeemLocal'; 12 | import { redeemRemoteOnBalancer__task } from '../exec/balancer/08-balancer-redeemRemote'; 13 | import { TAP_TASK } from 'tapioca-sdk'; 14 | import { balancerInnitConnectedOft__task } from 'tasks/exec/balancer/balancerInnitConnectedOft__task'; 15 | 16 | const balancerScope = scope('balancer', 'Balancer.sol tasks'); 17 | 18 | TAP_TASK( 19 | balancerScope.task( 20 | 'initConnectedOFT', 21 | 'Init a connected OFT on Balancer', 22 | balancerInnitConnectedOft__task, 23 | ), 24 | ); 25 | 26 | balancerScope.task( 27 | 'toggleSwapEth', 28 | 'Disable/Enable swap eth on balancer', 29 | toggleSwapEth__task, 30 | ); 31 | 32 | balancerScope.task( 33 | 'emergencySaveTokens', 34 | 'Emergency save tokens from Balancer', 35 | emergencySaveTokens__task, 36 | ); 37 | 38 | // balancerScope.task( 39 | // 'initConnectedOFT', 40 | // 'Init a connected OFT on Balancer', 41 | // initConnectedOFT__task, 42 | // ); 43 | 44 | balancerScope.task( 45 | 'setRebalanceAmount', 46 | 'Set rebalanceable amount to Balancer', 47 | addRebalanceAmount__task, 48 | ); 49 | 50 | balancerScope.task( 51 | 'retryRevertOnBalancer', 52 | 'Retry revert on Balancer', 53 | retryRevertOnBalancer__task, 54 | ); 55 | 56 | balancerScope.task( 57 | 'instantRedeemLocalOnBalancer', 58 | 'Instant redeem on Balancer', 59 | instantRedeemLocalOnBalancer__task, 60 | ); 61 | 62 | balancerScope.task( 63 | 'redeemLocalOnBalancer', 64 | 'Redeem local on Balancer', 65 | redeemLocalOnBalancer__task, 66 | ); 67 | 68 | balancerScope.task( 69 | 'redeemRemoteOnBalancer', 70 | 'Redeem remote on Balancer', 71 | redeemRemoteOnBalancer__task, 72 | ); 73 | -------------------------------------------------------------------------------- /tasks/scopes/deployScope.ts: -------------------------------------------------------------------------------- 1 | import '@nomiclabs/hardhat-ethers'; 2 | import { scope } from 'hardhat/config'; 3 | import { TAP_TASK } from 'tapioca-sdk'; 4 | import { deployPostLbp__task } from 'tasks/deploy/1-deployPostLbp__task'; 5 | import { deployFinal__task } from 'tasks/deploy/2-deployFinal__task'; 6 | 7 | const deployScope = scope('deploys', 'Deployment tasks'); 8 | 9 | TAP_TASK( 10 | deployScope 11 | .task( 12 | 'postLbp', 13 | 'Will deploy Balancer, mtETH, tWSTETH, and tRETH.', 14 | deployPostLbp__task, 15 | ) 16 | .addParam('sdaiHostChainName', 'Host chain name of the sDai.'), 17 | ); 18 | 19 | TAP_TASK( 20 | deployScope 21 | .task( 22 | 'final', 23 | 'Will deploy SGL sDAI market OFT on Arb + Eth and link them.', 24 | deployFinal__task, 25 | ) 26 | .addParam( 27 | 'sdaiMarketChainName', 28 | 'Host chain name of the SGL sDai market.', 29 | ), 30 | ); 31 | -------------------------------------------------------------------------------- /tasks/scopes/toftScope.ts: -------------------------------------------------------------------------------- 1 | import '@nomiclabs/hardhat-ethers'; 2 | import { glob } from 'glob'; 3 | import { scope } from 'hardhat/config'; 4 | 5 | import { updateConnectedChain__task } from '../exec/toft/09-mOft-updateConnectedChain'; 6 | import { updateBalancerState__task } from '../exec/toft/10-mOft-updateBalancerState'; 7 | import { rescueEthFromOft__task } from '../exec/toft/11-oft-rescueEth'; 8 | import { setStargateRouterOnOft__task } from '../exec/toft/12-oft-setStargateRouter'; 9 | import { setCluster__task } from '../exec/toft/13-setCluster'; 10 | 11 | const tOFTScope = scope('oft', 'TOFT & mTOFT tasks'); 12 | 13 | tOFTScope 14 | .task('setCluster', 'Set cluster', setCluster__task) 15 | .addParam('address', 'mTapiocaOFT address') 16 | .addParam('cluster', 'Cluster address') 17 | .addFlag('oft', 'true for TOFT contract; false for mTOFT contract'); 18 | 19 | tOFTScope 20 | .task( 21 | 'updateConnectedChain', 22 | 'Update a connected chain status for mTapiocaOFT contract', 23 | updateConnectedChain__task, 24 | ) 25 | .addParam('address', 'mTOFT address'); 26 | 27 | tOFTScope 28 | .task( 29 | 'updateBalancerState', 30 | 'Update a balancer status for mTapiocaOFT contract', 31 | updateBalancerState__task, 32 | ) 33 | .addParam('address', 'mTOFT address'); 34 | 35 | tOFTScope 36 | .task('rescueEthFromOft', 'Rescue ETH from OFT', rescueEthFromOft__task) 37 | .addParam('address', 'mTOFT address') 38 | .addFlag('oft', 'true for TOFT contract; false for mTOFT contract'); 39 | 40 | tOFTScope 41 | .task( 42 | 'setStargateRouterOnOft', 43 | 'Rescue ETH from OFT', 44 | setStargateRouterOnOft__task, 45 | ) 46 | .addParam('address', 'mTOFT address'); 47 | -------------------------------------------------------------------------------- /tasks/utils.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import _ from 'lodash'; 3 | import { typechain } from 'tapioca-sdk'; 4 | 5 | export const loadVM = async ( 6 | hre: HardhatRuntimeEnvironment, 7 | tag: string, 8 | debugMode = false, 9 | ) => { 10 | const VM = new hre.SDK.DeployerVM(hre, { 11 | // Change this if you get bytecode size error / gas required exceeds allowance (550000000)/ anything related to bytecode size 12 | // Could be different by network/RPC provider 13 | bytecodeSizeLimit: 100_000, 14 | debugMode, 15 | tag, 16 | }); 17 | return VM; 18 | }; 19 | -------------------------------------------------------------------------------- /tasks/views/listDeploy.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { TDeployment } from '../constants'; 3 | import { readTOFTDeployments } from '../scripts/utils'; 4 | 5 | export const getDeployments = async ( 6 | {}, 7 | hre: HardhatRuntimeEnvironment, 8 | ): Promise => { 9 | const deployments = await hre.deployments.all(); 10 | const formatted: any = {}; 11 | for (const key of Object.keys(deployments)) { 12 | formatted[key] = deployments[key].address; 13 | } 14 | 15 | return { 16 | ...(readTOFTDeployments()[await hre.getChainId()] ?? {}), 17 | ...formatted, 18 | }; 19 | }; 20 | 21 | export const listDeploy__task = async ({}, hre: HardhatRuntimeEnvironment) => { 22 | console.log(await getDeployments({}, hre)); 23 | }; 24 | -------------------------------------------------------------------------------- /test/ERC20Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // External 5 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | 8 | // Tapioca 9 | import {ERC20PermitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 10 | 11 | contract ERC20Mock is ERC20 { 12 | constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} 13 | 14 | function mint(address to, uint256 amount) external { 15 | _mint(to, amount); 16 | } 17 | 18 | // for tests support 19 | // when the ERC20 is acting as a TOFT 20 | function erc20() external view returns (address) { 21 | return address(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/ERC721Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // External 5 | import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 6 | 7 | // Tapioca 8 | import {ERC721Permit} from "contracts/util/ERC4494.sol"; 9 | 10 | contract ERC721Mock is ERC721, ERC721Permit { 11 | constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) ERC721Permit(name_) {} 12 | 13 | function mint(address to, uint256 tokenId) external { 14 | _mint(to, tokenId); 15 | } 16 | 17 | function _afterTokenTransfer( 18 | address from, 19 | address to, 20 | uint256 firstTokenId, 21 | uint256 batchSize 22 | ) internal override(ERC721Permit, ERC721) { 23 | super._afterTokenTransfer(from, to, firstTokenId, batchSize); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/ERC20Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.22; 3 | 4 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract ERC20Mock is ERC20 { 7 | constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {} 8 | 9 | function mint(address _to, uint256 _amount) public { 10 | _mint(_to, _amount); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/ExecutorFeeLibMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | 3 | pragma solidity ^0.8.22; 4 | 5 | import {ExecutorFeeLib} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/ExecutorFeeLib.sol"; 6 | 7 | contract ExecutorFeeLibMock is ExecutorFeeLib { 8 | constructor() ExecutorFeeLib(1e18) {} 9 | 10 | function _isV1Eid(uint32 /*_eid*/ ) internal pure override returns (bool) { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/OFTAdapterMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {OFTAdapter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFTAdapter.sol"; 5 | 6 | contract OFTAdapterMock is OFTAdapter { 7 | constructor(address _token, address _lzEndpoint, address _owner) OFTAdapter(_token, _lzEndpoint, _owner) {} 8 | 9 | // @dev expose internal functions for testing purposes 10 | function debit(uint256 _amountToSendLD, uint256 _minAmountToCreditLD, uint32 _dstEid) 11 | public 12 | returns (uint256 amountDebitedLD, uint256 amountToCreditLD) 13 | { 14 | return _debit(msg.sender, _amountToSendLD, _minAmountToCreditLD, _dstEid); 15 | } 16 | 17 | function debitView(uint256 _amountToSendLD, uint256 _minAmountToCreditLD, uint32 _dstEid) 18 | public 19 | view 20 | returns (uint256 amountDebitedLD, uint256 amountToCreditLD) 21 | { 22 | return _debitView(_amountToSendLD, _minAmountToCreditLD, _dstEid); 23 | } 24 | 25 | function credit(address _to, uint256 _amountToCreditLD, uint32 _srcEid) public returns (uint256 amountReceivedLD) { 26 | return _credit(_to, _amountToCreditLD, _srcEid); 27 | } 28 | 29 | function removeDust(uint256 _amountLD) public view returns (uint256 amountLD) { 30 | return _removeDust(_amountLD); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/OFTComposerMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {IOAppComposer} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/interfaces/IOAppComposer.sol"; 5 | 6 | contract OFTComposerMock is IOAppComposer { 7 | // default empty values for testing a lzCompose received message 8 | address public from; 9 | bytes32 public guid; 10 | bytes public message; 11 | address public executor; 12 | bytes public extraData; 13 | 14 | function lzCompose( 15 | address _from, 16 | bytes32 _guid, 17 | bytes calldata _message, 18 | address _executor, 19 | bytes calldata /*_extraData*/ 20 | ) external payable { 21 | from = _from; 22 | guid = _guid; 23 | message = _message; 24 | executor = _executor; 25 | extraData = _message; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/OFTInspectorMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {IOAppMsgInspector} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/interfaces/IOAppMsgInspector.sol"; 5 | 6 | contract OFTInspectorMock is IOAppMsgInspector { 7 | function inspect(bytes calldata _message, bytes calldata _options) external pure returns (bool) { 8 | revert InspectionFailed(_message, _options); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/OFTMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {OFT} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFT.sol"; 5 | import {SendParam} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFTCore.sol"; 6 | 7 | contract OFTMock is OFT { 8 | constructor(string memory _name, string memory _symbol, address _lzEndpoint, address _owner) 9 | OFT(_name, _symbol, _lzEndpoint, _owner) 10 | {} 11 | 12 | function mint(address _to, uint256 _amount) public { 13 | _mint(_to, _amount); 14 | } 15 | 16 | // @dev expose internal functions for testing purposes 17 | function debit(uint256 _amountToSendLD, uint256 _minAmountToCreditLD, uint32 _dstEid) 18 | public 19 | returns (uint256 amountDebitedLD, uint256 amountToCreditLD) 20 | { 21 | return _debit(msg.sender, _amountToSendLD, _minAmountToCreditLD, _dstEid); 22 | } 23 | 24 | function debitView(uint256 _amountToSendLD, uint256 _minAmountToCreditLD, uint32 _dstEid) 25 | public 26 | view 27 | returns (uint256 amountDebitedLD, uint256 amountToCreditLD) 28 | { 29 | return _debitView(_amountToSendLD, _minAmountToCreditLD, _dstEid); 30 | } 31 | 32 | function removeDust(uint256 _amountLD) public view returns (uint256 amountLD) { 33 | return _removeDust(_amountLD); 34 | } 35 | 36 | function toLD(uint64 _amountSD) public view returns (uint256 amountLD) { 37 | return _toLD(_amountSD); 38 | } 39 | 40 | function toSD(uint256 _amountLD) public view returns (uint64 amountSD) { 41 | return _toSD(_amountLD); 42 | } 43 | 44 | function credit(address _to, uint256 _amountToCreditLD, uint32 _srcEid) public returns (uint256 amountReceivedLD) { 45 | return _credit(_to, _amountToCreditLD, _srcEid); 46 | } 47 | 48 | function buildMsgAndOptions(SendParam calldata _sendParam, uint256 _amountToCreditLD) 49 | public 50 | view 51 | returns (bytes memory message, bytes memory options) 52 | { 53 | return _buildMsgAndOptions(_sendParam, _amountToCreditLD); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/OptionsHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {ExecutorOptions} from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol"; 6 | import {UlnOptions} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/UlnOptions.sol"; 7 | 8 | contract UlnOptionsMock { 9 | using UlnOptions for bytes; 10 | 11 | function decode(bytes calldata _options) 12 | public 13 | pure 14 | returns (bytes memory executorOptions, bytes memory dvnOptions) 15 | { 16 | return UlnOptions.decode(_options); 17 | } 18 | } 19 | 20 | contract OptionsHelper { 21 | UlnOptionsMock ulnOptions = new UlnOptionsMock(); 22 | 23 | function _parseExecutorLzReceiveOption(bytes memory _options) internal view returns (uint256 gas, uint256 value) { 24 | (bool exist, bytes memory option) = 25 | _getExecutorOptionByOptionType(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE); 26 | require(exist, "OptionsHelper: lzReceive option not found"); 27 | (gas, value) = this.decodeLzReceiveOption(option); 28 | } 29 | 30 | function _parseExecutorNativeDropOption(bytes memory _options) 31 | internal 32 | view 33 | returns (uint256 amount, bytes32 receiver) 34 | { 35 | (bool exist, bytes memory option) = 36 | _getExecutorOptionByOptionType(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP); 37 | require(exist, "OptionsHelper: nativeDrop option not found"); 38 | (amount, receiver) = this.decodeNativeDropOption(option); 39 | } 40 | 41 | function _parseExecutorLzComposeOption(bytes memory _options) 42 | internal 43 | view 44 | returns (uint16 index, uint256 gas, uint256 value) 45 | { 46 | (bool exist, bytes memory option) = 47 | _getExecutorOptionByOptionType(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE); 48 | require(exist, "OptionsHelper: lzCompose option not found"); 49 | return this.decodeLzComposeOption(option); 50 | } 51 | 52 | function _executorOptionExists(bytes memory _options, uint8 _executorOptionType) 53 | internal 54 | view 55 | returns (bool exist) 56 | { 57 | (exist,) = _getExecutorOptionByOptionType(_options, _executorOptionType); 58 | } 59 | 60 | function _getExecutorOptionByOptionType(bytes memory _options, uint8 _executorOptionType) 61 | internal 62 | view 63 | returns (bool exist, bytes memory option) 64 | { 65 | (bytes memory executorOpts,) = ulnOptions.decode(_options); 66 | 67 | uint256 cursor; 68 | while (cursor < executorOpts.length) { 69 | (uint8 optionType, bytes memory op, uint256 nextCursor) = this.nextExecutorOption(executorOpts, cursor); 70 | if (optionType == _executorOptionType) { 71 | return (true, op); 72 | } 73 | cursor = nextCursor; 74 | } 75 | } 76 | 77 | function nextExecutorOption(bytes calldata _options, uint256 _cursor) 78 | external 79 | pure 80 | returns (uint8 optionType, bytes calldata option, uint256 cursor) 81 | { 82 | return ExecutorOptions.nextExecutorOption(_options, _cursor); 83 | } 84 | 85 | function decodeLzReceiveOption(bytes calldata _option) external pure returns (uint128 gas, uint128 value) { 86 | return ExecutorOptions.decodeLzReceiveOption(_option); 87 | } 88 | 89 | function decodeNativeDropOption(bytes calldata _option) external pure returns (uint128 amount, bytes32 receiver) { 90 | return ExecutorOptions.decodeNativeDropOption(_option); 91 | } 92 | 93 | function decodeLzComposeOption(bytes calldata _option) 94 | external 95 | pure 96 | returns (uint16 index, uint128 gas, uint128 value) 97 | { 98 | return ExecutorOptions.decodeLzComposeOption(_option); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/SendUln302Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {MessagingFee} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; 5 | import {SendUln302} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/uln302/SendUln302.sol"; 6 | import {Packet} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol"; 7 | 8 | import {TestHelper} from "../TestHelper.sol"; 9 | 10 | contract SendUln302Mock is SendUln302 { 11 | // offchain packets schedule 12 | TestHelper public testHelper; 13 | 14 | constructor( 15 | address payable _verifyHelper, 16 | address _endpoint, 17 | uint256 _treasuryGasCap, 18 | uint256 _treasuryGasForFeeCap 19 | ) SendUln302(_endpoint, _treasuryGasCap, _treasuryGasForFeeCap) { 20 | testHelper = TestHelper(_verifyHelper); 21 | } 22 | 23 | function send(Packet calldata _packet, bytes calldata _options, bool _payInLzToken) 24 | public 25 | override 26 | returns (MessagingFee memory fee, bytes memory encodedPacket) 27 | { 28 | (fee, encodedPacket) = super.send(_packet, _options, _payInLzToken); 29 | testHelper.schedulePacket(encodedPacket, _options); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/LZSetup/mocks/SimpleMessageLibMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.0; 3 | 4 | import {SimpleMessageLib} from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/SimpleMessageLib.sol"; 5 | 6 | import {TestHelper} from "../TestHelper.sol"; 7 | 8 | contract SimpleMessageLibMock is SimpleMessageLib { 9 | // offchain packets schedule 10 | TestHelper public testHelper; 11 | 12 | constructor(address payable _verifyHelper, address _endpoint) SimpleMessageLib(_endpoint, address(0x0)) { 13 | testHelper = TestHelper(_verifyHelper); 14 | } 15 | 16 | function _handleMessagingParamsHook(bytes memory _encodedPacket, bytes memory _options) internal override { 17 | testHelper.schedulePacket(_encodedPacket, _options); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/MarketHelperMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // External 5 | import {Rebase} from "@boringcrypto/boring-solidity/contracts/libraries/BoringRebase.sol"; 6 | 7 | import {IMarketLiquidatorReceiver} from "tap-utils/interfaces/bar/IMarketLiquidatorReceiver.sol"; 8 | import {ITapiocaOracle} from "tap-utils/interfaces/periph/ITapiocaOracle.sol"; 9 | import {ISingularity} from "tap-utils/interfaces/bar/ISingularity.sol"; 10 | import {IYieldBox} from "tap-utils/interfaces/yieldbox/IYieldBox.sol"; 11 | import {IMarket, Module} from "tap-utils/interfaces/bar/IMarket.sol"; 12 | import {SingularityMock} from "./SingularityMock.sol"; 13 | 14 | contract MarketHelperMock { 15 | error ExchangeRateNotValid(); 16 | 17 | /// @notice Adds `collateral` from msg.sender to the account `to`. 18 | /// @param from Account to transfer shares from. 19 | /// @param to The receiver of the tokens. 20 | /// @param skim True if the amount should be skimmed from the deposit balance of msg.sender. 21 | /// False if tokens from msg.sender in `yieldBox` should be transferred. 22 | /// @param share The amount of shares to add for `to`. 23 | function addCollateral(address from, address to, bool skim, uint256 amount, uint256 share) 24 | external 25 | pure 26 | returns (Module[] memory modules, bytes[] memory calls) 27 | { 28 | modules = new Module[](1); 29 | calls = new bytes[](1); 30 | modules[0] = Module.Collateral; 31 | calls[0] = abi.encodeWithSelector(SingularityMock.addCollateral.selector, from, to, skim, amount, share); 32 | } 33 | 34 | /// @notice Removes `share` amount of collateral and transfers it to `to`. 35 | /// @param from Account to debit collateral from. 36 | /// @param to The receiver of the shares. 37 | /// @param share Amount of shares to remove. 38 | function removeCollateral(address from, address to, uint256 share) 39 | external 40 | pure 41 | returns (Module[] memory modules, bytes[] memory calls) 42 | { 43 | modules = new Module[](1); 44 | calls = new bytes[](1); 45 | modules[0] = Module.Collateral; 46 | calls[0] = abi.encodeWithSelector(SingularityMock.removeCollateral.selector, from, to, share); 47 | } 48 | 49 | /// @notice Sender borrows `amount` and transfers it to `to`. 50 | /// @param from Account to borrow for. 51 | /// @param to The receiver of borrowed tokens. 52 | /// @param amount Amount to borrow. 53 | function borrow(address from, address to, uint256 amount) 54 | external 55 | pure 56 | returns (Module[] memory modules, bytes[] memory calls) 57 | { 58 | modules = new Module[](1); 59 | calls = new bytes[](1); 60 | modules[0] = Module.Borrow; 61 | calls[0] = abi.encodeWithSelector(SingularityMock.borrow.selector, from, to, amount); 62 | } 63 | 64 | /// @notice View the result of a borrow operation. 65 | function borrowView(bytes calldata result) external pure returns (uint256 part, uint256 share) { 66 | (part, share) = abi.decode(result, (uint256, uint256)); 67 | } 68 | 69 | /// @notice Lever up: Borrow more and buy collateral with it. 70 | /// @param from The user who buys 71 | /// @param borrowAmount Amount of extra asset borrowed 72 | /// @param supplyAmount Amount of asset supplied (down payment) 73 | /// @param data LeverageExecutor data 74 | function buyCollateral(address from, uint256 borrowAmount, uint256 supplyAmount, bytes calldata data) 75 | external 76 | pure 77 | returns (Module[] memory modules, bytes[] memory calls) 78 | { 79 | modules = new Module[](1); 80 | calls = new bytes[](1); 81 | modules[0] = Module.Leverage; 82 | calls[0] = 83 | abi.encodeWithSelector(SingularityMock.buyCollateral.selector, from, borrowAmount, supplyAmount, data); 84 | } 85 | 86 | /// @notice view the result of a buyCollateral operation. 87 | function buyCollateralView(bytes calldata result) external pure returns (uint256 amountOut) { 88 | amountOut = abi.decode(result, (uint256)); 89 | } 90 | 91 | function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) { 92 | if (_returnData.length > 1000) return "Market: reason too long"; 93 | // If the _res length is less than 68, then the transaction failed silently (without a revert message) 94 | if (_returnData.length < 68) return "Market: no return data"; 95 | // solhint-disable-next-line no-inline-assembly 96 | assembly { 97 | // Slice the sighash. 98 | _returnData := add(_returnData, 0x04) 99 | } 100 | return abi.decode(_returnData, (string)); // All that remains is the revert string 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/StargateRouterMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | interface IStargateRouterMock { 8 | struct lzTxObj { 9 | uint256 dstGasForCall; 10 | uint256 dstNativeAmount; 11 | bytes dstNativeAddr; 12 | } 13 | 14 | function swap( 15 | uint16 _dstChainId, 16 | uint256 _srcPoolId, 17 | uint256 _dstPoolId, 18 | address payable _refundAddress, 19 | uint256 _amountLD, 20 | uint256 _minAmountLD, 21 | lzTxObj memory _lzTxParams, 22 | bytes calldata _to, 23 | bytes calldata _payload 24 | ) external payable; 25 | } 26 | 27 | interface IToftMock { 28 | function sgReceive(uint16, bytes memory, uint256, address, uint256 amountLD, bytes memory) external; 29 | } 30 | 31 | contract StargateFactoryMock { 32 | address public pool; 33 | 34 | constructor() { 35 | pool = address(new StargatePoolMock()); 36 | } 37 | 38 | function getPool(uint256) external view returns (address) { 39 | return pool; 40 | } 41 | } 42 | 43 | contract StargatePoolMock { 44 | function localDecimals() external pure returns (uint256) { 45 | return 18; 46 | } 47 | 48 | function sharedDecimals() external pure returns (uint256) { 49 | return 18; 50 | } 51 | 52 | function convertRate() external pure returns (uint256) { 53 | return 1; 54 | } 55 | } 56 | 57 | contract StargateRouterMock is IStargateRouterMock { 58 | IERC20 public token; 59 | 60 | constructor(IERC20 _token) { 61 | token = _token; 62 | } 63 | 64 | function swap( 65 | uint16, 66 | uint256, 67 | uint256, 68 | address payable _refundAddress, 69 | uint256 _amountLD, 70 | uint256, 71 | IStargateRouterMock.lzTxObj memory, 72 | bytes memory _to, 73 | bytes calldata 74 | ) external payable override { 75 | require(_amountLD > 0, "Stargate: cannot swap 0"); 76 | require(_refundAddress != address(0x0), "Stargate: _refundAddress cannot be 0x0"); 77 | 78 | address tempAddress; 79 | assembly { 80 | let offset := add(_to, 20) 81 | tempAddress := mload(offset) 82 | } 83 | 84 | token.transferFrom(msg.sender, address(this), _amountLD); 85 | token.transfer(tempAddress, _amountLD); 86 | 87 | IToftMock(tempAddress).sgReceive(0, "0x", 0, address(0), _amountLD, "0x"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/TOFTMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // Tapioca 5 | import {TOFTInitStruct, TOFTModulesInitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 6 | import {TOFT} from "contracts/tOFT/TOFT.sol"; 7 | 8 | /* 9 | __/\\\\\\\\\\\\\\\_____/\\\\\\\\\_____/\\\\\\\\\\\\\____/\\\\\\\\\\\_______/\\\\\_____________/\\\\\\\\\_____/\\\\\\\\\____ 10 | _\///////\\\/////____/\\\\\\\\\\\\\__\/\\\/////////\\\_\/////\\\///______/\\\///\\\________/\\\////////____/\\\\\\\\\\\\\__ 11 | _______\/\\\________/\\\/////////\\\_\/\\\_______\/\\\_____\/\\\_______/\\\/__\///\\\____/\\\/____________/\\\/////////\\\_ 12 | _______\/\\\_______\/\\\_______\/\\\_\/\\\\\\\\\\\\\/______\/\\\______/\\\______\//\\\__/\\\_____________\/\\\_______\/\\\_ 13 | _______\/\\\_______\/\\\\\\\\\\\\\\\_\/\\\/////////________\/\\\_____\/\\\_______\/\\\_\/\\\_____________\/\\\\\\\\\\\\\\\_ 14 | _______\/\\\_______\/\\\/////////\\\_\/\\\_________________\/\\\_____\//\\\______/\\\__\//\\\____________\/\\\/////////\\\_ 15 | _______\/\\\_______\/\\\_______\/\\\_\/\\\_________________\/\\\______\///\\\__/\\\_____\///\\\__________\/\\\_______\/\\\_ 16 | _______\/\\\_______\/\\\_______\/\\\_\/\\\______________/\\\\\\\\\\\____\///\\\\\/________\////\\\\\\\\\_\/\\\_______\/\\\_ 17 | _______\///________\///________\///__\///______________\///////////_______\/////_____________\/////////__\///________\///__ 18 | 19 | */ 20 | 21 | contract TOFTMock is TOFT { 22 | constructor(TOFTInitStruct memory _tOFTData, TOFTModulesInitStruct memory _moduleData) 23 | TOFT(_tOFTData, _moduleData) 24 | {} 25 | } 26 | -------------------------------------------------------------------------------- /test/TOFTTestHelper.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | 3 | pragma solidity 0.8.22; 4 | 5 | // External 6 | import {IERC20} from "@boringcrypto/boring-solidity/contracts/interfaces/IERC20.sol"; 7 | 8 | // Lz 9 | import {TestHelper} from "./LZSetup/TestHelper.sol"; 10 | 11 | // Tapioca 12 | import {TOFTInitStruct, TOFTModulesInitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 13 | import {ERC20WithoutStrategy} from "yieldbox/strategies/ERC20WithoutStrategy.sol"; 14 | import {Pearlmit, IPearlmit} from "tap-utils/pearlmit/Pearlmit.sol"; 15 | import {IWrappedNative} from "yieldbox/interfaces/IWrappedNative.sol"; 16 | import {YieldBoxURIBuilder} from "yieldbox/YieldBoxURIBuilder.sol"; 17 | import {TokenType} from "yieldbox/enums/YieldBoxTokenType.sol"; 18 | import {IYieldBox} from "yieldbox/interfaces/IYieldBox.sol"; 19 | import {IStrategy} from "yieldbox/interfaces/IStrategy.sol"; 20 | import {Cluster} from "tap-utils/Cluster/Cluster.sol"; 21 | import {SingularityMock} from "./SingularityMock.sol"; 22 | import {MagnetarMock} from "./MagnetarMock.sol"; 23 | import {YieldBox} from "yieldbox/YieldBox.sol"; 24 | import {TestUtils} from "./TestUtils.t.sol"; 25 | 26 | contract TOFTTestHelper is TestHelper, TestUtils { 27 | // function createMagnetar(address cluster, address owner) public returns (MagnetarV2) { 28 | // MagnetarMarketModule marketModule = new MagnetarMarketModule(); 29 | // return new MagnetarV2(cluster, owner, payable(marketModule)); 30 | // } 31 | 32 | function createSingularity( 33 | address _yieldBox, 34 | uint256 _collateralId, 35 | uint256 _assetId, 36 | address _collateral, 37 | address _asset 38 | ) public returns (SingularityMock) { 39 | return new SingularityMock(_yieldBox, _collateralId, _assetId, _collateral, _asset); 40 | } 41 | 42 | function createYieldBoxEmptyStrategy(address _yieldBox, address _erc20) public returns (ERC20WithoutStrategy) { 43 | return new ERC20WithoutStrategy(IYieldBox(_yieldBox), IERC20(_erc20)); 44 | } 45 | 46 | function registerYieldBoxAsset(address _yieldBox, address _token, address _strategy) public returns (uint256) { 47 | return YieldBox(_yieldBox).registerAsset(TokenType.ERC20, _token, IStrategy(_strategy), 0); 48 | } 49 | 50 | function createMagnetar(address cluster, IPearlmit pearlmit) public returns (MagnetarMock) { 51 | return new MagnetarMock(cluster, pearlmit); 52 | } 53 | 54 | function createYieldBox(Pearlmit pearlmit, address owner) public returns (YieldBox) { 55 | YieldBoxURIBuilder uriBuilder = new YieldBoxURIBuilder(); 56 | 57 | return new YieldBox(IWrappedNative(address(0)), uriBuilder, pearlmit, owner); 58 | } 59 | 60 | function createCluster(uint32 hostEid, address owner) public returns (Cluster) { 61 | return new Cluster(hostEid, owner); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/TapiocaOptionsBrokerMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import {PearlmitHandler, IPearlmit} from "tap-utils/pearlmit/PearlmitHandler.sol"; 6 | 7 | contract OTapMock { 8 | address public owner; 9 | 10 | // IERC721(oTap).isApprovedForAll(oTapOwner,_options.from) 11 | function ownerOf(uint256) external view returns (address) { 12 | return owner != address(0) ? owner : msg.sender; 13 | } 14 | 15 | function setOwner(address _owner) external { 16 | owner = _owner; 17 | } 18 | 19 | function isApprovedForAll(address, address) external pure returns (bool) { 20 | return true; 21 | } 22 | } 23 | 24 | contract TapiocaOptionsBrokerMock is PearlmitHandler { 25 | using SafeERC20 for IERC20; 26 | 27 | address public tapOFT; 28 | uint256 public paymentTokenAmount; 29 | address public oTapMock; 30 | 31 | error TapiocaOptionsBrokerMock_NotValid(); 32 | 33 | constructor(address _tap, IPearlmit _pearlmit) PearlmitHandler(_pearlmit) { 34 | tapOFT = _tap; 35 | oTapMock = address(new OTapMock()); 36 | } 37 | 38 | function setPaymentTokenAmount(uint256 _am) external { 39 | paymentTokenAmount = _am; 40 | } 41 | 42 | // @dev make sure to set `paymentTokenAmount` before call 43 | // @dev contract needs enough `tapOFT` for this to be executed successfully 44 | function exerciseOption(uint256, IERC20 _paymentToken, uint256 _tapAmount) external { 45 | // @dev 10% is subtracted to test out payment token refund 46 | uint256 actualPaymentTokenAmount = paymentTokenAmount - paymentTokenAmount * 1e4 / 1e5; 47 | 48 | // IERC20(address(_paymentToken)).safeTransferFrom(msg.sender, address(this), actualPaymentTokenAmount); 49 | bool isErr = 50 | pearlmit.transferFromERC20(msg.sender, address(this), address(_paymentToken), actualPaymentTokenAmount); 51 | if (isErr) revert TapiocaOptionsBrokerMock_NotValid(); 52 | IERC20(tapOFT).safeTransfer(msg.sender, _tapAmount); 53 | } 54 | 55 | function oTAP() external view returns (address) { 56 | return address(oTapMock); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/TestUtils.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | 3 | pragma solidity 0.8.22; 4 | 5 | // Tapioca 6 | import { 7 | YieldBoxApproveAllMsg, 8 | YieldBoxApproveAssetMsg, 9 | MarketPermitActionMsg 10 | } from "tap-utils/interfaces/periph/ITapiocaOmnichainEngine.sol"; 11 | import {ERC20PermitApprovalMsg, ERC20PermitStruct} from "tap-utils/interfaces/oft/ITOFT.sol"; 12 | 13 | import "forge-std/Test.sol"; 14 | 15 | contract TestUtils is Test { 16 | /** 17 | * @dev Helper to build an ERC20PermitApprovalMsg. 18 | * @param _permit The permit data. 19 | * @param _digest The typed data digest. 20 | * @param _token The token contract to receive the permit. 21 | * @param _pkSigner The private key signer. 22 | */ 23 | function __getERC20PermitData(ERC20PermitStruct memory _permit, bytes32 _digest, address _token, uint256 _pkSigner) 24 | internal 25 | pure 26 | returns (ERC20PermitApprovalMsg memory permitApproval_) 27 | { 28 | (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(_pkSigner, _digest); 29 | 30 | permitApproval_ = ERC20PermitApprovalMsg({ 31 | token: _token, 32 | owner: _permit.owner, 33 | spender: _permit.spender, 34 | value: _permit.value, 35 | deadline: _permit.deadline, 36 | v: v_, 37 | r: r_, 38 | s: s_ 39 | }); 40 | } 41 | 42 | function __getYieldBoxPermitAllData( 43 | ERC20PermitStruct memory _permit, 44 | address _target, 45 | bool _isPermit, 46 | bytes32 _digest, 47 | uint256 _pkSigner 48 | ) internal pure returns (YieldBoxApproveAllMsg memory permitApproval_) { 49 | (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(_pkSigner, _digest); 50 | 51 | permitApproval_ = YieldBoxApproveAllMsg({ 52 | target: _target, 53 | owner: _permit.owner, 54 | spender: _permit.spender, 55 | deadline: _permit.deadline, 56 | v: v_, 57 | r: r_, 58 | s: s_, 59 | permit: _isPermit 60 | }); 61 | } 62 | 63 | function __getYieldBoxPermitAssetData( 64 | ERC20PermitStruct memory _permit, 65 | address _target, 66 | bool _isPermit, 67 | bytes32 _digest, 68 | uint256 _pkSigner 69 | ) internal pure returns (YieldBoxApproveAssetMsg memory permitApproval_) { 70 | (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(_pkSigner, _digest); 71 | 72 | permitApproval_ = YieldBoxApproveAssetMsg({ 73 | target: _target, 74 | owner: _permit.owner, 75 | spender: _permit.spender, 76 | assetId: _permit.value, 77 | deadline: _permit.deadline, 78 | v: v_, 79 | r: r_, 80 | s: s_, 81 | permit: _isPermit 82 | }); 83 | } 84 | 85 | function __getMarketPermitData(MarketPermitActionMsg memory _permit, bytes32 _digest, uint256 _pkSigner) 86 | internal 87 | pure 88 | returns (MarketPermitActionMsg memory permitApproval_) 89 | { 90 | (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(_pkSigner, _digest); 91 | 92 | permitApproval_ = MarketPermitActionMsg({ 93 | target: _permit.target, 94 | owner: _permit.owner, 95 | spender: _permit.spender, 96 | value: _permit.value, 97 | deadline: _permit.deadline, 98 | v: v_, 99 | r: r_, 100 | s: s_, 101 | permitAsset: _permit.permitAsset 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/btt/mocks/ERC20Mock_test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.22; 3 | 4 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract ERC20Mock_test is ERC20 { 7 | uint8 _decimals; 8 | constructor(string memory _name, string memory _symbol, uint8 __decimals) ERC20(_name, _symbol) { 9 | _decimals = __decimals; 10 | } 11 | 12 | function decimals() public view override returns (uint8) { 13 | return _decimals; 14 | } 15 | function mint(address _to, uint256 _amount) public { 16 | _mint(_to, _amount); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/btt/mocks/FeeGetterMock_test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.22; 3 | 4 | contract FeeGetterMock_test { 5 | uint256 _percentage = 1e4; 6 | uint256 constant PERCENTAGE_PRECISION = 1e5; 7 | 8 | function setPercentage(uint256 _perc) external { 9 | require(_perc < PERCENTAGE_PRECISION, "not valid"); 10 | _percentage = _perc; 11 | } 12 | 13 | function getWrapFee(uint256) external pure returns (uint256) { 14 | return 0; 15 | } 16 | function getUnwrapFee(uint256 _amount) external view returns (uint256) { 17 | if (_percentage == 0) return _amount; 18 | return _amount * _percentage / PERCENTAGE_PRECISION; 19 | } 20 | } -------------------------------------------------------------------------------- /test/btt/unit/concrete/OFT/OFT_constructor.tree: -------------------------------------------------------------------------------- 1 | OFT_constructor.t.sol 2 | ├── when TOFT is created 3 | │ ├── when TOFT created with the right parameters 4 | │ │ ├── it should set the sender module 5 | │ │ ├── it should set the receiver module 6 | │ │ ├── it should set the market receiver module 7 | │ │ ├── it should set the options receiver module 8 | │ │ ├── it should set the generic receiver module 9 | │ │ ├── it should claim vault ownership 10 | │ │ ├── it should set the owner 11 | │ │ ├── it should set the host endpoint 12 | │ │ ├── it should have the right underlying token 13 | │ │ ├── it should return 18 decimals 14 | │ │ └── it should set YieldBox 15 | │ └── when TOFT created with wrong parameters 16 | │ ├── when empty address is used for TOFT 17 | │ │ ├── it should revert for sender module 18 | │ │ ├── it should revert for receiver module 19 | │ │ ├── it should revert for market receiver module 20 | │ │ ├── it should revert for options receiver module 21 | │ │ └── it should revert for generic receiver module 22 | │ └── when TOFT vault token is different 23 | │ └── it should revert 24 | └── when mTOFT is created 25 | ├── when mTOFT created with the right parameters 26 | │ ├── it should set the sender module 27 | │ ├── it should set the receiver module 28 | │ ├── it should set the market receiver module 29 | │ ├── it should set the options receiver module 30 | │ ├── it should set the generic receiver module 31 | │ ├── it should claim vault ownership 32 | │ ├── it should set the owner 33 | │ ├── it should set the host endpoint 34 | │ ├── it should have the right underlying token 35 | │ ├── it should return 18 decimals 36 | │ ├── it should set YieldBox 37 | │ └── it should set a default mint cap 38 | └── when mTOFT created with wrong parameters 39 | ├── when empty address is used for mTOFT 40 | │ ├── it should revert for sender module 41 | │ ├── it should revert for receiver module 42 | │ ├── it should revert for market receiver module 43 | │ ├── it should revert for options receiver module 44 | │ └── it should revert for generic receiver module 45 | └── when mTOFT vault token is different 46 | └── it should revert -------------------------------------------------------------------------------- /test/btt/unit/concrete/OFT/OFT_setters.tree: -------------------------------------------------------------------------------- 1 | TOFT_setters.t.sol 2 | ├── when called for mTOFT 3 | │ ├── when mTOFT rescueEth is called from non-owner 4 | │ │ └── it should revert 5 | │ ├── when mTOFT recueEth is called from owner with an invalid amount 6 | │ │ └── it should revert 7 | │ ├── when mTOFT rescueEth is called from owner with an available amount 8 | │ │ └── it should transfer ETH 9 | │ ├── when mTOFT setPause is called from non-owner and non-pauser 10 | │ │ └── it should revert 11 | │ ├── when mTOFT setPause is called from owner 12 | │ │ └── it should pause or unpause 13 | │ ├── when mTOFT setPause is called from Pauser 14 | │ │ └── it should pause or unpause 15 | │ ├── when withdrawFees called from non-owner 16 | │ │ └── it should revert 17 | │ ├── when withdrawFees called from owner 18 | │ │ └── it should withdraw vault fees 19 | │ ├── when extractUnderlying called from non balancer 20 | │ │ └── it should revert 21 | │ ├── when extractUnderlying called with amount 0 22 | │ │ └── it should revert 23 | │ ├── when extractUnderlying is called from balancer with valid amount 24 | │ │ └── it should withdraw from vault 25 | │ ├── when setOwnerState is called from non owner 26 | │ │ └── it should revert 27 | │ └── when setOwnerState is called from owner 28 | │ ├── when mintCap is smaller than supply 29 | │ │ └── it should revert 30 | │ └── it should update all different values 31 | └── when called for TOFT 32 | ├── when rescueEth is called from non-owner 33 | │ └── it should revert 34 | ├── when recueEth is called from owner with an invalid amount 35 | │ └── it should revert 36 | ├── when rescueEth is called from owner with an available amount 37 | │ └── it should transfer ETH 38 | ├── when setPause is called from non-owner and non-pauser 39 | │ └── it should revert 40 | ├── when setPause is called from owner 41 | │ └── it should pause or unpause 42 | └── when setPause is called from Pauser 43 | └── it should pause or unpause -------------------------------------------------------------------------------- /test/btt/unit/concrete/OFT/OFT_wrapUnwrap.tree: -------------------------------------------------------------------------------- 1 | TOFT_wrapUnwrap.t.sol 2 | ├── when called against TOFT 3 | │ ├── when TOFT wrap reverts 4 | │ │ ├── when TOFT wrap called for paused contract 5 | │ │ │ └── it should revert 6 | │ │ └── when TOFT wrap called from non host chain 7 | │ │ └── it should revert 8 | │ ├── when TOFT wrap does not revert 9 | │ │ ├── when TOFT wrap called for ETH 10 | │ │ │ ├── it should deposit ETH amount to the vault 11 | │ │ │ ├── it should mint OFT to the reicever 12 | │ │ │ └── it should increase supply 13 | │ │ ├── when TOFT wrap called for token with the same decimals 14 | │ │ │ ├── it should deposit token amount to the vault 15 | │ │ │ ├── it should mint OFT to the reicever 16 | │ │ │ └── it should increase supply 17 | │ │ └── when TOFT wrap called for token with the different decimals 18 | │ │ ├── it should deposit token amount to the vault 19 | │ │ ├── it should mint OFT to the reicever 20 | │ │ └── it should increase supply 21 | │ ├── when TOFT unwrap reverts 22 | │ │ ├── when TOFT unwrap called from non host chain 23 | │ │ │ └── it should revert 24 | │ │ └── when TOFT unwrap called with invalid amount 25 | │ │ └── it should revert 26 | │ └── when TOFT unwrap does not revert 27 | │ ├── when TOFT unwrap called for ETH 28 | │ │ ├── it should withdraw from vault 29 | │ │ └── it should decrease supply 30 | │ └── when TOFT unwrap called for token 31 | │ ├── it should withdraw from vault 32 | │ └── it should decrease supply 33 | └── when called against mTOFT 34 | ├── when mTOFT wrap reverts 35 | │ ├── when mTOFT wrap called for paused contract 36 | │ │ └── it should revert 37 | │ └── when mTOFT wrap called from non host chain 38 | │ └── it should revert 39 | ├── when mTOFT wrap does not revert 40 | │ ├── when mTOFT wrap called for ETH 41 | │ │ ├── it should deposit ETH amount to the vault 42 | │ │ ├── it should mint OFT to the reicever 43 | │ │ └── it should increase supply 44 | │ ├── when mTOFT wrap called for token with the same decimals 45 | │ │ ├── it should deposit token amount to the vault 46 | │ │ ├── it should mint OFT to the reicever 47 | │ │ └── it should increase supply 48 | │ └── when mTOFT wrap called for token with the different decimals 49 | │ ├── it should deposit token amount to the vault 50 | │ ├── it should mint OFT to the reicever 51 | │ └── it should increase supply 52 | ├── when mTOFT unwrap reverts 53 | │ ├── when mTOFT unwrap called from non host chain 54 | │ │ └── it should revert 55 | │ └── when mTOFT unwrap called with invalid amount 56 | │ └── it should revert 57 | └── when mTOFT unwrap does not revert 58 | ├── when mTOFT unwrap called for ETH 59 | │ ├── it should withdraw from vault 60 | │ ├── it should check fees 61 | │ └── it should decrease supply 62 | └── when mTOFT unwrap called for token 63 | ├── it should withdraw from vault 64 | ├── it should check fees 65 | └── it should decrease supply -------------------------------------------------------------------------------- /test/btt/unit/shared/OFT_Unit_Shared.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // Tapioca 5 | import {TOFTVault} from "contracts/tOFT/TOFTVault.sol"; 6 | import {BaseTOFT} from "contracts/tOFT/BaseTOFT.sol"; 7 | import {mTOFT} from "contracts/tOFT/mTOFT.sol"; 8 | import {TOFT} from "contracts/tOFT/TOFT.sol"; 9 | 10 | // tests 11 | import {Base_Test} from "../../Base_Test.t.sol"; 12 | import {ERC20Mock_test} from "../../mocks/ERC20Mock_test.sol"; 13 | 14 | abstract contract OFT_Unit_Shared is Base_Test { 15 | // ************* // 16 | // *** SETUP *** // 17 | // ************* // 18 | function setUp() public virtual override { 19 | super.setUp(); 20 | } 21 | 22 | // ***************** // 23 | // *** MODIFIERS *** // 24 | // ***************** // 25 | modifier whenPaused() { 26 | toft.setPause(true); 27 | mToft.setPause(true); 28 | _; 29 | } 30 | 31 | // *************** // 32 | // *** HELPERS *** // 33 | // *************** // 34 | function _wrapOft(uint256 amount, address token, address payable oft) internal 35 | whenApprovedViaERC20(token, address(this), address(pearlmit), type(uint256).max) 36 | whenApprovedViaPearlmit( 37 | TOKEN_TYPE_ERC20, 38 | token, 39 | 0, 40 | address(this), 41 | oft, 42 | type(uint200).max, 43 | uint48(block.timestamp) 44 | ) 45 | { 46 | 47 | __wrap(amount, token, oft); 48 | } 49 | function __wrap(uint256 amount, address token, address payable oft) private { 50 | { 51 | ERC20Mock_test(token).mint(address(this), amount); 52 | } 53 | TOFT(oft).wrap(address(this), address(this), amount); 54 | } 55 | } -------------------------------------------------------------------------------- /test/btt/utils/Constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | /// @notice Helper contract containing constants for testing. 5 | abstract contract Constants { 6 | // *************** // 7 | // *** GENERIC *** // 8 | // *************** // 9 | uint256 public constant SMALL_AMOUNT = 10 ether; 10 | uint256 public constant MEDIUM_AMOUNT = 100 ether; 11 | uint256 public constant LARGE_AMOUNT = 1000 ether; 12 | 13 | uint256 public constant LOW_DECIMALS_SMALL_AMOUNT = 10 * 1e6; 14 | uint256 public constant LOW_DECIMALS_MEDIUM_AMOUNT = 100 * 1e6; 15 | uint256 public constant LOW_DECIMALS_LARGE_AMOUNT = 1000 * 1e6; 16 | 17 | uint256 public constant USER_A_PKEY = 0x1; 18 | uint256 public constant USER_B_PKEY = 0x2; 19 | 20 | address public constant ADDRESS_ZERO = address(0); 21 | uint256 public constant VALUE_ZERO = 0; 22 | 23 | // **************** // 24 | // *** PEARLMIT *** // 25 | // **************** // 26 | /// @dev Constant value representing the ERC721 token type for signatures and transfer hooks 27 | uint256 constant TOKEN_TYPE_ERC721 = 721; 28 | /// @dev Constant value representing the ERC1155 token type for signatures and transfer hooks 29 | uint256 constant TOKEN_TYPE_ERC1155 = 1155; 30 | /// @dev Constant value representing the ERC20 token type for signatures and transfer hooks 31 | uint256 constant TOKEN_TYPE_ERC20 = 20; 32 | 33 | // ************* // 34 | // *** MTOFT *** // 35 | // ************* // 36 | uint256 constant DEFAULT_MINT_CAP = 1_000_000 * 1e18; 37 | } -------------------------------------------------------------------------------- /test/btt/utils/Events.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | abstract contract Events { 5 | 6 | // ************* // 7 | // *** mTOFT *** // 8 | // ************* // 9 | event Rebalancing(address indexed _balancer, uint256 indexed _amount, bool indexed _isNative); 10 | 11 | 12 | } -------------------------------------------------------------------------------- /test/btt/utils/Types.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | import {IMtoftFeeGetter} from "tap-utils/interfaces/oft/IMToftFeeGetter.sol"; 5 | 6 | abstract contract Types { 7 | struct SetOwnerStateData { 8 | address stargateRouter; 9 | IMtoftFeeGetter feeGetter; 10 | uint256 mintCap; 11 | // connected chains 12 | uint256 connectedChain; 13 | bool connectedChainState; 14 | // balancer 15 | address balancerStateAddress; 16 | bool balancerState; 17 | } 18 | } -------------------------------------------------------------------------------- /test/btt/utils/Utils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.22; 3 | 4 | // external 5 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | // utils 8 | import {Constants} from "./Constants.sol"; 9 | 10 | // Tapioca 11 | import {IWrappedNative} from "yieldbox/interfaces/IWrappedNative.sol"; 12 | import {IPearlmit, Pearlmit} from "tap-utils/pearlmit/Pearlmit.sol"; 13 | import {YieldBoxURIBuilder} from "yieldbox/YieldBoxURIBuilder.sol"; 14 | import {Cluster} from "tap-utils/Cluster/Cluster.sol"; 15 | import {YieldBox} from "yieldbox/YieldBox.sol"; 16 | 17 | 18 | // tests 19 | import {ERC20Mock_test} from "../mocks/ERC20Mock_test.sol"; 20 | import {Test} from "forge-std/Test.sol"; 21 | 22 | /// @notice Helper contract containing utilities. 23 | abstract contract Utils is Test, Constants { 24 | // ************************ // 25 | // *** GENERAL: HELPERS *** // 26 | // ************************ // 27 | /// @dev Stops the active prank and sets a new one. 28 | function _resetPrank(address msgSender) internal { 29 | vm.stopPrank(); 30 | vm.startPrank(msgSender); 31 | } 32 | 33 | // ********************** // 34 | // *** DEPLOY HELPERS *** // 35 | // ********************** // 36 | // ERC20Mock_test 37 | function _createToken(string memory _name, uint8 _decimals) internal returns (ERC20Mock_test) { 38 | ERC20Mock_test _token = new ERC20Mock_test(_name, _name, _decimals); 39 | vm.label(address(_token), _name); 40 | return _token; 41 | } 42 | 43 | // Creates user from Private key 44 | function _createUser(uint256 _key, string memory _name) internal returns (address) { 45 | address _user = vm.addr(_key); 46 | vm.deal(_user, LARGE_AMOUNT); 47 | vm.label(_user, _name); 48 | return _user; 49 | } 50 | 51 | // Creates real Pearlmit 52 | function _createPearlmit(address _owner) internal returns (Pearlmit) { 53 | Pearlmit pearlmit = new Pearlmit("Pearlmit Test", "1", _owner, 0); 54 | vm.label(address(pearlmit), "Pearlmit Test"); 55 | return pearlmit; 56 | } 57 | 58 | // Creates real Cluster 59 | function _createCluster(address _owner) internal returns (Cluster) { 60 | Cluster cluster = new Cluster(0, _owner); 61 | vm.label(address(cluster), "Cluster Test"); 62 | return cluster; 63 | } 64 | 65 | // Creates real YieldBox 66 | function _createYieldBox(address _owner, Pearlmit _pearlmit) internal returns (YieldBox) { 67 | YieldBoxURIBuilder uriBuilder = new YieldBoxURIBuilder(); 68 | YieldBox yieldBox = new YieldBox(IWrappedNative(address(0)), uriBuilder, _pearlmit, _owner); 69 | return yieldBox; 70 | } 71 | 72 | // ************************ // 73 | // *** APPROVAL HELPERS *** // 74 | // ************************ // 75 | 76 | function _approveViaERC20(address token, address from, address operator, uint256 amount) internal { 77 | _resetPrank({msgSender: from}); 78 | IERC20(token).approve(address(operator), amount); 79 | } 80 | 81 | 82 | function _approveViaPearlmit( 83 | uint256 tokenType, 84 | address token, 85 | IPearlmit pearlmit, 86 | address from, 87 | address operator, 88 | uint256 amount, 89 | uint256 expiration, 90 | uint256 tokenId 91 | ) internal { 92 | // Reset prank 93 | _resetPrank({msgSender: from}); 94 | 95 | // Approve via pearlmit 96 | pearlmit.approve(tokenType, token, tokenId, operator, uint200(amount), uint48(expiration)); 97 | } 98 | 99 | function _approveYieldBoxAssetId(YieldBox yieldBox, address from, address operator, uint256 assetId) internal { 100 | _resetPrank({msgSender: from}); 101 | yieldBox.setApprovalForAsset(operator, assetId, true); 102 | } 103 | 104 | function _approveYieldBoxForAll(YieldBox yieldBox, address from, address operator) internal { 105 | _resetPrank({msgSender: from}); 106 | yieldBox.setApprovalForAll(operator, true); 107 | } 108 | } -------------------------------------------------------------------------------- /test_hardhat/test.utils.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { useUtils } from '../scripts/utils'; 3 | import hre, { ethers, network } from 'hardhat'; 4 | import { time } from '@nomicfoundation/hardhat-network-helpers'; 5 | import { MTapiocaOFT } from 'tapioca-sdk/dist/typechain/tapiocaz'; 6 | import { Cluster__factory } from '@tapioca-sdk/typechain/tapioca-periphery'; 7 | 8 | export const register = async (hre: HardhatRuntimeEnvironment) => { 9 | const { ethers } = hre; 10 | const signer = (await ethers.getSigners())[0]; 11 | 12 | const { deployYieldBoxMock } = useUtils(hre, signer); 13 | 14 | const utils = useUtils(hre, signer); 15 | return { 16 | signer, 17 | YieldBox_0: await deployYieldBoxMock(), 18 | YieldBox_10: await deployYieldBoxMock(), 19 | utils, 20 | }; 21 | }; 22 | 23 | export async function setBalance(addr: string, ether: number) { 24 | await ethers.provider.send('hardhat_setBalance', [ 25 | addr, 26 | ethers.utils.hexStripZeros(ethers.utils.parseEther(String(ether))._hex), 27 | ]); 28 | } 29 | 30 | export async function impersonateAccount(address: string) { 31 | return network.provider.request({ 32 | method: 'hardhat_impersonateAccount', 33 | params: [address], 34 | }); 35 | } 36 | export async function registerFork() { 37 | await impersonateAccount(process.env.BINANCE_WALLET_ADDRESS!); 38 | const binanceWallet = await ethers.getSigner( 39 | process.env.BINANCE_WALLET_ADDRESS!, 40 | ); 41 | 42 | /** 43 | * INITIAL SETUP 44 | */ 45 | const deployer = (await ethers.getSigners())[0]; 46 | 47 | const eoa1 = new ethers.Wallet( 48 | ethers.Wallet.createRandom().privateKey, 49 | ethers.provider, 50 | ); 51 | await setBalance(eoa1.address, 100000); 52 | 53 | const { deployYieldBoxMock } = useUtils(hre, deployer); 54 | 55 | const yieldBox = await deployYieldBoxMock(); 56 | 57 | const balancer = await ( 58 | await hre.ethers.getContractFactory('Balancer') 59 | ).deploy( 60 | process.env.STARGATE_ROUTER_ETH!, // stargateRouterETHMock.address, //routerETH 0x150f94b44927f078737562f0fcf3c95c01cc2376 61 | process.env.STARGATE_ROUTER!, //stargateRouterMock.address, //router 0x8731d54e9d02c286767d56ac03e8037c07e01e98 62 | deployer.address, 63 | ); 64 | 65 | const Cluster = new Cluster__factory(deployer); 66 | const cluster = await Cluster.deploy( 67 | process.env.LZ_ENDPOINT!, 68 | deployer.address, 69 | ); 70 | 71 | return { 72 | deployer, 73 | yieldBox, 74 | balancer, 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ts-node": { 3 | "swc": true 4 | }, 5 | "compilerOptions": { 6 | "target": "es2020", 7 | "module": "commonjs", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "outDir": "dist", 11 | "declaration": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "baseUrl": ".", 15 | "paths": { 16 | "@tapioca-sdk/*": ["gitmodule/tapioca-sdk/src/*"], 17 | "@typechain/*": ["gen/typechain/*"], 18 | "@tapioca-periph/config":["gitmodule/tapioca-periph/tasks/deploy/DEPLOY_CONFIG.ts"], 19 | "@tapioca-bar/config":["gitmodule/tapioca-bar/tasks/deploy/DEPLOY_CONFIG.ts"], 20 | } 21 | }, 22 | "include": [ 23 | "./hardhat.config.ts", 24 | "./hardhat_scripts", 25 | "./test_hardhat", 26 | "./gen/typechain", 27 | "./tasks", 28 | "./deploy" 29 | ] 30 | } 31 | --------------------------------------------------------------------------------