Full details of the Twyne protocol are found at https://twyne.gitbook.io/
The Twyne protocol builds on Euler Finance's EVC and EVK to offer lender extra yield and borrowers extra borrowing power. As of the release on June 16, the Twyne codebase contains 667 lines of custom code in the src/twyne and src/TwyneFactory directories.
Remember to use forge install to install all dependencies before running tests. If you are setting up your .env for the first time: cp .env.example .env
forge test -vvforge test --match-test "test_e_second_creditDeposit" -vvforge test --match-contract "EulerTestEdgeCases|EulerLiquidationTest" -vvNote: using llama RPCs like https://eth.llamarpc.com can result in errors due to rate limiting. Blutgang is recommended to avoid this.
Assuming you have cloned py-fuzz repo in the same directory as this repo, and you have installed all dependencies in py-fuzz:
source ../py-fuzz/venv/bin/activate
forge test --match-contract "testFuzz_e_IRMTwyneCurve" -vvforge coverage --no-match-coverage "test|script"And if you want a lcov file to use with a VSCode extension such as Coverage Gutters, add the --report lcov argument to the above.
To save a gas-snapshot of tests:
forge snapshot --match-path "test/twyne/*"To compare the gas consumption with the saved gas-snapshot:
forge snapshot --match-path "test/twyne/*" --diffTo view the gas consumption of contract functions:
forge test --match-path "test/twyne/EulerTestNormalActions.t.sol" --gas-reportNote: If you will reuse an existing EVK deployment instead of spending gas on a fresh EVK deployment, keep in mind that a new GenericFactory should be deployed (and address updated in the Base deployment script) if you do NOT want the old intermediate vaults showing up on the frontend.
- Clone evk-periphery and checkout the
deployment-scriptsbranch. Runforge install. You need to have the euler-interfaces repository cloned to the same parent directory where evk-periphery is located. Edit the script at scripts/50_CoreAndPeriphery.s.sol to remove logic related to "Deploying EUL" and "Deploying rEUL" because these are not needed. You should make sure to delete the euler-interfaces/address/8453 directory (or whichever chain you are deploying to) and evk-periphery/script/deployments/* directories. - Set .env to specify the values
DEPLOYMENT_RPC_URLandDEPLOYER_KEY, then run Euler's deploy script withFORCE_MULTISIG_ADDRESSES=true ./script/interactiveDeployment.shand choose option 50. Choose "No" for the OFT Adapter question and enter the deployer address for any address prompts and press enter to use the default value for Uniswap and other prompts. - Copy the output files with deployed addresses at evk-periphery/script/deployments/onchain/8453/output to the tech-notes/ repo in a new directory for this specific deployment to store the addresses in a shared place.
- Now back in the twyne-contracts repo, make sure the .env file has the production private keys with gas to deploy to Base. Also edit script/TwyneDeployEulerIntegration.s.sol so
productionSetup()contains the addresses of the contracts just deployed by the EVK deploy script. Finally, edit script/TwyneDeployEulerIntegration.s.sol to comment out everything inrun()exceptproductionSetup()andtwyneStuff(). - Run the Twyne deploy script:
forge script script/TwyneDeployEulerIntegration.s.sol:TwyneDeployEulerIntegration --broadcast -vv --verify --verifier etherscan --verifier-url https://api.basescan.org/api --etherscan-api-key <YOUR_ETHERSCAN_API_KEY> - Copy the TwyneAddresses_output.json output file to the tech-notes repo to store the addresses in a shared place and push the commit.
Use forge to verify contracts after deploying.
-
Verify
EulerRouterUse
cast abi-encode "constructor(address,address)"to generate the constructor calldata needed for verification:forge verify-contract --watch --chain base 0xb18e8F37F51A5C5ccA92aF0B926aA25E7B4Bda77 lib/euler-price-oracle/src/EulerRouter.sol:EulerRouter --verifier etherscan --etherscan-api-key <YOUR_ETHERSCAN_API_KEY> --constructor-args 0x000000000000000000000000c36aed7b7816aa21b660a33a637a8f9b9b70ad6c000000000000000000000000224b9735166658a049bc8813be062dca65a3a949
-
Verify intermediate vault's proxy contract:
forge verify-contract --chain base --num-of-optimizations 20000 --watch --constructor-args $(cast abi-encode "constructor(bytes)" $(cast abi-encode --packed "(bytes4,bytes)" 00000000 $(cast abi-encode --packed "(address,address,address)" <COLLATERAL_ADDRESS> <ORACLE_ADDRESS> <UNIT_OF_ACCOUNT>))) --verifier etherscan --etherscan-api-key <YOUR_ETHERSCAN_API_KEY> --compiler-version 0.8.24+commit.e11b9ed9 0xB49414341e06986FE83f17c971cCA14bD4362aF0 lib/euler-vault-kit/src/GenericFactory/BeaconProxy.sol:BeaconProxy
- Update TwyneAdminTransfer.s.sol script to include the correct multisig address that will become the new owner of the Twyne protocol. Also make sure that TwyneAddresses_output.json exists in the main directory with the on-chain addresses that you wish to change ownership for.
- Update the .env file to set
DEPLOYER_PRIVATE_KEYto the deployer EOA's private key. Test the deploy script without the broadcast flag to verify it works:forge script script/TwyneAdminTransfer.s.sol:TwyneAdminTransfer -vv - Run the same script command with the
--broadcastflag
- Make sure the proper commit hash of the codebase is checked out
- Make sure that TwyneAddresses_output.json exists in the main directory with the on-chain addresses that you wish to verify.
- Update the .env file to set
DEPLOYER_ADDRESSandDEPLOYER_PRIVATE_KEYwith the deployer EOA info. - Test the periphery deployment script runs without errors:
forge script script/TwynePeriphery.s.sol:TwynePeriphery -vv - To run the periphery deployment script on-chain:
forge script script/TwynePeriphery.s.sol:TwynePeriphery --broadcast -vv --verify --verifier etherscan --verifier-url https://api.basescan.org/api --etherscan-api-key <YOUR_ETHERSCAN_API_KEY> - Add the new contract address to the tech-notes repo in the appropriate JSON file
- Make sure that TwyneAddresses_output.json exists in the main directory with the on-chain addresses that you wish to verify.
- Run the post deployment checks script:
forge script script/PostDeploymentCheck.s.sol:PostDeploymentCheck -vv
Note: After the first deployment on a chain, a Gnosis Safe address is TwyneVaultManager's owner. To add new assets, TwyneAddVaultPair.s.sol publishes the required transactions to Gnosis Safe UI.
- Update TwyneAddVaultPair.s.sol script to include the correct asset addresses that will be added to the Twyne protocol. Also make sure that TwyneAddresses_output.json exists in the main directory with the on-chain addresses that you wish to change ownership for.
- Update the .env file to set
DEPLOYER_PRIVATE_KEYto the deployer EOA's private key. For eachPHASE = [0,1], (update PHASE, SEND, SAFE vars in the script, and the following env vars), execute the following command. In Phase 0, the deployer EOA is executing the actions. In Phase 1, the Safe will be executing the on-chain actions, which is when the hardware wallet args are needed in Phase 1. In order to find the correct MNEMONIC_INDEX, you need to find the index of your address in your wallet using Rabby or another wallet that displays all of your addresses.
CHAIN=base WALLET_TYPE=ledger MNEMONIC_INDEX=3 forge script script/TwyneAddVaultPair.s.sol --verify --verifier etherscan --verifier-url https://api.basescan.org/api --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>- Run the same script command from step 2 with the
--broadcastflag to execute on-chain. Do this for Phase 0 and then Phase 1.
- Update TwyneSetCaps.s.sol script to include the correct cap values. These values are acquired from the euler_supply_cap.py script found at twyne-frontend/scripts/euler_supply_cap.py. After updating the cap values, also make sure that vaultManager is set properly in the script.
- (NOTE: This step doesn't work on-chain because the vaultManager owner is the multisig). Update the .env file to set
DEPLOYER_PRIVATE_KEYto the deployer EOA's private key. Test the deploy script without the broadcast flag to verify it works:forge script script/TwyneSetCaps.s.sol:TwyneSetCaps -vv - Run the same script command with the
--broadcastflag
- Make sure that TwyneAddresses_output.json exists in the main directory with the on-chain addresses that you wish to check for correctness.
- Test the deploy script without the broadcast flag to verify it works:
forge script script/PostDeploymentCheck.s.sol:PostDeploymentCheck -vv
These steps cover testing and performing a proxy (beacon) upgrade for collateral vaults (e.g., upgrading to v1 where teleport supports Euler subaccounts).
- Create a test file under
test/upgradesto validate the specific contract changes using onchain addresses and a simulated proxy upgrade (e.g.,test_e_teleportSubAccountProxyUpgrade). - Add another test that performs a simulated proxy upgrade and verifies basic functions (deposit, borrow, withdraw, repay) work before and after the upgrade (e.g.,
test_e_postUpgradeCollateralVault). - Add a test that is intended to run after the real onchain upgrade to verify the basic functions continue to work (e.g.,
test_e_postRealUpgradeCollateralVault).
-
Update
script/TwyneUpgradeBeacon.s.sol:- Set
SAFEto your protocol Gnosis Safe address. - Set
PHASEto0to deploy the new collateral vault implementation. Phase 0 writesUpgradeBeaconPhase0_<chainid>.jsonfor Phase 1 to consume. - Ensure
TwyneAddresses_output.jsonexists at the repo root and includes.collateralVaultFactoryand.deployerExampleCollateralVault.
- Set
-
Run Phase 0 (dry run first):
FORGE_PROFILE=base CHAIN=base WALLET_TYPE=ledger MNEMONIC_INDEX=3 forge script script/TwyneUpgradeBeacon.s.sol --verify --verifier etherscan --verifier-url https://api.basescan.org/api --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>
-
Run Phase 0 with broadcast:
FORGE_PROFILE=base CHAIN=base WALLET_TYPE=ledger MNEMONIC_INDEX=3 forge script script/TwyneUpgradeBeacon.s.sol --verify --verifier etherscan --verifier-url https://api.basescan.org/api --etherscan-api-key <YOUR_ETHERSCAN_API_KEY> --broadcast
-
Update
PHASEto1inscript/TwyneUpgradeBeacon.s.soland run Phase 1 to upgrade theUpgradeableBeaconthat backs collateral vault proxies:FORGE_PROFILE=base CHAIN=base WALLET_TYPE=ledger MNEMONIC_INDEX=3 SENDER=<ADDRESS> forge script script/TwyneUpgradeBeacon.s.sol --ffi
Only
WALLET_TYPE=ledgeris currently supported. -
Validate the upgrade:
-
Confirm
collateralVaultFactory.collateralVaultBeacon(<TARGET_VAULT>)now points to the new implementation (visible on the block explorer), and the version increments on a sample vault. -
Optionally run the post-deployment checks script:
forge script script/PostDeploymentCheck.s.sol:PostDeploymentCheck -vv. -
Run the post-upgrade validation test:
forge test --match-contract EulerTestEdgeCases --match-test test_e_postRealUpgradeCollateralVault -vv