Address Contract Verified
Address
0xA49fBb307A33F6aCD694509Ba09913BB97A069EB
Balance
0 ETH
Nonce
1
Code Size
10753 bytes
Creator
0x74DE73F2...94cE at tx 0xd6a7539f...dd8de6
Indexed Transactions
0 (1 on-chain, 1.4% indexed)
Contract Bytecode
10753 bytes
0x60806040526004361015610011575f80fd5b5f3560e01c806306fdde0314610fb857806307a79fc714610f745780632e1a7d4d14610f295780633737bcb414610ee557806338d52e0f1461090d5780633a2c677714610ea15780633f5bbcda14610e645780634641257d14610d9e5780635144107d14610d7d57806353e3cb5f14610d6057806353ee961e14610cd75780635504bd5f14610c9a5780635c975abb14610c785780635dc565c214610c5b5780635fcbd28514610b965780636a4874a114610c175780636f00020114610bda5780637158da7c14610b96578063722713f714610b7c578063853828b614610a665780639bec828814610a225780639ce110d7146109fa5780639f19915c146109b6578063a6f19c8414610972578063a879851014610951578063a919802d1461090d578063aaf5eb68146108eb578063ab0ebba0146108a7578063b6b55f251461064e578063bb42e5cd14610590578063ccb6a270146105b2578063d03153aa14610595578063d6ef236514610590578063d8c143f71461043b578063db2e21bc1461030d578063ec47df94146102c9578063eded3fda146102a7578063f0fa55a91461021d578063f9759518146101fc5763fbfa77cf146101d1575f80fd5b346101f8575f3660031901126101f8575f546040516001600160a01b039091168152602090f35b5f80fd5b346101f8575f3660031901126101f857602060405166b1a2bc2ec500008152f35b346101f85760203660031901126101f8575f54600435906001600160a01b031633036102995766b1a2bc2ec50000811161028a5760407f07af09e2b23ebab5dd29fa2271d6ca4795031f308caf26ca605f4a719ffeed6191600554908060055582519182526020820152a1005b638199f5f360e01b5f5260045ffd5b6282b42960e81b5f5260045ffd5b346101f8575f3660031901126101f85760206102c1611326565b604051908152f35b346101f8575f3660031901126101f8576040517f0000000000000000000000004684ac2e15cc25174134ed7586a72399b123da7d6001600160a01b03168152602090f35b346101f8575f3660031901126101f8575f546001600160a01b03163303610299576002600454146102995760026004556103456119aa565b5f6002556040516370a0823160e01b8152306004820152907f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76020836024816001600160a01b0385165afa8015610430575f906103fd575b60209350806103df575b50507ff639e28400db464cf64a55c42636df1b181f180f2ad8519cbcec893760e6e6b782604051838152a16001600455604051908152f35b5f546103f6926001600160a01b0390911690611612565b82806103a7565b506020833d602011610428575b8161041760209383611061565b810103126101f8576020925161039d565b3d915061040a565b6040513d5f823e3d90fd5b346101f8575f3660031901126101f8575f546001600160a01b03163303610299576002600454146102995760026004555f60035460ff8082161516809160ff1916176003556104cb575b6020907f657d9755585644ef16264e8f7212a528a5f6ef63ab67fff29d9f533b5f8b448f604060ff60035416815190151581528385820152a16001600455604051908152f35b506104d461198c565b5f6002556040516370a0823160e01b8152306004820152907f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76020836024816001600160a01b0385165afa8015610430575f9061055d575b602093508061053f575b50509050610485565b5f54610556926001600160a01b0390911690611612565b8280610536565b506020833d602011610588575b8161057760209383611061565b810103126101f8576020925161052c565b3d915061056a565b61103f565b346101f8575f3660031901126101f8576020600554604051908152f35b346101f85760203660031901126101f8576004356001600160a01b038116908190036101f8575f546001600160a01b03811661063f57811561063f57600154906001600160a01b0382163303610299576001600160a01b031990811683175f90815591166001557f733be0fdaf77621eb46ef87502d63fea13e94bc406be6ae17d75d783e0ceaa809080a2005b63e6c4247b60e01b5f5260045ffd5b346101f85760203660031901126101f8575f54600435906001600160a01b031633036102995760ff60035416610898576002600454146102995760026004558015610889576040516323b872dd60e01b60208201908152336024830152306044830152606480830184905282527f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76001600160a01b031692915f9182916106f6608482611061565b519082865af16107046115bc565b9015908115610859575b5061084a57600254918183018093116108365760406108136107e17f06da3309189fa49284f335d2c2bcb4cb0b8ad2a59ad92a9bdebeeb8f1ceba51193602096600255610759611c30565b506005549086907f000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4906107989083906001600160a01b03841690612633565b7f0000000000000000000000000000000000000000000000000000000000000001907f0000000000000000000000000000000000000000000000000000000000000000906127bc565b7fbe8e7aa863a4607c244d308cfc4b7a04afbf9846de82821448f25171c75ccb8c8380518781528389820152a1612390565b9261081d846125a0565b5081519081528385820152a16001600455604051908152f35b634e487b7160e01b5f52601160045260245ffd5b6312171d8360e31b5f5260045ffd5b805180151592508261086e575b50508361070e565b61088192506020809183010191016115fa565b158380610866565b631f2a200560e01b5f5260045ffd5b63e628b94960e01b5f5260045ffd5b346101f8575f3660031901126101f8576040517f000000000000000000000000eef0c605546958c1f899b6fb336c20671f9cd49f6001600160a01b03168152602090f35b346101f8575f3660031901126101f8576020604051670de0b6b3a76400008152f35b346101f8575f3660031901126101f8576040517f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76001600160a01b03168152602090f35b346101f8575f3660031901126101f8576020604051662386f26fc100008152f35b346101f8575f3660031901126101f8576040517f000000000000000000000000f3c43e7d722963b9569d1e39873df9e2dfe8c0876001600160a01b03168152602090f35b346101f8575f3660031901126101f8576040517f000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf46001600160a01b03168152602090f35b346101f8575f3660031901126101f8576001546040516001600160a01b039091168152602090f35b346101f8575f3660031901126101f8576040517f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e6001600160a01b03168152602090f35b346101f8575f3660031901126101f8575f546001600160a01b0316330361029957600260045414610299576002600455610a9e61198c565b5f6002556040516370a0823160e01b8152306004820152907f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76020836024816001600160a01b0385165afa8015610430575f90610b49575b6020935080610b2b575b50505f51602061298c5f395f51905f52604080515f1981528385820152a16001600455604051908152f35b5f54610b42926001600160a01b0390911690611612565b8280610b00565b506020833d602011610b74575b81610b6360209383611061565b810103126101f85760209251610af6565b3d9150610b56565b346101f8575f3660031901126101f85760206102c16111f0565b346101f8575f3660031901126101f8576040517f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b03168152602090f35b346101f8575f3660031901126101f85760206040517f0000000000000000000000000000000000000000000000000000000000000001600f0b8152f35b346101f8575f3660031901126101f8576040517f000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd526001600160a01b03168152602090f35b346101f8575f3660031901126101f857602060405162015f908152f35b346101f8575f3660031901126101f857602060ff600354166040519015158152f35b346101f8575f3660031901126101f85760206040517f0000000000000000000000000000000000000000000000000000000000000000600f0b8152f35b346101f8575f3660031901126101f85760405163722713f760e01b8152602081600481305afa908115610430575f91610d2e575b506002549081811115610d24576020916102c1916110c9565b505060205f6102c1565b90506020813d602011610d58575b81610d4960209383611061565b810103126101f8575181610d0b565b3d9150610d3c565b346101f8575f3660031901126101f8576020600254604051908152f35b346101f8575f3660031901126101f85760206040516611c37937e080008152f35b346101f8575f3660031901126101f8575f546001600160a01b031633036102995760ff60035416610898576002600454146102995760026004556020610e2e610de5611c30565b7f0000000000000000000000003e7d1eab13ad0104d2750b8863b489d65364e32d907f000000000000000000000000eef0c605546958c1f899b6fb336c20671f9cd49f906117d9565b7f8e55ccfc9778ff8eba1646d765cf1982537ce0f9257054a17b48aad74525018382604051838152a16001600455604051908152f35b346101f8575f3660031901126101f85760206040517f0000000000000000000000000000000000000000000000000000000000000001600f0b8152f35b346101f8575f3660031901126101f8576040517f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d6001600160a01b03168152602090f35b346101f8575f3660031901126101f8576040517f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b03168152602090f35b346101f85760203660031901126101f8575f546001600160a01b03163303610299576002600454146102995760026004556020610f676004356110d6565b6001600455604051908152f35b346101f8575f3660031901126101f8576040517f0000000000000000000000003e7d1eab13ad0104d2750b8863b489d65364e32d6001600160a01b03168152602090f35b346101f8575f3660031901126101f857611017604051610fd9604082611061565b602081527f55534454202d3e20706d5553442f637276555344204c50205374726174656779602082015260405191829160208352602083019061101b565b0390f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346101f8575f3660031901126101f857602060405167016345785d8a00008152f35b601f909101601f19168101906001600160401b0382119082101761108457604052565b634e487b7160e01b5f52604160045260245ffd5b8181029291811591840414171561083657565b81156110b5570490565b634e487b7160e01b5f52601260045260245ffd5b9190820391821161083657565b9081156108895760405163722713f760e01b8152602081600481305afa908115610430575f916111be575b5080156111b8578083116111b0575b60406111355f51602061298c5f395f51905f529261113086600254611098565b6110ab565b9361113f81611426565b6002549095808211156111a857611155916110c9565b6002558461116c575b8151908152846020820152a1565b5f546111a39086906001600160a01b03167f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7611612565b61115e565b50505f611155565b915081611110565b505f9150565b90506020813d6020116111e8575b816111d960209383611061565b810103126101f857515f611101565b3d91506111cc565b6111f86116a0565b600181106112e857604051630176f71760e71b8152906020826004817f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b03165afa918215610430575f926112b2575b506112656112af92670de0b6b3a764000092611098565b047f0000000000000000000000003e7d1eab13ad0104d2750b8863b489d65364e32d907f000000000000000000000000eef0c605546958c1f899b6fb336c20671f9cd49f906117d9565b90565b91506020823d6020116112e0575b816112cd60209383611061565b810103126101f85790519061126561124e565b3d91506112c0565b505f90565b908160209103126101f857516001600160a01b03811681036101f85790565b6001600160a01b0391821681529116602082015260400190565b60405163022e74a560e61b81527f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d6001600160a01b031690602081600481855afa9182156104305761139a926020925f916113f9575b506040518080958194637a27db5760e01b835230906004840161130c565b03916001600160a01b03165afa5f91816113c5575b506113b957505f90565b6501d1a94a2000900490565b9091506020813d6020116113f1575b816113e160209383611061565b810103126101f85751905f6113af565b3d91506113d4565b6114199150833d851161141f575b6114118183611061565b8101906112ed565b5f61137c565b503d611407565b905f91611431611c30565b5061143a6111f0565b9060018210611555576040516370a0823160e01b81523060048201527f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d6001600160a01b0316929091602083602481875afa928315610430575f9361156d575b506001831061156457906111306114b19284611098565b9080821161155c575b5060018110611555576114eb5f92602092604051948580948193635d043b2960e11b83523090309060048501612028565b03925af18015610430575f90611521575b61150a915060055490612048565b806115125750565b6112af919250600554906121ee565b506020813d60201161154d575b8161153b60209383611061565b810103126101f85761150a90516114fc565b3d915061152e565b505f925050565b90505f6114ba565b505f9450505050565b9092506020813d602011611599575b8161158960209383611061565b810103126101f85751915f61149a565b3d915061157c565b6001600160a01b039091168152602081019190915260400190565b3d156115f5573d906001600160401b03821161108457604051916115ea601f8201601f191660200184611061565b82523d5f602084013e565b606090565b908160209103126101f8575180151581036101f85790565b5f92918361163c61164a8295604051928391602083019663a9059cbb60e01b8852602484016115a1565b03601f198101835282611061565b51926001600160a01b03165af161165f6115bc565b9015908115611670575b5061084a57565b8051801515925082611685575b50505f611669565b61169892506020809183010191016115fa565b155f8061167d565b6040516370a0823160e01b81523060048201527f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d6001600160a01b031690602081602481855afa908115610430575f91611761575b506001811061175b576020906024604051809481936303d1689d60e11b835260048301525afa908115610430575f9161172c575090565b90506020813d602011611753575b8161174760209383611061565b810103126101f8575190565b3d915061173a565b50505f90565b90506020813d60201161178b575b8161177c60209383611061565b810103126101f857515f6116f5565b3d915061176f565b51906001600160501b03821682036101f857565b908160a09103126101f8576117bb81611793565b916020820151916040810151916112af608060608401519301611793565b916001831061198557604051633fabe5a360e21b81529160a090839060049082906001600160a01b03165afa918215610430575f905f935f915f9161195a575b505f851392831593611943575b5050811561192c575b506118b657604051633fabe5a360e21b8152919060a090839060049082906001600160a01b03165afa918215610430575f905f935f915f916118f1575b505f8513928315936118da575b505081156118c3575b506118b6576118919192611098565b9064e8d4a5100081029080820464e8d4a510001490151715610836576112af916110ab565b505064e8d4a51000900490565b62015f9091506118d390426110c9565b115f611882565b6001600160501b0390811691161091505f80611879565b9250505061191891925060a03d60a011611925575b6119108183611061565b8101906117a7565b915093929193905f61186c565b503d611906565b62015f90915061193c90426110c9565b115f61182f565b6001600160501b0390811691161091505f80611826565b9250505061197891925060a03d60a011611925576119108183611061565b915093929193905f611819565b5050505f90565b5f9061199661226e565b600181106111b85760055461150a91612048565b5f906119b461226e565b600181106111b85760405163cc2b27d760e01b8152600481018290527f0000000000000000000000000000000000000000000000000000000000000001600f0b602482018190527f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b03169291602081604481875afa908115610430575f91611bfe575b50670c7d713b49da00009081810291818304149015171561083657670de0b6b3a7640000936020926064915f90611a9f87827f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b0316612633565b6040519788958694630d2680e960e11b865289600487015260248601520460448401525af1918215610430575f92611bc9575b5060405f51602061296c5f395f51905f52918151908152836020820152a180611af85750565b9091505f5160206129ac5f395f51905f526040611bb967016345785d8a0000847f000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4611b70826001600160a01b03808416907f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e16612633565b7f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000001906127bc565b928151908152836020820152a190565b9091506020813d602011611bf6575b81611be560209383611061565b810103126101f85751906040611ad2565b3d9150611bd8565b90506020813d602011611c28575b81611c1960209383611061565b810103126101f857515f611a3e565b3d9150611c0c565b60405163022e74a560e61b81525f906020816004817f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d6001600160a01b03165afa908115610430575f91612009575b5060408051611c8e8282611061565b6001815260208101601f19830180368337611ca88361233c565b7f000000000000000000000000f3c43e7d722963b9569d1e39873df9e2dfe8c0876001600160a01b03169052835190611ce18583611061565b600182525f5b818110611ff85750506020948451611cff8782611061565b5f8152611d0b8361233c565b52611d158261233c565b506001600160a01b031691823b156101f857845163f0a2351160e01b81526060600482015293516064850181905284929160848401915f5b89828210611fd8575050505060031983820301602484015281518082528782019188808360051b8301019401925f915b8a848410611fa85750505050505091815f81819530604483015203925af1611f93575b5080516370a0823160e01b81523060048201527f000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd526001600160a01b03168382602481845afa918215611f89578592611f5a575b5067016345785d8a00008210611f5357825163a9059cbb60e01b81527f0000000000000000000000004684ac2e15cc25174134ed7586a72399b123da7d6001600160a01b03169185908290818981611e4f8989600484016115a1565b03925af18015611f49579185918793611f1e575b506024855180948193634a5c8c6f60e11b83528760048401525af1859181611eef575b50611e9a5763081ceff360e41b8552600485fd5b929360018410611ee75750827ffa5fc4e614ff1acb4e5e98dccf2a504cb9fb51a469dce5a5eca07ff5491a677b93948351928352820152a1611ee3611ede82612390565b6125a0565b5090565b935050505090565b9091508481813d8311611f17575b611f078183611061565b810103126101f85751905f611e86565b503d611efd565b611f3d90833d8511611f42575b611f358183611061565b8101906115fa565b611e63565b503d611f2b565b84513d88823e3d90fd5b5050505090565b9091508381813d8311611f82575b611f728183611061565b810103126101f85751905f611df3565b503d611f68565b83513d87823e3d90fd5b611fa09193505f90611061565b5f915f611da0565b809193959750611fc66001939597601f19868203018752895161101b565b97019301930190928795949293611d7d565b83516001600160a01b031685528896509384019390920191600101611d4d565b806060602080938601015201611ce7565b612022915060203d60201161141f576114118183611061565b5f611c7f565b9081526001600160a01b0391821660208201529116604082015260600190565b60405163cc2b27d760e01b8152600481018290527f0000000000000000000000000000000000000000000000000000000000000001600f0b602482018190527f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b03169493602083604481895afa928315610430575f936121ba575b50670de0b6b3a76400000391670de0b6b3a7640000831161083657670de0b6b3a76400006120fc602094606493611098565b049161213285887f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb6001600160a01b0316612633565b5f6040519788948593630d2680e960e11b8552886004860152602485015260448401525af1928315610430575f93612185575b5060405f51602061296c5f395f51905f52918482519182526020820152a1565b9092506020813d6020116121b2575b816121a160209383611061565b810103126101f85751916040612165565b3d9150612194565b9092506020813d6020116121e6575b816121d660209383611061565b810103126101f85751915f6120ca565b3d91506121c9565b91905f5160206129ac5f395f51905f529060409061225f90857f000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4611b70826001600160a01b03808416907f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e16612633565b938151908152846020820152a1565b6040516370a0823160e01b81523060048201527f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d6001600160a01b0316602082602481845afa918215610430575f92612308575b506001821061175b576122f36020915f93604051948580948193635d043b2960e11b83523090309060048501612028565b03925af1908115610430575f9161172c575090565b9091506020813d602011612334575b8161232460209383611061565b810103126101f85751905f6122c2565b3d9150612317565b8051156123495760200190565b634e487b7160e01b5f52603260045260245ffd5b90602080835192838152019201905f5b81811061237a5750505090565b825184526020938401939092019160010161236d565b6001600160a01b037f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb81169291906123ed90829085907f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e16612633565b604051926123fc606085611061565b60028452604036602086013783517f0000000000000000000000000000000000000000000000000000000000000001906001600160801b03821610156123495760051b6020600160851b031684016020908101839052604080516307b60dbb60e31b8152600481019190915294908580612479604482018561235d565b600160248301520381855afa948515610430575f9561256c575b50670dcef33a6f838000850294808604670dcef33a6f8380001490151715610836576020916124eb5f670de0b6b3a76400009360405198899586948593635b96faef60e11b855260406004860152604485019061235d565b9104602483015203925af1928315610430575f93612537575b5060407f38f8a0c92f4c5b0b6877f878cb4c0c8d348a47b76d716c8e78f425043df9515b918482519182526020820152a1565b9092506020813d602011612564575b8161255360209383611061565b810103126101f85751916040612504565b3d9150612546565b9094506020813d602011612598575b8161258860209383611061565b810103126101f85751935f612493565b3d915061257b565b5f906020906001600160a01b037f0000000000000000000000007d3cde9ccf0109423e672c17bd36481cf8ce437d81169160449161260390829085907f000000000000000000000000ecb0f0d68c19bdaadaebe24f6752a4db34e2c2cb16612633565b6040519485938492636e553f6560e01b845260048401523060248401525af1908115610430575f9161172c575090565b604051636eb1769f60e11b81526001600160a01b039190911692906020818061266086306004840161130c565b0381875afa908115610430575f9161278a575b501061267d575050565b604051636eb1769f60e11b81526020818061269c85306004840161130c565b0381865afa908115610430575f91612758575b506126fb575b5f91829182604051602081019263095ea7b360e01b845260018060a01b0316602482015281196044820152604481526126ef606482611061565b51925af161165f6115bc565b60405163095ea7b360e01b602082019081526001600160a01b03831660248301525f6044808401829052835291829190612736606482611061565b519082865af16127446115bc565b506126b5576312171d8360e31b5f5260045ffd5b90506020813d602011612782575b8161277360209383611061565b810103126101f857515f6126af565b3d9150612766565b90506020813d6020116127b4575b816127a560209383611061565b810103126101f857515f612673565b3d9150612798565b93929360018060a01b03169060405190635e0d443f60e01b8252600f0b92836004830152600f0b93846024830152856044830152602082606481865afa918215610430575f92612937575b50670de0b6b3a76400000390670de0b6b3a7640000821161083657670de0b6b3a76400009161283591611098565b0460405163ddc1f59d60e01b815283600482015284602482015285604482015281606482015230608482015260208160a4815f875af15f9181612903575b506128fa5750915f608492602095946040519788968795630f7c084960e21b875260048701526024860152604485015260648401525af15f91816128c6575b506112af57637cacd40760e01b5f5260045ffd5b9091506020813d6020116128f2575b816128e260209383611061565b810103126101f85751905f6128b2565b3d91506128d5565b94505050505090565b9091506020813d60201161292f575b8161291f60209383611061565b810103126101f85751905f612873565b3d9150612912565b9091506020813d602011612963575b8161295360209383611061565b810103126101f85751905f612807565b3d915061294656fe6f0f96292ae0038c04f9b6bab30f185d9ca02c471d0983f563f2a4f674aef1370c875c8d391179c5cf7ad8303d268efd50b8beb78b671f85cd54bfb91eb8ef40cf3ac02000d191e34429d258b04e042652e59ad03e9bac3f8c7989ccc7d52edda2646970667358221220f444e0242753b5f09a2b8e7f7ea3f1997e97f4e9aa1901c28882db568e8cb67364736f6c63430008210033
Verified Source Code Full Match
Compiler: v0.8.33+commit.64118f21
EVM: cancun
Optimization: Yes (1 runs)
PmUsdCrvUsdStrategy.sol 316 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {BaseCurveRewardVaultStrategy} from "./BaseCurveRewardVaultStrategy.sol";
import {IYieldStrategy} from "../interfaces/IYieldStrategy.sol";
import {IChainlinkOracle} from "../interfaces/IChainlinkOracle.sol";
import {IAccountant} from "../interfaces/IAccountant.sol";
import {ICrvSwapper} from "../interfaces/ICrvSwapper.sol";
import {ICurveStableSwap} from "../interfaces/ICurveStableSwap.sol";
import {ICurveStableSwapNG} from "../interfaces/ICurveStableSwapNG.sol";
import {IERC20} from "../interfaces/IERC20.sol";
import {IStakeDaoRewardVault} from "../interfaces/IStakeDaoRewardVault.sol";
import {CurveUsdtSwapLib} from "../libraries/CurveUsdtSwapLib.sol";
/// @title PmUsdCrvUsdStrategy
/// @notice USDT -> crvUSD -> pmUSD/crvUSD LP -> Stake DAO RewardVault
/// @dev Accepts USDT debt, swaps to crvUSD, provides single-sided liquidity to pmUSD/crvUSD pool,
/// stakes LP in Stake DAO reward vault, and harvests CRV rewards back into the strategy
contract PmUsdCrvUsdStrategy is BaseCurveRewardVaultStrategy {
error SwapFailed();
// ============ Constants ============
uint256 public constant DEFAULT_SLIPPAGE = 1e16; // 1% for stablecoin swaps
uint256 public constant MAX_SLIPPAGE = 5e16; // 5% governance max
uint256 public constant EMERGENCY_SLIPPAGE = 1e17; // 10% emergency
uint256 public constant LP_SLIPPAGE = 5e15; // 0.5% for LP ops (stable pairs)
uint256 public constant MIN_HARVEST_THRESHOLD = 1e17; // 0.1 CRV minimum
uint256 public constant MAX_ORACLE_STALENESS = 90000; // 25 hours
// ============ Immutables ============
IERC20 public immutable crvUSD;
IERC20 public immutable crv;
ICurveStableSwap public immutable usdtCrvUsdPool;
ICurveStableSwapNG public immutable lpPool;
IChainlinkOracle public immutable crvUsdOracle;
IChainlinkOracle public immutable usdtOracle;
ICrvSwapper public immutable crvSwapper;
address public immutable gauge;
int128 public immutable usdtIndex;
int128 public immutable crvUsdIndex;
int128 public immutable lpCrvUsdIndex;
// ============ State ============
uint256 public slippageTolerance = DEFAULT_SLIPPAGE;
// ============ Events ============
event SlippageUpdated(uint256 oldSlippage, uint256 newSlippage);
event SwappedUsdtToCrvUsd(uint256 usdtAmount, uint256 crvUsdReceived);
event SwappedCrvUsdToUsdt(uint256 crvUsdAmount, uint256 usdtReceived);
event LiquidityAdded(uint256 crvUsdAmount, uint256 lpReceived);
event LiquidityRemoved(uint256 lpBurned, uint256 crvUsdReceived);
event RewardsHarvested(uint256 crvAmount, uint256 crvUsdCompounded);
// ============ Constructor ============
/// @param _usdt USDT token address (debt asset)
/// @param _crvUsd crvUSD token address
/// @param _crv CRV token address
/// @param _vault Zenji vault address
/// @param _usdtCrvUsdPool Curve USDT/crvUSD StableSwap pool
/// @param _lpPool Curve pmUSD/crvUSD StableSwapNG pool
/// @param _rewardVault Stake DAO ERC4626 reward vault
/// @param _crvSwapper CRV -> crvUSD swapper address
/// @param _gauge Curve gauge for pmUSD/crvUSD LP (for accountant claims)
/// @param _usdtIndex USDT coin index in USDT/crvUSD pool
/// @param _crvUsdIndex crvUSD coin index in USDT/crvUSD pool
/// @param _lpCrvUsdIndex crvUSD coin index in pmUSD/crvUSD pool
/// @param _crvUsdOracle Chainlink crvUSD/USD oracle
/// @param _usdtOracle Chainlink USDT/USD oracle
constructor(
address _usdt,
address _crvUsd,
address _crv,
address _vault,
address _usdtCrvUsdPool,
address _lpPool,
address _rewardVault,
address _crvSwapper,
address _gauge,
int128 _usdtIndex,
int128 _crvUsdIndex,
int128 _lpCrvUsdIndex,
address _crvUsdOracle,
address _usdtOracle
) BaseCurveRewardVaultStrategy(_usdt, _vault, _rewardVault) {
if (_crvUsd == address(0) || _crv == address(0)) revert InvalidAddress();
if (_usdtCrvUsdPool == address(0) || _lpPool == address(0)) revert InvalidAddress();
if (_crvSwapper == address(0)) revert InvalidAddress();
if (_gauge == address(0)) revert InvalidAddress();
if (_crvUsdOracle == address(0) || _usdtOracle == address(0)) revert InvalidAddress();
crvUSD = IERC20(_crvUsd);
crv = IERC20(_crv);
usdtCrvUsdPool = ICurveStableSwap(_usdtCrvUsdPool);
lpPool = ICurveStableSwapNG(_lpPool);
crvSwapper = ICrvSwapper(_crvSwapper);
gauge = _gauge;
usdtIndex = _usdtIndex;
crvUsdIndex = _crvUsdIndex;
lpCrvUsdIndex = _lpCrvUsdIndex;
crvUsdOracle = IChainlinkOracle(_crvUsdOracle);
usdtOracle = IChainlinkOracle(_usdtOracle);
}
// ============ Admin ============
function setSlippage(uint256 newSlippage) external onlyVault {
if (newSlippage > MAX_SLIPPAGE) revert SlippageExceeded();
uint256 oldSlippage = slippageTolerance;
slippageTolerance = newSlippage;
emit SlippageUpdated(oldSlippage, newSlippage);
}
// ============ View Functions ============
/// @inheritdoc IYieldStrategy
function underlyingAsset() external view override returns (address) {
return address(lpToken);
}
/// @inheritdoc IYieldStrategy
function name() external pure override returns (string memory) {
return "USDT -> pmUSD/crvUSD LP Strategy";
}
/// @inheritdoc IYieldStrategy
function balanceOf() public view override returns (uint256) {
uint256 lpTokens = _rewardVaultBalance();
if (lpTokens < 1) return 0;
// LP -> crvUSD value via virtual_price (manipulation-resistant)
uint256 virtualPrice = lpPool.get_virtual_price();
uint256 crvUsdValue = (lpTokens * virtualPrice) / 1e18;
return CurveUsdtSwapLib.convertCrvUsdToUsdt(
crvUsdValue, crvUsdOracle, usdtOracle, MAX_ORACLE_STALENESS
);
}
/// @inheritdoc IYieldStrategy
function pendingRewards() external view override returns (uint256) {
address accountant = rewardVault.ACCOUNTANT();
try IAccountant(accountant).getPendingRewards(address(rewardVault), address(this)) returns (uint256 pendingCrv)
{
return pendingCrv / 2e12; // rough CRV->USDT view conversion
} catch {
return 0;
}
}
// ============ Internal: Core Strategy ============
function _deposit(uint256 usdtAmount) internal override returns (uint256 underlyingDeposited) {
// Auto-compound any pending CRV rewards
_claimAndCompound();
// 1. Swap USDT -> crvUSD
uint256 crvUsdReceived = _swapUsdtToCrvUsd(usdtAmount, slippageTolerance);
// 2. Add single-sided liquidity to pmUSD/crvUSD pool
uint256 lpReceived = _addLiquidity(crvUsdReceived);
// 3. Stake LP tokens in reward vault
_depositToRewardVault(lpReceived);
// Return value in USDT terms
underlyingDeposited = lpReceived;
}
function _withdraw(uint256 usdtAmount) internal override returns (uint256 usdtReceived) {
// Auto-compound any pending CRV rewards
_claimAndCompound();
uint256 currentValue = balanceOf();
if (currentValue < 1) return 0;
uint256 shares = rewardVault.balanceOf(address(this));
if (shares < 1) return 0;
// Calculate proportional shares to redeem
uint256 sharesToRedeem = (shares * usdtAmount) / currentValue;
if (sharesToRedeem > shares) sharesToRedeem = shares;
if (sharesToRedeem < 1) return 0;
// 1. Unstake from reward vault
uint256 lpReceived = _redeemFromRewardVault(sharesToRedeem);
// 2. Remove liquidity (single-sided crvUSD)
uint256 crvUsdReceived = _removeLiquidity(lpReceived, slippageTolerance);
// 3. Swap crvUSD -> USDT
if (crvUsdReceived > 0) {
usdtReceived = _swapCrvUsdToUsdt(crvUsdReceived, slippageTolerance);
}
}
function _withdrawAll() internal override returns (uint256 usdtReceived) {
// 1. Unstake all LP tokens
uint256 lpReceived = _redeemAllFromRewardVault();
if (lpReceived < 1) return 0;
// 2. Remove all liquidity for crvUSD
uint256 crvUsdReceived = _removeLiquidity(lpReceived, slippageTolerance);
// 3. Swap all crvUSD -> USDT
if (crvUsdReceived > 0) {
usdtReceived = _swapCrvUsdToUsdt(crvUsdReceived, slippageTolerance);
}
}
function _harvest() internal override returns (uint256 rewardsValue) {
uint256 crvUsdCompounded = _claimAndCompound();
rewardsValue = CurveUsdtSwapLib.convertCrvUsdToUsdt(
crvUsdCompounded, crvUsdOracle, usdtOracle, MAX_ORACLE_STALENESS
);
}
/// @notice Claim CRV from accountant, swap to crvUSD, and compound back into LP
/// @return crvUsdCompounded Amount of crvUSD compounded (0 if nothing to harvest)
function _claimAndCompound() internal returns (uint256 crvUsdCompounded) {
_accountantClaim();
uint256 crvBalance = crv.balanceOf(address(this));
if (crvBalance < MIN_HARVEST_THRESHOLD) return 0;
crv.transfer(address(crvSwapper), crvBalance);
uint256 crvUsdReceived = _swapCrv(crvBalance);
if (crvUsdReceived < 1) return 0;
emit RewardsHarvested(crvBalance, crvUsdReceived);
uint256 lpReceived = _addLiquidity(crvUsdReceived);
_depositToRewardVault(lpReceived);
crvUsdCompounded = crvUsdReceived;
}
function _swapCrv(uint256 amount) internal returns (uint256 crvUsdReceived) {
try crvSwapper.swap(amount) returns (uint256 received) {
crvUsdReceived = received;
} catch {
revert SwapFailed();
}
}
function _accountantClaim() internal {
address accountant = rewardVault.ACCOUNTANT();
address[] memory gauges = new address[](1);
gauges[0] = gauge;
bytes[] memory harvestData = new bytes[](1);
harvestData[0] = bytes("");
// NoPendingRewards is expected when nothing has accrued yet
try IAccountant(accountant).claim(gauges, harvestData, address(this)) {} catch {}
}
function _emergencyWithdraw() internal override returns (uint256 usdtReceived) {
// 1. Unstake all LP tokens
uint256 lpReceived = _redeemAllFromRewardVault();
if (lpReceived < 1) return 0;
// 2. Remove all liquidity with emergency slippage
uint256 crvUsdReceived = _removeLiquidity(lpReceived, EMERGENCY_SLIPPAGE);
// 3. Swap all crvUSD -> USDT with emergency slippage
if (crvUsdReceived > 0) {
usdtReceived = _swapCrvUsdToUsdt(crvUsdReceived, EMERGENCY_SLIPPAGE);
}
}
// ============ Internal: LP Operations ============
/// @notice Add single-sided crvUSD liquidity to pmUSD/crvUSD pool
function _addLiquidity(uint256 crvUsdAmount) internal returns (uint256 lpReceived) {
_ensureApprove(address(crvUSD), address(lpPool), crvUsdAmount);
// Build amounts array: only crvUSD side
uint256[] memory amounts = new uint256[](2);
amounts[uint256(uint128(lpCrvUsdIndex))] = crvUsdAmount;
uint256 expectedLp = lpPool.calc_token_amount(amounts, true);
uint256 minLp = (expectedLp * (PRECISION - LP_SLIPPAGE)) / PRECISION;
lpReceived = lpPool.add_liquidity(amounts, minLp);
emit LiquidityAdded(crvUsdAmount, lpReceived);
}
/// @notice Remove single-sided crvUSD liquidity from pmUSD/crvUSD pool
function _removeLiquidity(uint256 lpAmount, uint256 slippage) internal returns (uint256 crvUsdReceived) {
uint256 expectedOut = lpPool.calc_withdraw_one_coin(lpAmount, lpCrvUsdIndex);
uint256 minOut = (expectedOut * (PRECISION - slippage)) / PRECISION;
_ensureApprove(address(lpToken), address(lpPool), lpAmount);
crvUsdReceived = lpPool.remove_liquidity_one_coin(lpAmount, lpCrvUsdIndex, minOut);
emit LiquidityRemoved(lpAmount, crvUsdReceived);
}
// ============ Internal: USDT/crvUSD Swaps ============
function _swapUsdtToCrvUsd(uint256 usdtAmount, uint256 slippage) internal returns (uint256 crvUsdReceived) {
_ensureApprove(address(debtAsset), address(usdtCrvUsdPool), usdtAmount);
crvUsdReceived =
CurveUsdtSwapLib.swapUsdtToCrvUsd(usdtCrvUsdPool, usdtIndex, crvUsdIndex, usdtAmount, slippage);
emit SwappedUsdtToCrvUsd(usdtAmount, crvUsdReceived);
}
function _swapCrvUsdToUsdt(uint256 crvUsdAmount, uint256 slippage) internal returns (uint256 usdtReceived) {
_ensureApprove(address(crvUSD), address(usdtCrvUsdPool), crvUsdAmount);
usdtReceived =
CurveUsdtSwapLib.swapCrvUsdToUsdt(usdtCrvUsdPool, crvUsdIndex, usdtIndex, crvUsdAmount, slippage);
emit SwappedCrvUsdToUsdt(crvUsdAmount, usdtReceived);
}
}
BaseCurveRewardVaultStrategy.sol 68 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {BaseYieldStrategy} from "./BaseYieldStrategy.sol";
import {IStakeDaoRewardVault} from "../interfaces/IStakeDaoRewardVault.sol";
import {IERC20} from "../interfaces/IERC20.sol";
/// @title BaseCurveRewardVaultStrategy
/// @notice Abstract base for strategies that stake Curve LP tokens in ERC4626 reward vaults
/// @dev Provides shared deposit/redeem/balance helpers for Stake DAO-style reward vaults
abstract contract BaseCurveRewardVaultStrategy is BaseYieldStrategy {
// ============ Immutables ============
/// @notice Stake DAO ERC4626 reward vault
IStakeDaoRewardVault public immutable rewardVault;
/// @notice The Curve LP token staked in the reward vault
IERC20 public immutable lpToken;
// ============ Constructor ============
constructor(address _debtAsset, address _vault, address _rewardVault)
BaseYieldStrategy(_debtAsset, _vault)
{
if (_rewardVault == address(0)) revert InvalidAddress();
rewardVault = IStakeDaoRewardVault(_rewardVault);
lpToken = IERC20(rewardVault.asset());
}
// ============ Reward Vault Helpers ============
/// @notice Deposit LP tokens into the reward vault
/// @param lpAmount Amount of LP tokens to deposit
/// @return shares Vault shares minted
function _depositToRewardVault(uint256 lpAmount) internal returns (uint256 shares) {
_ensureApprove(address(lpToken), address(rewardVault), lpAmount);
shares = rewardVault.deposit(lpAmount, address(this));
}
/// @notice Redeem specific number of shares from reward vault
/// @param shares Number of shares to redeem
/// @return lpReceived Amount of LP tokens received
function _redeemFromRewardVault(uint256 shares) internal returns (uint256 lpReceived) {
lpReceived = rewardVault.redeem(shares, address(this), address(this));
}
/// @notice Redeem all shares from reward vault
/// @return lpReceived Amount of LP tokens received
function _redeemAllFromRewardVault() internal returns (uint256 lpReceived) {
uint256 shares = rewardVault.balanceOf(address(this));
if (shares < 1) return 0;
lpReceived = rewardVault.redeem(shares, address(this), address(this));
}
/// @notice Claim all pending rewards from the reward vault
/// @return amounts Array of reward amounts claimed
function _claimRewards() internal returns (uint256[] memory amounts) {
amounts = rewardVault.claim();
}
/// @notice Get total LP token balance in reward vault
/// @return LP token amount (converted from shares)
function _rewardVaultBalance() internal view returns (uint256) {
uint256 shares = rewardVault.balanceOf(address(this));
if (shares < 1) return 0;
return rewardVault.convertToAssets(shares);
}
}
IYieldStrategy.sol 82 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title IYieldStrategy
/// @notice Interface for yield strategies used by Zenji
/// @dev All strategies accept the debt asset from the vault and deploy to yield-generating protocols
interface IYieldStrategy {
// ============ Events ============
event Deposited(uint256 debtAssetAmount, uint256 underlyingDeposited);
event Withdrawn(uint256 debtAssetAmount, uint256 debtAssetReceived);
event Harvested(uint256 rewardsValue);
event EmergencyWithdrawn(uint256 debtAssetReceived);
event StrategyPauseToggled(bool paused, uint256 debtAssetReceived);
// ============ Errors ============
error Unauthorized();
error ZeroAmount();
error StrategyPaused();
error SlippageExceeded();
error TransferFailed();
error InvalidAddress();
// ============ Core Functions ============
/// @notice Deposit debt asset into the yield strategy
/// @param debtAssetAmount Amount of debt asset to deposit
/// @return underlyingDeposited Amount deposited into the underlying protocol
function deposit(uint256 debtAssetAmount) external returns (uint256 underlyingDeposited);
/// @notice Withdraw debt asset from the yield strategy
/// @param debtAssetAmount Amount of debt asset to withdraw
/// @return debtAssetReceived Actual debt asset received (may differ due to slippage)
function withdraw(uint256 debtAssetAmount) external returns (uint256 debtAssetReceived);
/// @notice Withdraw all assets from the yield strategy
/// @return debtAssetReceived Total debt asset received
function withdrawAll() external returns (uint256 debtAssetReceived);
/// @notice Harvest rewards and compound them back into the strategy
/// @return rewardsValue Value of rewards harvested (in debt asset terms)
function harvest() external returns (uint256 rewardsValue);
/// @notice Emergency withdraw all assets, bypassing slippage checks
/// @return debtAssetReceived Total debt asset received
function emergencyWithdraw() external returns (uint256 debtAssetReceived);
/// @notice Toggle pause state for the strategy
/// @dev When pausing, should unwind all deployed assets back to the vault
/// @return debtAssetReceived Total debt asset received if unwound (0 when unpausing)
function pauseStrategy() external returns (uint256 debtAssetReceived);
// ============ View Functions ============
/// @notice Returns the asset accepted by the strategy (debt asset)
function asset() external view returns (address);
/// @notice Returns the underlying asset of the yield protocol (debt asset or swapped asset)
function underlyingAsset() external view returns (address);
/// @notice Returns the current value of the strategy holdings in debt asset terms
function balanceOf() external view returns (uint256);
/// @notice Returns the total debt asset deposited (cost basis)
function costBasis() external view returns (uint256);
/// @notice Returns unrealized profit (current value - cost basis)
function unrealizedProfit() external view returns (uint256);
/// @notice Returns pending rewards value in debt asset terms
function pendingRewards() external view returns (uint256);
/// @notice Returns whether the strategy is paused
function paused() external view returns (bool);
/// @notice Returns the strategy name
function name() external view returns (string memory);
/// @notice Returns the vault address
function vault() external view returns (address);
}
IChainlinkOracle.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title IChainlinkOracle
/// @notice Interface for Chainlink price feeds
interface IChainlinkOracle {
/// @notice Get the latest round data
/// @return roundId The round ID
/// @return answer The price answer
/// @return startedAt Timestamp when the round started
/// @return updatedAt Timestamp when the round was updated
/// @return answeredInRound The round in which the answer was computed
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
/// @notice Get decimals of the price feed
/// @return Decimals
function decimals() external view returns (uint8);
/// @notice Get description of the price feed
/// @return Description string
function description() external view returns (string memory);
/// @notice Get the latest answer
/// @return Latest price
function latestAnswer() external view returns (int256);
}
IAccountant.sol 49 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import { IStrategy } from "src/interfaces/IStrategy.sol";
interface IAccountant {
function checkpoint(
address gauge,
address from,
address to,
uint128 amount,
IStrategy.PendingRewards calldata pendingRewards,
IStrategy.HarvestPolicy policy
) external;
function checkpoint(
address gauge,
address from,
address to,
uint128 amount,
IStrategy.PendingRewards calldata pendingRewards,
IStrategy.HarvestPolicy policy,
address referrer
) external;
function totalSupply(address asset) external view returns (uint128);
function balanceOf(address asset, address account) external view returns (uint128);
function claim(address[] calldata _gauges, bytes[] calldata harvestData) external;
function claim(address[] calldata _gauges, bytes[] calldata harvestData, address receiver)
external;
function claim(
address[] calldata _gauges,
address account,
bytes[] calldata harvestData,
address receiver
) external;
function claimProtocolFees() external;
function harvest(address[] calldata _gauges, bytes[] calldata _harvestData, address _receiver)
external;
function REWARD_TOKEN() external view returns (address);
function getPendingRewards(address vault) external view returns (uint128);
function getPendingRewards(address vault, address account) external view returns (uint256);
function SCALING_FACTOR() external view returns (uint128);
}
ICrvSwapper.sol 11 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title ICrvSwapper
/// @notice Minimal interface for CRV -> crvUSD swapper contracts
interface ICrvSwapper {
/// @notice Swap CRV into crvUSD
/// @param amount CRV amount to swap
/// @return crvUsdReceived Amount of crvUSD received
function swap(uint256 amount) external returns (uint256 crvUsdReceived);
}
ICurveStableSwap.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title ICurveStableSwap
/// @notice Interface for Curve StableSwap pools (used for crvUSD/USDC swaps)
interface ICurveStableSwap {
/// @notice Exchange tokens
/// @param i Index of input token
/// @param j Index of output token
/// @param dx Amount of input token
/// @param min_dy Minimum amount of output token
/// @return Amount of output token received
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256);
/// @notice Get expected output amount
/// @param i Index of input token
/// @param j Index of output token
/// @param dx Amount of input token
/// @return Expected output amount
function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
/// @notice Get coin address by index
/// @param i Coin index
/// @return Coin address
function coins(uint256 i) external view returns (address);
/// @notice Get pool balances
/// @param i Coin index
/// @return Balance of coin at index
function balances(uint256 i) external view returns (uint256);
/// @notice Get virtual price of LP token
/// @return Virtual price scaled by 1e18
function get_virtual_price() external view returns (uint256);
}
ICurveStableSwapNG.sol 63 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title ICurveStableSwapNG
/// @notice Interface for Curve StableSwapNG pools (e.g., pmUSD/crvUSD)
/// @dev Uses uint256 indices instead of int128 (NG pools use uint256)
interface ICurveStableSwapNG {
/// @notice Add liquidity to the pool
/// @param amounts Array of token amounts to deposit [coin0, coin1]
/// @param min_mint_amount Minimum LP tokens to mint
/// @return LP tokens minted
function add_liquidity(uint256[] calldata amounts, uint256 min_mint_amount) external returns (uint256);
/// @notice Remove liquidity in a single coin
/// @param burn_amount Amount of LP tokens to burn
/// @param i Index of coin to withdraw
/// @param min_received Minimum amount of coin to receive
/// @return Amount of coin received
function remove_liquidity_one_coin(uint256 burn_amount, int128 i, uint256 min_received)
external
returns (uint256);
/// @notice Calculate expected LP tokens from deposit
/// @param amounts Array of token amounts [coin0, coin1]
/// @param is_deposit True for deposit, false for withdrawal
/// @return Expected LP token amount
function calc_token_amount(uint256[] calldata amounts, bool is_deposit) external view returns (uint256);
/// @notice Calculate expected output from removing liquidity in one coin
/// @param burn_amount Amount of LP tokens to burn
/// @param i Index of coin to withdraw
/// @return Expected amount of coin received
function calc_withdraw_one_coin(uint256 burn_amount, int128 i) external view returns (uint256);
/// @notice Get the virtual price of the LP token
/// @return Virtual price scaled by 1e18
function get_virtual_price() external view returns (uint256);
/// @notice Get coin address by index
/// @param i Coin index
/// @return Coin address
function coins(uint256 i) external view returns (address);
/// @notice Get pool balances
/// @param i Coin index
/// @return Balance of coin at index
function balances(uint256 i) external view returns (uint256);
/// @notice Exchange tokens
/// @param i Index of input token
/// @param j Index of output token
/// @param dx Amount of input token
/// @param min_dy Minimum amount of output token
/// @return Amount of output token received
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256);
/// @notice Get expected output amount for exchange
/// @param i Index of input token
/// @param j Index of output token
/// @param dx Amount of input token
/// @return Expected output amount
function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
}
IERC20.sol 16 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title IERC20
/// @notice Standard ERC20 interface
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function name() external view returns (string memory);
}
IStakeDaoRewardVault.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {ICurveRewardVault} from "./ICurveRewardVault.sol";
/// @title IStakeDaoRewardVault
/// @notice Minimal extension for Stake DAO reward vaults exposing the accountant address
interface IStakeDaoRewardVault is ICurveRewardVault {
/// @notice Stake DAO accountant contract
/// @return accountant address
function ACCOUNTANT() external view returns (address);
}
CurveUsdtSwapLib.sol 87 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {ICurveStableSwap} from "../interfaces/ICurveStableSwap.sol";
import {ICurveStableSwapWithReceiver} from "../interfaces/ICurveStableSwapWithReceiver.sol";
import {IChainlinkOracle} from "../interfaces/IChainlinkOracle.sol";
/// @title CurveUsdtSwapLib
/// @notice Shared USDT/crvUSD swap + oracle conversion logic
library CurveUsdtSwapLib {
uint256 internal constant PRECISION = 1e18;
error ExchangeFailed();
/// @notice Swap USDT to crvUSD via Curve StableSwap
function swapUsdtToCrvUsd(
ICurveStableSwap pool,
int128 usdtIndex,
int128 crvUsdIndex,
uint256 usdtAmount,
uint256 slippage
) internal returns (uint256 crvUsdReceived) {
uint256 expectedOut = pool.get_dy(usdtIndex, crvUsdIndex, usdtAmount);
uint256 minOut = (expectedOut * (PRECISION - slippage)) / PRECISION;
crvUsdReceived = exchange(pool, usdtIndex, crvUsdIndex, usdtAmount, minOut);
}
/// @notice Swap crvUSD to USDT via Curve StableSwap
function swapCrvUsdToUsdt(
ICurveStableSwap pool,
int128 crvUsdIndex,
int128 usdtIndex,
uint256 crvUsdAmount,
uint256 slippage
) internal returns (uint256 usdtReceived) {
uint256 expectedOut = pool.get_dy(crvUsdIndex, usdtIndex, crvUsdAmount);
uint256 minOut = (expectedOut * (PRECISION - slippage)) / PRECISION;
usdtReceived = exchange(pool, crvUsdIndex, usdtIndex, crvUsdAmount, minOut);
}
/// @notice Exchange on Curve with receiver fallback
function exchange(ICurveStableSwap pool, int128 i, int128 j, uint256 dx, uint256 minOut)
internal
returns (uint256 amountOut)
{
try ICurveStableSwapWithReceiver(address(pool)).exchange(i, j, dx, minOut, address(this))
returns (uint256 outWithReceiver) {
amountOut = outWithReceiver;
} catch {
try pool.exchange(i, j, dx, minOut) returns (uint256 outStandard) {
amountOut = outStandard;
} catch {
revert ExchangeFailed();
}
}
}
/// @notice Convert crvUSD value (18 dec) to USDT value (6 dec) using oracles
function convertCrvUsdToUsdt(
uint256 crvUsdValue,
IChainlinkOracle crvUsdOracle,
IChainlinkOracle usdtOracle,
uint256 maxStaleness
) internal view returns (uint256) {
if (crvUsdValue < 1) return 0;
(uint80 crvRoundId, int256 crvUsdPrice,, uint256 crvUsdUpdatedAt, uint80 crvAnswered) =
crvUsdOracle.latestRoundData();
if (
crvUsdPrice <= 0 || crvAnswered < crvRoundId
|| block.timestamp - crvUsdUpdatedAt > maxStaleness
) {
return crvUsdValue / 1e12; // fallback to 1:1
}
(uint80 usdtRoundId, int256 usdtPrice,, uint256 usdtUpdatedAt, uint80 usdtAnswered) =
usdtOracle.latestRoundData();
if (
usdtPrice <= 0 || usdtAnswered < usdtRoundId
|| block.timestamp - usdtUpdatedAt > maxStaleness
) {
return crvUsdValue / 1e12; // fallback to 1:1
}
return (crvUsdValue * uint256(crvUsdPrice)) / (uint256(usdtPrice) * 1e12);
}
}
BaseYieldStrategy.sol 234 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {IYieldStrategy} from "../interfaces/IYieldStrategy.sol";
import {IERC20} from "../interfaces/IERC20.sol";
import {SafeTransferLib} from "../libraries/SafeTransferLib.sol";
/// @title BaseYieldStrategy
/// @notice Abstract base contract for yield strategies with cost basis tracking
/// @dev All strategies inherit from this and implement protocol-specific logic
abstract contract BaseYieldStrategy is IYieldStrategy {
using SafeTransferLib for IERC20;
// ============ Constants ============
uint256 public constant PRECISION = 1e18;
// ============ Immutables ============
/// @notice The debt asset token (asset accepted/returned by this strategy)
IERC20 public immutable debtAsset;
// ============ State ============
/// @notice The vault that owns this strategy
address public override vault;
/// @notice Deployer address for deferred vault initialization
address public initializer;
/// @notice Total debt asset deposited (cost basis for profit calculation)
uint256 internal _costBasis;
/// @notice Whether the strategy is paused
bool public override paused;
/// @notice Reentrancy guard
uint256 private _status;
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
// ============ Events ============
event VaultInitialized(address indexed vault);
// ============ Modifiers ============
modifier onlyVault() {
if (msg.sender != vault) revert Unauthorized();
_;
}
modifier whenNotPaused() {
if (paused) revert StrategyPaused();
_;
}
modifier nonReentrant() {
if (_status == _ENTERED) revert Unauthorized();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
// ============ Constructor ============
/// @param _debtAsset The debt asset token address
/// @param _vault The vault address (or zero for deferred initialization)
constructor(address _debtAsset, address _vault) {
if (_debtAsset == address(0)) revert InvalidAddress();
debtAsset = IERC20(_debtAsset);
if (_vault != address(0)) {
vault = _vault;
initializer = address(0);
} else {
initializer = msg.sender;
}
_status = _NOT_ENTERED;
}
// ============ Deferred Initialization ============
/// @notice Initialize the vault address (can only be called once by deployer)
function initializeVault(address newVault) external {
if (vault != address(0)) revert InvalidAddress();
if (newVault == address(0)) revert InvalidAddress();
if (msg.sender != initializer) revert Unauthorized();
vault = newVault;
initializer = address(0);
emit VaultInitialized(newVault);
}
// ============ Core Functions ============
/// @inheritdoc IYieldStrategy
function deposit(uint256 amount)
external
override
onlyVault
whenNotPaused
nonReentrant
returns (uint256 underlyingDeposited)
{
if (amount == 0) revert ZeroAmount();
debtAsset.safeTransferFrom(msg.sender, address(this), amount);
_costBasis += amount;
underlyingDeposited = _deposit(amount);
emit Deposited(amount, underlyingDeposited);
}
/// @inheritdoc IYieldStrategy
function withdraw(uint256 amount)
external
override
onlyVault
nonReentrant
returns (uint256 received)
{
if (amount == 0) revert ZeroAmount();
uint256 currentValue = this.balanceOf();
if (currentValue == 0) return 0;
if (amount > currentValue) amount = currentValue;
uint256 basisReduction = (_costBasis * amount) / currentValue;
received = _withdraw(amount);
_costBasis = _costBasis > basisReduction ? _costBasis - basisReduction : 0;
if (received > 0) {
debtAsset.safeTransfer(vault, received);
}
emit Withdrawn(amount, received);
}
/// @inheritdoc IYieldStrategy
function withdrawAll() external override onlyVault nonReentrant returns (uint256 received) {
received = _withdrawAll();
_costBasis = 0;
uint256 balance = debtAsset.balanceOf(address(this));
if (balance > 0) {
debtAsset.safeTransfer(vault, balance);
}
emit Withdrawn(type(uint256).max, received);
}
/// @inheritdoc IYieldStrategy
function harvest()
external
override
onlyVault
whenNotPaused
nonReentrant
returns (uint256 rewardsValue)
{
rewardsValue = _harvest();
emit Harvested(rewardsValue);
}
/// @inheritdoc IYieldStrategy
function emergencyWithdraw()
external
override
onlyVault
nonReentrant
returns (uint256 received)
{
received = _emergencyWithdraw();
_costBasis = 0;
uint256 balance = debtAsset.balanceOf(address(this));
if (balance > 0) {
debtAsset.safeTransfer(vault, balance);
}
emit EmergencyWithdrawn(received);
}
/// @inheritdoc IYieldStrategy
function pauseStrategy() external override onlyVault nonReentrant returns (uint256 received) {
paused = !paused;
if (paused) {
received = _withdrawAll();
_costBasis = 0;
uint256 balance = debtAsset.balanceOf(address(this));
if (balance > 0) {
debtAsset.safeTransfer(vault, balance);
}
}
emit StrategyPauseToggled(paused, received);
}
// ============ View Functions ============
/// @inheritdoc IYieldStrategy
function asset() external view override returns (address) {
return address(debtAsset);
}
/// @inheritdoc IYieldStrategy
function costBasis() external view override returns (uint256) {
return _costBasis;
}
/// @inheritdoc IYieldStrategy
function balanceOf() public view virtual override returns (uint256);
/// @inheritdoc IYieldStrategy
function unrealizedProfit() external view override returns (uint256) {
uint256 currentValue = this.balanceOf();
return currentValue > _costBasis ? currentValue - _costBasis : 0;
}
// ============ Internal Functions (to be implemented by derived contracts) ============
function _deposit(uint256 amount) internal virtual returns (uint256 underlyingDeposited);
function _withdraw(uint256 amount) internal virtual returns (uint256 received);
function _withdrawAll() internal virtual returns (uint256 received);
function _harvest() internal virtual returns (uint256 rewardsValue);
function _emergencyWithdraw() internal virtual returns (uint256 received);
// ============ Helper Functions ============
function _ensureApprove(address token, address spender, uint256 amount) internal {
IERC20(token).ensureApproval(spender, amount);
}
}
IStrategy.sol 35 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.33;
import "src/interfaces/IAllocator.sol";
interface IStrategy {
/// @notice The policy for harvesting rewards.
enum HarvestPolicy {
CHECKPOINT,
HARVEST
}
struct PendingRewards {
uint128 feeSubjectAmount;
uint128 totalAmount;
}
function deposit(IAllocator.Allocation calldata allocation, HarvestPolicy policy)
external
returns (PendingRewards memory pendingRewards);
function withdraw(
IAllocator.Allocation calldata allocation,
HarvestPolicy policy,
address receiver
) external returns (PendingRewards memory pendingRewards);
function balanceOf(address gauge) external view returns (uint256 balance);
function harvest(address gauge, bytes calldata extraData)
external
returns (PendingRewards memory pendingRewards);
function flush() external;
function shutdown(address gauge) external;
}
ICurveRewardVault.sol 49 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title ICurveRewardVault
/// @notice Interface for Stake DAO ERC4626 reward vaults
/// @dev These vaults wrap Curve LP tokens and distribute CRV/SDT rewards
interface ICurveRewardVault {
// ============ ERC4626 Functions ============
function asset() external view returns (address);
function totalAssets() external view returns (uint256);
function convertToShares(uint256 assets) external view returns (uint256);
function convertToAssets(uint256 shares) external view returns (uint256);
function maxDeposit(address receiver) external view returns (uint256);
function maxWithdraw(address owner) external view returns (uint256);
function maxRedeem(address owner) external view returns (uint256);
function previewDeposit(uint256 assets) external view returns (uint256);
function previewWithdraw(uint256 assets) external view returns (uint256);
function previewRedeem(uint256 shares) external view returns (uint256);
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
// ============ ERC20 Functions ============
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function totalSupply() external view returns (uint256);
// ============ Reward Functions ============
/// @notice Claim all pending rewards for the caller
/// @return Array of reward amounts claimed (ordered by rewardTokens())
function claim() external returns (uint256[] memory);
/// @notice Get pending rewards for an account
/// @param account Address to check
/// @param token Reward token address
/// @return Pending reward amount
function earned(address account, address token) external view returns (uint256);
/// @notice Get all reward token addresses
/// @return Array of reward token addresses
function rewardTokens(uint256 index) external view returns (address);
/// @notice Get the number of reward tokens
/// @return Number of reward tokens
function rewardTokensLength() external view returns (uint256);
}
ICurveStableSwapWithReceiver.sol 17 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title ICurveStableSwapWithReceiver
/// @notice Optional Curve exchange variant that supports specifying a receiver
interface ICurveStableSwapWithReceiver {
/// @notice Exchange tokens to a receiver
/// @param i Index of input token
/// @param j Index of output token
/// @param dx Amount of input token
/// @param min_dy Minimum amount of output token
/// @param receiver Address to receive the output token
/// @return Amount of output token received
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver)
external
returns (uint256);
}
SafeTransferLib.sol 57 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import { IERC20 } from "../interfaces/IERC20.sol";
/// @title SafeTransferLib
/// @notice Gas-efficient safe transfer functions for ERC20 tokens
library SafeTransferLib {
error TransferFailed();
/// @notice Safe transfer that reverts on failure
function safeTransfer(IERC20 token, address to, uint256 amount) internal {
(bool success, bytes memory data) =
address(token).call(abi.encodeWithSelector(token.transfer.selector, to, amount));
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
revert TransferFailed();
}
}
/// @notice Safe transferFrom that reverts on failure
function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(token.transferFrom.selector, from, to, amount)
);
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
revert TransferFailed();
}
}
/// @notice Ensure approval is sufficient, set to max if not
/// @dev Uses safeApprove pattern for USDT compatibility (non-standard ERC20)
function ensureApproval(IERC20 token, address spender, uint256 amount) internal {
if (token.allowance(address(this), spender) < amount) {
safeApprove(token, spender, type(uint256).max);
}
}
/// @notice Safe approve that handles non-standard ERC20 tokens like USDT
/// @dev USDT requires setting allowance to 0 first before setting a new non-zero value
/// and returns void instead of bool
function safeApprove(IERC20 token, address spender, uint256 amount) internal {
// For USDT: first reset to 0 if there's existing allowance and we're setting non-zero
if (amount > 0 && token.allowance(address(this), spender) > 0) {
(bool resetSuccess,) =
address(token).call(abi.encodeWithSelector(token.approve.selector, spender, 0));
// Ignore return value for void-returning tokens like USDT
if (!resetSuccess) revert TransferFailed();
}
(bool success, bytes memory data) =
address(token).call(abi.encodeWithSelector(token.approve.selector, spender, amount));
// Handle both standard (returns bool) and non-standard (returns nothing) ERC20
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
revert TransferFailed();
}
}
}
IAllocator.sol 26 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.7;
interface IAllocator {
struct Allocation {
address asset;
address gauge;
address[] targets;
uint256[] amounts;
}
function getDepositAllocation(address asset, address gauge, uint256 amount)
external
view
returns (Allocation memory);
function getWithdrawalAllocation(address asset, address gauge, uint256 amount)
external
view
returns (Allocation memory);
function getRebalancedAllocation(address asset, address gauge, uint256 amount)
external
view
returns (Allocation memory);
function getAllocationTargets(address gauge) external view returns (address[] memory);
}
Read Contract
DEFAULT_SLIPPAGE 0xa8798510 → uint256
EMERGENCY_SLIPPAGE 0xbb42e5cd → uint256
LP_SLIPPAGE 0x5144107d → uint256
MAX_ORACLE_STALENESS 0x5dc565c2 → uint256
MAX_SLIPPAGE 0xf9759518 → uint256
MIN_HARVEST_THRESHOLD 0xd6ef2365 → uint256
PRECISION 0xaaf5eb68 → uint256
asset 0x38d52e0f → address
balanceOf 0x722713f7 → uint256
costBasis 0x53e3cb5f → uint256
crv 0x6a4874a1 → address
crvSwapper 0xec47df94 → address
crvUSD 0x9bec8288 → address
crvUsdIndex 0x6f000201 → int128
crvUsdOracle 0xab0ebba0 → address
debtAsset 0xa919802d → address
gauge 0xa6f19c84 → address
initializer 0x9ce110d7 → address
lpCrvUsdIndex 0x3f5bbcda → int128
lpPool 0x3737bcb4 → address
lpToken 0x5fcbd285 → address
name 0x06fdde03 → string
paused 0x5c975abb → bool
pendingRewards 0xeded3fda → uint256
rewardVault 0x3a2c6777 → address
slippageTolerance 0xd03153aa → uint256
underlyingAsset 0x7158da7c → address
unrealizedProfit 0x53ee961e → uint256
usdtCrvUsdPool 0x9f19915c → address
usdtIndex 0x5504bd5f → int128
usdtOracle 0x07a79fc7 → address
vault 0xfbfa77cf → address
Write Contract 8 functions
These functions modify contract state and require a wallet transaction to execute.
deposit 0xb6b55f25
uint256 amount
returns: uint256
emergencyWithdraw 0xdb2e21bc
No parameters
returns: uint256
harvest 0x4641257d
No parameters
returns: uint256
initializeVault 0xccb6a270
address newVault
pauseStrategy 0xd8c143f7
No parameters
returns: uint256
setSlippage 0xf0fa55a9
uint256 newSlippage
withdraw 0x2e1a7d4d
uint256 amount
returns: uint256
withdrawAll 0x853828b6
No parameters
returns: uint256
Recent Transactions
This address has 1 on-chain transactions, but only 1.4% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →