diff --git a/.env.example b/.env.example index 4a5b5228..0166d868 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,9 @@ VEDA_ADAPTER_OWNER_ADDRESS= VEDA_BORING_VAULT_ADDRESS= VEDA_TELLER_ADDRESS= VEDA_DEPOSIT_TOKEN_ADDRESS= +VAULT_MIGRATION_HELPER_OWNER_ADDRESS= +BASE_VEDA_ADAPTER_ADDRESS= +PREMIUM_VEDA_ADAPTER_ADDRESS= # Required for verifying contracts ETHERSCAN_API_KEY= diff --git a/audits/cyfrin/cyfrin-10-26.pdf b/audits/cyfrin/cyfrin-10-26-1.pdf similarity index 100% rename from audits/cyfrin/cyfrin-10-26.pdf rename to audits/cyfrin/cyfrin-10-26-1.pdf diff --git a/audits/cyfrin/cyfrin-10-26-2.pdf b/audits/cyfrin/cyfrin-10-26-2.pdf new file mode 100644 index 00000000..3da3db85 Binary files /dev/null and b/audits/cyfrin/cyfrin-10-26-2.pdf differ diff --git a/broadcast/DeployVaultMigrationHelper.s.sol/143/run-1792141383544.json b/broadcast/DeployVaultMigrationHelper.s.sol/143/run-1792141383544.json new file mode 100644 index 00000000..a60bc06c --- /dev/null +++ b/broadcast/DeployVaultMigrationHelper.s.sol/143/run-1792141383544.json @@ -0,0 +1,68 @@ +{ + "transactions": [ + { + "hash": "0x23e66573a3c40b2b7fd6134189b2125b44bdd098bd1b268d77847e33940e80e1", + "transactionType": "CREATE2", + "contractName": "VaultMigrationHelper", + "contractAddress": "0x15644829b53fcf7e2aab67d6fc68f39908121276", + "function": null, + "arguments": [ + "0x74dbD45B505fB3b9F94C31E5f15c41b8E9883B04", + "0x8E6baE671D7406898D958B9799C37B96058aBA01", + "0x0A9bb266E06e1f003a61ab02CaDa1eE147951eB1" + ], + "transaction": { + "from": "0xb0403b32f54d0bd752113f4009e8b534c6669f44", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "gas": "0x2ebfb2", + "value": "0x0", + "input": "0x4741544f5200000000000000000000000000000000000000000000000000000060c06040523480156200001157600080fd5b5060405162002a5738038062002a57833981016040819052620000349162000151565b826001600160a01b0381166200006457604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006f81620000c6565b506001600160a01b03821615806200008e57506001600160a01b038116155b15620000ad5760405163f6b2911f60e01b815260040160405180910390fd5b6001600160a01b039182166080521660a052506200019b565b600180546001600160a01b0319169055620000e181620000e4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200014c57600080fd5b919050565b6000806000606084860312156200016757600080fd5b620001728462000134565b9250620001826020850162000134565b9150620001926040850162000134565b90509250925092565b60805160a0516128496200020e60003960008181610114015281816106570152818161084f01528181610b2b01528181610e54015281816110e7015281816112d9015261161301526000818161021c015281816105d1015281816107c30152818161116d015261136501526128496000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80638da5cb5b11610097578063e30c397811610066578063e30c397814610206578063f0494ae914610217578063f2fde38b1461023e578063fe7e01401461025157600080fd5b80638da5cb5b146101bc57806392c9d9c4146101cd578063959c192f146101e0578063b29c691a146101f357600080fd5b80631d6b1eaa116100d35780631d6b1eaa14610166578063715018a614610199578063758d9458146101a157806379ba5097146101b457600080fd5b8063025297a3146100fa5780630a6156731461010f5780631ac1d7f614610153575b600080fd5b61010d610108366004612036565b610264565b005b6101367f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61010d610161366004612090565b610302565b6101896101743660046120cd565b60026020526000908152604090205460ff1681565b604051901515815260200161014a565b61010d61030e565b61010d6101af3660046120fb565b610322565b61010d6103b7565b6000546001600160a01b0316610136565b61010d6101db366004612090565b6103fd565b61010d6101ee36600461213d565b610406565b61010d610201366004612036565b61040f565b6001546001600160a01b0316610136565b6101367f000000000000000000000000000000000000000000000000000000000000000081565b61010d61024c366004612178565b6104a4565b61010d61025f366004612036565b610515565b8060008190036102875760405163ca3487f760e01b815260040160405180910390fd5b60005b818110156102c6576102be8484838181106102a7576102a7612195565b90506020028101906102b991906121ab565b6105aa565b60010161028a565b5060405181815233907f8a3422e5ded26de54a5c194b005f77bdef98038ebe69a79d292eba9af8616f5c906020015b60405180910390a2505050565b61030b81610a19565b50565b610316611035565b6103206000611062565b565b61032a611035565b6001600160a01b03811661035157604051634e46966960e11b815260040160405180910390fd5b6103656001600160a01b038416828461107b565b806001600160a01b0316836001600160a01b03167f0e191ee1e354192688af5ec7da0cc3d7f54ecb056e6418d42e73ddc4c37d3637846040516103aa91815260200190565b60405180910390a3505050565b60015433906001600160a01b031681146103f45760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b61030b81611062565b61030b816105aa565b61030b816110d2565b8060008190036104325760405163ca3487f760e01b815260040160405180910390fd5b60005b818110156104715761046984848381811061045257610452612195565b905060200281019061046491906121ab565b610a19565b600101610435565b5060405181815233907f52b52f27937e467c9a09b7f37300dc1ff6eb112008c835fe3e116a7ddf7fffb1906020016102f5565b6104ac611035565b600180546001600160a01b0383166001600160a01b031990911681179091556104dd6000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b8060008190036105385760405163ca3487f760e01b815260040160405180910390fd5b60005b818110156105775761056f84848381811061055857610558612195565b905060200281019061056a91906121cb565b6110d2565b60010161053b565b5060405181815233907f0c0092b6f5703abec5bec7519093f9529559b9a67133c9e954d5a894dc4ca3c1906020016102f5565b60006105cb6105b983806121e1565b6105c660408601866121e1565b611443565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa15801561062d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610651919061222b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106d7919061222b565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918416906370a0823190602401602060405180830381865afa158015610723573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107479190612248565b6040516370a0823160e01b81526001600160a01b0386811660048301529192506000918416906370a0823190602401602060405180830381865afa158015610793573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b79190612248565b90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016632a4903316107f288806121e1565b89602001356040518463ffffffff1660e01b8152600401610815939291906124b6565b600060405180830381600087803b15801561082f57600080fd5b505af1158015610843573d6000803e3d6000fd5b50506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169150638bd29119905061088560408901896121e1565b60608a013561089760808c018c6124da565b6040518563ffffffff1660e01b81526004016108b694939291906124f0565b600060405180830381600087803b1580156108d057600080fd5b505af11580156108e4573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038089166004830181905293507f45a3075437d94bea02b2e2e31bc66925ac1e7057fc250dfd25b82d95b1ee454e92508716906370a08231906024015b602060405180830381865afa158015610954573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109789190612248565b610982908561255b565b6040516370a0823160e01b81526001600160a01b0389811660048301528591908816906370a0823190602401602060405180830381865afa1580156109cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ef9190612248565b6109f9919061255b565b6040805192835260208301919091520160405180910390a2505050505050565b6000610a286020830183612178565b90506000610a3c6040840160208501612178565b90506001600160a01b0382161580610a5b57506001600160a01b038116155b15610a795760405163f6b2911f60e01b815260040160405180910390fd5b6000610a8860408501856121e1565b9150506002811015610aac57604051622d38d560e61b815260040160405180910390fd5b6001600160a01b038316610ac360408601866121e1565b610ace60018561255b565b818110610add57610add612195565b9050602002810190610aef919061256e565b610b00906040810190602001612178565b6001600160a01b031614610b27576040516333c9793760e11b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bab919061222b565b6040516370a0823160e01b81526001600160a01b0386811660048301529192506000918316906370a0823190602401602060405180830381865afa158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1b9190612248565b9050610c718585610c2f60408a018a6121e1565b610c3a60028961255b565b818110610c4957610c49612195565b9050602002810190610c5b919061256e565b60400135610c6c60808b018b612584565b61152e565b610c8a85858484610c8560608c018c6124da565b61160f565b604080516001808252818301909252600091816020015b6060815260200190600190039081610ca1579050509050610cc560408801886121e1565b604051602001610cd69291906125cb565b60405160208183030381529060405281600081518110610cf857610cf8612195565b6020908102919091010152604080516001808252818301909252600091816020016020820280368337019050509050610d2f611a20565b81600081518110610d4257610d42612195565b6020908102919091010152604080516001808252818301909252600091816020015b6060815260200190600190039081610d645790505060405130602482015260448101869052909150610dc790869060009060640160408051601f198184030181529190526020810180516001600160e01b031663a9059cbb60e01b179052611a33565b81600081518110610dda57610dda612195565b60209081029190910101526040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e509190612248565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed4919061222b565b6001600160a01b031663cef6d2098585856040518463ffffffff1660e01b8152600401610f0393929190612689565b600060405180830381600087803b158015610f1d57600080fd5b505af1158015610f31573d6000803e3d6000fd5b505050508481610f4191906126f3565b6040516370a0823160e01b81523060048201526001600160a01b038816906370a0823190602401602060405180830381865afa158015610f85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa99190612248565b1015610fc857604051630e2b109d60e21b815260040160405180910390fd5b610fdc6001600160a01b038716898761107b565b876001600160a01b0316896001600160a01b03167f8a3c24afa49ca98f9c0a5d6eed2e8db7de0d3aa54606e195e048ab203a26c9ab8760405161102191815260200190565b60405180910390a350505050505050505050565b6000546001600160a01b031633146103205760405163118cdaa760e01b81523360048201526024016103eb565b600180546001600160a01b031916905561030b81611a62565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526110cd908490611ab2565b505050565b60006110e16105b983806121e1565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015611143573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611167919061222b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ed919061222b565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918416906370a0823190602401602060405180830381865afa158015611239573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061125d9190612248565b6040516370a0823160e01b81526001600160a01b0386811660048301529192506000918416906370a0823190602401602060405180830381865afa1580156112a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112cd9190612248565b90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016632a49033161130888806121e1565b89602001356040518463ffffffff1660e01b815260040161132b939291906124b6565b600060405180830381600087803b15801561134557600080fd5b505af1158015611359573d6000803e3d6000fd5b50506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016915063ae92a63a905061139b60408901896121e1565b89606001356040518463ffffffff1660e01b81526004016113be939291906124b6565b600060405180830381600087803b1580156113d857600080fd5b505af11580156113ec573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038089166004830181905293507f76a562da51cd826e7bf7840f66c6d83c26b25240116db9f841fd0d321cb60d3792508716906370a0823190602401610937565b6000838260028210806114565750600281105b1561147357604051622d38d560e61b815260040160405180910390fd5b868661148060018561255b565b81811061148f5761148f612195565b90506020028101906114a1919061256e565b6114b2906040810190602001612178565b925084846114c160018461255b565b8181106114d0576114d0612195565b90506020028101906114e2919061256e565b6114f3906040810190602001612178565b6001600160a01b0316836001600160a01b031614611524576040516333c9793760e11b815260040160405180910390fd5b5050949350505050565b604080513060208083019190915246828401526001600160a01b0388811660608401528716608083015260a08083018790528351808403909101815260c090920190925280519101207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c812090506115e9878286868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b1592505050565b611606576040516338fed95f60e21b815260040160405180910390fd5b50505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166357edab4e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561166f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611693919061222b565b90506000816001600160a01b031663e680e5906040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f99190612706565b905060fe1960ff82160161172057604051633a04c9c560e11b815260040160405180910390fd5b604080513060208201526001600160a01b0380851692820192909252466060820152818916608082015281881660a082015290861660c082015260e0810185905283356101008201526000906101200160408051601f1981840301815291815281516020928301206000818152600290935291205490915060ff16156117b95760405163df787a6d60e01b815260040160405180910390fd5b83354211156117db57604051638939364960e01b815260040160405180910390fd5b6000836001600160a01b031663050ae9a86040518163ffffffff1660e01b8152600401602060405180830381865afa15801561181b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183f9190612729565b90506000816001600160601b031611801561186c57506118686001600160601b038216426126f3565b8535115b1561188a576040516333c00ef760e01b815260040160405180910390fd5b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c812090611905826118cb60208a018a612584565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b7992505050565b9050856001600160a01b031663bf7e214f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611945573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611969919061222b565b60405163753e513b60e11b81526001600160a01b03838116600483015260ff88166024830152919091169063ea7ca27690604401602060405180830381865afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de9190612752565b6119fb57604051636249c9a360e11b815260040160405180910390fd5b5050506000908152600260205260409020805460ff1916600117905550505050505050565b6000611a2e81808080611ba5565b905090565b6060838383604051602001611a4a93929190612774565b60405160208183030381529060405290509392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000611ac76001600160a01b03841683611c10565b90508051600014158015611aec575080806020019051810190611aea9190612752565b155b156110cd57604051635274afe760e01b81526001600160a01b03841660048201526024016103eb565b6000806000611b248585611c1e565b5090925090506000816003811115611b3e57611b3e6127ae565b148015611b5c5750856001600160a01b0316826001600160a01b0316145b80611b6d5750611b6d868686611c6b565b925050505b9392505050565b600080600080611b898686611c1e565b925092509250611b998282611d46565b50909150505b92915050565b604080516001600160f81b03198087166020830152851660218201526000602282018190526001600160e01b03198516602683015269ffffffffffffffffffff198416602a8301529101604051602081830303815290604052611c07906127c4565b95945050505050565b6060611b7283836000611e03565b60008060008351604103611c585760208401516040850151606086015160001a611c4a88828585611e96565b955095509550505050611c64565b50508151600091506002905b9250925092565b6000806000856001600160a01b03168585604051602401611c8d9291906127e8565b60408051601f198184030181529181526020820180516001600160e01b0316630b135d3f60e11b17905251611cc29190612801565b600060405180830381855afa9150503d8060008114611cfd576040519150601f19603f3d011682016040523d82523d6000602084013e611d02565b606091505b5091509150818015611d1657506020815110155b8015611b6d57508051630b135d3f60e11b90611d3b9083016020908101908401612248565b149695505050505050565b6000826003811115611d5a57611d5a6127ae565b03611d63575050565b6001826003811115611d7757611d776127ae565b03611d955760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611da957611da96127ae565b03611dca5760405163fce698f760e01b8152600481018290526024016103eb565b6003826003811115611dde57611dde6127ae565b03611dff576040516335e2f38360e21b8152600481018290526024016103eb565b5050565b606081471015611e285760405163cd78605960e01b81523060048201526024016103eb565b600080856001600160a01b03168486604051611e449190612801565b60006040518083038185875af1925050503d8060008114611e81576040519150601f19603f3d011682016040523d82523d6000602084013e611e86565b606091505b5091509150611b6d868383611f65565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611ed15750600091506003905082611f5b565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611f25573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611f5157506000925060019150829050611f5b565b9250600091508190505b9450945094915050565b606082611f7a57611f7582611fc1565b611b72565b8151158015611f9157506001600160a01b0384163b155b15611fba57604051639996b31560e01b81526001600160a01b03851660048201526024016103eb565b5080611b72565b805115611fd15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60008083601f840112611ffc57600080fd5b50813567ffffffffffffffff81111561201457600080fd5b6020830191508360208260051b850101111561202f57600080fd5b9250929050565b6000806020838503121561204957600080fd5b823567ffffffffffffffff81111561206057600080fd5b61206c85828601611fea565b90969095509350505050565b600060a0828403121561208a57600080fd5b50919050565b6000602082840312156120a257600080fd5b813567ffffffffffffffff8111156120b957600080fd5b6120c584828501612078565b949350505050565b6000602082840312156120df57600080fd5b5035919050565b6001600160a01b038116811461030b57600080fd5b60008060006060848603121561211057600080fd5b833561211b816120e6565b9250602084013591506040840135612132816120e6565b809150509250925092565b60006020828403121561214f57600080fd5b813567ffffffffffffffff81111561216657600080fd5b820160808185031215611b7257600080fd5b60006020828403121561218a57600080fd5b8135611b72816120e6565b634e487b7160e01b600052603260045260246000fd5b60008235609e198336030181126121c157600080fd5b9190910192915050565b60008235607e198336030181126121c157600080fd5b6000808335601e198436030181126121f857600080fd5b83018035915067ffffffffffffffff82111561221357600080fd5b6020019150600581901b360382131561202f57600080fd5b60006020828403121561223d57600080fd5b8151611b72816120e6565b60006020828403121561225a57600080fd5b5051919050565b6000808335601e1984360301811261227857600080fd5b830160208101925035905067ffffffffffffffff81111561229857600080fd5b80360382131561202f57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008383855260208086019550808560051b830101846000805b8881101561238357858403601f19018a52823536899003605e1901811261230f578283fd5b88016060813561231e816120e6565b6001600160a01b0316865261233582880183612261565b828989015261234783890182846122a7565b92505050604061235981840184612261565b93508783038289015261236d8385836122a7565b9d89019d975050509386019350506001016122ea565b509198975050505050505050565b818352602080840193600091600585811b8301820185855b888110156124a857858303601f19018a5281353689900360be190181126123cf57600080fd5b880160c081356123de816120e6565b6001600160a01b03908116865282880135906123f9826120e6565b16858801526040828101359086015260608083013536849003601e1901811261242157600080fd5b8301888101903567ffffffffffffffff81111561243d57600080fd5b80891b360382131561244e57600080fd5b838389015261246084890182846122d0565b9350505050608080830135818701525060a061247e81840184612261565b9350868303828801526124928385836122a7565b9d89019d965050509286019250506001016123a9565b509098975050505050505050565b6040815260006124ca604083018587612391565b9050826020830152949350505050565b60008235603e198336030181126121c157600080fd5b606081526000612504606083018688612391565b8460208401528281036040840152833581526125236020850185612261565b604060208401526125386040840182846122a7565b9998505050505050505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611b9f57611b9f612545565b6000823560be198336030181126121c157600080fd5b6000808335601e1984360301811261259b57600080fd5b83018035915067ffffffffffffffff8211156125b657600080fd5b60200191503681900382131561202f57600080fd5b6020815260006120c5602083018486612391565b60005b838110156125fa5781810151838201526020016125e2565b50506000910152565b6000815180845261261b8160208601602086016125df565b601f01601f19169290920160200192915050565b60008282518085526020808601955060208260051b8401016020860160005b8481101561267c57601f1986840301895261266a838351612603565b9884019892509083019060010161264e565b5090979650505050505050565b60608152600061269c606083018661262f565b82810360208481019190915285518083528682019282019060005b818110156126d3578451835293830193918301916001016126b7565b505084810360408601526126e7818761262f565b98975050505050505050565b80820180821115611b9f57611b9f612545565b60006020828403121561271857600080fd5b815160ff81168114611b7257600080fd5b60006020828403121561273b57600080fd5b81516001600160601b0381168114611b7257600080fd5b60006020828403121561276457600080fd5b81518015158114611b7257600080fd5b6001600160601b03198460601b1681528260148201526000825161279f8160348501602087016125df565b91909101603401949350505050565b634e487b7160e01b600052602160045260246000fd5b8051602080830151919081101561208a5760001960209190910360031b1b16919050565b8281526040602082015260006120c56040830184612603565b600082516121c18184602087016125df56fea2646970667358221220b9587085641a71c5e2699086a9dc9296d3c7d0c349f07fe7c220d2ef10d6d17a64736f6c6343000817003300000000000000000000000074dbd45b505fb3b9f94c31e5f15c41b8e9883b040000000000000000000000008e6bae671d7406898d958b9799c37b96058aba010000000000000000000000000a9bb266e06e1f003a61ab02cada1ee147951eb1", + "nonce": "0x32", + "chainId": "0x8f" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x10a39ee", + "logs": [ + { + "address": "0x15644829b53fcf7e2aab67d6fc68f39908121276", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000074dbd45b505fb3b9f94c31e5f15c41b8e9883b04" + ], + "data": "0x", + "blockHash": "0x8e866efbc22c10b9f27e18db142ffe66c22988ef7519aeb1c7a13225cf04aa72", + "blockNumber": "0x6c952b3", + "blockTimestamp": "0x6ad1e847", + "transactionHash": "0x23e66573a3c40b2b7fd6134189b2125b44bdd098bd1b268d77847e33940e80e1", + "transactionIndex": "0x12", + "logIndex": "0x74", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000001000000000000000000000000004000000000000000000000000000000000000000000000000000000000000200000001000000000000000000000000000000000080020000000000000000000800000010000000800000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "transactionHash": "0x23e66573a3c40b2b7fd6134189b2125b44bdd098bd1b268d77847e33940e80e1", + "transactionIndex": "0x12", + "blockHash": "0x8e866efbc22c10b9f27e18db142ffe66c22988ef7519aeb1c7a13225cf04aa72", + "blockNumber": "0x6c952b3", + "gasUsed": "0x2ebfb2", + "effectiveGasPrice": "0x17bfac7c00", + "from": "0xb0403b32f54d0bd752113f4009e8b534c6669f44", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1792141383544, + "chain": 143, + "commit": "5edf9d8" +} \ No newline at end of file diff --git a/broadcast/DeployVaultMigrationHelper.s.sol/143/run-latest.json b/broadcast/DeployVaultMigrationHelper.s.sol/143/run-latest.json new file mode 100644 index 00000000..a60bc06c --- /dev/null +++ b/broadcast/DeployVaultMigrationHelper.s.sol/143/run-latest.json @@ -0,0 +1,68 @@ +{ + "transactions": [ + { + "hash": "0x23e66573a3c40b2b7fd6134189b2125b44bdd098bd1b268d77847e33940e80e1", + "transactionType": "CREATE2", + "contractName": "VaultMigrationHelper", + "contractAddress": "0x15644829b53fcf7e2aab67d6fc68f39908121276", + "function": null, + "arguments": [ + "0x74dbD45B505fB3b9F94C31E5f15c41b8E9883B04", + "0x8E6baE671D7406898D958B9799C37B96058aBA01", + "0x0A9bb266E06e1f003a61ab02CaDa1eE147951eB1" + ], + "transaction": { + "from": "0xb0403b32f54d0bd752113f4009e8b534c6669f44", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "gas": "0x2ebfb2", + "value": "0x0", + "input": "0x4741544f5200000000000000000000000000000000000000000000000000000060c06040523480156200001157600080fd5b5060405162002a5738038062002a57833981016040819052620000349162000151565b826001600160a01b0381166200006457604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006f81620000c6565b506001600160a01b03821615806200008e57506001600160a01b038116155b15620000ad5760405163f6b2911f60e01b815260040160405180910390fd5b6001600160a01b039182166080521660a052506200019b565b600180546001600160a01b0319169055620000e181620000e4565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200014c57600080fd5b919050565b6000806000606084860312156200016757600080fd5b620001728462000134565b9250620001826020850162000134565b9150620001926040850162000134565b90509250925092565b60805160a0516128496200020e60003960008181610114015281816106570152818161084f01528181610b2b01528181610e54015281816110e7015281816112d9015261161301526000818161021c015281816105d1015281816107c30152818161116d015261136501526128496000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80638da5cb5b11610097578063e30c397811610066578063e30c397814610206578063f0494ae914610217578063f2fde38b1461023e578063fe7e01401461025157600080fd5b80638da5cb5b146101bc57806392c9d9c4146101cd578063959c192f146101e0578063b29c691a146101f357600080fd5b80631d6b1eaa116100d35780631d6b1eaa14610166578063715018a614610199578063758d9458146101a157806379ba5097146101b457600080fd5b8063025297a3146100fa5780630a6156731461010f5780631ac1d7f614610153575b600080fd5b61010d610108366004612036565b610264565b005b6101367f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61010d610161366004612090565b610302565b6101896101743660046120cd565b60026020526000908152604090205460ff1681565b604051901515815260200161014a565b61010d61030e565b61010d6101af3660046120fb565b610322565b61010d6103b7565b6000546001600160a01b0316610136565b61010d6101db366004612090565b6103fd565b61010d6101ee36600461213d565b610406565b61010d610201366004612036565b61040f565b6001546001600160a01b0316610136565b6101367f000000000000000000000000000000000000000000000000000000000000000081565b61010d61024c366004612178565b6104a4565b61010d61025f366004612036565b610515565b8060008190036102875760405163ca3487f760e01b815260040160405180910390fd5b60005b818110156102c6576102be8484838181106102a7576102a7612195565b90506020028101906102b991906121ab565b6105aa565b60010161028a565b5060405181815233907f8a3422e5ded26de54a5c194b005f77bdef98038ebe69a79d292eba9af8616f5c906020015b60405180910390a2505050565b61030b81610a19565b50565b610316611035565b6103206000611062565b565b61032a611035565b6001600160a01b03811661035157604051634e46966960e11b815260040160405180910390fd5b6103656001600160a01b038416828461107b565b806001600160a01b0316836001600160a01b03167f0e191ee1e354192688af5ec7da0cc3d7f54ecb056e6418d42e73ddc4c37d3637846040516103aa91815260200190565b60405180910390a3505050565b60015433906001600160a01b031681146103f45760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b61030b81611062565b61030b816105aa565b61030b816110d2565b8060008190036104325760405163ca3487f760e01b815260040160405180910390fd5b60005b818110156104715761046984848381811061045257610452612195565b905060200281019061046491906121ab565b610a19565b600101610435565b5060405181815233907f52b52f27937e467c9a09b7f37300dc1ff6eb112008c835fe3e116a7ddf7fffb1906020016102f5565b6104ac611035565b600180546001600160a01b0383166001600160a01b031990911681179091556104dd6000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b8060008190036105385760405163ca3487f760e01b815260040160405180910390fd5b60005b818110156105775761056f84848381811061055857610558612195565b905060200281019061056a91906121cb565b6110d2565b60010161053b565b5060405181815233907f0c0092b6f5703abec5bec7519093f9529559b9a67133c9e954d5a894dc4ca3c1906020016102f5565b60006105cb6105b983806121e1565b6105c660408601866121e1565b611443565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa15801561062d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610651919061222b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106d7919061222b565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918416906370a0823190602401602060405180830381865afa158015610723573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107479190612248565b6040516370a0823160e01b81526001600160a01b0386811660048301529192506000918416906370a0823190602401602060405180830381865afa158015610793573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b79190612248565b90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016632a4903316107f288806121e1565b89602001356040518463ffffffff1660e01b8152600401610815939291906124b6565b600060405180830381600087803b15801561082f57600080fd5b505af1158015610843573d6000803e3d6000fd5b50506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169150638bd29119905061088560408901896121e1565b60608a013561089760808c018c6124da565b6040518563ffffffff1660e01b81526004016108b694939291906124f0565b600060405180830381600087803b1580156108d057600080fd5b505af11580156108e4573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038089166004830181905293507f45a3075437d94bea02b2e2e31bc66925ac1e7057fc250dfd25b82d95b1ee454e92508716906370a08231906024015b602060405180830381865afa158015610954573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109789190612248565b610982908561255b565b6040516370a0823160e01b81526001600160a01b0389811660048301528591908816906370a0823190602401602060405180830381865afa1580156109cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ef9190612248565b6109f9919061255b565b6040805192835260208301919091520160405180910390a2505050505050565b6000610a286020830183612178565b90506000610a3c6040840160208501612178565b90506001600160a01b0382161580610a5b57506001600160a01b038116155b15610a795760405163f6b2911f60e01b815260040160405180910390fd5b6000610a8860408501856121e1565b9150506002811015610aac57604051622d38d560e61b815260040160405180910390fd5b6001600160a01b038316610ac360408601866121e1565b610ace60018561255b565b818110610add57610add612195565b9050602002810190610aef919061256e565b610b00906040810190602001612178565b6001600160a01b031614610b27576040516333c9793760e11b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bab919061222b565b6040516370a0823160e01b81526001600160a01b0386811660048301529192506000918316906370a0823190602401602060405180830381865afa158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1b9190612248565b9050610c718585610c2f60408a018a6121e1565b610c3a60028961255b565b818110610c4957610c49612195565b9050602002810190610c5b919061256e565b60400135610c6c60808b018b612584565b61152e565b610c8a85858484610c8560608c018c6124da565b61160f565b604080516001808252818301909252600091816020015b6060815260200190600190039081610ca1579050509050610cc560408801886121e1565b604051602001610cd69291906125cb565b60405160208183030381529060405281600081518110610cf857610cf8612195565b6020908102919091010152604080516001808252818301909252600091816020016020820280368337019050509050610d2f611a20565b81600081518110610d4257610d42612195565b6020908102919091010152604080516001808252818301909252600091816020015b6060815260200190600190039081610d645790505060405130602482015260448101869052909150610dc790869060009060640160408051601f198184030181529190526020810180516001600160e01b031663a9059cbb60e01b179052611a33565b81600081518110610dda57610dda612195565b60209081029190910101526040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e509190612248565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed4919061222b565b6001600160a01b031663cef6d2098585856040518463ffffffff1660e01b8152600401610f0393929190612689565b600060405180830381600087803b158015610f1d57600080fd5b505af1158015610f31573d6000803e3d6000fd5b505050508481610f4191906126f3565b6040516370a0823160e01b81523060048201526001600160a01b038816906370a0823190602401602060405180830381865afa158015610f85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa99190612248565b1015610fc857604051630e2b109d60e21b815260040160405180910390fd5b610fdc6001600160a01b038716898761107b565b876001600160a01b0316896001600160a01b03167f8a3c24afa49ca98f9c0a5d6eed2e8db7de0d3aa54606e195e048ab203a26c9ab8760405161102191815260200190565b60405180910390a350505050505050505050565b6000546001600160a01b031633146103205760405163118cdaa760e01b81523360048201526024016103eb565b600180546001600160a01b031916905561030b81611a62565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526110cd908490611ab2565b505050565b60006110e16105b983806121e1565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015611143573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611167919061222b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ed919061222b565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918416906370a0823190602401602060405180830381865afa158015611239573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061125d9190612248565b6040516370a0823160e01b81526001600160a01b0386811660048301529192506000918416906370a0823190602401602060405180830381865afa1580156112a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112cd9190612248565b90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016632a49033161130888806121e1565b89602001356040518463ffffffff1660e01b815260040161132b939291906124b6565b600060405180830381600087803b15801561134557600080fd5b505af1158015611359573d6000803e3d6000fd5b50506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016915063ae92a63a905061139b60408901896121e1565b89606001356040518463ffffffff1660e01b81526004016113be939291906124b6565b600060405180830381600087803b1580156113d857600080fd5b505af11580156113ec573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038089166004830181905293507f76a562da51cd826e7bf7840f66c6d83c26b25240116db9f841fd0d321cb60d3792508716906370a0823190602401610937565b6000838260028210806114565750600281105b1561147357604051622d38d560e61b815260040160405180910390fd5b868661148060018561255b565b81811061148f5761148f612195565b90506020028101906114a1919061256e565b6114b2906040810190602001612178565b925084846114c160018461255b565b8181106114d0576114d0612195565b90506020028101906114e2919061256e565b6114f3906040810190602001612178565b6001600160a01b0316836001600160a01b031614611524576040516333c9793760e11b815260040160405180910390fd5b5050949350505050565b604080513060208083019190915246828401526001600160a01b0388811660608401528716608083015260a08083018790528351808403909101815260c090920190925280519101207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c812090506115e9878286868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b1592505050565b611606576040516338fed95f60e21b815260040160405180910390fd5b50505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166357edab4e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561166f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611693919061222b565b90506000816001600160a01b031663e680e5906040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f99190612706565b905060fe1960ff82160161172057604051633a04c9c560e11b815260040160405180910390fd5b604080513060208201526001600160a01b0380851692820192909252466060820152818916608082015281881660a082015290861660c082015260e0810185905283356101008201526000906101200160408051601f1981840301815291815281516020928301206000818152600290935291205490915060ff16156117b95760405163df787a6d60e01b815260040160405180910390fd5b83354211156117db57604051638939364960e01b815260040160405180910390fd5b6000836001600160a01b031663050ae9a86040518163ffffffff1660e01b8152600401602060405180830381865afa15801561181b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183f9190612729565b90506000816001600160601b031611801561186c57506118686001600160601b038216426126f3565b8535115b1561188a576040516333c00ef760e01b815260040160405180910390fd5b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c812090611905826118cb60208a018a612584565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b7992505050565b9050856001600160a01b031663bf7e214f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611945573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611969919061222b565b60405163753e513b60e11b81526001600160a01b03838116600483015260ff88166024830152919091169063ea7ca27690604401602060405180830381865afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de9190612752565b6119fb57604051636249c9a360e11b815260040160405180910390fd5b5050506000908152600260205260409020805460ff1916600117905550505050505050565b6000611a2e81808080611ba5565b905090565b6060838383604051602001611a4a93929190612774565b60405160208183030381529060405290509392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000611ac76001600160a01b03841683611c10565b90508051600014158015611aec575080806020019051810190611aea9190612752565b155b156110cd57604051635274afe760e01b81526001600160a01b03841660048201526024016103eb565b6000806000611b248585611c1e565b5090925090506000816003811115611b3e57611b3e6127ae565b148015611b5c5750856001600160a01b0316826001600160a01b0316145b80611b6d5750611b6d868686611c6b565b925050505b9392505050565b600080600080611b898686611c1e565b925092509250611b998282611d46565b50909150505b92915050565b604080516001600160f81b03198087166020830152851660218201526000602282018190526001600160e01b03198516602683015269ffffffffffffffffffff198416602a8301529101604051602081830303815290604052611c07906127c4565b95945050505050565b6060611b7283836000611e03565b60008060008351604103611c585760208401516040850151606086015160001a611c4a88828585611e96565b955095509550505050611c64565b50508151600091506002905b9250925092565b6000806000856001600160a01b03168585604051602401611c8d9291906127e8565b60408051601f198184030181529181526020820180516001600160e01b0316630b135d3f60e11b17905251611cc29190612801565b600060405180830381855afa9150503d8060008114611cfd576040519150601f19603f3d011682016040523d82523d6000602084013e611d02565b606091505b5091509150818015611d1657506020815110155b8015611b6d57508051630b135d3f60e11b90611d3b9083016020908101908401612248565b149695505050505050565b6000826003811115611d5a57611d5a6127ae565b03611d63575050565b6001826003811115611d7757611d776127ae565b03611d955760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611da957611da96127ae565b03611dca5760405163fce698f760e01b8152600481018290526024016103eb565b6003826003811115611dde57611dde6127ae565b03611dff576040516335e2f38360e21b8152600481018290526024016103eb565b5050565b606081471015611e285760405163cd78605960e01b81523060048201526024016103eb565b600080856001600160a01b03168486604051611e449190612801565b60006040518083038185875af1925050503d8060008114611e81576040519150601f19603f3d011682016040523d82523d6000602084013e611e86565b606091505b5091509150611b6d868383611f65565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611ed15750600091506003905082611f5b565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611f25573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611f5157506000925060019150829050611f5b565b9250600091508190505b9450945094915050565b606082611f7a57611f7582611fc1565b611b72565b8151158015611f9157506001600160a01b0384163b155b15611fba57604051639996b31560e01b81526001600160a01b03851660048201526024016103eb565b5080611b72565b805115611fd15780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60008083601f840112611ffc57600080fd5b50813567ffffffffffffffff81111561201457600080fd5b6020830191508360208260051b850101111561202f57600080fd5b9250929050565b6000806020838503121561204957600080fd5b823567ffffffffffffffff81111561206057600080fd5b61206c85828601611fea565b90969095509350505050565b600060a0828403121561208a57600080fd5b50919050565b6000602082840312156120a257600080fd5b813567ffffffffffffffff8111156120b957600080fd5b6120c584828501612078565b949350505050565b6000602082840312156120df57600080fd5b5035919050565b6001600160a01b038116811461030b57600080fd5b60008060006060848603121561211057600080fd5b833561211b816120e6565b9250602084013591506040840135612132816120e6565b809150509250925092565b60006020828403121561214f57600080fd5b813567ffffffffffffffff81111561216657600080fd5b820160808185031215611b7257600080fd5b60006020828403121561218a57600080fd5b8135611b72816120e6565b634e487b7160e01b600052603260045260246000fd5b60008235609e198336030181126121c157600080fd5b9190910192915050565b60008235607e198336030181126121c157600080fd5b6000808335601e198436030181126121f857600080fd5b83018035915067ffffffffffffffff82111561221357600080fd5b6020019150600581901b360382131561202f57600080fd5b60006020828403121561223d57600080fd5b8151611b72816120e6565b60006020828403121561225a57600080fd5b5051919050565b6000808335601e1984360301811261227857600080fd5b830160208101925035905067ffffffffffffffff81111561229857600080fd5b80360382131561202f57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008383855260208086019550808560051b830101846000805b8881101561238357858403601f19018a52823536899003605e1901811261230f578283fd5b88016060813561231e816120e6565b6001600160a01b0316865261233582880183612261565b828989015261234783890182846122a7565b92505050604061235981840184612261565b93508783038289015261236d8385836122a7565b9d89019d975050509386019350506001016122ea565b509198975050505050505050565b818352602080840193600091600585811b8301820185855b888110156124a857858303601f19018a5281353689900360be190181126123cf57600080fd5b880160c081356123de816120e6565b6001600160a01b03908116865282880135906123f9826120e6565b16858801526040828101359086015260608083013536849003601e1901811261242157600080fd5b8301888101903567ffffffffffffffff81111561243d57600080fd5b80891b360382131561244e57600080fd5b838389015261246084890182846122d0565b9350505050608080830135818701525060a061247e81840184612261565b9350868303828801526124928385836122a7565b9d89019d965050509286019250506001016123a9565b509098975050505050505050565b6040815260006124ca604083018587612391565b9050826020830152949350505050565b60008235603e198336030181126121c157600080fd5b606081526000612504606083018688612391565b8460208401528281036040840152833581526125236020850185612261565b604060208401526125386040840182846122a7565b9998505050505050505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611b9f57611b9f612545565b6000823560be198336030181126121c157600080fd5b6000808335601e1984360301811261259b57600080fd5b83018035915067ffffffffffffffff8211156125b657600080fd5b60200191503681900382131561202f57600080fd5b6020815260006120c5602083018486612391565b60005b838110156125fa5781810151838201526020016125e2565b50506000910152565b6000815180845261261b8160208601602086016125df565b601f01601f19169290920160200192915050565b60008282518085526020808601955060208260051b8401016020860160005b8481101561267c57601f1986840301895261266a838351612603565b9884019892509083019060010161264e565b5090979650505050505050565b60608152600061269c606083018661262f565b82810360208481019190915285518083528682019282019060005b818110156126d3578451835293830193918301916001016126b7565b505084810360408601526126e7818761262f565b98975050505050505050565b80820180821115611b9f57611b9f612545565b60006020828403121561271857600080fd5b815160ff81168114611b7257600080fd5b60006020828403121561273b57600080fd5b81516001600160601b0381168114611b7257600080fd5b60006020828403121561276457600080fd5b81518015158114611b7257600080fd5b6001600160601b03198460601b1681528260148201526000825161279f8160348501602087016125df565b91909101603401949350505050565b634e487b7160e01b600052602160045260246000fd5b8051602080830151919081101561208a5760001960209190910360031b1b16919050565b8281526040602082015260006120c56040830184612603565b600082516121c18184602087016125df56fea2646970667358221220b9587085641a71c5e2699086a9dc9296d3c7d0c349f07fe7c220d2ef10d6d17a64736f6c6343000817003300000000000000000000000074dbd45b505fb3b9f94c31e5f15c41b8e9883b040000000000000000000000008e6bae671d7406898d958b9799c37b96058aba010000000000000000000000000a9bb266e06e1f003a61ab02cada1ee147951eb1", + "nonce": "0x32", + "chainId": "0x8f" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x10a39ee", + "logs": [ + { + "address": "0x15644829b53fcf7e2aab67d6fc68f39908121276", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000074dbd45b505fb3b9f94c31e5f15c41b8e9883b04" + ], + "data": "0x", + "blockHash": "0x8e866efbc22c10b9f27e18db142ffe66c22988ef7519aeb1c7a13225cf04aa72", + "blockNumber": "0x6c952b3", + "blockTimestamp": "0x6ad1e847", + "transactionHash": "0x23e66573a3c40b2b7fd6134189b2125b44bdd098bd1b268d77847e33940e80e1", + "transactionIndex": "0x12", + "logIndex": "0x74", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000001000000000000000000000000004000000000000000000000000000000000000000000000000000000000000200000001000000000000000000000000000000000080020000000000000000000800000010000000800000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "transactionHash": "0x23e66573a3c40b2b7fd6134189b2125b44bdd098bd1b268d77847e33940e80e1", + "transactionIndex": "0x12", + "blockHash": "0x8e866efbc22c10b9f27e18db142ffe66c22988ef7519aeb1c7a13225cf04aa72", + "blockNumber": "0x6c952b3", + "gasUsed": "0x2ebfb2", + "effectiveGasPrice": "0x17bfac7c00", + "from": "0xb0403b32f54d0bd752113f4009e8b534c6669f44", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1792141383544, + "chain": 143, + "commit": "5edf9d8" +} \ No newline at end of file diff --git a/script/DeployVaultMigrationHelper.s.sol b/script/DeployVaultMigrationHelper.s.sol new file mode 100644 index 00000000..17c5cc27 --- /dev/null +++ b/script/DeployVaultMigrationHelper.s.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 +pragma solidity 0.8.23; + +import "forge-std/Script.sol"; +import { console2 } from "forge-std/console2.sol"; + +import { VaultMigrationHelper } from "../src/helpers/VaultMigrationHelper.sol"; + +/** + * @title DeployVaultMigrationHelper + * @notice Deploys VaultMigrationHelper deterministically with CREATE2. + * @dev Fill the required variables in the .env file + * @dev run the script with: + * forge script script/DeployVaultMigrationHelper.s.sol --rpc-url --private-key $PRIVATE_KEY --broadcast + */ +contract DeployVaultMigrationHelper is Script { + bytes32 internal salt; + address internal owner; + address internal baseAdapter; + address internal premiumAdapter; + + function setUp() public { + salt = bytes32(abi.encodePacked(vm.envString("SALT"))); + owner = vm.envAddress("VAULT_MIGRATION_HELPER_OWNER_ADDRESS"); + baseAdapter = vm.envAddress("BASE_VEDA_ADAPTER_ADDRESS"); + premiumAdapter = vm.envAddress("PREMIUM_VEDA_ADAPTER_ADDRESS"); + + console2.log("~~~"); + console2.log("Owner: %s", owner); + console2.log("BaseAdapter: %s", baseAdapter); + console2.log("PremiumAdapter: %s", premiumAdapter); + console2.log("Salt:"); + console2.logBytes32(salt); + } + + function run() public { + vm.startBroadcast(); + address deployed = address(new VaultMigrationHelper{ salt: salt }(owner, baseAdapter, premiumAdapter)); + vm.stopBroadcast(); + + console2.log("VaultMigrationHelper: %s", deployed); + } +} diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol new file mode 100644 index 00000000..98fffdff --- /dev/null +++ b/src/helpers/VaultMigrationHelper.sol @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 +pragma solidity 0.8.23; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; +import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + +import { VedaAdapter } from "./VedaAdapter.sol"; +import { ComplianceVedaAdapter } from "./ComplianceVedaAdapter.sol"; +import { IComplianceVedaTeller } from "./interfaces/IComplianceVedaTeller.sol"; +import { IRolesAuthority } from "./interfaces/IRolesAuthority.sol"; +import { Delegation, ModeCode } from "../utils/Types.sol"; + +/** + * @title VaultMigrationHelper + * @notice Atomically composes the existing base and premium Veda adapters, and moves premium shares with + * dual destination control (user + compliance backend). + * @dev Vault-to-vault migrations keep delegations targeted at the adapter that redeems them. Premium share + * transfers are redeemed by this helper. The user's root must: + * - pin the redeemed call with `AllowedCalldataEnforcer` terms + * `abi.encodePacked(uint256(0), IERC20.transfer.selector, abi.encode(address(this)))`, so the call is + * `transfer` to this helper and the amount stays unset on the root; + * - name the premium vault in `AllowedTargetsEnforcer`; + * - name this contract in `RedeemerEnforcer`. + * The premium-transfer root does not use `ERC20TransferAmountEnforcer`. Migration leaves do; each adapter + * reads its amount from that leaf's `caveats[0].terms[20:52]` (see `ToPremiumParams` and `ToBaseParams`). + * This helper must hold `transferAllowedRole` on the + * premium Teller so Veda allows the first hop (helper is `to`) and the second hop (helper is `from`/`operator`). + * + * Destination control for `premiumTransfer` is dual, not backend-only and not user-only: + * - The share owner (`from`) picks the recipient and ERC-1271-signs the destination digest below. + * That locks `to` to the user's root delegation so a caller or backend cannot redirect the second hop. + * - The compliance backend still EIP-191-signs a digest that includes the same `to`, proving the + * recipient is premium-enabled / KYC'd. A user cannot send to an unapproved address even if they + * signed it. + * Neither party can complete a transfer to an address the other did not attest. The amount is the + * owner's full premium balance at execution (`balanceOf`). It is not read from a leaf caveat. + * The compliance digest includes that amount, so the backend still attests the quantity moved. + * First hop stays helper-only via the root caveats above. + * `premiumTransfer` and `premiumTransferBatch` move premium vault shares and do not update the Teller's + * per-user principal history. `getPrincipalHistoryPaginated` must not be used alone as a per-holder + * principal record. A complete record requires reconciling Teller deposits and withdrawals with vault + * `Transfer` events under an explicit rule for allocating principal when shares move. Transfer events + * alone do not establish that basis. + * + * Signing schemes — wallets and CHOMP must implement both. They are not interchangeable: + * 1. Delegation chain (root + leaf): EIP-712 typed data through DelegationManager + * (`toTypedDataHash(domainHash, EncoderLib._getDelegationHash(delegation))`). The share owner + * signs the root; CHOMP (root `delegate`) signs the leaf. + * 2. Destination proof (`userSignature`): EIP-191 `personal_sign`, not EIP-712. Inner hash: + * `keccak256(abi.encode(address(this), block.chainid, from, to, delegations[length - 2].authority))`. + * Signed payload: `keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", inner))`. + * Verified with ERC-1271 on `from` via SignatureChecker. The chain is leaf-to-root, so + * `delegations[length - 2].authority` is the hash of the root the share owner signed + * (`EncoderLib._getDelegationHash`, the same identifier `disableDelegation` uses), not the leaf + * redelegation. The DelegationManager reverts unless that authority equals the root hash, so the + * proof is bound to this root without hashing the full delegation on-chain. + * 3. Compliance (`compliance.signature`): also EIP-191 `personal_sign`. Inner hash: + * `keccak256(abi.encode(address(this), teller, block.chainid, from, to, vault, amount, deadline))`. + * Same prefix as (2) and as Teller deposits, but the inner tuple starts with this helper so a + * Teller deposit signature cannot be reused. + */ +contract VaultMigrationHelper is Ownable2Step { + using SafeERC20 for IERC20; + + /** + * @notice Parameters for one base-to-premium migration. + * @param withdrawalDelegations Delegation chain redeemed by the base adapter to withdraw base vault shares. + * The leaf's first caveat MUST be an `ERC20TransferAmountEnforcer` encoding the base vault and the + * exact share amount. The base adapter reads that amount from `caveats[0].terms[20:52]`. + * @param minimumAssets Minimum underlying assets that the base withdrawal must return. + * @param depositDelegations Delegation chain redeemed by the premium adapter to deposit the withdrawn assets. + * The same first-caveat requirement applies, encoding mUSD and the exact deposit amount. That amount + * is also bound in `compliance`. + * @param minimumMint Minimum premium vault shares that the destination deposit must mint. + * @param compliance Compliance authorization forwarded unchanged to the premium adapter. + */ + struct ToPremiumParams { + Delegation[] withdrawalDelegations; + uint256 minimumAssets; + Delegation[] depositDelegations; + uint256 minimumMint; + IComplianceVedaTeller.ComplianceData compliance; + } + + /** + * @notice Parameters for one premium-to-base migration. + * @param withdrawalDelegations Delegation chain redeemed by the premium adapter to withdraw premium shares. + * The leaf's first caveat MUST be an `ERC20TransferAmountEnforcer` encoding the premium vault and + * the exact share amount. The premium adapter reads that amount from `caveats[0].terms[20:52]`. + * @param minimumAssets Minimum underlying assets that the premium withdrawal must return. + * @param depositDelegations Delegation chain redeemed by the base adapter to deposit the withdrawn assets. + * The same first-caveat requirement applies, encoding mUSD and the exact deposit amount. + * @param minimumMint Minimum base vault shares that the destination deposit must mint. + */ + struct ToBaseParams { + Delegation[] withdrawalDelegations; + uint256 minimumAssets; + Delegation[] depositDelegations; + uint256 minimumMint; + } + + /** + * @notice Parameters for one dual-signed premium share transfer. + * @param from Share owner and root delegator. + * @param to Recipient chosen by `from` and attested as premium-enabled by the compliance backend. + * @param delegations Chain sorted leaf to root. Leaf `delegate` must be this helper. + * @param compliance Backend-issued EIP-191 approval (`deadline` + `signature`) that includes `to`. + * @param userSignature EIP-191 `personal_sign` from `from` (not EIP-712). Exact inner hash is documented on the contract. + */ + struct PremiumTransferParams { + address from; + address to; + Delegation[] delegations; + IComplianceVedaTeller.ComplianceData compliance; + bytes userSignature; + } + + ////////////////////////////// Events ////////////////////////////// + + /** + * @notice Emitted after a base-to-premium migration completes. + * @param delegator Root delegator whose vault position was migrated. + * @param sourceShares Base vault shares withdrawn. + * @param destShares Premium vault shares minted. + */ + event MigrationToPremiumExecuted(address indexed delegator, uint256 sourceShares, uint256 destShares); + + /** + * @notice Emitted after a premium-to-base migration completes. + * @param delegator Root delegator whose vault position was migrated. + * @param sourceShares Premium vault shares withdrawn. + * @param destShares Base vault shares minted. + */ + event MigrationToBaseExecuted(address indexed delegator, uint256 sourceShares, uint256 destShares); + + /** + * @notice Emitted after a batch of base-to-premium migrations completes. + * @param caller Address that submitted the permissionless batch. + * @param count Number of migrations executed. + */ + event BatchMigrationToPremiumExecuted(address indexed caller, uint256 count); + + /** + * @notice Emitted after a batch of premium-to-base migrations completes. + * @param caller Address that submitted the permissionless batch. + * @param count Number of migrations executed. + */ + event BatchMigrationToBaseExecuted(address indexed caller, uint256 count); + + /** + * @notice Emitted after a dual-signed premium share transfer. + * @param from Share owner whose full premium balance was moved. + * @param to Recipient chosen by `from` and attested as premium-enabled by the compliance backend. + * @param amount Number of premium vault shares transferred. + */ + event PremiumTransferExecuted(address indexed from, address indexed to, uint256 amount); + + /** + * @notice Emitted after a batch of premium share transfers completes. + * @param caller Address that submitted the permissionless batch. + * @param count Number of transfers executed. + */ + event BatchPremiumTransferExecuted(address indexed caller, uint256 count); + + /** + * @notice Emitted when stuck tokens are withdrawn by owner. + * @param token Address of the token withdrawn. + * @param recipient Address of the recipient. + * @param amount Amount of tokens withdrawn. + */ + event StuckTokensWithdrawn(IERC20 indexed token, address indexed recipient, uint256 amount); + + ////////////////////////////// Errors ////////////////////////////// + + /// @dev Thrown when a required constructor address is zero. + error InvalidZeroAddress(); + + /// @dev Thrown when a zero address is provided for the recipient. + error InvalidRecipient(); + + /// @dev Thrown when either delegation chain contains fewer than two delegations. + error InvalidDelegationsLength(); + + /// @dev Thrown when a batch is empty. + error InvalidBatchLength(); + + /// @dev Thrown when the withdrawal and deposit chains have different root delegators. + error DelegatorMismatch(); + + /// @dev Thrown when a transfer compliance approval has already been consumed. + error ComplianceSignatureAlreadyUsed(); + + /// @dev Thrown when a transfer compliance approval has expired. + error ComplianceSignatureExpired(); + + /// @dev Thrown when a transfer compliance deadline exceeds the Teller's configured compliance window. + error ComplianceDeadlineExceedsWindow(); + + /// @dev Thrown when the recovered transfer compliance signer does not hold the Teller's compliance signer role. + error InvalidComplianceSigner(); + + /// @dev Thrown when the premium Teller has compliance signer checks disabled (`complianceSignerRole == 255`). + error ComplianceDisabled(); + + /// @dev Thrown when the share owner's destination signature is missing or does not match from, to, and leaf. + error InvalidUserSignature(); + + /// @dev Thrown when the redeemed transfer did not deliver the approved shares to this helper. + error PremiumTransferNotDelivered(); + + ////////////////////////////// State ////////////////////////////// + + /** + * @notice Base vault adapter used for base deposits and withdrawals. + */ + VedaAdapter public immutable baseAdapter; + + /** + * @notice Premium compliance-gated adapter used for premium deposits and withdrawals. + */ + ComplianceVedaAdapter public immutable premiumAdapter; + + /** + * @notice Compliance message hashes already consumed by `premiumTransfer`. + */ + mapping(bytes32 messageHash => bool used) public usedComplianceSignatures; + + ////////////////////////////// Constructor ////////////////////////////// + + /** + * @notice Initializes the helper with its owner and the base and premium Veda adapters. + * @param _owner Address of the contract owner. + * @param _baseAdapter Address of the base Veda adapter. + * @param _premiumAdapter Address of the premium compliance-gated Veda adapter. + */ + constructor(address _owner, address _baseAdapter, address _premiumAdapter) Ownable(_owner) { + if (_baseAdapter == address(0) || _premiumAdapter == address(0)) { + revert InvalidZeroAddress(); + } + baseAdapter = VedaAdapter(_baseAdapter); + premiumAdapter = ComplianceVedaAdapter(_premiumAdapter); + } + + ////////////////////////////// External Methods ////////////////////////////// + + /** + * @notice Migrates one delegator from the base vault to the premium vault. + * @dev The withdrawal chain is redeemed by `baseAdapter`; the deposit chain and compliance data are + * forwarded to `premiumAdapter`. + * @param _params Withdrawal, deposit, slippage, and compliance parameters for the migration. + * @notice Security consideration: Callable by anyone. Security remains enforced by the delegation chains, + * their caveats, and the premium Teller's compliance validation. + */ + function migrateToPremiumByDelegation(ToPremiumParams calldata _params) external { + _migrateToPremiumByDelegation(_params); + } + + /** + * @notice Migrates multiple delegators from base to premium sequentially in one atomic transaction. + * @dev A revert from any withdrawal or deposit leg rolls back the entire batch. + * @param _params Migration parameters for each stream. + * @notice Security consideration: Callable by anyone. Every delegation chain remains independently enforced. + */ + function migrateToPremiumByDelegationBatch(ToPremiumParams[] calldata _params) external { + uint256 length_ = _params.length; + if (length_ == 0) revert InvalidBatchLength(); + + for (uint256 i = 0; i < length_;) { + _migrateToPremiumByDelegation(_params[i]); + unchecked { + ++i; + } + } + + emit BatchMigrationToPremiumExecuted(msg.sender, length_); + } + + /** + * @notice Migrates one delegator from the premium vault to the base vault. + * @dev The withdrawal chain is redeemed by `premiumAdapter`; the deposit chain is redeemed by `baseAdapter`. + * @param _params Withdrawal, deposit, and slippage parameters for the migration. + * @notice Security consideration: Callable by anyone. Security remains enforced by the delegation chains + * and their caveats. + */ + function migrateToBaseByDelegation(ToBaseParams calldata _params) external { + _migrateToBaseByDelegation(_params); + } + + /** + * @notice Migrates multiple delegators from premium to base sequentially in one atomic transaction. + * @dev A revert from any withdrawal or deposit leg rolls back the entire batch. + * @param _params Migration parameters for each stream. + * @notice Security consideration: Callable by anyone. Every delegation chain remains independently enforced. + */ + function migrateToBaseByDelegationBatch(ToBaseParams[] calldata _params) external { + uint256 length_ = _params.length; + if (length_ == 0) revert InvalidBatchLength(); + + for (uint256 i = 0; i < length_;) { + _migrateToBaseByDelegation(_params[i]); + unchecked { + ++i; + } + } + + emit BatchMigrationToBaseExecuted(msg.sender, length_); + } + + /** + * @notice Moves `from`'s entire premium vault balance to `to` after user and compliance checks. + * @dev The delegation chain must be redeemable only by this helper. The root pins `transfer` to this helper + * via `AllowedCalldataEnforcer` (selector plus recipient) and the premium vault via `AllowedTargetsEnforcer`. + * Production must grant this helper `transferAllowedRole` on the premium Teller. + * The amount is `balanceOf(from)` at execution, not a value encoded in the leaf. + * This moves premium vault shares and does not update the Teller's per-user principal history. + * `getPrincipalHistoryPaginated` must not be used alone as a per-holder principal record. A complete + * record requires reconciling Teller deposits and withdrawals with vault `Transfer` events under an + * explicit rule for allocating principal when shares move. Transfer events alone do not establish + * that basis. + * @param _params From, to, chain, compliance, and user destination signature. + * @notice Security consideration: Callable by anyone. Destination is dual-controlled: the user signature + * binds `to` to this helper, chain, `from`, and the root delegation hash + * (`delegations[length - 2].authority`, the identifier `disableDelegation` uses); the compliance + * signature still includes `to` and the full balance so only a premium-enabled recipient can receive + * that exact amount. Delegations are EIP-712; `userSignature` and compliance are EIP-191 `personal_sign` + * over the exact inner hashes documented on the contract. + * A Teller deposit signature cannot be reused because it binds the Teller address, not this helper. + * Reverts when Teller compliance is disabled. + */ + function premiumTransfer(PremiumTransferParams calldata _params) external { + _premiumTransfer(_params); + } + + /** + * @notice Moves multiple delegators' entire premium vault balances sequentially in one atomic transaction. + * @dev A revert from any transfer rolls back the entire batch. Each stream needs its own user signature + * (destination) and compliance signature (premium-enabled `to`) because both digests include `from`/`to`. + * Every stream moves premium vault shares and does not update the Teller's per-user principal history. + * `getPrincipalHistoryPaginated` must not be used alone as a per-holder principal record. A complete + * record requires reconciling Teller deposits and withdrawals with vault `Transfer` events under an + * explicit rule for allocating principal when shares move. Transfer events alone do not establish + * that basis. + * @param _params Transfer parameters for each stream. + * @notice Security consideration: Callable by anyone. Every delegation chain, user destination signature, + * and compliance signature remains independently enforced. + */ + function premiumTransferBatch(PremiumTransferParams[] calldata _params) external { + uint256 length_ = _params.length; + if (length_ == 0) revert InvalidBatchLength(); + + for (uint256 i = 0; i < length_;) { + _premiumTransfer(_params[i]); + unchecked { + ++i; + } + } + + emit BatchPremiumTransferExecuted(msg.sender, length_); + } + + /** + * @notice Emergency function to recover tokens accidentally sent to this contract. + * @dev Migrations should not leave balances here. `premiumTransfer` only holds shares mid-transaction. + * This function is only for recovering tokens sent to this contract by mistake. + * @param _token The token to be recovered. + * @param _amount The amount of tokens to recover. + * @param _recipient The address to receive the recovered tokens. + */ + function withdrawEmergency(IERC20 _token, uint256 _amount, address _recipient) external onlyOwner { + if (_recipient == address(0)) revert InvalidRecipient(); + + _token.safeTransfer(_recipient, _amount); + + emit StuckTokensWithdrawn(_token, _recipient, _amount); + } + + ////////////////////////////// Private/Internal Methods ////////////////////////////// + + /** + * @notice Executes one dual-signed premium share transfer. + * @dev User destination signature is checked before the backend compliance signature and before redeem. + * The second hop may spend only shares received in this call. `redeemDelegations` does not prove the + * root moved shares, so this records the helper's vault balance before redemption and reverts unless + * that balance grew by at least `amount_`. Any surplus stays here for `withdrawEmergency`. + * The share move does not update the Teller's per-user principal history. + * `getPrincipalHistoryPaginated` must not be used alone as a per-holder principal record. A complete + * record requires reconciling Teller deposits and withdrawals with vault `Transfer` events under an + * explicit rule for allocating principal when shares move. Transfer events alone do not establish + * that basis. + */ + function _premiumTransfer(PremiumTransferParams calldata _params) internal { + address from_ = _params.from; + address to_ = _params.to; + if (from_ == address(0) || to_ == address(0)) revert InvalidZeroAddress(); + + uint256 length_ = _params.delegations.length; + if (length_ < 2) revert InvalidDelegationsLength(); + if (_params.delegations[length_ - 1].delegator != from_) revert DelegatorMismatch(); + + IERC20 vault_ = IERC20(premiumAdapter.boringVault()); + uint256 amount_ = vault_.balanceOf(from_); + + // DelegationManager reverts unless this authority equals the root delegation hash. + _verifyUserTransferSignature(from_, to_, _params.delegations[length_ - 2].authority, _params.userSignature); + _verifyTransferCompliance(from_, to_, address(vault_), amount_, _params.compliance); + + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(_params.delegations); + + ModeCode[] memory encodedModes_ = new ModeCode[](1); + encodedModes_[0] = ModeLib.encodeSimpleSingle(); + + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = + ExecutionLib.encodeSingle(address(vault_), 0, abi.encodeCall(IERC20.transfer, (address(this), amount_))); + + uint256 helperBefore_ = vault_.balanceOf(address(this)); + premiumAdapter.delegationManager().redeemDelegations(permissionContexts_, encodedModes_, executionCallDatas_); + if (vault_.balanceOf(address(this)) < helperBefore_ + amount_) revert PremiumTransferNotDelivered(); + vault_.safeTransfer(to_, amount_); + + emit PremiumTransferExecuted(from_, to_, amount_); + } + + /** + * @notice Executes one base-to-premium migration. + * @param _params Migration parameters forwarded to the base and premium adapters. + */ + function _migrateToPremiumByDelegation(ToPremiumParams calldata _params) internal { + address delegator_ = _validateDelegators(_params.withdrawalDelegations, _params.depositDelegations); + IERC20 sourceVault_ = IERC20(baseAdapter.boringVault()); + IERC20 destVault_ = IERC20(premiumAdapter.boringVault()); + uint256 sourceBefore_ = sourceVault_.balanceOf(delegator_); + uint256 destBefore_ = destVault_.balanceOf(delegator_); + + baseAdapter.withdrawByDelegation(_params.withdrawalDelegations, _params.minimumAssets); + premiumAdapter.depositByDelegation(_params.depositDelegations, _params.minimumMint, _params.compliance); + + emit MigrationToPremiumExecuted( + delegator_, sourceBefore_ - sourceVault_.balanceOf(delegator_), destVault_.balanceOf(delegator_) - destBefore_ + ); + } + + /** + * @notice Executes one premium-to-base migration. + * @param _params Migration parameters forwarded to the premium and base adapters. + */ + function _migrateToBaseByDelegation(ToBaseParams calldata _params) internal { + address delegator_ = _validateDelegators(_params.withdrawalDelegations, _params.depositDelegations); + IERC20 sourceVault_ = IERC20(premiumAdapter.boringVault()); + IERC20 destVault_ = IERC20(baseAdapter.boringVault()); + uint256 sourceBefore_ = sourceVault_.balanceOf(delegator_); + uint256 destBefore_ = destVault_.balanceOf(delegator_); + + premiumAdapter.withdrawByDelegation(_params.withdrawalDelegations, _params.minimumAssets); + baseAdapter.depositByDelegation(_params.depositDelegations, _params.minimumMint); + + emit MigrationToBaseExecuted( + delegator_, sourceBefore_ - sourceVault_.balanceOf(delegator_), destVault_.balanceOf(delegator_) - destBefore_ + ); + } + + /** + * @notice Validates both delegation chains and returns their shared root delegator. + * @param _withdrawalDelegations Source-vault withdrawal chain sorted from leaf to root. + * @param _depositDelegations Destination-vault deposit chain sorted from leaf to root. + * @return delegator_ Shared root delegator that owns both sides of the migration. + */ + function _validateDelegators( + Delegation[] calldata _withdrawalDelegations, + Delegation[] calldata _depositDelegations + ) + private + pure + returns (address delegator_) + { + uint256 withdrawalLength_ = _withdrawalDelegations.length; + uint256 depositLength_ = _depositDelegations.length; + if (withdrawalLength_ < 2 || depositLength_ < 2) revert InvalidDelegationsLength(); + + delegator_ = _withdrawalDelegations[withdrawalLength_ - 1].delegator; + if (delegator_ != _depositDelegations[depositLength_ - 1].delegator) revert DelegatorMismatch(); + } + + /** + * @notice Verifies that `from` authorized this exact destination for their root delegation. + * @dev Destination proof is EIP-191 `personal_sign`, not the EIP-712 scheme used for delegations. + * Inner: `keccak256(abi.encode(address(this), block.chainid, from, to, delegations[length - 2].authority))`. + * Signed: `toEthSignedMessageHash(inner)` == `"\x19Ethereum Signed Message:\n32" || inner`. + * Verified with ERC-1271 on `from` via SignatureChecker so a backend cannot swap `to` after + * the user signed. The chain is sorted leaf to root, so the bound value is + * `delegations[length - 2].authority`: the root delegation hash (`EncoderLib._getDelegationHash`, + * the identifier `disableDelegation` uses), not the root signature and not the redelegation at + * `delegations[0]`. The DelegationManager reverts `InvalidAuthority` unless that authority equals + * the root hash, so this ties `to` to this root without hashing the full delegation on-chain. + */ + function _verifyUserTransferSignature( + address _from, + address _to, + bytes32 _rootDelegationHash, + bytes calldata _userSignature + ) + private + view + { + bytes32 messageHash_ = keccak256(abi.encode(address(this), block.chainid, _from, _to, _rootDelegationHash)); + bytes32 ethSignedHash_ = MessageHashUtils.toEthSignedMessageHash(messageHash_); + if (!SignatureChecker.isValidSignatureNow(_from, ethSignedHash_, _userSignature)) { + revert InvalidUserSignature(); + } + } + + /** + * @notice Verifies a transfer compliance signature using the Teller's signer role and window. + * @dev Destination is not user-only: this digest still includes `_to` so the backend attests that + * the recipient is premium-enabled / KYC'd. A user-signed `to` that the backend did not approve + * cannot receive. EIP-191 `personal_sign` (same prefix as `userSignature` and Teller deposits): + * inner `keccak256(abi.encode(address(this), teller, chainId, from, to, vault, amount, deadline))`. + * Starts with this helper so it cannot equal a Teller deposit hash. Reverts if the Teller has + * disabled compliance (`complianceSignerRole == 255`). + */ + function _verifyTransferCompliance( + address _from, + address _to, + address _vault, + uint256 _amount, + IComplianceVedaTeller.ComplianceData calldata _compliance + ) + private + { + IComplianceVedaTeller teller_ = premiumAdapter.teller(); + uint8 complianceSignerRole_ = teller_.complianceSignerRole(); + if (complianceSignerRole_ == type(uint8).max) revert ComplianceDisabled(); + + bytes32 messageHash_ = keccak256( + abi.encode(address(this), address(teller_), block.chainid, _from, _to, _vault, _amount, _compliance.deadline) + ); + if (usedComplianceSignatures[messageHash_]) revert ComplianceSignatureAlreadyUsed(); + if (block.timestamp > _compliance.deadline) revert ComplianceSignatureExpired(); + + uint96 complianceWindow_ = teller_.complianceWindow(); + if (complianceWindow_ > 0 && _compliance.deadline > block.timestamp + complianceWindow_) { + revert ComplianceDeadlineExceedsWindow(); + } + + bytes32 ethSignedHash_ = MessageHashUtils.toEthSignedMessageHash(messageHash_); + address recovered_ = ECDSA.recover(ethSignedHash_, _compliance.signature); + if (!IRolesAuthority(teller_.authority()).doesUserHaveRole(recovered_, complianceSignerRole_)) { + revert InvalidComplianceSigner(); + } + + usedComplianceSignatures[messageHash_] = true; + } +} diff --git a/src/helpers/interfaces/IComplianceVedaTeller.sol b/src/helpers/interfaces/IComplianceVedaTeller.sol index b247f1e0..57df77a0 100644 --- a/src/helpers/interfaces/IComplianceVedaTeller.sol +++ b/src/helpers/interfaces/IComplianceVedaTeller.sol @@ -44,4 +44,20 @@ interface IComplianceVedaTeller { ) external returns (uint256 assetsOut); + + /** + * @notice RolesAuthority used to check compliance-signer and transfer-allowlist roles. + */ + function authority() external view returns (address); + + /** + * @notice Role ID that a recovered compliance signer must hold, or 255 to disable Teller checks. + * @dev `VaultMigrationHelper.premiumTransfer` reverts when this value is 255. + */ + function complianceSignerRole() external view returns (uint8); + + /** + * @notice Maximum seconds a compliance deadline may extend beyond `block.timestamp`, or 0 for no cap. + */ + function complianceWindow() external view returns (uint96); } diff --git a/src/helpers/interfaces/IRolesAuthority.sol b/src/helpers/interfaces/IRolesAuthority.sol new file mode 100644 index 00000000..d1511445 --- /dev/null +++ b/src/helpers/interfaces/IRolesAuthority.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 +pragma solidity 0.8.23; + +/** + * @title IRolesAuthority + * @notice Minimal interface for Solmate-style RolesAuthority role checks used by Veda tellers. + */ +interface IRolesAuthority { + function doesUserHaveRole(address user, uint8 role) external view returns (bool); +} diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol new file mode 100644 index 00000000..3f43e38a --- /dev/null +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -0,0 +1,1395 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 +pragma solidity 0.8.23; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; + +import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; +import { BasicERC20 } from "../utils/BasicERC20.t.sol"; +import { IComplianceVedaTeller } from "../../src/helpers/interfaces/IComplianceVedaTeller.sol"; +import { IVedaTeller } from "../../src/helpers/interfaces/IVedaTeller.sol"; +import { VedaAdapter } from "../../src/helpers/VedaAdapter.sol"; +import { ComplianceVedaAdapter } from "../../src/helpers/ComplianceVedaAdapter.sol"; +import { VaultMigrationHelper } from "../../src/helpers/VaultMigrationHelper.sol"; +import { ERC20TransferAmountEnforcer } from "../../src/enforcers/ERC20TransferAmountEnforcer.sol"; +import { RedeemerEnforcer } from "../../src/enforcers/RedeemerEnforcer.sol"; +import { AllowedCalldataEnforcer } from "../../src/enforcers/AllowedCalldataEnforcer.sol"; +import { AllowedTargetsEnforcer } from "../../src/enforcers/AllowedTargetsEnforcer.sol"; +import { Delegation, Caveat, ModeCode } from "../../src/utils/Types.sol"; +import { BaseTest } from "../utils/BaseTest.t.sol"; +import { Implementation, SignatureType, TestUser } from "../utils/Types.t.sol"; + +interface IRolesAuthority { + function owner() external view returns (address); + function setUserRole(address user, uint8 role, bool enabled) external; + function setRoleCapability(uint8 role, address target, bytes4 functionSig, bool enabled) external; + function doesUserHaveRole(address user, uint8 role) external view returns (bool); +} + +interface ITellerConfiguration { + function owner() external view returns (address); + function setTransferRestrictions(uint8 transferAllowedRole, uint8 allowlistedRouterRole) external; +} + +interface IVedaAccountant { + function decimals() external view returns (uint8); + function getRateInQuoteSafe(address quote) external view returns (uint256); +} + +/// @dev Accepts any ERC-1271 signature and returns from `executeFromExecutor` without moving shares. +contract NoOpAccount { + function isValidSignature(bytes32, bytes calldata) external pure returns (bytes4) { + return 0x1626ba7e; + } + + function executeFromExecutor(ModeCode, bytes calldata) external payable returns (bytes[] memory) { + return new bytes[](0); + } +} + +/// @dev Accepts an empty signature for every hash it has approved, and does not move shares. +contract ApprovedHashAccount { + mapping(bytes32 hash => bool approved) public approvedHashes; + + function approveHash(bytes32 _hash) external { + approvedHashes[_hash] = true; + } + + function isValidSignature(bytes32 _hash, bytes calldata _signature) external view returns (bytes4) { + if (_signature.length == 0 && approvedHashes[_hash]) return 0x1626ba7e; + return 0xffffffff; + } + + function executeFromExecutor(ModeCode, bytes calldata) external payable returns (bytes[] memory) { + return new bytes[](0); + } +} + +/// forge-config: default.evm_version = "cancun" +contract VaultMigrationHelperTest is BaseTest { + IERC20 internal constant MUSD = IERC20(0xacA92E438df0B2401fF60dA7E4337B687a2435DA); + IERC20 internal constant BASE_VAULT = IERC20(0xb4563bcD3B7764CCBf497f515585f70B6C3EA5Ae); + IVedaTeller internal constant BASE_TELLER = IVedaTeller(0x2D49EA58A4C70b62c8B56DE971310d9e999c8117); + IVedaAccountant internal constant BASE_ACCOUNTANT = IVedaAccountant(0x7382c5b8B51B8C4f127B3123C1039581BAA5A06B); + address internal constant BASE_LENS = 0xA816ECd922de94c6879AD23B9A884dB257F20947; + address internal constant BUFFER_LENS = 0xf484d54AF87199F421967E2231Dd6dC348C7Ee6A; + address internal constant DEPLOYED_BASE_ADAPTER = 0xaD4c09d065fDb6320FA8ADf69460CDd9d472C25A; + + IComplianceVedaTeller internal constant PREMIUM_TELLER = IComplianceVedaTeller(0xB0025a2eBc0474d4F28E975F0D3E70471246ebae); + IERC20 internal constant PREMIUM_VAULT = IERC20(0xBFeC8c2b1ccea3931a1363E4CaC27352c1C908B7); + IVedaAccountant internal constant PREMIUM_ACCOUNTANT = IVedaAccountant(0xb4c9e25D3D6a4E42A7d52C21822D6016d29Ee0A2); + IRolesAuthority internal constant PREMIUM_ROLES_AUTHORITY = IRolesAuthority(0x00f0CF4f540D1d47470f565Ecb11a75E073c2dd0); + IRolesAuthority internal constant BASE_ROLES_AUTHORITY = IRolesAuthority(0x1eC540C9a4656a50F2D6DaaBd647753F97f082D1); + + address internal constant MUSD_WHALE = 0x6cb5094cfe45Ee97938702637478fe7146A8FA0f; + uint8 internal constant COMPLIANCE_SIGNER_ROLE = 36; + uint8 internal constant TRANSFER_ALLOWED_ROLE = 37; + uint8 internal constant TEST_ROUTER_ROLE = 254; + uint8 internal constant BASE_ADAPTER_ROLE = 90; + + uint256 internal constant INITIAL_MUSD_BALANCE = 100_000e6; + uint256 internal constant DEPOSIT_AMOUNT = 1_000e6; + uint256 internal constant SHARE_LOCK_SECONDS = 61; + uint256 internal constant COMPLIANCE_SIGNER_KEY = 0xC011A11CE; + + ERC20TransferAmountEnforcer internal erc20TransferAmountEnforcer; + RedeemerEnforcer internal redeemerEnforcer; + AllowedCalldataEnforcer internal allowedCalldataEnforcer; + AllowedTargetsEnforcer internal allowedTargetsEnforcer; + VedaAdapter internal baseAdapter; + ComplianceVedaAdapter internal premiumAdapter; + VaultMigrationHelper internal migrationHelper; + address internal adapterOwner; + address internal complianceSigner; + + event MigrationToPremiumExecuted(address indexed delegator, uint256 sourceShares, uint256 destShares); + event MigrationToBaseExecuted(address indexed delegator, uint256 sourceShares, uint256 destShares); + event BatchMigrationToPremiumExecuted(address indexed caller, uint256 count); + event BatchMigrationToBaseExecuted(address indexed caller, uint256 count); + event PremiumTransferExecuted(address indexed from, address indexed to, uint256 amount); + event BatchPremiumTransferExecuted(address indexed caller, uint256 count); + event StuckTokensWithdrawn(IERC20 indexed token, address indexed recipient, uint256 amount); + + function setUp() public override { + vm.createSelectFork(vm.envString("MONAD_RPC_URL")); + + IMPLEMENTATION = Implementation.Hybrid; + SIGNATURE_TYPE = SignatureType.RawP256; + super.setUp(); + + adapterOwner = makeAddr("VaultMigrationHelper Owner"); + complianceSigner = vm.addr(COMPLIANCE_SIGNER_KEY); + erc20TransferAmountEnforcer = new ERC20TransferAmountEnforcer(); + redeemerEnforcer = new RedeemerEnforcer(); + allowedCalldataEnforcer = new AllowedCalldataEnforcer(); + allowedTargetsEnforcer = new AllowedTargetsEnforcer(); + + baseAdapter = + new VedaAdapter(adapterOwner, address(delegationManager), address(BASE_VAULT), address(BASE_TELLER), address(MUSD)); + premiumAdapter = new ComplianceVedaAdapter( + adapterOwner, address(delegationManager), address(PREMIUM_VAULT), address(PREMIUM_TELLER), address(MUSD) + ); + migrationHelper = new VaultMigrationHelper(adapterOwner, address(baseAdapter), address(premiumAdapter)); + + _configureBaseAdapterRole(); + _configurePremiumForkRoles(); + _fundUser(users.alice); + _fundUser(users.carol); + + vm.label(address(MUSD), "mUSD"); + vm.label(address(BASE_VAULT), "Base mUSD Vault"); + vm.label(address(PREMIUM_VAULT), "Premium mUSD Vault"); + vm.label(address(BASE_TELLER), "Base Veda Teller"); + vm.label(address(PREMIUM_TELLER), "Premium Veda Teller"); + vm.label(address(BASE_ACCOUNTANT), "Base Accountant"); + vm.label(address(PREMIUM_ACCOUNTANT), "Premium Accountant"); + vm.label(BASE_LENS, "Base Lens"); + vm.label(BUFFER_LENS, "Buffer Lens"); + vm.label(DEPLOYED_BASE_ADAPTER, "Deployed Base Adapter"); + vm.label(address(baseAdapter), "VedaAdapter"); + vm.label(address(premiumAdapter), "ComplianceVedaAdapter"); + vm.label(address(migrationHelper), "VaultMigrationHelper"); + vm.label(complianceSigner, "Compliance Signer"); + } + + function test_constructor_wiresAdaptersAndRevertsOnZeroAddresses() public { + assertEq(address(migrationHelper.baseAdapter()), address(baseAdapter)); + assertEq(address(migrationHelper.premiumAdapter()), address(premiumAdapter)); + assertEq(migrationHelper.owner(), adapterOwner); + + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, address(0))); + new VaultMigrationHelper(address(0), address(baseAdapter), address(premiumAdapter)); + + vm.expectRevert(VaultMigrationHelper.InvalidZeroAddress.selector); + new VaultMigrationHelper(adapterOwner, address(0), address(premiumAdapter)); + + vm.expectRevert(VaultMigrationHelper.InvalidZeroAddress.selector); + new VaultMigrationHelper(adapterOwner, address(baseAdapter), address(0)); + } + + function test_lifecycle_depositAndWithdrawBaseThenPremium() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 0); + assertGt(baseShares_, 0); + assertEq(MUSD.balanceOf(address(users.alice.deleGator)), INITIAL_MUSD_BALANCE - DEPOSIT_AMOUNT); + _assertNoAdapterDust(); + + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 assetsOut_ = _withdrawFromBase(users.alice, baseShares_, 1); + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertApproxEqAbs(assetsOut_, DEPOSIT_AMOUNT, DEPOSIT_AMOUNT / 100); + _assertNoAdapterDust(); + + uint256 premiumShares_ = _depositToPremium(users.alice, assetsOut_, 2, block.timestamp + 30 minutes); + assertGt(premiumShares_, 0); + assertEq(MUSD.balanceOf(address(users.alice.deleGator)), INITIAL_MUSD_BALANCE - DEPOSIT_AMOUNT); + _assertNoAdapterDust(); + + uint256 musdBeforePremiumWithdraw_ = MUSD.balanceOf(address(users.alice.deleGator)); + _withdrawFromPremium(users.alice, premiumShares_, 3); + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertGt(MUSD.balanceOf(address(users.alice.deleGator)), musdBeforePremiumWithdraw_); + _assertNoAdapterDust(); + } + + function test_migrateToPremium_movesSharesAndLeavesNoDust() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 10); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + uint256 musdBefore_ = MUSD.balanceOf(address(users.alice.deleGator)); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 11, quotedAssets_, 12, block.timestamp + 30 minutes); + + vm.expectEmit(true, false, false, false, address(migrationHelper)); + emit MigrationToPremiumExecuted(address(users.alice.deleGator), 0, 0); + vm.prank(address(users.bob.deleGator)); + migrationHelper.migrateToPremiumByDelegation(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertGt(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(MUSD.balanceOf(address(users.alice.deleGator)), musdBefore_); + _assertNoAdapterDust(); + } + + function test_migrateToBase_movesSharesAndLeavesNoDust() public { + uint256 premiumShares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 20, block.timestamp + 30 minutes); + uint256 quotedAssets_ = _quotePremiumAssets(premiumShares_); + uint256 musdBefore_ = MUSD.balanceOf(address(users.alice.deleGator)); + VaultMigrationHelper.ToBaseParams memory params_ = _toBaseParams(users.alice, premiumShares_, 21, quotedAssets_, 22); + + vm.expectEmit(true, false, false, false, address(migrationHelper)); + emit MigrationToBaseExecuted(address(users.alice.deleGator), 0, 0); + vm.prank(address(users.bob.deleGator)); + migrationHelper.migrateToBaseByDelegation(params_); + + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertGt(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(MUSD.balanceOf(address(users.alice.deleGator)), musdBefore_); + _assertNoAdapterDust(); + } + + function test_migrateToPremium_isPermissionless() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 30); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + + vm.prank(address(users.carol.deleGator)); + migrationHelper.migrateToPremiumByDelegation( + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 31, quotedAssets_, 32, block.timestamp + 30 minutes) + ); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertGt(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(PREMIUM_VAULT.balanceOf(address(users.carol.deleGator)), 0); + _assertNoAdapterDust(); + } + + function test_migrateToPremium_revertsBothLegsWhenDepositFails() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 40); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + uint256 musdBefore_ = MUSD.balanceOf(address(users.alice.deleGator)); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 41, quotedAssets_, 42, block.timestamp + 30 minutes); + params_.minimumMint = type(uint256).max; + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegation(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), baseShares_); + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(MUSD.balanceOf(address(users.alice.deleGator)), musdBefore_); + _assertNoAdapterDust(); + } + + function test_migrateToBase_revertsOnDelegatorMismatch() public { + uint256 aliceShares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 50, block.timestamp + 30 minutes); + uint256 quotedAssets_ = _quotePremiumAssets(aliceShares_); + + VaultMigrationHelper.ToBaseParams memory params_ = VaultMigrationHelper.ToBaseParams({ + withdrawalDelegations: _createDelegationChain( + users.alice, address(premiumAdapter), address(PREMIUM_VAULT), aliceShares_, 51 + ), + minimumAssets: 0, + depositDelegations: _createDelegationChain(users.carol, address(baseAdapter), address(MUSD), quotedAssets_, 52), + minimumMint: 0 + }); + + vm.expectRevert(VaultMigrationHelper.DelegatorMismatch.selector); + migrationHelper.migrateToBaseByDelegation(params_); + + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), aliceShares_); + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(BASE_VAULT.balanceOf(address(users.carol.deleGator)), 0); + } + + function test_migrateToPremium_revertsOnShortDelegationChain() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 60); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 61, quotedAssets_, 62, block.timestamp + 30 minutes); + params_.withdrawalDelegations = new Delegation[](1); + + vm.expectRevert(VaultMigrationHelper.InvalidDelegationsLength.selector); + migrationHelper.migrateToPremiumByDelegation(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), baseShares_); + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + } + + function test_migrateToPremiumBatch_movesExactAmountsForEachDelegator() public { + uint256 aliceAmount_ = 300e6; + uint256 carolAmount_ = 400e6; + uint256 aliceShares_ = _depositToBase(users.alice, aliceAmount_, 70); + uint256 carolShares_ = _depositToBase(users.carol, carolAmount_, 71); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + + uint256 aliceAssets_ = _quoteBaseAssets(aliceShares_); + uint256 carolAssets_ = _quoteBaseAssets(carolShares_); + VaultMigrationHelper.ToPremiumParams[] memory params_ = new VaultMigrationHelper.ToPremiumParams[](2); + params_[0] = _toPremiumParams(users.alice, aliceShares_, aliceAssets_, 72, aliceAssets_, 73, block.timestamp + 20 minutes); + params_[1] = _toPremiumParams(users.carol, carolShares_, carolAssets_, 74, carolAssets_, 75, block.timestamp + 21 minutes); + + vm.expectEmit(true, false, false, true, address(migrationHelper)); + emit BatchMigrationToPremiumExecuted(address(users.bob.deleGator), 2); + vm.prank(address(users.bob.deleGator)); + migrationHelper.migrateToPremiumByDelegationBatch(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(BASE_VAULT.balanceOf(address(users.carol.deleGator)), 0); + assertGt(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertGt(PREMIUM_VAULT.balanceOf(address(users.carol.deleGator)), 0); + _assertNoAdapterDust(); + } + + function test_migrateToBaseBatch_movesExactAmountsForEachDelegator() public { + uint256 aliceAmount_ = 250e6; + uint256 carolAmount_ = 350e6; + uint256 aliceShares_ = _depositToPremium(users.alice, aliceAmount_, 80, block.timestamp + 20 minutes); + uint256 carolShares_ = _depositToPremium(users.carol, carolAmount_, 81, block.timestamp + 21 minutes); + + uint256 aliceAssets_ = _quotePremiumAssets(aliceShares_); + uint256 carolAssets_ = _quotePremiumAssets(carolShares_); + VaultMigrationHelper.ToBaseParams[] memory params_ = new VaultMigrationHelper.ToBaseParams[](2); + params_[0] = _toBaseParams(users.alice, aliceShares_, 82, aliceAssets_, 83); + params_[1] = _toBaseParams(users.carol, carolShares_, 84, carolAssets_, 85); + + vm.expectEmit(true, false, false, true, address(migrationHelper)); + emit BatchMigrationToBaseExecuted(address(users.bob.deleGator), 2); + vm.prank(address(users.bob.deleGator)); + migrationHelper.migrateToBaseByDelegationBatch(params_); + + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(PREMIUM_VAULT.balanceOf(address(users.carol.deleGator)), 0); + assertGt(BASE_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertGt(BASE_VAULT.balanceOf(address(users.carol.deleGator)), 0); + _assertNoAdapterDust(); + } + + function test_migrateToPremiumBatch_revertsAllWhenLaterStreamFails() public { + uint256 aliceShares_ = _depositToBase(users.alice, 300e6, 90); + uint256 carolShares_ = _depositToBase(users.carol, 400e6, 91); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + + uint256 aliceAssets_ = _quoteBaseAssets(aliceShares_); + uint256 carolAssets_ = _quoteBaseAssets(carolShares_); + VaultMigrationHelper.ToPremiumParams[] memory params_ = new VaultMigrationHelper.ToPremiumParams[](2); + params_[0] = _toPremiumParams(users.alice, aliceShares_, aliceAssets_, 92, aliceAssets_, 93, block.timestamp + 20 minutes); + params_[1] = _toPremiumParams(users.carol, carolShares_, carolAssets_, 94, carolAssets_, 95, block.timestamp + 21 minutes); + params_[1].minimumMint = type(uint256).max; + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegationBatch(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), aliceShares_); + assertEq(BASE_VAULT.balanceOf(address(users.carol.deleGator)), carolShares_); + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + assertEq(PREMIUM_VAULT.balanceOf(address(users.carol.deleGator)), 0); + } + + function test_emptyBatchesRevert() public { + VaultMigrationHelper.ToPremiumParams[] memory premiumParams_ = new VaultMigrationHelper.ToPremiumParams[](0); + VaultMigrationHelper.ToBaseParams[] memory baseParams_ = new VaultMigrationHelper.ToBaseParams[](0); + + vm.expectRevert(VaultMigrationHelper.InvalidBatchLength.selector); + migrationHelper.migrateToPremiumByDelegationBatch(premiumParams_); + + vm.expectRevert(VaultMigrationHelper.InvalidBatchLength.selector); + migrationHelper.migrateToBaseByDelegationBatch(baseParams_); + + VaultMigrationHelper.PremiumTransferParams[] memory transferParams_ = new VaultMigrationHelper.PremiumTransferParams[](0); + vm.expectRevert(VaultMigrationHelper.InvalidBatchLength.selector); + migrationHelper.premiumTransferBatch(transferParams_); + } + + function test_migrateToPremium_revertsOnExpiredCompliance() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 100); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 101, quotedAssets_, 102, block.timestamp - 1); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegation(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), baseShares_); + assertEq(PREMIUM_VAULT.balanceOf(address(users.alice.deleGator)), 0); + } + + function test_migrateToPremium_revertsOnWrongComplianceSigner() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 110); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 111, quotedAssets_, 112, block.timestamp + 30 minutes); + params_.compliance = IComplianceVedaTeller.ComplianceData({ + deadline: block.timestamp + 30 minutes, + signature: _signCompliance(users.alice, quotedAssets_, block.timestamp + 30 minutes, 0xBAD) + }); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegation(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), baseShares_); + } + + function test_migrateToPremium_revertsOnReplayedCompliance() public { + uint256 firstShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 120); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 firstAssets_ = _quoteBaseAssets(firstShares_); + uint256 deadline_ = block.timestamp + 30 minutes; + VaultMigrationHelper.ToPremiumParams memory firstParams_ = + _toPremiumParams(users.alice, firstShares_, firstAssets_, 121, firstAssets_, 122, deadline_); + + vm.prank(address(users.bob.deleGator)); + migrationHelper.migrateToPremiumByDelegation(firstParams_); + + uint256 secondShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 123); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 secondAssets_ = _quoteBaseAssets(secondShares_); + VaultMigrationHelper.ToPremiumParams memory replayParams_ = + _toPremiumParams(users.alice, secondShares_, secondAssets_, 124, secondAssets_, 125, deadline_ + 1 hours); + replayParams_.compliance = firstParams_.compliance; + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegation(replayParams_); + } + + function test_migrateToPremium_revertsOnWithdrawSlippage() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 130); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 131, quotedAssets_, 132, block.timestamp + 30 minutes); + params_.minimumAssets = type(uint256).max; + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegation(params_); + + assertEq(BASE_VAULT.balanceOf(address(users.alice.deleGator)), baseShares_); + } + + function test_migrateToPremium_revertsOnDelegationReplay() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 140); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); + VaultMigrationHelper.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, quotedAssets_, 141, quotedAssets_, 142, block.timestamp + 30 minutes); + + vm.prank(address(users.bob.deleGator)); + migrationHelper.migrateToPremiumByDelegation(params_); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.migrateToPremiumByDelegation(params_); + } + + function test_premiumTransfer_movesFullBalanceToRecipient() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 200, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 201, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.expectEmit(true, true, false, true, address(migrationHelper)); + emit PremiumTransferExecuted(from_, to_, shares_); + vm.prank(address(users.bob.deleGator)); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), 0); + assertEq(PREMIUM_VAULT.balanceOf(to_), shares_); + _assertNoAdapterDust(); + } + + function test_premiumTransfer_isPermissionless() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 210, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 211, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.prank(address(users.carol.deleGator)); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), 0); + assertEq(PREMIUM_VAULT.balanceOf(to_), shares_); + _assertNoAdapterDust(); + } + + function test_premiumTransfer_revertsOnExpiredCompliance() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 220, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 221, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp - 1, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceSignatureExpired.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsWhenComplianceDeadlineExceedsWindow() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 225, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 226, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.mockCall( + address(PREMIUM_TELLER), + abi.encodeWithSelector(IComplianceVedaTeller.complianceWindow.selector), + abi.encode(uint96(10 minutes)) + ); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceDeadlineExceedsWindow.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + vm.clearMockedCalls(); + } + + function test_premiumTransfer_revertsOnWrongComplianceSigner() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 230, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 231, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, 0xBAD); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + } + + function test_premiumTransfer_revertsOnReplayedCompliance() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 240, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + uint256 deadline_ = block.timestamp + 30 minutes; + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, deadline_, COMPLIANCE_SIGNER_KEY); + Delegation[] memory firstDelegations_ = _createPremiumTransferChain(users.alice, 241, address(migrationHelper)); + bytes memory firstUserSignature_ = _userTransferSignature(users.alice, to_, firstDelegations_); + + vm.prank(address(users.bob.deleGator)); + _callPremiumTransfer(from_, to_, firstDelegations_, compliance_, firstUserSignature_); + + vm.startPrank(PREMIUM_ROLES_AUTHORITY.owner()); + PREMIUM_ROLES_AUTHORITY.setUserRole(to_, TRANSFER_ALLOWED_ROLE, true); + vm.stopPrank(); + vm.prank(to_); + PREMIUM_VAULT.transfer(from_, shares_); + Delegation[] memory secondDelegations_ = _createPremiumTransferChain(users.alice, 243, address(migrationHelper)); + bytes memory secondUserSignature_ = _userTransferSignature(users.alice, to_, secondDelegations_); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceSignatureAlreadyUsed.selector); + _callPremiumTransfer(from_, to_, secondDelegations_, compliance_, secondUserSignature_); + } + + function test_premiumTransfer_revertsOnFromMismatch() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 250, block.timestamp + 30 minutes); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 251, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = _transferComplianceData( + address(users.carol.deleGator), to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY + ); + + vm.expectRevert(VaultMigrationHelper.DelegatorMismatch.selector); + _callPremiumTransfer(address(users.carol.deleGator), to_, delegations_, compliance_, hex""); + } + + function test_premiumTransfer_revertsOnComplianceAmountMismatch() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 260, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 261, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_ - 1, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsOnShortDelegationChain() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 270, block.timestamp + 30 minutes); + Delegation[] memory delegations_ = new Delegation[](1); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.expectRevert(VaultMigrationHelper.InvalidDelegationsLength.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, hex""); + } + + function test_premiumTransfer_revertsOnZeroTo() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 280, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 281, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, address(0), shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.expectRevert(VaultMigrationHelper.InvalidZeroAddress.selector); + _callPremiumTransfer(from_, address(0), delegations_, compliance_, hex""); + } + + function test_premiumTransfer_revertsWhenHelperNotAllowlisted() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 290, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 291, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.prank(PREMIUM_ROLES_AUTHORITY.owner()); + PREMIUM_ROLES_AUTHORITY.setUserRole(address(migrationHelper), TRANSFER_ALLOWED_ROLE, false); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsWhenCalldataToIsNotHelper() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 300, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 301, to_); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert("AllowedCalldataEnforcer:invalid-calldata"); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsOnDepositComplianceSignature() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 310, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + uint256 deadline_ = block.timestamp + 30 minutes; + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 311, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory depositCompliance_ = IComplianceVedaTeller.ComplianceData({ + deadline: deadline_, signature: _signCompliance(users.alice, shares_, deadline_, COMPLIANCE_SIGNER_KEY) + }); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); + _callPremiumTransfer(from_, to_, delegations_, depositCompliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsWhenComplianceDisabled() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 320, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 321, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.mockCall( + address(PREMIUM_TELLER), + abi.encodeWithSelector(IComplianceVedaTeller.complianceSignerRole.selector), + abi.encode(type(uint8).max) + ); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceDisabled.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + vm.clearMockedCalls(); + } + + function test_premiumTransfer_revertsOnUserSignatureForDifferentTo() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 350, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + address otherTo_ = makeAddr("otherPremiumRecipient"); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 351, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, otherTo_, delegations_); + + vm.expectRevert(VaultMigrationHelper.InvalidUserSignature.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsOnUserSignatureForDifferentRoot() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 360, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + Delegation[] memory signedDelegations_ = _createPremiumTransferChain(users.alice, 361, address(migrationHelper)); + Delegation[] memory usedDelegations_ = _createPremiumTransferChain(users.alice, 362, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, signedDelegations_); + + vm.expectRevert(VaultMigrationHelper.InvalidUserSignature.selector); + _callPremiumTransfer(from_, to_, usedDelegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsWhenEmptySignatureIsReusedOnAnotherRoot() public { + ApprovedHashAccount account_ = new ApprovedHashAccount(); + uint256 shares_ = _depositToPremium(users.carol, 100e6, 900, block.timestamp + 30 minutes); + address payee_ = makeAddr("approvedHashPayee"); + + vm.prank(address(users.bob.deleGator)); + migrationHelper.premiumTransfer( + _premiumTransferParams(users.carol, shares_, 901, address(account_), block.timestamp + 30 minutes) + ); + assertEq(PREMIUM_VAULT.balanceOf(address(account_)), shares_); + + Delegation[] memory rootA_ = _createEmptySignatureTransferChain(address(account_), 902); + Delegation[] memory rootB_ = _createEmptySignatureTransferChain(address(account_), 903); + bytes32 domainHash_ = delegationManager.getDomainHash(); + account_.approveHash(MessageHashUtils.toTypedDataHash(domainHash_, EncoderLib._getDelegationHash(rootA_[1]))); + account_.approveHash(MessageHashUtils.toTypedDataHash(domainHash_, EncoderLib._getDelegationHash(rootB_[1]))); + account_.approveHash(_destinationEthSignedHash(address(account_), payee_, rootA_[0].authority)); + + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(address(account_), payee_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.expectRevert(VaultMigrationHelper.InvalidUserSignature.selector); + _callPremiumTransfer(address(account_), payee_, rootB_, compliance_, hex""); + + vm.expectRevert(VaultMigrationHelper.PremiumTransferNotDelivered.selector); + _callPremiumTransfer(address(account_), payee_, rootA_, compliance_, hex""); + + assertEq(PREMIUM_VAULT.balanceOf(address(account_)), shares_); + assertEq(PREMIUM_VAULT.balanceOf(payee_), 0); + } + + function test_premiumTransfer_revertsOnComplianceSignatureForDifferentTo() public { + uint256 shares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 370, block.timestamp + 30 minutes); + address from_ = address(users.alice.deleGator); + address to_ = address(users.carol.deleGator); + address otherTo_ = makeAddr("unapprovedPremiumRecipient"); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 371, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, otherTo_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); + + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + function test_premiumTransfer_revertsWhenRedemptionDoesNotDeliverShares() public { + uint256 strayShares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 800, block.timestamp + 30 minutes); + address victim_ = makeAddr("victimEoa"); + vm.prank(address(users.bob.deleGator)); + migrationHelper.premiumTransfer( + _premiumTransferParams(users.alice, strayShares_, 801, victim_, block.timestamp + 30 minutes) + ); + + vm.prank(victim_); + require(PREMIUM_VAULT.transfer(address(migrationHelper), strayShares_), "stray transfer failed"); + + NoOpAccount attacker_ = new NoOpAccount(); + uint256 attackerShares_ = _depositToPremium(users.carol, 100e6, 810, block.timestamp + 30 minutes); + address payee_ = makeAddr("attackerPayee"); + vm.prank(address(users.bob.deleGator)); + migrationHelper.premiumTransfer( + _premiumTransferParams(users.carol, attackerShares_, 811, address(attacker_), block.timestamp + 30 minutes) + ); + + VaultMigrationHelper.PremiumTransferParams memory params_ = + _customRootTransferParams(address(attacker_), payee_, attackerShares_, 820); + + vm.expectRevert(VaultMigrationHelper.PremiumTransferNotDelivered.selector); + vm.prank(makeAddr("anyone")); + migrationHelper.premiumTransfer(params_); + + assertEq(PREMIUM_VAULT.balanceOf(address(attacker_)), attackerShares_); + assertEq(PREMIUM_VAULT.balanceOf(payee_), 0); + assertEq(PREMIUM_VAULT.balanceOf(address(migrationHelper)), strayShares_); + } + + function test_premiumTransferBatch_movesEachDelegatorToRecipient() public { + uint256 aliceShares_ = _depositToPremium(users.alice, 300e6, 330, block.timestamp + 20 minutes); + uint256 carolShares_ = _depositToPremium(users.carol, 400e6, 331, block.timestamp + 21 minutes); + address recipient_ = makeAddr("premiumTransferRecipient"); + address alice_ = address(users.alice.deleGator); + address carol_ = address(users.carol.deleGator); + + VaultMigrationHelper.PremiumTransferParams[] memory params_ = new VaultMigrationHelper.PremiumTransferParams[](2); + params_[0] = _premiumTransferParams(users.alice, aliceShares_, 332, recipient_, block.timestamp + 20 minutes); + params_[1] = _premiumTransferParams(users.carol, carolShares_, 333, recipient_, block.timestamp + 21 minutes); + + vm.expectEmit(true, false, false, true, address(migrationHelper)); + emit BatchPremiumTransferExecuted(address(users.bob.deleGator), 2); + vm.prank(address(users.bob.deleGator)); + migrationHelper.premiumTransferBatch(params_); + + assertEq(PREMIUM_VAULT.balanceOf(alice_), 0); + assertEq(PREMIUM_VAULT.balanceOf(carol_), 0); + assertEq(PREMIUM_VAULT.balanceOf(recipient_), aliceShares_ + carolShares_); + _assertNoAdapterDust(); + } + + function test_premiumTransferBatch_revertsAllWhenLaterStreamFails() public { + uint256 aliceShares_ = _depositToPremium(users.alice, 300e6, 340, block.timestamp + 20 minutes); + uint256 carolShares_ = _depositToPremium(users.carol, 400e6, 341, block.timestamp + 21 minutes); + address recipient_ = makeAddr("premiumTransferRecipient"); + address alice_ = address(users.alice.deleGator); + address carol_ = address(users.carol.deleGator); + + VaultMigrationHelper.PremiumTransferParams[] memory params_ = new VaultMigrationHelper.PremiumTransferParams[](2); + params_[0] = _premiumTransferParams(users.alice, aliceShares_, 342, recipient_, block.timestamp + 20 minutes); + params_[1] = _premiumTransferParams(users.carol, carolShares_, 343, recipient_, block.timestamp - 1); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceSignatureExpired.selector); + migrationHelper.premiumTransferBatch(params_); + + assertEq(PREMIUM_VAULT.balanceOf(alice_), aliceShares_); + assertEq(PREMIUM_VAULT.balanceOf(carol_), carolShares_); + assertEq(PREMIUM_VAULT.balanceOf(recipient_), 0); + _assertNoAdapterDust(); + } + + function test_withdrawEmergency_revertsOnNonOwner() public { + BasicERC20 testToken_ = new BasicERC20(adapterOwner, "TestToken", "TST", 0); + vm.prank(adapterOwner); + testToken_.mint(address(migrationHelper), 100 ether); + + vm.prank(address(users.alice.deleGator)); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, address(users.alice.deleGator))); + migrationHelper.withdrawEmergency(testToken_, 50 ether, address(users.alice.deleGator)); + + assertEq(testToken_.balanceOf(address(migrationHelper)), 100 ether); + } + + function test_withdrawEmergency_recoverTokens() public { + BasicERC20 testToken_ = new BasicERC20(adapterOwner, "TestToken", "TST", 0); + vm.prank(adapterOwner); + testToken_.mint(address(migrationHelper), 100 ether); + + vm.expectEmit(true, true, false, true, address(migrationHelper)); + emit StuckTokensWithdrawn(testToken_, address(users.alice.deleGator), 50 ether); + + vm.prank(adapterOwner); + migrationHelper.withdrawEmergency(testToken_, 50 ether, address(users.alice.deleGator)); + + assertEq(testToken_.balanceOf(address(migrationHelper)), 50 ether); + assertEq(testToken_.balanceOf(address(users.alice.deleGator)), 50 ether); + } + + function test_withdrawEmergency_revertsOnZeroRecipient() public { + BasicERC20 testToken_ = new BasicERC20(adapterOwner, "TestToken", "TST", 0); + vm.prank(adapterOwner); + testToken_.mint(address(migrationHelper), 100 ether); + + vm.expectRevert(VaultMigrationHelper.InvalidRecipient.selector); + vm.prank(adapterOwner); + migrationHelper.withdrawEmergency(testToken_, 50 ether, address(0)); + } + + function _configureBaseAdapterRole() internal { + vm.prank(BASE_ROLES_AUTHORITY.owner()); + BASE_ROLES_AUTHORITY.setUserRole(address(baseAdapter), BASE_ADAPTER_ROLE, true); + } + + function _configurePremiumForkRoles() internal { + address authorityOwner_ = PREMIUM_ROLES_AUTHORITY.owner(); + address tellerOwner_ = ITellerConfiguration(address(PREMIUM_TELLER)).owner(); + assertEq(authorityOwner_, tellerOwner_); + + vm.startPrank(authorityOwner_); + PREMIUM_ROLES_AUTHORITY.setUserRole(complianceSigner, COMPLIANCE_SIGNER_ROLE, true); + PREMIUM_ROLES_AUTHORITY.setUserRole(address(premiumAdapter), TRANSFER_ALLOWED_ROLE, true); + PREMIUM_ROLES_AUTHORITY.setUserRole(address(migrationHelper), TRANSFER_ALLOWED_ROLE, true); + PREMIUM_ROLES_AUTHORITY.setUserRole(address(premiumAdapter), TEST_ROUTER_ROLE, true); + PREMIUM_ROLES_AUTHORITY.setRoleCapability( + TEST_ROUTER_ROLE, address(PREMIUM_TELLER), IComplianceVedaTeller.withdraw.selector, true + ); + ITellerConfiguration(address(PREMIUM_TELLER)).setTransferRestrictions(TRANSFER_ALLOWED_ROLE, TEST_ROUTER_ROLE); + vm.stopPrank(); + } + + function _fundUser(TestUser memory _user) internal { + vm.prank(MUSD_WHALE); + require(MUSD.transfer(address(_user.deleGator), INITIAL_MUSD_BALANCE), "mUSD funding failed"); + } + + function _quoteBaseAssets(uint256 _shares) internal view returns (uint256) { + return _quoteAssets(BASE_ACCOUNTANT, _shares); + } + + function _quotePremiumAssets(uint256 _shares) internal view returns (uint256) { + return _quoteAssets(PREMIUM_ACCOUNTANT, _shares); + } + + function _quoteAssets(IVedaAccountant _accountant, uint256 _shares) internal view returns (uint256) { + return _shares * _accountant.getRateInQuoteSafe(address(MUSD)) / (10 ** _accountant.decimals()); + } + + function _depositToBase(TestUser memory _delegator, uint256 _amount, uint256 _salt) internal returns (uint256 shares_) { + uint256 sharesBefore_ = BASE_VAULT.balanceOf(address(_delegator.deleGator)); + Delegation[] memory delegations_ = _createDelegationChain(_delegator, address(baseAdapter), address(MUSD), _amount, _salt); + + vm.prank(address(users.bob.deleGator)); + baseAdapter.depositByDelegation(delegations_, 0); + shares_ = BASE_VAULT.balanceOf(address(_delegator.deleGator)) - sharesBefore_; + } + + function _withdrawFromBase( + TestUser memory _delegator, + uint256 _shareAmount, + uint256 _salt + ) + internal + returns (uint256 assetsOut_) + { + uint256 musdBefore_ = MUSD.balanceOf(address(_delegator.deleGator)); + Delegation[] memory delegations_ = + _createDelegationChain(_delegator, address(baseAdapter), address(BASE_VAULT), _shareAmount, _salt); + + vm.prank(address(users.bob.deleGator)); + baseAdapter.withdrawByDelegation(delegations_, 0); + assetsOut_ = MUSD.balanceOf(address(_delegator.deleGator)) - musdBefore_; + } + + function _depositToPremium( + TestUser memory _delegator, + uint256 _amount, + uint256 _salt, + uint256 _deadline + ) + internal + returns (uint256 shares_) + { + uint256 sharesBefore_ = PREMIUM_VAULT.balanceOf(address(_delegator.deleGator)); + Delegation[] memory delegations_ = + _createDelegationChain(_delegator, address(premiumAdapter), address(MUSD), _amount, _salt); + IComplianceVedaTeller.ComplianceData memory compliance_ = _complianceData(_delegator, _amount, _deadline); + + vm.prank(address(users.bob.deleGator)); + premiumAdapter.depositByDelegation(delegations_, 0, compliance_); + shares_ = PREMIUM_VAULT.balanceOf(address(_delegator.deleGator)) - sharesBefore_; + } + + function _withdrawFromPremium(TestUser memory _delegator, uint256 _shareAmount, uint256 _salt) internal { + Delegation[] memory delegations_ = + _createDelegationChain(_delegator, address(premiumAdapter), address(PREMIUM_VAULT), _shareAmount, _salt); + + vm.prank(address(users.bob.deleGator)); + premiumAdapter.withdrawByDelegation(delegations_, 0); + } + + function _toPremiumParams( + TestUser memory _delegator, + uint256 _shareAmount, + uint256 _assetAmount, + uint256 _withdrawalSalt, + uint256 _depositAmount, + uint256 _depositSalt, + uint256 _deadline + ) + internal + view + returns (VaultMigrationHelper.ToPremiumParams memory) + { + return VaultMigrationHelper.ToPremiumParams({ + withdrawalDelegations: _createDelegationChain( + _delegator, address(baseAdapter), address(BASE_VAULT), _shareAmount, _withdrawalSalt + ), + minimumAssets: 0, + depositDelegations: _createDelegationChain( + _delegator, address(premiumAdapter), address(MUSD), _depositAmount, _depositSalt + ), + minimumMint: 0, + compliance: _complianceData(_delegator, _assetAmount, _deadline) + }); + } + + function _toBaseParams( + TestUser memory _delegator, + uint256 _shareAmount, + uint256 _withdrawalSalt, + uint256 _depositAmount, + uint256 _depositSalt + ) + internal + view + returns (VaultMigrationHelper.ToBaseParams memory) + { + return VaultMigrationHelper.ToBaseParams({ + withdrawalDelegations: _createDelegationChain( + _delegator, address(premiumAdapter), address(PREMIUM_VAULT), _shareAmount, _withdrawalSalt + ), + minimumAssets: 0, + depositDelegations: _createDelegationChain( + _delegator, address(baseAdapter), address(MUSD), _depositAmount, _depositSalt + ), + minimumMint: 0 + }); + } + + function _callPremiumTransfer( + address _from, + address _to, + Delegation[] memory _delegations, + IComplianceVedaTeller.ComplianceData memory _compliance, + bytes memory _userSignature + ) + internal + { + migrationHelper.premiumTransfer( + VaultMigrationHelper.PremiumTransferParams({ + from: _from, to: _to, delegations: _delegations, compliance: _compliance, userSignature: _userSignature + }) + ); + } + + function _premiumTransferParams( + TestUser memory _delegator, + uint256 _amount, + uint256 _salt, + address _to, + uint256 _deadline + ) + internal + view + returns (VaultMigrationHelper.PremiumTransferParams memory) + { + address from_ = address(_delegator.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(_delegator, _salt, address(migrationHelper)); + return VaultMigrationHelper.PremiumTransferParams({ + from: from_, + to: _to, + delegations: delegations_, + compliance: _transferComplianceData(from_, _to, _amount, _deadline, COMPLIANCE_SIGNER_KEY), + userSignature: _userTransferSignature(_delegator, _to, delegations_) + }); + } + + function _customRootTransferParams( + address _from, + address _to, + uint256 _amount, + uint256 _salt + ) + internal + view + returns (VaultMigrationHelper.PremiumTransferParams memory) + { + Caveat[] memory noCaveats_ = new Caveat[](0); + Delegation memory root_ = Delegation({ + delegate: address(users.bob.deleGator), + delegator: _from, + authority: ROOT_AUTHORITY, + caveats: noCaveats_, + salt: _salt, + signature: hex"c0ffee" + }); + Delegation memory leaf_ = Delegation({ + delegate: address(migrationHelper), + delegator: address(users.bob.deleGator), + authority: EncoderLib._getDelegationHash(root_), + caveats: noCaveats_, + salt: _salt, + signature: hex"" + }); + leaf_ = signDelegation(users.bob, leaf_); + + Delegation[] memory delegations_ = new Delegation[](2); + delegations_[0] = leaf_; + delegations_[1] = root_; + + return VaultMigrationHelper.PremiumTransferParams({ + from: _from, + to: _to, + delegations: delegations_, + compliance: _transferComplianceData(_from, _to, _amount, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY), + userSignature: hex"01" + }); + } + + function _createDelegationChain( + TestUser memory _delegator, + address _adapter, + address _token, + uint256 _amount, + uint256 _salt + ) + internal + view + returns (Delegation[] memory delegations_) + { + Delegation memory root_ = _createTransferDelegation(_delegator, _adapter, _token, type(uint256).max, _salt); + bytes32 authority_ = EncoderLib._getDelegationHash(root_); + Delegation memory leaf_ = _createAdapterRedelegation(_adapter, authority_, _token, _amount, _salt); + + delegations_ = new Delegation[](2); + delegations_[0] = leaf_; + delegations_[1] = root_; + } + + function _createTransferDelegation( + TestUser memory _delegator, + address _adapter, + address _token, + uint256 _amount, + uint256 _salt + ) + internal + view + returns (Delegation memory) + { + Caveat[] memory caveats_ = new Caveat[](2); + caveats_[0] = + Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: abi.encodePacked(_token, _amount) }); + caveats_[1] = Caveat({ args: hex"", enforcer: address(redeemerEnforcer), terms: abi.encodePacked(_adapter) }); + + Delegation memory delegation_ = Delegation({ + delegate: address(users.bob.deleGator), + delegator: address(_delegator.deleGator), + authority: ROOT_AUTHORITY, + caveats: caveats_, + salt: _salt, + signature: hex"" + }); + return signDelegation(_delegator, delegation_); + } + + function _createAdapterRedelegation( + address _adapter, + bytes32 _authority, + address _token, + uint256 _amount, + uint256 _salt + ) + internal + view + returns (Delegation memory) + { + Caveat[] memory caveats_ = new Caveat[](1); + caveats_[0] = + Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: abi.encodePacked(_token, _amount) }); + + Delegation memory delegation_ = Delegation({ + delegate: _adapter, + delegator: address(users.bob.deleGator), + authority: _authority, + caveats: caveats_, + salt: _salt, + signature: hex"" + }); + return signDelegation(users.bob, delegation_); + } + + function _createPremiumTransferChain( + TestUser memory _delegator, + uint256 _salt, + address _calldataTo + ) + internal + view + returns (Delegation[] memory delegations_) + { + Caveat[] memory rootCaveats_ = new Caveat[](3); + rootCaveats_[0] = + Caveat({ args: hex"", enforcer: address(redeemerEnforcer), terms: abi.encodePacked(address(migrationHelper)) }); + rootCaveats_[1] = Caveat({ + args: hex"", + enforcer: address(allowedCalldataEnforcer), + terms: abi.encodePacked(uint256(0), IERC20.transfer.selector, abi.encode(_calldataTo)) + }); + rootCaveats_[2] = + Caveat({ args: hex"", enforcer: address(allowedTargetsEnforcer), terms: abi.encodePacked(address(PREMIUM_VAULT)) }); + + Delegation memory root_ = Delegation({ + delegate: address(users.bob.deleGator), + delegator: address(_delegator.deleGator), + authority: ROOT_AUTHORITY, + caveats: rootCaveats_, + salt: _salt, + signature: hex"" + }); + root_ = signDelegation(_delegator, root_); + + Caveat[] memory leafCaveats_ = new Caveat[](0); + Delegation memory leaf_ = Delegation({ + delegate: address(migrationHelper), + delegator: address(users.bob.deleGator), + authority: EncoderLib._getDelegationHash(root_), + caveats: leafCaveats_, + salt: _salt, + signature: hex"" + }); + leaf_ = signDelegation(users.bob, leaf_); + + delegations_ = new Delegation[](2); + delegations_[0] = leaf_; + delegations_[1] = root_; + } + + function _createEmptySignatureTransferChain( + address _delegator, + uint256 _salt + ) + internal + view + returns (Delegation[] memory delegations_) + { + Caveat[] memory rootCaveats_ = new Caveat[](3); + rootCaveats_[0] = + Caveat({ args: hex"", enforcer: address(redeemerEnforcer), terms: abi.encodePacked(address(migrationHelper)) }); + rootCaveats_[1] = Caveat({ + args: hex"", + enforcer: address(allowedCalldataEnforcer), + terms: abi.encodePacked(uint256(0), IERC20.transfer.selector, abi.encode(address(migrationHelper))) + }); + rootCaveats_[2] = + Caveat({ args: hex"", enforcer: address(allowedTargetsEnforcer), terms: abi.encodePacked(address(PREMIUM_VAULT)) }); + + Delegation memory root_ = Delegation({ + delegate: address(users.bob.deleGator), + delegator: _delegator, + authority: ROOT_AUTHORITY, + caveats: rootCaveats_, + salt: _salt, + signature: hex"" + }); + + Caveat[] memory leafCaveats_ = new Caveat[](0); + Delegation memory leaf_ = Delegation({ + delegate: address(migrationHelper), + delegator: address(users.bob.deleGator), + authority: EncoderLib._getDelegationHash(root_), + caveats: leafCaveats_, + salt: _salt, + signature: hex"" + }); + leaf_ = signDelegation(users.bob, leaf_); + + delegations_ = new Delegation[](2); + delegations_[0] = leaf_; + delegations_[1] = root_; + } + + function _destinationEthSignedHash(address _from, address _to, bytes32 _rootDelegationHash) internal view returns (bytes32) { + bytes32 messageHash_ = keccak256(abi.encode(address(migrationHelper), block.chainid, _from, _to, _rootDelegationHash)); + return MessageHashUtils.toEthSignedMessageHash(messageHash_); + } + + function _userTransferSignature( + TestUser memory _delegator, + address _to, + Delegation[] memory _delegations + ) + internal + view + returns (bytes memory) + { + bytes32 messageHash_ = keccak256( + abi.encode( + address(migrationHelper), + block.chainid, + address(_delegator.deleGator), + _to, + _delegations[_delegations.length - 2].authority + ) + ); + bytes32 ethSignedMessageHash_ = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)); + // Destination proof is EIP-191 personal_sign (same prefix as compliance), not EIP-712 like the chain. + return signHash(_delegator, ethSignedMessageHash_); + } + + function _transferComplianceData( + address _from, + address _to, + uint256 _amount, + uint256 _deadline, + uint256 _signerKey + ) + internal + view + returns (IComplianceVedaTeller.ComplianceData memory) + { + bytes32 messageHash_ = keccak256( + abi.encode( + address(migrationHelper), + address(PREMIUM_TELLER), + block.chainid, + _from, + _to, + address(PREMIUM_VAULT), + _amount, + _deadline + ) + ); + bytes32 ethSignedMessageHash_ = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)); + (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(_signerKey, ethSignedMessageHash_); + return IComplianceVedaTeller.ComplianceData({ deadline: _deadline, signature: abi.encodePacked(r_, s_, v_) }); + } + + function _complianceData( + TestUser memory _delegator, + uint256 _amount, + uint256 _deadline + ) + internal + view + returns (IComplianceVedaTeller.ComplianceData memory) + { + return IComplianceVedaTeller.ComplianceData({ + deadline: _deadline, signature: _signCompliance(_delegator, _amount, _deadline, COMPLIANCE_SIGNER_KEY) + }); + } + + function _signCompliance( + TestUser memory _delegator, + uint256 _amount, + uint256 _deadline, + uint256 _signerKey + ) + internal + view + returns (bytes memory) + { + bytes32 messageHash_ = keccak256( + abi.encode( + address(PREMIUM_TELLER), + block.chainid, + address(premiumAdapter), + address(_delegator.deleGator), + address(MUSD), + _amount, + _deadline + ) + ); + bytes32 ethSignedMessageHash_ = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)); + (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(_signerKey, ethSignedMessageHash_); + return abi.encodePacked(r_, s_, v_); + } + + function _assertNoAdapterDust() internal { + assertEq(MUSD.balanceOf(address(baseAdapter)), 0); + assertEq(MUSD.balanceOf(address(premiumAdapter)), 0); + assertEq(MUSD.balanceOf(address(migrationHelper)), 0); + assertEq(BASE_VAULT.balanceOf(address(baseAdapter)), 0); + assertEq(PREMIUM_VAULT.balanceOf(address(premiumAdapter)), 0); + assertEq(BASE_VAULT.balanceOf(address(migrationHelper)), 0); + assertEq(PREMIUM_VAULT.balanceOf(address(migrationHelper)), 0); + } +}