From 4fb0a4fe5dd033f79dbfe7edd29a535cf419d1e8 Mon Sep 17 00:00:00 2001 From: Idris Bowman <34751375+V00D00-child@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:04:37 -0400 Subject: [PATCH 01/16] feat: add atomic vault migration adapter --- .env.example | 3 + script/DeployVaultMigrationAdapter.s.sol | 40 ++++ src/helpers/VaultMigrationAdapter.sol | 283 +++++++++++++++++++++++ test/helpers/VaultMigrationAdapter.t.sol | 255 ++++++++++++++++++++ 4 files changed, 581 insertions(+) create mode 100644 script/DeployVaultMigrationAdapter.s.sol create mode 100644 src/helpers/VaultMigrationAdapter.sol create mode 100644 test/helpers/VaultMigrationAdapter.t.sol diff --git a/.env.example b/.env.example index d4dd0284..ae080a68 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_ADAPTER_OWNER_ADDRESS= +BASE_VEDA_ADAPTER_ADDRESS= +PREMIUM_VEDA_ADAPTER_ADDRESS= # Required for verifying contracts ETHERSCAN_API_KEY= diff --git a/script/DeployVaultMigrationAdapter.s.sol b/script/DeployVaultMigrationAdapter.s.sol new file mode 100644 index 00000000..136939fd --- /dev/null +++ b/script/DeployVaultMigrationAdapter.s.sol @@ -0,0 +1,40 @@ +// 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 { VaultMigrationAdapter } from "../src/helpers/VaultMigrationAdapter.sol"; + +/** + * @title DeployVaultMigrationAdapter + * @notice Deploys VaultMigrationAdapter deterministically with CREATE2. + */ +contract DeployVaultMigrationAdapter 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_ADAPTER_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 VaultMigrationAdapter{ salt: salt }(owner, baseAdapter, premiumAdapter)); + vm.stopBroadcast(); + + console2.log("VaultMigrationAdapter: %s", deployed); + } +} diff --git a/src/helpers/VaultMigrationAdapter.sol b/src/helpers/VaultMigrationAdapter.sol new file mode 100644 index 00000000..7bdb7ec5 --- /dev/null +++ b/src/helpers/VaultMigrationAdapter.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 +pragma solidity 0.8.23; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; + +import { IComplianceVedaTeller } from "./interfaces/IComplianceVedaTeller.sol"; +import { Delegation } from "../utils/Types.sol"; + +/** + * @title IVedaAdapter + * @notice Minimal interface for the base Veda adapter methods used during migrations. + */ +interface IVedaAdapter { + /** + * @notice Deposits assets through a delegation chain. + * @param delegations Delegation chain sorted from leaf to root. + * @param minimumMint Minimum number of vault shares that must be minted. + */ + function depositByDelegation(Delegation[] calldata delegations, uint256 minimumMint) external; + + /** + * @notice Withdraws assets through a delegation chain. + * @param delegations Delegation chain sorted from leaf to root. + * @param minimumAssets Minimum number of underlying assets that must be returned. + */ + function withdrawByDelegation(Delegation[] calldata delegations, uint256 minimumAssets) external; +} + +/** + * @title IComplianceVedaAdapter + * @notice Minimal interface for the premium compliance-gated Veda adapter methods used during migrations. + */ +interface IComplianceVedaAdapter { + /** + * @notice Deposits assets through a delegation chain with compliance authorization. + * @param delegations Delegation chain sorted from leaf to root. + * @param minimumMint Minimum number of premium vault shares that must be minted. + * @param compliance Compliance authorization forwarded unchanged to the premium adapter. + */ + function depositByDelegation( + Delegation[] calldata delegations, + uint256 minimumMint, + IComplianceVedaTeller.ComplianceData calldata compliance + ) + external; + + /** + * @notice Withdraws assets through a delegation chain. + * @param delegations Delegation chain sorted from leaf to root. + * @param minimumAssets Minimum number of underlying assets that must be returned. + */ + function withdrawByDelegation(Delegation[] calldata delegations, uint256 minimumAssets) external; +} + +/** + * @title VaultMigrationAdapter + * @notice Atomically composes the existing base and premium Veda adapters. + * @dev Delegations remain targeted at the adapter that redeems them. This contract never redeems a delegation + * or holds migration assets: the source adapter sends assets to the root delegator and the destination + * adapter pulls those assets from the same root delegator. If either leg reverts, the entire migration + * transaction (and every preceding item in a batch) is rolled back. + */ +contract VaultMigrationAdapter is Ownable2Step { + /** + * @notice Parameters for one base-to-premium migration. + * @param withdrawalDelegations Delegation chain redeemed by the base adapter to withdraw base vault shares. + * @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. + * @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. + * @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. + * @param minimumMint Minimum base vault shares that the destination deposit must mint. + */ + struct ToBaseParams { + Delegation[] withdrawalDelegations; + uint256 minimumAssets; + Delegation[] depositDelegations; + uint256 minimumMint; + } + + ////////////////////////////// Events ////////////////////////////// + + /** + * @notice Emitted after a base-to-premium migration completes. + * @param delegator Root delegator whose vault position was migrated. + * @param minimumAssets Minimum asset bound supplied to the withdrawal leg. + * @param minimumMint Minimum share bound supplied to the deposit leg. + */ + event MigrationToPremiumExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); + + /** + * @notice Emitted after a premium-to-base migration completes. + * @param delegator Root delegator whose vault position was migrated. + * @param minimumAssets Minimum asset bound supplied to the withdrawal leg. + * @param minimumMint Minimum share bound supplied to the deposit leg. + */ + event MigrationToBaseExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); + + /** + * @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); + + ////////////////////////////// Errors ////////////////////////////// + + /// @dev Thrown when a required constructor address is zero. + error InvalidZeroAddress(); + + /// @dev Thrown when either delegation chain contains fewer than two delegations. + error InvalidDelegationsLength(); + + /// @dev Thrown when a migration batch is empty. + error InvalidBatchLength(); + + /// @dev Thrown when the withdrawal and deposit chains have different root delegators. + error DelegatorMismatch(); + + ////////////////////////////// State ////////////////////////////// + + /** + * @notice Base vault adapter used for base deposits and withdrawals. + */ + IVedaAdapter public immutable baseAdapter; + + /** + * @notice Premium compliance-gated adapter used for premium deposits and withdrawals. + */ + IComplianceVedaAdapter public immutable premiumAdapter; + + ////////////////////////////// Constructor ////////////////////////////// + + /** + * @notice Initializes the atomic migration composer. + * @param _owner Address that owns this contract through Ownable2Step. + * @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 (_owner == address(0) || _baseAdapter == address(0) || _premiumAdapter == address(0)) { + revert InvalidZeroAddress(); + } + baseAdapter = IVedaAdapter(_baseAdapter); + premiumAdapter = IComplianceVedaAdapter(_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_); + } + + ////////////////////////////// Private/Internal Methods ////////////////////////////// + + /** + * @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); + + baseAdapter.withdrawByDelegation(_params.withdrawalDelegations, _params.minimumAssets); + premiumAdapter.depositByDelegation(_params.depositDelegations, _params.minimumMint, _params.compliance); + + emit MigrationToPremiumExecuted(delegator_, _params.minimumAssets, _params.minimumMint); + } + + /** + * @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); + + premiumAdapter.withdrawByDelegation(_params.withdrawalDelegations, _params.minimumAssets); + baseAdapter.depositByDelegation(_params.depositDelegations, _params.minimumMint); + + emit MigrationToBaseExecuted(delegator_, _params.minimumAssets, _params.minimumMint); + } + + /** + * @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(); + } +} diff --git a/test/helpers/VaultMigrationAdapter.t.sol b/test/helpers/VaultMigrationAdapter.t.sol new file mode 100644 index 00000000..c7fdd6d1 --- /dev/null +++ b/test/helpers/VaultMigrationAdapter.t.sol @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 +pragma solidity 0.8.23; + +import { Test } from "forge-std/Test.sol"; + +import { IComplianceVedaAdapter, IVedaAdapter, VaultMigrationAdapter } from "../../src/helpers/VaultMigrationAdapter.sol"; +import { IComplianceVedaTeller } from "../../src/helpers/interfaces/IComplianceVedaTeller.sol"; +import { Caveat, Delegation } from "../../src/utils/Types.sol"; + +contract MockVedaAdapter is IVedaAdapter, IComplianceVedaAdapter { + uint256 public depositCalls; + uint256 public withdrawalCalls; + uint256 public lastMinimumMint; + uint256 public lastMinimumAssets; + uint256 public lastDepositDelegationsLength; + uint256 public lastWithdrawalDelegationsLength; + uint256 public lastComplianceDeadline; + bytes32 public lastComplianceSignatureHash; + bool public revertDeposit; + MockVedaAdapter public requiredWithdrawalAdapter; + + error DepositFailed(); + error WithdrawalDidNotRunFirst(); + + function setRevertDeposit(bool _revertDeposit) external { + revertDeposit = _revertDeposit; + } + + function setRequiredWithdrawalAdapter(MockVedaAdapter _adapter) external { + requiredWithdrawalAdapter = _adapter; + } + + function depositByDelegation(Delegation[] calldata _delegations, uint256 _minimumMint) external override { + _recordDeposit(_delegations, _minimumMint); + } + + function depositByDelegation( + Delegation[] calldata _delegations, + uint256 _minimumMint, + IComplianceVedaTeller.ComplianceData calldata _compliance + ) + external + override + { + lastComplianceDeadline = _compliance.deadline; + lastComplianceSignatureHash = keccak256(_compliance.signature); + _recordDeposit(_delegations, _minimumMint); + } + + function withdrawByDelegation( + Delegation[] calldata _delegations, + uint256 _minimumAssets + ) + external + override(IVedaAdapter, IComplianceVedaAdapter) + { + ++withdrawalCalls; + lastMinimumAssets = _minimumAssets; + lastWithdrawalDelegationsLength = _delegations.length; + } + + function _recordDeposit(Delegation[] calldata _delegations, uint256 _minimumMint) private { + if (revertDeposit) revert DepositFailed(); + if (address(requiredWithdrawalAdapter) != address(0) && requiredWithdrawalAdapter.withdrawalCalls() == 0) { + revert WithdrawalDidNotRunFirst(); + } + ++depositCalls; + lastMinimumMint = _minimumMint; + lastDepositDelegationsLength = _delegations.length; + } +} + +contract VaultMigrationAdapterTest is Test { + event MigrationToPremiumExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); + event MigrationToBaseExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); + event BatchMigrationToPremiumExecuted(address indexed caller, uint256 count); + event BatchMigrationToBaseExecuted(address indexed caller, uint256 count); + + address internal constant ALICE = address(0xA11CE); + address internal constant BOB = address(0xB0B); + + MockVedaAdapter internal baseAdapter; + MockVedaAdapter internal premiumAdapter; + VaultMigrationAdapter internal migrationAdapter; + + function setUp() public { + baseAdapter = new MockVedaAdapter(); + premiumAdapter = new MockVedaAdapter(); + migrationAdapter = new VaultMigrationAdapter(address(this), address(baseAdapter), address(premiumAdapter)); + } + + function test_migrateToPremium_forwardsParametersAndCompliance() public { + premiumAdapter.setRequiredWithdrawalAdapter(baseAdapter); + VaultMigrationAdapter.ToPremiumParams memory params_ = _toPremiumParams(ALICE, 91, 82, 1234, hex"abcd"); + + vm.expectEmit(true, false, false, true); + emit MigrationToPremiumExecuted(ALICE, 91, 82); + migrationAdapter.migrateToPremiumByDelegation(params_); + + assertEq(baseAdapter.withdrawalCalls(), 1); + assertEq(baseAdapter.lastMinimumAssets(), 91); + assertEq(baseAdapter.lastWithdrawalDelegationsLength(), 2); + assertEq(premiumAdapter.depositCalls(), 1); + assertEq(premiumAdapter.lastMinimumMint(), 82); + assertEq(premiumAdapter.lastDepositDelegationsLength(), 2); + assertEq(premiumAdapter.lastComplianceDeadline(), 1234); + assertEq(premiumAdapter.lastComplianceSignatureHash(), keccak256(hex"abcd")); + } + + function test_migrateToBase_withdrawsBeforeDeposit() public { + baseAdapter.setRequiredWithdrawalAdapter(premiumAdapter); + VaultMigrationAdapter.ToBaseParams memory params_ = _toBaseParams(ALICE, 71, 62); + + vm.expectEmit(true, false, false, true); + emit MigrationToBaseExecuted(ALICE, 71, 62); + migrationAdapter.migrateToBaseByDelegation(params_); + + assertEq(premiumAdapter.withdrawalCalls(), 1); + assertEq(premiumAdapter.lastMinimumAssets(), 71); + assertEq(baseAdapter.depositCalls(), 1); + assertEq(baseAdapter.lastMinimumMint(), 62); + } + + function test_migrateToPremium_isPermissionless() public { + vm.prank(BOB); + migrationAdapter.migrateToPremiumByDelegation(_toPremiumParams(ALICE, 1, 1, 1, hex"01")); + + assertEq(baseAdapter.withdrawalCalls(), 1); + assertEq(premiumAdapter.depositCalls(), 1); + } + + function test_migrateToPremium_revertsBothLegsWhenDepositFails() public { + premiumAdapter.setRevertDeposit(true); + + vm.expectRevert(MockVedaAdapter.DepositFailed.selector); + migrationAdapter.migrateToPremiumByDelegation(_toPremiumParams(ALICE, 1, 1, 1, hex"01")); + + assertEq(baseAdapter.withdrawalCalls(), 0); + assertEq(premiumAdapter.depositCalls(), 0); + } + + function test_migrateToBase_revertsOnDelegatorMismatch() public { + VaultMigrationAdapter.ToBaseParams memory params_ = VaultMigrationAdapter.ToBaseParams({ + withdrawalDelegations: _chain(ALICE, 1), minimumAssets: 1, depositDelegations: _chain(BOB, 2), minimumMint: 1 + }); + + vm.expectRevert(VaultMigrationAdapter.DelegatorMismatch.selector); + migrationAdapter.migrateToBaseByDelegation(params_); + } + + function test_migrateToPremium_revertsOnShortDelegationChain() public { + VaultMigrationAdapter.ToPremiumParams memory params_ = _toPremiumParams(ALICE, 1, 1, 1, hex"01"); + params_.withdrawalDelegations = new Delegation[](1); + + vm.expectRevert(VaultMigrationAdapter.InvalidDelegationsLength.selector); + migrationAdapter.migrateToPremiumByDelegation(params_); + } + + function test_migrateToPremiumBatch_executesAllItems() public { + VaultMigrationAdapter.ToPremiumParams[] memory params_ = new VaultMigrationAdapter.ToPremiumParams[](2); + params_[0] = _toPremiumParams(ALICE, 10, 11, 100, hex"01"); + params_[1] = _toPremiumParams(BOB, 20, 21, 200, hex"02"); + + vm.expectEmit(true, false, false, true); + emit BatchMigrationToPremiumExecuted(address(this), 2); + migrationAdapter.migrateToPremiumByDelegationBatch(params_); + + assertEq(baseAdapter.withdrawalCalls(), 2); + assertEq(premiumAdapter.depositCalls(), 2); + assertEq(premiumAdapter.lastMinimumMint(), 21); + assertEq(premiumAdapter.lastComplianceDeadline(), 200); + } + + function test_migrateToBaseBatch_executesAllItems() public { + VaultMigrationAdapter.ToBaseParams[] memory params_ = new VaultMigrationAdapter.ToBaseParams[](2); + params_[0] = _toBaseParams(ALICE, 10, 11); + params_[1] = _toBaseParams(BOB, 20, 21); + + vm.expectEmit(true, false, false, true); + emit BatchMigrationToBaseExecuted(address(this), 2); + migrationAdapter.migrateToBaseByDelegationBatch(params_); + + assertEq(premiumAdapter.withdrawalCalls(), 2); + assertEq(baseAdapter.depositCalls(), 2); + } + + function test_emptyBatchesRevert() public { + VaultMigrationAdapter.ToPremiumParams[] memory premiumParams_ = new VaultMigrationAdapter.ToPremiumParams[](0); + VaultMigrationAdapter.ToBaseParams[] memory baseParams_ = new VaultMigrationAdapter.ToBaseParams[](0); + + vm.expectRevert(VaultMigrationAdapter.InvalidBatchLength.selector); + migrationAdapter.migrateToPremiumByDelegationBatch(premiumParams_); + + vm.expectRevert(VaultMigrationAdapter.InvalidBatchLength.selector); + migrationAdapter.migrateToBaseByDelegationBatch(baseParams_); + } + + function _toPremiumParams( + address _delegator, + uint256 _minimumAssets, + uint256 _minimumMint, + uint256 _deadline, + bytes memory _signature + ) + private + pure + returns (VaultMigrationAdapter.ToPremiumParams memory) + { + return VaultMigrationAdapter.ToPremiumParams({ + withdrawalDelegations: _chain(_delegator, 1), + minimumAssets: _minimumAssets, + depositDelegations: _chain(_delegator, 2), + minimumMint: _minimumMint, + compliance: IComplianceVedaTeller.ComplianceData({ deadline: _deadline, signature: _signature }) + }); + } + + function _toBaseParams( + address _delegator, + uint256 _minimumAssets, + uint256 _minimumMint + ) + private + pure + returns (VaultMigrationAdapter.ToBaseParams memory) + { + return VaultMigrationAdapter.ToBaseParams({ + withdrawalDelegations: _chain(_delegator, 1), + minimumAssets: _minimumAssets, + depositDelegations: _chain(_delegator, 2), + minimumMint: _minimumMint + }); + } + + function _chain(address _delegator, uint256 _salt) private pure returns (Delegation[] memory delegations_) { + delegations_ = new Delegation[](2); + Caveat[] memory caveats_ = new Caveat[](0); + delegations_[0] = Delegation({ + delegate: address(0xD1), + delegator: address(0xD2), + authority: bytes32(0), + caveats: caveats_, + salt: _salt, + signature: hex"" + }); + delegations_[1] = Delegation({ + delegate: address(0xD2), + delegator: _delegator, + authority: bytes32(0), + caveats: caveats_, + salt: _salt + 1, + signature: hex"" + }); + } +} From a8ba1479cc87416edf64eac6127195f93d90710a Mon Sep 17 00:00:00 2001 From: Idris Bowman <34751375+V00D00-child@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:39:24 -0400 Subject: [PATCH 02/16] test: assert vault migration token and share amounts Replace mock call-count checks with signed delegation chains through the real adapters so migrations are verified by mUSD and vault-share balances. --- test/helpers/VaultMigrationAdapter.t.sol | 556 +++++++++++++++++------ 1 file changed, 426 insertions(+), 130 deletions(-) diff --git a/test/helpers/VaultMigrationAdapter.t.sol b/test/helpers/VaultMigrationAdapter.t.sol index c7fdd6d1..43dc6849 100644 --- a/test/helpers/VaultMigrationAdapter.t.sol +++ b/test/helpers/VaultMigrationAdapter.t.sol @@ -1,187 +1,366 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; -import { Test } from "forge-std/Test.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IComplianceVedaAdapter, IVedaAdapter, VaultMigrationAdapter } from "../../src/helpers/VaultMigrationAdapter.sol"; +import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; import { IComplianceVedaTeller } from "../../src/helpers/interfaces/IComplianceVedaTeller.sol"; -import { Caveat, Delegation } from "../../src/utils/Types.sol"; - -contract MockVedaAdapter is IVedaAdapter, IComplianceVedaAdapter { - uint256 public depositCalls; - uint256 public withdrawalCalls; - uint256 public lastMinimumMint; - uint256 public lastMinimumAssets; - uint256 public lastDepositDelegationsLength; - uint256 public lastWithdrawalDelegationsLength; +import { IVedaTeller } from "../../src/helpers/interfaces/IVedaTeller.sol"; +import { VedaAdapter } from "../../src/helpers/VedaAdapter.sol"; +import { ComplianceVedaAdapter } from "../../src/helpers/ComplianceVedaAdapter.sol"; +import { VaultMigrationAdapter } from "../../src/helpers/VaultMigrationAdapter.sol"; +import { ERC20TransferAmountEnforcer } from "../../src/enforcers/ERC20TransferAmountEnforcer.sol"; +import { RedeemerEnforcer } from "../../src/enforcers/RedeemerEnforcer.sol"; +import { Delegation, Caveat } from "../../src/utils/Types.sol"; +import { BaseTest } from "../utils/BaseTest.t.sol"; +import { BasicERC20 } from "../utils/BasicERC20.t.sol"; +import { Implementation, SignatureType, TestUser } from "../utils/Types.t.sol"; + +contract MockBoringVault is ERC20, Ownable { + using SafeERC20 for IERC20; + + constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) Ownable(msg.sender) { } + + function enter(address _from, IERC20 _asset, uint256 _assets, address _to, uint256 _shares) external onlyOwner { + _asset.safeTransferFrom(_from, address(this), _assets); + _mint(_to, _shares); + } + + function exit(address _from, IERC20 _asset, uint256 _assets, address _to, uint256 _shares) external onlyOwner { + _burn(_from, _shares); + _asset.safeTransfer(_to, _assets); + } +} + +contract MockVedaTeller is IVedaTeller, IComplianceVedaTeller { + MockBoringVault public immutable vault; + IERC20 public immutable asset; + + bool public revertDeposit; uint256 public lastComplianceDeadline; bytes32 public lastComplianceSignatureHash; - bool public revertDeposit; - MockVedaAdapter public requiredWithdrawalAdapter; error DepositFailed(); - error WithdrawalDidNotRunFirst(); + error InsufficientMint(); + error InsufficientAssets(); + + constructor(IERC20 _asset, string memory _vaultName, string memory _vaultSymbol) { + asset = _asset; + vault = new MockBoringVault(_vaultName, _vaultSymbol); + } function setRevertDeposit(bool _revertDeposit) external { revertDeposit = _revertDeposit; } - function setRequiredWithdrawalAdapter(MockVedaAdapter _adapter) external { - requiredWithdrawalAdapter = _adapter; + function deposit( + address _depositAsset, + uint256 _depositAmount, + uint256 _minimumMint, + address + ) + external + payable + override + returns (uint256) + { + return _deposit(_depositAsset, _depositAmount, _minimumMint, msg.sender); } - function depositByDelegation(Delegation[] calldata _delegations, uint256 _minimumMint) external override { - _recordDeposit(_delegations, _minimumMint); + function deposit( + address _depositAsset, + uint256 _depositAmount, + uint256 _minimumMint, + address _to, + address + ) + external + payable + override + returns (uint256) + { + return _deposit(_depositAsset, _depositAmount, _minimumMint, _to); } - function depositByDelegation( - Delegation[] calldata _delegations, - uint256 _minimumMint, - IComplianceVedaTeller.ComplianceData calldata _compliance + function deposit( + DepositParams calldata _params, + address _to, + address, + ComplianceData calldata _compliance ) external + payable override + returns (uint256) { lastComplianceDeadline = _compliance.deadline; lastComplianceSignatureHash = keccak256(_compliance.signature); - _recordDeposit(_delegations, _minimumMint); + return _deposit(_params.depositAsset, _params.depositAmount, _params.minimumMint, _to); } - function withdrawByDelegation( - Delegation[] calldata _delegations, - uint256 _minimumAssets + function withdraw( + address _withdrawAsset, + uint256 _shareAmount, + uint256 _minimumAssets, + address _to ) external - override(IVedaAdapter, IComplianceVedaAdapter) + override(IVedaTeller, IComplianceVedaTeller) + returns (uint256) { - ++withdrawalCalls; - lastMinimumAssets = _minimumAssets; - lastWithdrawalDelegationsLength = _delegations.length; + if (_shareAmount < _minimumAssets) revert InsufficientAssets(); + vault.exit(msg.sender, IERC20(_withdrawAsset), _shareAmount, _to, _shareAmount); + return _shareAmount; } - function _recordDeposit(Delegation[] calldata _delegations, uint256 _minimumMint) private { + function _deposit(address _depositAsset, uint256 _depositAmount, uint256 _minimumMint, address _to) private returns (uint256) { if (revertDeposit) revert DepositFailed(); - if (address(requiredWithdrawalAdapter) != address(0) && requiredWithdrawalAdapter.withdrawalCalls() == 0) { - revert WithdrawalDidNotRunFirst(); - } - ++depositCalls; - lastMinimumMint = _minimumMint; - lastDepositDelegationsLength = _delegations.length; + if (_depositAmount < _minimumMint) revert InsufficientMint(); + vault.enter(msg.sender, IERC20(_depositAsset), _depositAmount, _to, _depositAmount); + return _depositAmount; } } -contract VaultMigrationAdapterTest is Test { +/** + * @title VaultMigrationAdapter Test + * @notice Tests atomic base <-> premium vault migrations through signed delegation chains. + * @dev Uses in-process mock Tellers that actually pull tokens and mint/burn shares at 1:1 so + * assertions can follow token amounts the same way as VedaLending and ComplianceVedaLending. + */ +contract VaultMigrationAdapterTest is BaseTest { + uint256 internal constant INITIAL_MUSD_BALANCE = 100_000e6; + uint256 internal constant DEPOSIT_AMOUNT = 1_000e6; + uint256 internal constant COMPLIANCE_SIGNER_KEY = 0xC011A11CE; + + ERC20TransferAmountEnforcer internal erc20TransferAmountEnforcer; + RedeemerEnforcer internal redeemerEnforcer; + + BasicERC20 internal musd; + MockVedaTeller internal baseTeller; + MockVedaTeller internal premiumTeller; + IERC20 internal baseVault; + IERC20 internal premiumVault; + VedaAdapter internal baseAdapter; + ComplianceVedaAdapter internal premiumAdapter; + VaultMigrationAdapter internal migrationAdapter; + address internal adapterOwner; + event MigrationToPremiumExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); event MigrationToBaseExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); event BatchMigrationToPremiumExecuted(address indexed caller, uint256 count); event BatchMigrationToBaseExecuted(address indexed caller, uint256 count); - address internal constant ALICE = address(0xA11CE); - address internal constant BOB = address(0xB0B); + function setUp() public override { + IMPLEMENTATION = Implementation.Hybrid; + SIGNATURE_TYPE = SignatureType.RawP256; + super.setUp(); + + adapterOwner = makeAddr("VaultMigrationAdapter Owner"); + erc20TransferAmountEnforcer = new ERC20TransferAmountEnforcer(); + redeemerEnforcer = new RedeemerEnforcer(); + + musd = new BasicERC20(address(this), "mUSD", "mUSD", 0); + baseTeller = new MockVedaTeller(musd, "Base mUSD Vault", "bmUSD"); + premiumTeller = new MockVedaTeller(musd, "Premium mUSD Vault", "pmUSD"); + baseVault = IERC20(address(baseTeller.vault())); + premiumVault = IERC20(address(premiumTeller.vault())); + + baseAdapter = + new VedaAdapter(adapterOwner, address(delegationManager), address(baseVault), address(baseTeller), address(musd)); + premiumAdapter = new ComplianceVedaAdapter( + adapterOwner, address(delegationManager), address(premiumVault), address(premiumTeller), address(musd) + ); + migrationAdapter = new VaultMigrationAdapter(adapterOwner, address(baseAdapter), address(premiumAdapter)); + + musd.mint(address(users.alice.deleGator), INITIAL_MUSD_BALANCE); + musd.mint(address(users.carol.deleGator), INITIAL_MUSD_BALANCE); + + vm.label(address(musd), "mUSD"); + vm.label(address(baseVault), "Base Vault"); + vm.label(address(premiumVault), "Premium Vault"); + vm.label(address(baseAdapter), "VedaAdapter"); + vm.label(address(premiumAdapter), "ComplianceVedaAdapter"); + vm.label(address(migrationAdapter), "VaultMigrationAdapter"); + } - MockVedaAdapter internal baseAdapter; - MockVedaAdapter internal premiumAdapter; - VaultMigrationAdapter internal migrationAdapter; + function test_constructor_revertsOnZeroAddresses() public { + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, address(0))); + new VaultMigrationAdapter(address(0), address(baseAdapter), address(premiumAdapter)); - function setUp() public { - baseAdapter = new MockVedaAdapter(); - premiumAdapter = new MockVedaAdapter(); - migrationAdapter = new VaultMigrationAdapter(address(this), address(baseAdapter), address(premiumAdapter)); + vm.expectRevert(VaultMigrationAdapter.InvalidZeroAddress.selector); + new VaultMigrationAdapter(adapterOwner, address(0), address(premiumAdapter)); + + vm.expectRevert(VaultMigrationAdapter.InvalidZeroAddress.selector); + new VaultMigrationAdapter(adapterOwner, address(baseAdapter), address(0)); } - function test_migrateToPremium_forwardsParametersAndCompliance() public { - premiumAdapter.setRequiredWithdrawalAdapter(baseAdapter); - VaultMigrationAdapter.ToPremiumParams memory params_ = _toPremiumParams(ALICE, 91, 82, 1234, hex"abcd"); + function test_migrateToPremium_movesSharesAndLeavesNoDust() public { + _depositToBase(users.alice, DEPOSIT_AMOUNT, 0); + + uint256 aliceMusdBefore_ = musd.balanceOf(address(users.alice.deleGator)); + uint256 baseShares_ = baseVault.balanceOf(address(users.alice.deleGator)); + assertEq(baseShares_, DEPOSIT_AMOUNT); + assertEq(aliceMusdBefore_, INITIAL_MUSD_BALANCE - DEPOSIT_AMOUNT); - vm.expectEmit(true, false, false, true); - emit MigrationToPremiumExecuted(ALICE, 91, 82); + VaultMigrationAdapter.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, DEPOSIT_AMOUNT, 1, DEPOSIT_AMOUNT, 2, block.timestamp + 30 minutes); + + vm.expectEmit(true, false, false, true, address(migrationAdapter)); + emit MigrationToPremiumExecuted(address(users.alice.deleGator), 0, 0); + vm.prank(address(users.bob.deleGator)); migrationAdapter.migrateToPremiumByDelegation(params_); - assertEq(baseAdapter.withdrawalCalls(), 1); - assertEq(baseAdapter.lastMinimumAssets(), 91); - assertEq(baseAdapter.lastWithdrawalDelegationsLength(), 2); - assertEq(premiumAdapter.depositCalls(), 1); - assertEq(premiumAdapter.lastMinimumMint(), 82); - assertEq(premiumAdapter.lastDepositDelegationsLength(), 2); - assertEq(premiumAdapter.lastComplianceDeadline(), 1234); - assertEq(premiumAdapter.lastComplianceSignatureHash(), keccak256(hex"abcd")); + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0, "Base shares should be burned"); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT, "Premium shares should be minted 1:1"); + assertEq(musd.balanceOf(address(users.alice.deleGator)), aliceMusdBefore_, "Underlying should settle back with Alice"); + _assertNoAdapterDust(); + assertEq(premiumTeller.lastComplianceDeadline(), block.timestamp + 30 minutes); + assertEq( + premiumTeller.lastComplianceSignatureHash(), keccak256(_signCompliance(DEPOSIT_AMOUNT, block.timestamp + 30 minutes)) + ); } - function test_migrateToBase_withdrawsBeforeDeposit() public { - baseAdapter.setRequiredWithdrawalAdapter(premiumAdapter); - VaultMigrationAdapter.ToBaseParams memory params_ = _toBaseParams(ALICE, 71, 62); + function test_migrateToBase_movesSharesAndLeavesNoDust() public { + _depositToPremium(users.alice, DEPOSIT_AMOUNT, 10, block.timestamp + 30 minutes); + + uint256 aliceMusdBefore_ = musd.balanceOf(address(users.alice.deleGator)); + uint256 premiumShares_ = premiumVault.balanceOf(address(users.alice.deleGator)); + assertEq(premiumShares_, DEPOSIT_AMOUNT); - vm.expectEmit(true, false, false, true); - emit MigrationToBaseExecuted(ALICE, 71, 62); + VaultMigrationAdapter.ToBaseParams memory params_ = _toBaseParams(users.alice, premiumShares_, 11, DEPOSIT_AMOUNT, 12); + + vm.expectEmit(true, false, false, true, address(migrationAdapter)); + emit MigrationToBaseExecuted(address(users.alice.deleGator), 0, 0); + vm.prank(address(users.bob.deleGator)); migrationAdapter.migrateToBaseByDelegation(params_); - assertEq(premiumAdapter.withdrawalCalls(), 1); - assertEq(premiumAdapter.lastMinimumAssets(), 71); - assertEq(baseAdapter.depositCalls(), 1); - assertEq(baseAdapter.lastMinimumMint(), 62); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0, "Premium shares should be burned"); + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT, "Base shares should be minted 1:1"); + assertEq(musd.balanceOf(address(users.alice.deleGator)), aliceMusdBefore_, "Underlying should settle back with Alice"); + _assertNoAdapterDust(); } function test_migrateToPremium_isPermissionless() public { - vm.prank(BOB); - migrationAdapter.migrateToPremiumByDelegation(_toPremiumParams(ALICE, 1, 1, 1, hex"01")); - - assertEq(baseAdapter.withdrawalCalls(), 1); - assertEq(premiumAdapter.depositCalls(), 1); + _depositToBase(users.alice, DEPOSIT_AMOUNT, 20); + uint256 baseShares_ = baseVault.balanceOf(address(users.alice.deleGator)); + + vm.prank(address(users.carol.deleGator)); + migrationAdapter.migrateToPremiumByDelegation( + _toPremiumParams(users.alice, baseShares_, DEPOSIT_AMOUNT, 21, DEPOSIT_AMOUNT, 22, block.timestamp + 30 minutes) + ); + + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT); + assertEq(premiumVault.balanceOf(address(users.carol.deleGator)), 0, "Caller must not receive shares"); + _assertNoAdapterDust(); } function test_migrateToPremium_revertsBothLegsWhenDepositFails() public { - premiumAdapter.setRevertDeposit(true); - - vm.expectRevert(MockVedaAdapter.DepositFailed.selector); - migrationAdapter.migrateToPremiumByDelegation(_toPremiumParams(ALICE, 1, 1, 1, hex"01")); + _depositToBase(users.alice, DEPOSIT_AMOUNT, 30); + uint256 baseShares_ = baseVault.balanceOf(address(users.alice.deleGator)); + uint256 aliceMusdBefore_ = musd.balanceOf(address(users.alice.deleGator)); + premiumTeller.setRevertDeposit(true); + VaultMigrationAdapter.ToPremiumParams memory params_ = + _toPremiumParams(users.alice, baseShares_, DEPOSIT_AMOUNT, 31, DEPOSIT_AMOUNT, 32, block.timestamp + 30 minutes); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(MockVedaTeller.DepositFailed.selector); + migrationAdapter.migrateToPremiumByDelegation(params_); - assertEq(baseAdapter.withdrawalCalls(), 0); - assertEq(premiumAdapter.depositCalls(), 0); + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), baseShares_, "Base shares must roll back"); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0, "No premium shares should be minted"); + assertEq(musd.balanceOf(address(users.alice.deleGator)), aliceMusdBefore_, "Underlying must roll back"); + _assertNoAdapterDust(); } function test_migrateToBase_revertsOnDelegatorMismatch() public { + _depositToPremium(users.alice, DEPOSIT_AMOUNT, 40, block.timestamp + 30 minutes); + uint256 aliceShares_ = premiumVault.balanceOf(address(users.alice.deleGator)); + VaultMigrationAdapter.ToBaseParams memory params_ = VaultMigrationAdapter.ToBaseParams({ - withdrawalDelegations: _chain(ALICE, 1), minimumAssets: 1, depositDelegations: _chain(BOB, 2), minimumMint: 1 + withdrawalDelegations: _createDelegationChain( + users.alice, address(premiumAdapter), address(premiumVault), aliceShares_, 41 + ), + minimumAssets: 0, + depositDelegations: _createDelegationChain(users.carol, address(baseAdapter), address(musd), DEPOSIT_AMOUNT, 42), + minimumMint: 0 }); vm.expectRevert(VaultMigrationAdapter.DelegatorMismatch.selector); migrationAdapter.migrateToBaseByDelegation(params_); + + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), aliceShares_); + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0); + assertEq(baseVault.balanceOf(address(users.carol.deleGator)), 0); } function test_migrateToPremium_revertsOnShortDelegationChain() public { - VaultMigrationAdapter.ToPremiumParams memory params_ = _toPremiumParams(ALICE, 1, 1, 1, hex"01"); + _depositToBase(users.alice, DEPOSIT_AMOUNT, 50); + VaultMigrationAdapter.ToPremiumParams memory params_ = _toPremiumParams( + users.alice, + baseVault.balanceOf(address(users.alice.deleGator)), + DEPOSIT_AMOUNT, + 51, + DEPOSIT_AMOUNT, + 52, + block.timestamp + 30 minutes + ); params_.withdrawalDelegations = new Delegation[](1); vm.expectRevert(VaultMigrationAdapter.InvalidDelegationsLength.selector); migrationAdapter.migrateToPremiumByDelegation(params_); + + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0); } - function test_migrateToPremiumBatch_executesAllItems() public { + function test_migrateToPremiumBatch_movesExactAmountsForEachDelegator() public { + uint256 aliceAmount_ = 300e6; + uint256 carolAmount_ = 400e6; + _depositToBase(users.alice, aliceAmount_, 60); + _depositToBase(users.carol, carolAmount_, 61); + VaultMigrationAdapter.ToPremiumParams[] memory params_ = new VaultMigrationAdapter.ToPremiumParams[](2); - params_[0] = _toPremiumParams(ALICE, 10, 11, 100, hex"01"); - params_[1] = _toPremiumParams(BOB, 20, 21, 200, hex"02"); + params_[0] = _toPremiumParams(users.alice, aliceAmount_, aliceAmount_, 62, aliceAmount_, 63, block.timestamp + 20 minutes); + params_[1] = _toPremiumParams(users.carol, carolAmount_, carolAmount_, 64, carolAmount_, 65, block.timestamp + 21 minutes); - vm.expectEmit(true, false, false, true); - emit BatchMigrationToPremiumExecuted(address(this), 2); + vm.expectEmit(true, false, false, true, address(migrationAdapter)); + emit BatchMigrationToPremiumExecuted(address(users.bob.deleGator), 2); + vm.prank(address(users.bob.deleGator)); migrationAdapter.migrateToPremiumByDelegationBatch(params_); - assertEq(baseAdapter.withdrawalCalls(), 2); - assertEq(premiumAdapter.depositCalls(), 2); - assertEq(premiumAdapter.lastMinimumMint(), 21); - assertEq(premiumAdapter.lastComplianceDeadline(), 200); + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0); + assertEq(baseVault.balanceOf(address(users.carol.deleGator)), 0); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), aliceAmount_); + assertEq(premiumVault.balanceOf(address(users.carol.deleGator)), carolAmount_); + assertEq(musd.balanceOf(address(users.alice.deleGator)), INITIAL_MUSD_BALANCE - aliceAmount_); + assertEq(musd.balanceOf(address(users.carol.deleGator)), INITIAL_MUSD_BALANCE - carolAmount_); + _assertNoAdapterDust(); } - function test_migrateToBaseBatch_executesAllItems() public { + function test_migrateToBaseBatch_movesExactAmountsForEachDelegator() public { + uint256 aliceAmount_ = 250e6; + uint256 carolAmount_ = 350e6; + _depositToPremium(users.alice, aliceAmount_, 70, block.timestamp + 20 minutes); + _depositToPremium(users.carol, carolAmount_, 71, block.timestamp + 21 minutes); + VaultMigrationAdapter.ToBaseParams[] memory params_ = new VaultMigrationAdapter.ToBaseParams[](2); - params_[0] = _toBaseParams(ALICE, 10, 11); - params_[1] = _toBaseParams(BOB, 20, 21); + params_[0] = _toBaseParams(users.alice, aliceAmount_, 72, aliceAmount_, 73); + params_[1] = _toBaseParams(users.carol, carolAmount_, 74, carolAmount_, 75); - vm.expectEmit(true, false, false, true); - emit BatchMigrationToBaseExecuted(address(this), 2); + vm.expectEmit(true, false, false, true, address(migrationAdapter)); + emit BatchMigrationToBaseExecuted(address(users.bob.deleGator), 2); + vm.prank(address(users.bob.deleGator)); migrationAdapter.migrateToBaseByDelegationBatch(params_); - assertEq(premiumAdapter.withdrawalCalls(), 2); - assertEq(baseAdapter.depositCalls(), 2); + assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0); + assertEq(premiumVault.balanceOf(address(users.carol.deleGator)), 0); + assertEq(baseVault.balanceOf(address(users.alice.deleGator)), aliceAmount_); + assertEq(baseVault.balanceOf(address(users.carol.deleGator)), carolAmount_); + _assertNoAdapterDust(); } function test_emptyBatchesRevert() public { @@ -195,61 +374,178 @@ contract VaultMigrationAdapterTest is Test { migrationAdapter.migrateToBaseByDelegationBatch(baseParams_); } + function _depositToBase(TestUser memory _delegator, uint256 _amount, uint256 _salt) internal { + Delegation[] memory delegations_ = _createDelegationChain(_delegator, address(baseAdapter), address(musd), _amount, _salt); + + vm.prank(address(users.bob.deleGator)); + baseAdapter.depositByDelegation(delegations_, 0); + } + + function _depositToPremium(TestUser memory _delegator, uint256 _amount, uint256 _salt, uint256 _deadline) internal { + Delegation[] memory delegations_ = + _createDelegationChain(_delegator, address(premiumAdapter), address(musd), _amount, _salt); + IComplianceVedaTeller.ComplianceData memory compliance_ = _complianceData(_amount, _deadline); + + vm.prank(address(users.bob.deleGator)); + premiumAdapter.depositByDelegation(delegations_, 0, compliance_); + } + function _toPremiumParams( - address _delegator, - uint256 _minimumAssets, - uint256 _minimumMint, - uint256 _deadline, - bytes memory _signature + TestUser memory _delegator, + uint256 _shareAmount, + uint256 _assetAmount, + uint256 _withdrawalSalt, + uint256 _depositAmount, + uint256 _depositSalt, + uint256 _deadline ) - private - pure + internal + view returns (VaultMigrationAdapter.ToPremiumParams memory) { return VaultMigrationAdapter.ToPremiumParams({ - withdrawalDelegations: _chain(_delegator, 1), - minimumAssets: _minimumAssets, - depositDelegations: _chain(_delegator, 2), - minimumMint: _minimumMint, - compliance: IComplianceVedaTeller.ComplianceData({ deadline: _deadline, signature: _signature }) + withdrawalDelegations: _createDelegationChain( + _delegator, address(baseAdapter), address(baseVault), _shareAmount, _withdrawalSalt + ), + minimumAssets: 0, + depositDelegations: _createDelegationChain( + _delegator, address(premiumAdapter), address(musd), _depositAmount, _depositSalt + ), + minimumMint: 0, + compliance: _complianceData(_assetAmount, _deadline) }); } function _toBaseParams( - address _delegator, - uint256 _minimumAssets, - uint256 _minimumMint + TestUser memory _delegator, + uint256 _shareAmount, + uint256 _withdrawalSalt, + uint256 _depositAmount, + uint256 _depositSalt ) - private - pure + internal + view returns (VaultMigrationAdapter.ToBaseParams memory) { return VaultMigrationAdapter.ToBaseParams({ - withdrawalDelegations: _chain(_delegator, 1), - minimumAssets: _minimumAssets, - depositDelegations: _chain(_delegator, 2), - minimumMint: _minimumMint + withdrawalDelegations: _createDelegationChain( + _delegator, address(premiumAdapter), address(premiumVault), _shareAmount, _withdrawalSalt + ), + minimumAssets: 0, + depositDelegations: _createDelegationChain( + _delegator, address(baseAdapter), address(musd), _depositAmount, _depositSalt + ), + minimumMint: 0 }); } - function _chain(address _delegator, uint256 _salt) private pure returns (Delegation[] memory delegations_) { + 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); + Delegation memory leaf_ = _createAdapterRedelegation(_adapter, EncoderLib._getDelegationHash(root_), _token, _amount, _salt); + delegations_ = new Delegation[](2); - Caveat[] memory caveats_ = new Caveat[](0); - delegations_[0] = Delegation({ - delegate: address(0xD1), - delegator: address(0xD2), - authority: bytes32(0), + 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"" }); - delegations_[1] = Delegation({ - delegate: address(0xD2), - delegator: _delegator, - authority: bytes32(0), + 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 + 1, + salt: _salt, signature: hex"" }); + return signDelegation(users.bob, delegation_); + } + + function _complianceData( + uint256 _amount, + uint256 _deadline + ) + internal + view + returns (IComplianceVedaTeller.ComplianceData memory) + { + return IComplianceVedaTeller.ComplianceData({ deadline: _deadline, signature: _signCompliance(_amount, _deadline) }); + } + + function _signCompliance(uint256 _amount, uint256 _deadline) internal view returns (bytes memory) { + bytes32 messageHash_ = keccak256( + abi.encode( + address(premiumTeller), + block.chainid, + address(premiumAdapter), + address(users.alice.deleGator), + address(musd), + _amount, + _deadline + ) + ); + bytes32 ethSignedMessageHash_ = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)); + (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(COMPLIANCE_SIGNER_KEY, 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(migrationAdapter)), 0); + assertEq(baseVault.balanceOf(address(baseAdapter)), 0); + assertEq(premiumVault.balanceOf(address(premiumAdapter)), 0); + assertEq(baseVault.balanceOf(address(migrationAdapter)), 0); + assertEq(premiumVault.balanceOf(address(migrationAdapter)), 0); } } From fa63881357cf54f4d65d65054869530a17cfae60 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Thu, 24 Sep 2026 14:55:13 +0200 Subject: [PATCH 03/16] Clean up code, add fork tests and add functionality to transfer premium balance --- .env.example | 2 +- ...s.sol => DeployVaultMigrationHelper.s.sol} | 14 +- src/helpers/VaultMigrationAdapter.sol | 283 ----- src/helpers/VaultMigrationHelper.sol | 424 +++++++ .../interfaces/IComplianceVedaTeller.sol | 16 + src/helpers/interfaces/IRolesAuthority.sol | 10 + test/helpers/VaultMigrationAdapter.t.sol | 551 --------- test/helpers/VaultMigrationHelper.t.sol | 1017 +++++++++++++++++ 8 files changed, 1475 insertions(+), 842 deletions(-) rename script/{DeployVaultMigrationAdapter.s.sol => DeployVaultMigrationHelper.s.sol} (64%) delete mode 100644 src/helpers/VaultMigrationAdapter.sol create mode 100644 src/helpers/VaultMigrationHelper.sol create mode 100644 src/helpers/interfaces/IRolesAuthority.sol delete mode 100644 test/helpers/VaultMigrationAdapter.t.sol create mode 100644 test/helpers/VaultMigrationHelper.t.sol diff --git a/.env.example b/.env.example index ae080a68..2aa46d72 100644 --- a/.env.example +++ b/.env.example @@ -12,7 +12,7 @@ VEDA_ADAPTER_OWNER_ADDRESS= VEDA_BORING_VAULT_ADDRESS= VEDA_TELLER_ADDRESS= VEDA_DEPOSIT_TOKEN_ADDRESS= -VAULT_MIGRATION_ADAPTER_OWNER_ADDRESS= +VAULT_MIGRATION_HELPER_OWNER_ADDRESS= BASE_VEDA_ADAPTER_ADDRESS= PREMIUM_VEDA_ADAPTER_ADDRESS= diff --git a/script/DeployVaultMigrationAdapter.s.sol b/script/DeployVaultMigrationHelper.s.sol similarity index 64% rename from script/DeployVaultMigrationAdapter.s.sol rename to script/DeployVaultMigrationHelper.s.sol index 136939fd..a296fb24 100644 --- a/script/DeployVaultMigrationAdapter.s.sol +++ b/script/DeployVaultMigrationHelper.s.sol @@ -4,13 +4,13 @@ pragma solidity 0.8.23; import "forge-std/Script.sol"; import { console2 } from "forge-std/console2.sol"; -import { VaultMigrationAdapter } from "../src/helpers/VaultMigrationAdapter.sol"; +import { VaultMigrationHelper } from "../src/helpers/VaultMigrationHelper.sol"; /** - * @title DeployVaultMigrationAdapter - * @notice Deploys VaultMigrationAdapter deterministically with CREATE2. + * @title DeployVaultMigrationHelper + * @notice Deploys VaultMigrationHelper deterministically with CREATE2. */ -contract DeployVaultMigrationAdapter is Script { +contract DeployVaultMigrationHelper is Script { bytes32 internal salt; address internal owner; address internal baseAdapter; @@ -18,7 +18,7 @@ contract DeployVaultMigrationAdapter is Script { function setUp() public { salt = bytes32(abi.encodePacked(vm.envString("SALT"))); - owner = vm.envAddress("VAULT_MIGRATION_ADAPTER_OWNER_ADDRESS"); + owner = vm.envAddress("VAULT_MIGRATION_HELPER_OWNER_ADDRESS"); baseAdapter = vm.envAddress("BASE_VEDA_ADAPTER_ADDRESS"); premiumAdapter = vm.envAddress("PREMIUM_VEDA_ADAPTER_ADDRESS"); @@ -32,9 +32,9 @@ contract DeployVaultMigrationAdapter is Script { function run() public { vm.startBroadcast(); - address deployed = address(new VaultMigrationAdapter{ salt: salt }(owner, baseAdapter, premiumAdapter)); + address deployed = address(new VaultMigrationHelper{ salt: salt }(owner, baseAdapter, premiumAdapter)); vm.stopBroadcast(); - console2.log("VaultMigrationAdapter: %s", deployed); + console2.log("VaultMigrationHelper: %s", deployed); } } diff --git a/src/helpers/VaultMigrationAdapter.sol b/src/helpers/VaultMigrationAdapter.sol deleted file mode 100644 index 7bdb7ec5..00000000 --- a/src/helpers/VaultMigrationAdapter.sol +++ /dev/null @@ -1,283 +0,0 @@ -// SPDX-License-Identifier: MIT AND Apache-2.0 -pragma solidity 0.8.23; - -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; - -import { IComplianceVedaTeller } from "./interfaces/IComplianceVedaTeller.sol"; -import { Delegation } from "../utils/Types.sol"; - -/** - * @title IVedaAdapter - * @notice Minimal interface for the base Veda adapter methods used during migrations. - */ -interface IVedaAdapter { - /** - * @notice Deposits assets through a delegation chain. - * @param delegations Delegation chain sorted from leaf to root. - * @param minimumMint Minimum number of vault shares that must be minted. - */ - function depositByDelegation(Delegation[] calldata delegations, uint256 minimumMint) external; - - /** - * @notice Withdraws assets through a delegation chain. - * @param delegations Delegation chain sorted from leaf to root. - * @param minimumAssets Minimum number of underlying assets that must be returned. - */ - function withdrawByDelegation(Delegation[] calldata delegations, uint256 minimumAssets) external; -} - -/** - * @title IComplianceVedaAdapter - * @notice Minimal interface for the premium compliance-gated Veda adapter methods used during migrations. - */ -interface IComplianceVedaAdapter { - /** - * @notice Deposits assets through a delegation chain with compliance authorization. - * @param delegations Delegation chain sorted from leaf to root. - * @param minimumMint Minimum number of premium vault shares that must be minted. - * @param compliance Compliance authorization forwarded unchanged to the premium adapter. - */ - function depositByDelegation( - Delegation[] calldata delegations, - uint256 minimumMint, - IComplianceVedaTeller.ComplianceData calldata compliance - ) - external; - - /** - * @notice Withdraws assets through a delegation chain. - * @param delegations Delegation chain sorted from leaf to root. - * @param minimumAssets Minimum number of underlying assets that must be returned. - */ - function withdrawByDelegation(Delegation[] calldata delegations, uint256 minimumAssets) external; -} - -/** - * @title VaultMigrationAdapter - * @notice Atomically composes the existing base and premium Veda adapters. - * @dev Delegations remain targeted at the adapter that redeems them. This contract never redeems a delegation - * or holds migration assets: the source adapter sends assets to the root delegator and the destination - * adapter pulls those assets from the same root delegator. If either leg reverts, the entire migration - * transaction (and every preceding item in a batch) is rolled back. - */ -contract VaultMigrationAdapter is Ownable2Step { - /** - * @notice Parameters for one base-to-premium migration. - * @param withdrawalDelegations Delegation chain redeemed by the base adapter to withdraw base vault shares. - * @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. - * @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. - * @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. - * @param minimumMint Minimum base vault shares that the destination deposit must mint. - */ - struct ToBaseParams { - Delegation[] withdrawalDelegations; - uint256 minimumAssets; - Delegation[] depositDelegations; - uint256 minimumMint; - } - - ////////////////////////////// Events ////////////////////////////// - - /** - * @notice Emitted after a base-to-premium migration completes. - * @param delegator Root delegator whose vault position was migrated. - * @param minimumAssets Minimum asset bound supplied to the withdrawal leg. - * @param minimumMint Minimum share bound supplied to the deposit leg. - */ - event MigrationToPremiumExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); - - /** - * @notice Emitted after a premium-to-base migration completes. - * @param delegator Root delegator whose vault position was migrated. - * @param minimumAssets Minimum asset bound supplied to the withdrawal leg. - * @param minimumMint Minimum share bound supplied to the deposit leg. - */ - event MigrationToBaseExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); - - /** - * @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); - - ////////////////////////////// Errors ////////////////////////////// - - /// @dev Thrown when a required constructor address is zero. - error InvalidZeroAddress(); - - /// @dev Thrown when either delegation chain contains fewer than two delegations. - error InvalidDelegationsLength(); - - /// @dev Thrown when a migration batch is empty. - error InvalidBatchLength(); - - /// @dev Thrown when the withdrawal and deposit chains have different root delegators. - error DelegatorMismatch(); - - ////////////////////////////// State ////////////////////////////// - - /** - * @notice Base vault adapter used for base deposits and withdrawals. - */ - IVedaAdapter public immutable baseAdapter; - - /** - * @notice Premium compliance-gated adapter used for premium deposits and withdrawals. - */ - IComplianceVedaAdapter public immutable premiumAdapter; - - ////////////////////////////// Constructor ////////////////////////////// - - /** - * @notice Initializes the atomic migration composer. - * @param _owner Address that owns this contract through Ownable2Step. - * @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 (_owner == address(0) || _baseAdapter == address(0) || _premiumAdapter == address(0)) { - revert InvalidZeroAddress(); - } - baseAdapter = IVedaAdapter(_baseAdapter); - premiumAdapter = IComplianceVedaAdapter(_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_); - } - - ////////////////////////////// Private/Internal Methods ////////////////////////////// - - /** - * @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); - - baseAdapter.withdrawByDelegation(_params.withdrawalDelegations, _params.minimumAssets); - premiumAdapter.depositByDelegation(_params.depositDelegations, _params.minimumMint, _params.compliance); - - emit MigrationToPremiumExecuted(delegator_, _params.minimumAssets, _params.minimumMint); - } - - /** - * @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); - - premiumAdapter.withdrawByDelegation(_params.withdrawalDelegations, _params.minimumAssets); - baseAdapter.depositByDelegation(_params.depositDelegations, _params.minimumMint); - - emit MigrationToBaseExecuted(delegator_, _params.minimumAssets, _params.minimumMint); - } - - /** - * @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(); - } -} diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol new file mode 100644 index 00000000..de9fefe4 --- /dev/null +++ b/src/helpers/VaultMigrationHelper.sol @@ -0,0 +1,424 @@ +// 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 { 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 + * a compliance signature. + * @dev Vault-to-vault migrations keep delegations targeted at the adapter that redeems them. Premium share + * transfers are redeemed by this helper: the chain must pin `transfer` `to` to this contract and name + * this contract as redeemer. 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`). + * + * Leaf Caveat Format: + * - For `premiumTransfer`, the first caveat of the leaf delegation (`_delegations[0].caveats[0]`) must + * follow the ERC20TransferAmountEnforcer terms format: abi.encodePacked(address token, uint256 amount) + * (52 bytes). This helper parses only the amount from these terms; the token address encoded in + * bytes 0–19 is consumed by the enforcer itself and is not read here. A delegation without this + * enforcer as the first caveat (or with an amount other than the owner's full premium balance) + * will revert. + */ +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. + * @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. + * @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. + * @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. + * @param minimumMint Minimum base vault shares that the destination deposit must mint. + */ + struct ToBaseParams { + Delegation[] withdrawalDelegations; + uint256 minimumAssets; + Delegation[] depositDelegations; + uint256 minimumMint; + } + + ////////////////////////////// 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 compliance-gated premium share transfer. + * @param from Share owner whose full premium balance was moved. + * @param to Compliance-approved recipient. + * @param amount Number of premium vault shares transferred. + */ + event PremiumTransferExecuted(address indexed from, address indexed to, uint256 amount); + + /** + * @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 migration batch is empty. + error InvalidBatchLength(); + + /// @dev Thrown when the withdrawal and deposit chains have different root delegators. + error DelegatorMismatch(); + + /// @dev Thrown when the leaf caveat terms are shorter than 52 bytes. + error InvalidTermsLength(); + + /// @dev Thrown when the premium share balance is zero or does not match the leaf caveat amount. + error InvalidTransferAmount(); + + /// @dev Thrown when compliance signature verification fails. + error ComplianceCheckFailed(); + + /// @dev Thrown when the premium Teller has compliance signer checks disabled (`complianceSignerRole == 255`). + error ComplianceDisabled(); + + ////////////////////////////// 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 a compliance signature check. + * @dev The delegation chain must be redeemable only by this helper and must pin `transfer` `to` to this + * helper. Production must grant this helper `transferAllowedRole` on the premium Teller. + * The transfer amount is parsed from the first caveat of the leaf delegation + * (`_delegations[0].caveats[0].terms`), which must follow the ERC20TransferAmountEnforcer + * format: abi.encodePacked(address token, uint256 amount). + * @param _from Share owner and root delegator. + * @param _to Compliance-approved recipient of the premium shares. + * @param _delegations Chain sorted leaf to root. Leaf `delegate` must be this helper. + * @param _compliance Backend-issued EIP-191 approval (`deadline` + `signature`). + * @notice Security consideration: Callable by anyone. The compliance signature is bound to this helper, + * the premium Teller, `from`, `to`, the premium vault, the full share balance, and the deadline. + * A Teller deposit signature cannot be reused because it binds the Teller address, not this helper. + * The redelegation MUST include an `ERC20TransferAmountEnforcer` as its first caveat (`caveats[0]`), + * capped to exactly the owner's full premium balance. Reverts when Teller compliance is disabled. + */ + function premiumTransfer( + address _from, + address _to, + Delegation[] calldata _delegations, + IComplianceVedaTeller.ComplianceData calldata _compliance + ) + external + { + if (_from == address(0) || _to == address(0)) revert InvalidZeroAddress(); + + uint256 length_ = _delegations.length; + if (length_ < 2) revert InvalidDelegationsLength(); + if (_delegations[length_ - 1].delegator != _from) revert DelegatorMismatch(); + + IERC20 vault_ = IERC20(premiumAdapter.boringVault()); + uint256 amount_ = vault_.balanceOf(_from); + if (amount_ == 0 || amount_ != _parseERC20TransferTerms(_delegations[0].caveats[0].terms)) { + revert InvalidTransferAmount(); + } + + _verifyTransferCompliance(_from, _to, address(vault_), amount_, _compliance); + + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(_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_))); + + premiumAdapter.delegationManager().redeemDelegations(permissionContexts_, encodedModes_, executionCallDatas_); + vault_.safeTransfer(_to, amount_); + + emit PremiumTransferExecuted(_from, _to, amount_); + } + + /** + * @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 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 Parses the transfer amount from ERC20TransferAmountEnforcer terms. + * @dev Terms format: abi.encodePacked(address token, uint256 amount) = 52 bytes. + * The token address (bytes 0–19) is validated by the enforcer itself and is not read here. + * Only the amount (bytes 20–51) is returned. + */ + function _parseERC20TransferTerms(bytes calldata _terms) private pure returns (uint256 amount_) { + if (_terms.length < 52) revert InvalidTermsLength(); + amount_ = uint256(bytes32(_terms[20:52])); + } + + /** + * @notice Verifies a transfer compliance signature using the Teller's signer role and window. + * @dev The digest starts with this helper's address 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 ComplianceCheckFailed(); + if (block.timestamp > _compliance.deadline) revert ComplianceCheckFailed(); + + uint96 complianceWindow_ = teller_.complianceWindow(); + if (complianceWindow_ > 0 && _compliance.deadline > block.timestamp + complianceWindow_) { + revert ComplianceCheckFailed(); + } + + bytes32 ethSignedHash_ = MessageHashUtils.toEthSignedMessageHash(messageHash_); + address recovered_ = ECDSA.recover(ethSignedHash_, _compliance.signature); + if (!IRolesAuthority(teller_.authority()).doesUserHaveRole(recovered_, complianceSignerRole_)) { + revert ComplianceCheckFailed(); + } + + 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/VaultMigrationAdapter.t.sol b/test/helpers/VaultMigrationAdapter.t.sol deleted file mode 100644 index 43dc6849..00000000 --- a/test/helpers/VaultMigrationAdapter.t.sol +++ /dev/null @@ -1,551 +0,0 @@ -// SPDX-License-Identifier: MIT AND Apache-2.0 -pragma solidity 0.8.23; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; - -import { EncoderLib } from "../../src/libraries/EncoderLib.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 { VaultMigrationAdapter } from "../../src/helpers/VaultMigrationAdapter.sol"; -import { ERC20TransferAmountEnforcer } from "../../src/enforcers/ERC20TransferAmountEnforcer.sol"; -import { RedeemerEnforcer } from "../../src/enforcers/RedeemerEnforcer.sol"; -import { Delegation, Caveat } from "../../src/utils/Types.sol"; -import { BaseTest } from "../utils/BaseTest.t.sol"; -import { BasicERC20 } from "../utils/BasicERC20.t.sol"; -import { Implementation, SignatureType, TestUser } from "../utils/Types.t.sol"; - -contract MockBoringVault is ERC20, Ownable { - using SafeERC20 for IERC20; - - constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) Ownable(msg.sender) { } - - function enter(address _from, IERC20 _asset, uint256 _assets, address _to, uint256 _shares) external onlyOwner { - _asset.safeTransferFrom(_from, address(this), _assets); - _mint(_to, _shares); - } - - function exit(address _from, IERC20 _asset, uint256 _assets, address _to, uint256 _shares) external onlyOwner { - _burn(_from, _shares); - _asset.safeTransfer(_to, _assets); - } -} - -contract MockVedaTeller is IVedaTeller, IComplianceVedaTeller { - MockBoringVault public immutable vault; - IERC20 public immutable asset; - - bool public revertDeposit; - uint256 public lastComplianceDeadline; - bytes32 public lastComplianceSignatureHash; - - error DepositFailed(); - error InsufficientMint(); - error InsufficientAssets(); - - constructor(IERC20 _asset, string memory _vaultName, string memory _vaultSymbol) { - asset = _asset; - vault = new MockBoringVault(_vaultName, _vaultSymbol); - } - - function setRevertDeposit(bool _revertDeposit) external { - revertDeposit = _revertDeposit; - } - - function deposit( - address _depositAsset, - uint256 _depositAmount, - uint256 _minimumMint, - address - ) - external - payable - override - returns (uint256) - { - return _deposit(_depositAsset, _depositAmount, _minimumMint, msg.sender); - } - - function deposit( - address _depositAsset, - uint256 _depositAmount, - uint256 _minimumMint, - address _to, - address - ) - external - payable - override - returns (uint256) - { - return _deposit(_depositAsset, _depositAmount, _minimumMint, _to); - } - - function deposit( - DepositParams calldata _params, - address _to, - address, - ComplianceData calldata _compliance - ) - external - payable - override - returns (uint256) - { - lastComplianceDeadline = _compliance.deadline; - lastComplianceSignatureHash = keccak256(_compliance.signature); - return _deposit(_params.depositAsset, _params.depositAmount, _params.minimumMint, _to); - } - - function withdraw( - address _withdrawAsset, - uint256 _shareAmount, - uint256 _minimumAssets, - address _to - ) - external - override(IVedaTeller, IComplianceVedaTeller) - returns (uint256) - { - if (_shareAmount < _minimumAssets) revert InsufficientAssets(); - vault.exit(msg.sender, IERC20(_withdrawAsset), _shareAmount, _to, _shareAmount); - return _shareAmount; - } - - function _deposit(address _depositAsset, uint256 _depositAmount, uint256 _minimumMint, address _to) private returns (uint256) { - if (revertDeposit) revert DepositFailed(); - if (_depositAmount < _minimumMint) revert InsufficientMint(); - vault.enter(msg.sender, IERC20(_depositAsset), _depositAmount, _to, _depositAmount); - return _depositAmount; - } -} - -/** - * @title VaultMigrationAdapter Test - * @notice Tests atomic base <-> premium vault migrations through signed delegation chains. - * @dev Uses in-process mock Tellers that actually pull tokens and mint/burn shares at 1:1 so - * assertions can follow token amounts the same way as VedaLending and ComplianceVedaLending. - */ -contract VaultMigrationAdapterTest is BaseTest { - uint256 internal constant INITIAL_MUSD_BALANCE = 100_000e6; - uint256 internal constant DEPOSIT_AMOUNT = 1_000e6; - uint256 internal constant COMPLIANCE_SIGNER_KEY = 0xC011A11CE; - - ERC20TransferAmountEnforcer internal erc20TransferAmountEnforcer; - RedeemerEnforcer internal redeemerEnforcer; - - BasicERC20 internal musd; - MockVedaTeller internal baseTeller; - MockVedaTeller internal premiumTeller; - IERC20 internal baseVault; - IERC20 internal premiumVault; - VedaAdapter internal baseAdapter; - ComplianceVedaAdapter internal premiumAdapter; - VaultMigrationAdapter internal migrationAdapter; - address internal adapterOwner; - - event MigrationToPremiumExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); - event MigrationToBaseExecuted(address indexed delegator, uint256 minimumAssets, uint256 minimumMint); - event BatchMigrationToPremiumExecuted(address indexed caller, uint256 count); - event BatchMigrationToBaseExecuted(address indexed caller, uint256 count); - - function setUp() public override { - IMPLEMENTATION = Implementation.Hybrid; - SIGNATURE_TYPE = SignatureType.RawP256; - super.setUp(); - - adapterOwner = makeAddr("VaultMigrationAdapter Owner"); - erc20TransferAmountEnforcer = new ERC20TransferAmountEnforcer(); - redeemerEnforcer = new RedeemerEnforcer(); - - musd = new BasicERC20(address(this), "mUSD", "mUSD", 0); - baseTeller = new MockVedaTeller(musd, "Base mUSD Vault", "bmUSD"); - premiumTeller = new MockVedaTeller(musd, "Premium mUSD Vault", "pmUSD"); - baseVault = IERC20(address(baseTeller.vault())); - premiumVault = IERC20(address(premiumTeller.vault())); - - baseAdapter = - new VedaAdapter(adapterOwner, address(delegationManager), address(baseVault), address(baseTeller), address(musd)); - premiumAdapter = new ComplianceVedaAdapter( - adapterOwner, address(delegationManager), address(premiumVault), address(premiumTeller), address(musd) - ); - migrationAdapter = new VaultMigrationAdapter(adapterOwner, address(baseAdapter), address(premiumAdapter)); - - musd.mint(address(users.alice.deleGator), INITIAL_MUSD_BALANCE); - musd.mint(address(users.carol.deleGator), INITIAL_MUSD_BALANCE); - - vm.label(address(musd), "mUSD"); - vm.label(address(baseVault), "Base Vault"); - vm.label(address(premiumVault), "Premium Vault"); - vm.label(address(baseAdapter), "VedaAdapter"); - vm.label(address(premiumAdapter), "ComplianceVedaAdapter"); - vm.label(address(migrationAdapter), "VaultMigrationAdapter"); - } - - function test_constructor_revertsOnZeroAddresses() public { - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableInvalidOwner.selector, address(0))); - new VaultMigrationAdapter(address(0), address(baseAdapter), address(premiumAdapter)); - - vm.expectRevert(VaultMigrationAdapter.InvalidZeroAddress.selector); - new VaultMigrationAdapter(adapterOwner, address(0), address(premiumAdapter)); - - vm.expectRevert(VaultMigrationAdapter.InvalidZeroAddress.selector); - new VaultMigrationAdapter(adapterOwner, address(baseAdapter), address(0)); - } - - function test_migrateToPremium_movesSharesAndLeavesNoDust() public { - _depositToBase(users.alice, DEPOSIT_AMOUNT, 0); - - uint256 aliceMusdBefore_ = musd.balanceOf(address(users.alice.deleGator)); - uint256 baseShares_ = baseVault.balanceOf(address(users.alice.deleGator)); - assertEq(baseShares_, DEPOSIT_AMOUNT); - assertEq(aliceMusdBefore_, INITIAL_MUSD_BALANCE - DEPOSIT_AMOUNT); - - VaultMigrationAdapter.ToPremiumParams memory params_ = - _toPremiumParams(users.alice, baseShares_, DEPOSIT_AMOUNT, 1, DEPOSIT_AMOUNT, 2, block.timestamp + 30 minutes); - - vm.expectEmit(true, false, false, true, address(migrationAdapter)); - emit MigrationToPremiumExecuted(address(users.alice.deleGator), 0, 0); - vm.prank(address(users.bob.deleGator)); - migrationAdapter.migrateToPremiumByDelegation(params_); - - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0, "Base shares should be burned"); - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT, "Premium shares should be minted 1:1"); - assertEq(musd.balanceOf(address(users.alice.deleGator)), aliceMusdBefore_, "Underlying should settle back with Alice"); - _assertNoAdapterDust(); - assertEq(premiumTeller.lastComplianceDeadline(), block.timestamp + 30 minutes); - assertEq( - premiumTeller.lastComplianceSignatureHash(), keccak256(_signCompliance(DEPOSIT_AMOUNT, block.timestamp + 30 minutes)) - ); - } - - function test_migrateToBase_movesSharesAndLeavesNoDust() public { - _depositToPremium(users.alice, DEPOSIT_AMOUNT, 10, block.timestamp + 30 minutes); - - uint256 aliceMusdBefore_ = musd.balanceOf(address(users.alice.deleGator)); - uint256 premiumShares_ = premiumVault.balanceOf(address(users.alice.deleGator)); - assertEq(premiumShares_, DEPOSIT_AMOUNT); - - VaultMigrationAdapter.ToBaseParams memory params_ = _toBaseParams(users.alice, premiumShares_, 11, DEPOSIT_AMOUNT, 12); - - vm.expectEmit(true, false, false, true, address(migrationAdapter)); - emit MigrationToBaseExecuted(address(users.alice.deleGator), 0, 0); - vm.prank(address(users.bob.deleGator)); - migrationAdapter.migrateToBaseByDelegation(params_); - - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0, "Premium shares should be burned"); - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT, "Base shares should be minted 1:1"); - assertEq(musd.balanceOf(address(users.alice.deleGator)), aliceMusdBefore_, "Underlying should settle back with Alice"); - _assertNoAdapterDust(); - } - - function test_migrateToPremium_isPermissionless() public { - _depositToBase(users.alice, DEPOSIT_AMOUNT, 20); - uint256 baseShares_ = baseVault.balanceOf(address(users.alice.deleGator)); - - vm.prank(address(users.carol.deleGator)); - migrationAdapter.migrateToPremiumByDelegation( - _toPremiumParams(users.alice, baseShares_, DEPOSIT_AMOUNT, 21, DEPOSIT_AMOUNT, 22, block.timestamp + 30 minutes) - ); - - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0); - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT); - assertEq(premiumVault.balanceOf(address(users.carol.deleGator)), 0, "Caller must not receive shares"); - _assertNoAdapterDust(); - } - - function test_migrateToPremium_revertsBothLegsWhenDepositFails() public { - _depositToBase(users.alice, DEPOSIT_AMOUNT, 30); - uint256 baseShares_ = baseVault.balanceOf(address(users.alice.deleGator)); - uint256 aliceMusdBefore_ = musd.balanceOf(address(users.alice.deleGator)); - premiumTeller.setRevertDeposit(true); - VaultMigrationAdapter.ToPremiumParams memory params_ = - _toPremiumParams(users.alice, baseShares_, DEPOSIT_AMOUNT, 31, DEPOSIT_AMOUNT, 32, block.timestamp + 30 minutes); - - vm.prank(address(users.bob.deleGator)); - vm.expectRevert(MockVedaTeller.DepositFailed.selector); - migrationAdapter.migrateToPremiumByDelegation(params_); - - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), baseShares_, "Base shares must roll back"); - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0, "No premium shares should be minted"); - assertEq(musd.balanceOf(address(users.alice.deleGator)), aliceMusdBefore_, "Underlying must roll back"); - _assertNoAdapterDust(); - } - - function test_migrateToBase_revertsOnDelegatorMismatch() public { - _depositToPremium(users.alice, DEPOSIT_AMOUNT, 40, block.timestamp + 30 minutes); - uint256 aliceShares_ = premiumVault.balanceOf(address(users.alice.deleGator)); - - VaultMigrationAdapter.ToBaseParams memory params_ = VaultMigrationAdapter.ToBaseParams({ - withdrawalDelegations: _createDelegationChain( - users.alice, address(premiumAdapter), address(premiumVault), aliceShares_, 41 - ), - minimumAssets: 0, - depositDelegations: _createDelegationChain(users.carol, address(baseAdapter), address(musd), DEPOSIT_AMOUNT, 42), - minimumMint: 0 - }); - - vm.expectRevert(VaultMigrationAdapter.DelegatorMismatch.selector); - migrationAdapter.migrateToBaseByDelegation(params_); - - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), aliceShares_); - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0); - assertEq(baseVault.balanceOf(address(users.carol.deleGator)), 0); - } - - function test_migrateToPremium_revertsOnShortDelegationChain() public { - _depositToBase(users.alice, DEPOSIT_AMOUNT, 50); - VaultMigrationAdapter.ToPremiumParams memory params_ = _toPremiumParams( - users.alice, - baseVault.balanceOf(address(users.alice.deleGator)), - DEPOSIT_AMOUNT, - 51, - DEPOSIT_AMOUNT, - 52, - block.timestamp + 30 minutes - ); - params_.withdrawalDelegations = new Delegation[](1); - - vm.expectRevert(VaultMigrationAdapter.InvalidDelegationsLength.selector); - migrationAdapter.migrateToPremiumByDelegation(params_); - - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), DEPOSIT_AMOUNT); - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0); - } - - function test_migrateToPremiumBatch_movesExactAmountsForEachDelegator() public { - uint256 aliceAmount_ = 300e6; - uint256 carolAmount_ = 400e6; - _depositToBase(users.alice, aliceAmount_, 60); - _depositToBase(users.carol, carolAmount_, 61); - - VaultMigrationAdapter.ToPremiumParams[] memory params_ = new VaultMigrationAdapter.ToPremiumParams[](2); - params_[0] = _toPremiumParams(users.alice, aliceAmount_, aliceAmount_, 62, aliceAmount_, 63, block.timestamp + 20 minutes); - params_[1] = _toPremiumParams(users.carol, carolAmount_, carolAmount_, 64, carolAmount_, 65, block.timestamp + 21 minutes); - - vm.expectEmit(true, false, false, true, address(migrationAdapter)); - emit BatchMigrationToPremiumExecuted(address(users.bob.deleGator), 2); - vm.prank(address(users.bob.deleGator)); - migrationAdapter.migrateToPremiumByDelegationBatch(params_); - - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), 0); - assertEq(baseVault.balanceOf(address(users.carol.deleGator)), 0); - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), aliceAmount_); - assertEq(premiumVault.balanceOf(address(users.carol.deleGator)), carolAmount_); - assertEq(musd.balanceOf(address(users.alice.deleGator)), INITIAL_MUSD_BALANCE - aliceAmount_); - assertEq(musd.balanceOf(address(users.carol.deleGator)), INITIAL_MUSD_BALANCE - carolAmount_); - _assertNoAdapterDust(); - } - - function test_migrateToBaseBatch_movesExactAmountsForEachDelegator() public { - uint256 aliceAmount_ = 250e6; - uint256 carolAmount_ = 350e6; - _depositToPremium(users.alice, aliceAmount_, 70, block.timestamp + 20 minutes); - _depositToPremium(users.carol, carolAmount_, 71, block.timestamp + 21 minutes); - - VaultMigrationAdapter.ToBaseParams[] memory params_ = new VaultMigrationAdapter.ToBaseParams[](2); - params_[0] = _toBaseParams(users.alice, aliceAmount_, 72, aliceAmount_, 73); - params_[1] = _toBaseParams(users.carol, carolAmount_, 74, carolAmount_, 75); - - vm.expectEmit(true, false, false, true, address(migrationAdapter)); - emit BatchMigrationToBaseExecuted(address(users.bob.deleGator), 2); - vm.prank(address(users.bob.deleGator)); - migrationAdapter.migrateToBaseByDelegationBatch(params_); - - assertEq(premiumVault.balanceOf(address(users.alice.deleGator)), 0); - assertEq(premiumVault.balanceOf(address(users.carol.deleGator)), 0); - assertEq(baseVault.balanceOf(address(users.alice.deleGator)), aliceAmount_); - assertEq(baseVault.balanceOf(address(users.carol.deleGator)), carolAmount_); - _assertNoAdapterDust(); - } - - function test_emptyBatchesRevert() public { - VaultMigrationAdapter.ToPremiumParams[] memory premiumParams_ = new VaultMigrationAdapter.ToPremiumParams[](0); - VaultMigrationAdapter.ToBaseParams[] memory baseParams_ = new VaultMigrationAdapter.ToBaseParams[](0); - - vm.expectRevert(VaultMigrationAdapter.InvalidBatchLength.selector); - migrationAdapter.migrateToPremiumByDelegationBatch(premiumParams_); - - vm.expectRevert(VaultMigrationAdapter.InvalidBatchLength.selector); - migrationAdapter.migrateToBaseByDelegationBatch(baseParams_); - } - - function _depositToBase(TestUser memory _delegator, uint256 _amount, uint256 _salt) internal { - Delegation[] memory delegations_ = _createDelegationChain(_delegator, address(baseAdapter), address(musd), _amount, _salt); - - vm.prank(address(users.bob.deleGator)); - baseAdapter.depositByDelegation(delegations_, 0); - } - - function _depositToPremium(TestUser memory _delegator, uint256 _amount, uint256 _salt, uint256 _deadline) internal { - Delegation[] memory delegations_ = - _createDelegationChain(_delegator, address(premiumAdapter), address(musd), _amount, _salt); - IComplianceVedaTeller.ComplianceData memory compliance_ = _complianceData(_amount, _deadline); - - vm.prank(address(users.bob.deleGator)); - premiumAdapter.depositByDelegation(delegations_, 0, compliance_); - } - - function _toPremiumParams( - TestUser memory _delegator, - uint256 _shareAmount, - uint256 _assetAmount, - uint256 _withdrawalSalt, - uint256 _depositAmount, - uint256 _depositSalt, - uint256 _deadline - ) - internal - view - returns (VaultMigrationAdapter.ToPremiumParams memory) - { - return VaultMigrationAdapter.ToPremiumParams({ - withdrawalDelegations: _createDelegationChain( - _delegator, address(baseAdapter), address(baseVault), _shareAmount, _withdrawalSalt - ), - minimumAssets: 0, - depositDelegations: _createDelegationChain( - _delegator, address(premiumAdapter), address(musd), _depositAmount, _depositSalt - ), - minimumMint: 0, - compliance: _complianceData(_assetAmount, _deadline) - }); - } - - function _toBaseParams( - TestUser memory _delegator, - uint256 _shareAmount, - uint256 _withdrawalSalt, - uint256 _depositAmount, - uint256 _depositSalt - ) - internal - view - returns (VaultMigrationAdapter.ToBaseParams memory) - { - return VaultMigrationAdapter.ToBaseParams({ - withdrawalDelegations: _createDelegationChain( - _delegator, address(premiumAdapter), address(premiumVault), _shareAmount, _withdrawalSalt - ), - minimumAssets: 0, - depositDelegations: _createDelegationChain( - _delegator, address(baseAdapter), address(musd), _depositAmount, _depositSalt - ), - minimumMint: 0 - }); - } - - 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); - Delegation memory leaf_ = _createAdapterRedelegation(_adapter, EncoderLib._getDelegationHash(root_), _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 _complianceData( - uint256 _amount, - uint256 _deadline - ) - internal - view - returns (IComplianceVedaTeller.ComplianceData memory) - { - return IComplianceVedaTeller.ComplianceData({ deadline: _deadline, signature: _signCompliance(_amount, _deadline) }); - } - - function _signCompliance(uint256 _amount, uint256 _deadline) internal view returns (bytes memory) { - bytes32 messageHash_ = keccak256( - abi.encode( - address(premiumTeller), - block.chainid, - address(premiumAdapter), - address(users.alice.deleGator), - address(musd), - _amount, - _deadline - ) - ); - bytes32 ethSignedMessageHash_ = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)); - (uint8 v_, bytes32 r_, bytes32 s_) = vm.sign(COMPLIANCE_SIGNER_KEY, 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(migrationAdapter)), 0); - assertEq(baseVault.balanceOf(address(baseAdapter)), 0); - assertEq(premiumVault.balanceOf(address(premiumAdapter)), 0); - assertEq(baseVault.balanceOf(address(migrationAdapter)), 0); - assertEq(premiumVault.balanceOf(address(migrationAdapter)), 0); - } -} diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol new file mode 100644 index 00000000..cf489194 --- /dev/null +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -0,0 +1,1017 @@ +// 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 { 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 } 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); +} + +/// 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 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); + 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 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(ACCOUNTANT), "Base 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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(aliceShares_); + uint256 carolAssets_ = _quoteAssets(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_ = _quoteAssets(aliceShares_); + uint256 carolAssets_ = _quoteAssets(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_ = _quoteAssets(aliceShares_); + uint256 carolAssets_ = _quoteAssets(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_); + } + + function test_migrateToPremium_revertsOnExpiredCompliance() public { + uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 100); + vm.warp(block.timestamp + SHARE_LOCK_SECONDS); + uint256 quotedAssets_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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_ = _quoteAssets(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, shares_, 201, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.expectEmit(true, true, false, true, address(migrationHelper)); + emit PremiumTransferExecuted(from_, to_, shares_); + vm.prank(address(users.bob.deleGator)); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + + 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, shares_, 211, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.prank(address(users.carol.deleGator)); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + + 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, shares_, 221, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp - 1, COMPLIANCE_SIGNER_KEY); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + + 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, shares_, 231, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, 0xBAD); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + } + + 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, shares_, 241, address(migrationHelper)); + + vm.prank(address(users.bob.deleGator)); + migrationHelper.premiumTransfer(from_, to_, firstDelegations_, compliance_); + + 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, shares_, 243, address(migrationHelper)); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + migrationHelper.premiumTransfer(from_, to_, secondDelegations_, compliance_); + } + + 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, shares_, 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); + migrationHelper.premiumTransfer(address(users.carol.deleGator), to_, delegations_, compliance_); + } + + function test_premiumTransfer_revertsOnLeafAmountMismatch() 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, shares_ - 1, 261, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.expectRevert(VaultMigrationHelper.InvalidTransferAmount.selector); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + } + + 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); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + } + + 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, shares_, 281, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, address(0), shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.expectRevert(VaultMigrationHelper.InvalidZeroAddress.selector); + migrationHelper.premiumTransfer(from_, address(0), delegations_, compliance_); + } + + 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, shares_, 291, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.prank(PREMIUM_ROLES_AUTHORITY.owner()); + PREMIUM_ROLES_AUTHORITY.setUserRole(address(migrationHelper), TRANSFER_ALLOWED_ROLE, false); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + + 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, shares_, 301, to_); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert("AllowedCalldataEnforcer:invalid-calldata"); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + + 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, shares_, 311, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory depositCompliance_ = IComplianceVedaTeller.ComplianceData({ + deadline: deadline_, signature: _signCompliance(users.alice, shares_, deadline_, COMPLIANCE_SIGNER_KEY) + }); + + vm.prank(address(users.bob.deleGator)); + vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + migrationHelper.premiumTransfer(from_, to_, delegations_, depositCompliance_); + + 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, shares_, 321, address(migrationHelper)); + IComplianceVedaTeller.ComplianceData memory compliance_ = + _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + + 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); + migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + vm.clearMockedCalls(); + } + + 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 _quoteAssets(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 _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 _amount, + uint256 _salt, + address _calldataTo + ) + internal + view + returns (Delegation[] memory delegations_) + { + Caveat[] memory rootCaveats_ = new Caveat[](4); + rootCaveats_[0] = Caveat({ + args: hex"", + enforcer: address(erc20TransferAmountEnforcer), + terms: abi.encodePacked(address(PREMIUM_VAULT), type(uint256).max) + }); + rootCaveats_[1] = + Caveat({ args: hex"", enforcer: address(redeemerEnforcer), terms: abi.encodePacked(address(migrationHelper)) }); + rootCaveats_[2] = Caveat({ + args: hex"", enforcer: address(allowedCalldataEnforcer), terms: abi.encodePacked(uint256(4), abi.encode(_calldataTo)) + }); + rootCaveats_[3] = + 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[](1); + leafCaveats_[0] = Caveat({ + args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: abi.encodePacked(address(PREMIUM_VAULT), _amount) + }); + + 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 _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); + } +} From 6ae5ae06d3236f3e5365568e5432f961214824a7 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Thu, 24 Sep 2026 15:23:50 +0200 Subject: [PATCH 04/16] add batch premium transfer --- src/helpers/VaultMigrationHelper.sol | 96 ++++++++++++++++++++----- test/helpers/VaultMigrationHelper.t.sol | 68 ++++++++++++++++++ 2 files changed, 145 insertions(+), 19 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index de9fefe4..e1ca9cb3 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -65,6 +65,20 @@ contract VaultMigrationHelper is Ownable2Step { uint256 minimumMint; } + /** + * @notice Parameters for one compliance-gated premium share transfer. + * @param from Share owner and root delegator. + * @param to Compliance-approved recipient of the premium shares. + * @param delegations Chain sorted leaf to root. Leaf `delegate` must be this helper. + * @param compliance Backend-issued EIP-191 approval (`deadline` + `signature`). + */ + struct PremiumTransferParams { + address from; + address to; + Delegation[] delegations; + IComplianceVedaTeller.ComplianceData compliance; + } + ////////////////////////////// Events ////////////////////////////// /** @@ -105,6 +119,13 @@ contract VaultMigrationHelper is Ownable2Step { */ 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. @@ -124,7 +145,7 @@ contract VaultMigrationHelper is Ownable2Step { /// @dev Thrown when either delegation chain contains fewer than two delegations. error InvalidDelegationsLength(); - /// @dev Thrown when a migration batch is empty. + /// @dev Thrown when a batch is empty. error InvalidBatchLength(); /// @dev Thrown when the withdrawal and deposit chains have different root delegators. @@ -264,6 +285,61 @@ contract VaultMigrationHelper is Ownable2Step { IComplianceVedaTeller.ComplianceData calldata _compliance ) external + { + _premiumTransfer(_from, _to, _delegations, _compliance); + } + + /** + * @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 compliance + * signature because the digest includes `from`, `to`, amount, and deadline. + * @param _params Transfer parameters for each stream. + * @notice Security consideration: Callable by anyone. Every delegation chain 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_;) { + PremiumTransferParams calldata params_ = _params[i]; + _premiumTransfer(params_.from, params_.to, params_.delegations, params_.compliance); + 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 compliance-gated premium share transfer. + */ + function _premiumTransfer( + address _from, + address _to, + Delegation[] calldata _delegations, + IComplianceVedaTeller.ComplianceData calldata _compliance + ) + internal { if (_from == address(0) || _to == address(0)) revert InvalidZeroAddress(); @@ -295,24 +371,6 @@ contract VaultMigrationHelper is Ownable2Step { emit PremiumTransferExecuted(_from, _to, amount_); } - /** - * @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 base-to-premium migration. * @param _params Migration parameters forwarded to the base and premium adapters. diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index cf489194..16761913 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -77,6 +77,7 @@ contract VaultMigrationHelperTest is BaseTest { 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 { @@ -347,6 +348,10 @@ contract VaultMigrationHelperTest is BaseTest { 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 { @@ -640,6 +645,49 @@ contract VaultMigrationHelperTest is BaseTest { vm.clearMockedCalls(); } + 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.ComplianceCheckFailed.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); @@ -810,6 +858,26 @@ contract VaultMigrationHelperTest is BaseTest { }); } + function _premiumTransferParams( + TestUser memory _delegator, + uint256 _amount, + uint256 _salt, + address _to, + uint256 _deadline + ) + internal + view + returns (VaultMigrationHelper.PremiumTransferParams memory) + { + address from_ = address(_delegator.deleGator); + return VaultMigrationHelper.PremiumTransferParams({ + from: from_, + to: _to, + delegations: _createPremiumTransferChain(_delegator, _amount, _salt, address(migrationHelper)), + compliance: _transferComplianceData(from_, _to, _amount, _deadline, COMPLIANCE_SIGNER_KEY) + }); + } + function _createDelegationChain( TestUser memory _delegator, address _adapter, From 914377c82fbc54a3a16618ffb4df35d347095122 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Fri, 25 Sep 2026 15:11:17 +0200 Subject: [PATCH 05/16] add user signature for premium transfer --- src/helpers/VaultMigrationHelper.sol | 144 ++++++++++++++++-------- test/helpers/VaultMigrationHelper.t.sol | 125 +++++++++++++++++--- 2 files changed, 204 insertions(+), 65 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index e1ca9cb3..226af202 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -6,6 +6,7 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s 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"; @@ -18,12 +19,35 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; /** * @title VaultMigrationHelper * @notice Atomically composes the existing base and premium Veda adapters, and moves premium shares with - * a compliance signature. + * 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 chain must pin `transfer` `to` to this contract and name * this contract as redeemer. 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 this exact leaf 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. Amount remains the + * full premium balance at execution (leaf terms). First hop stays helper-only via caveats. + * + * 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[0].signature))`. + * Signed payload: `keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", inner))`. + * Verified with ERC-1271 on `from` via SignatureChecker. `delegations[0].signature` is already + * in calldata; binding it ties `to` to this leaf without hashing the full delegation. + * 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. + * * Leaf Caveat Format: * - For `premiumTransfer`, the first caveat of the leaf delegation (`_delegations[0].caveats[0]`) must * follow the ERC20TransferAmountEnforcer terms format: abi.encodePacked(address token, uint256 amount) @@ -66,17 +90,19 @@ contract VaultMigrationHelper is Ownable2Step { } /** - * @notice Parameters for one compliance-gated premium share transfer. + * @notice Parameters for one dual-signed premium share transfer. * @param from Share owner and root delegator. - * @param to Compliance-approved recipient of the premium shares. + * @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`). + * @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 ////////////////////////////// @@ -112,9 +138,9 @@ contract VaultMigrationHelper is Ownable2Step { event BatchMigrationToBaseExecuted(address indexed caller, uint256 count); /** - * @notice Emitted after a compliance-gated premium share transfer. + * @notice Emitted after a dual-signed premium share transfer. * @param from Share owner whose full premium balance was moved. - * @param to Compliance-approved recipient. + * @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); @@ -163,6 +189,9 @@ contract VaultMigrationHelper is Ownable2Step { /// @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(); + ////////////////////////////// State ////////////////////////////// /** @@ -262,48 +291,39 @@ contract VaultMigrationHelper is Ownable2Step { } /** - * @notice Moves `from`'s entire premium vault balance to `to` after a compliance signature check. + * @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 and must pin `transfer` `to` to this * helper. Production must grant this helper `transferAllowedRole` on the premium Teller. * The transfer amount is parsed from the first caveat of the leaf delegation - * (`_delegations[0].caveats[0].terms`), which must follow the ERC20TransferAmountEnforcer + * (`delegations[0].caveats[0].terms`), which must follow the ERC20TransferAmountEnforcer * format: abi.encodePacked(address token, uint256 amount). - * @param _from Share owner and root delegator. - * @param _to Compliance-approved recipient of the premium shares. - * @param _delegations Chain sorted leaf to root. Leaf `delegate` must be this helper. - * @param _compliance Backend-issued EIP-191 approval (`deadline` + `signature`). - * @notice Security consideration: Callable by anyone. The compliance signature is bound to this helper, - * the premium Teller, `from`, `to`, the premium vault, the full share balance, and the deadline. + * @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 this leaf; the compliance signature still includes `to` + * so only a premium-enabled recipient can receive. 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. * The redelegation MUST include an `ERC20TransferAmountEnforcer` as its first caveat (`caveats[0]`), * capped to exactly the owner's full premium balance. Reverts when Teller compliance is disabled. */ - function premiumTransfer( - address _from, - address _to, - Delegation[] calldata _delegations, - IComplianceVedaTeller.ComplianceData calldata _compliance - ) - external - { - _premiumTransfer(_from, _to, _delegations, _compliance); + 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 compliance - * signature because the digest includes `from`, `to`, amount, and deadline. + * @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`. * @param _params Transfer parameters for each stream. - * @notice Security consideration: Callable by anyone. Every delegation chain and compliance signature - * remains independently enforced. + * @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_;) { - PremiumTransferParams calldata params_ = _params[i]; - _premiumTransfer(params_.from, params_.to, params_.delegations, params_.compliance); + _premiumTransfer(_params[i]); unchecked { ++i; } @@ -331,32 +351,29 @@ contract VaultMigrationHelper is Ownable2Step { ////////////////////////////// Private/Internal Methods ////////////////////////////// /** - * @notice Executes one compliance-gated premium share transfer. + * @notice Executes one dual-signed premium share transfer. + * @dev User destination signature is checked before the backend compliance signature and before redeem. */ - function _premiumTransfer( - address _from, - address _to, - Delegation[] calldata _delegations, - IComplianceVedaTeller.ComplianceData calldata _compliance - ) - internal - { - if (_from == address(0) || _to == address(0)) revert InvalidZeroAddress(); + 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_ = _delegations.length; + uint256 length_ = _params.delegations.length; if (length_ < 2) revert InvalidDelegationsLength(); - if (_delegations[length_ - 1].delegator != _from) revert DelegatorMismatch(); + if (_params.delegations[length_ - 1].delegator != from_) revert DelegatorMismatch(); IERC20 vault_ = IERC20(premiumAdapter.boringVault()); - uint256 amount_ = vault_.balanceOf(_from); - if (amount_ == 0 || amount_ != _parseERC20TransferTerms(_delegations[0].caveats[0].terms)) { + uint256 amount_ = vault_.balanceOf(from_); + if (amount_ == 0 || amount_ != _parseERC20TransferTerms(_params.delegations[0].caveats[0].terms)) { revert InvalidTransferAmount(); } - _verifyTransferCompliance(_from, _to, address(vault_), amount_, _compliance); + _verifyUserTransferSignature(from_, to_, _params.delegations[0].signature, _params.userSignature); + _verifyTransferCompliance(from_, to_, address(vault_), amount_, _params.compliance); bytes[] memory permissionContexts_ = new bytes[](1); - permissionContexts_[0] = abi.encode(_delegations); + permissionContexts_[0] = abi.encode(_params.delegations); ModeCode[] memory encodedModes_ = new ModeCode[](1); encodedModes_[0] = ModeLib.encodeSimpleSingle(); @@ -366,9 +383,9 @@ contract VaultMigrationHelper is Ownable2Step { ExecutionLib.encodeSingle(address(vault_), 0, abi.encodeCall(IERC20.transfer, (address(this), amount_))); premiumAdapter.delegationManager().redeemDelegations(permissionContexts_, encodedModes_, executionCallDatas_); - vault_.safeTransfer(_to, amount_); + vault_.safeTransfer(to_, amount_); - emit PremiumTransferExecuted(_from, _to, amount_); + emit PremiumTransferExecuted(from_, to_, amount_); } /** @@ -442,10 +459,39 @@ contract VaultMigrationHelper is Ownable2Step { amount_ = uint256(bytes32(_terms[20:52])); } + /** + * @notice Verifies that `from` authorized this exact destination for this leaf. + * @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, leaf.signature))`. + * 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. Binding `_delegations[0].signature` (already in calldata) ties `to` to this + * leaf without hashing the full delegation on-chain. + */ + function _verifyUserTransferSignature( + address _from, + address _to, + bytes calldata _leafSignature, + bytes calldata _userSignature + ) + private + view + { + bytes32 messageHash_ = keccak256(abi.encode(address(this), block.chainid, _from, _to, _leafSignature)); + 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 The digest starts with this helper's address so it cannot equal a Teller deposit hash. - * Reverts if the Teller has disabled compliance (`complianceSignerRole == 255`). + * @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, diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index 16761913..33e9ac29 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -447,11 +447,12 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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)); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), 0); assertEq(PREMIUM_VAULT.balanceOf(to_), shares_); @@ -465,9 +466,10 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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)); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), 0); assertEq(PREMIUM_VAULT.balanceOf(to_), shares_); @@ -481,10 +483,11 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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.ComplianceCheckFailed.selector); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); } @@ -496,10 +499,11 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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.ComplianceCheckFailed.selector); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); } function test_premiumTransfer_revertsOnReplayedCompliance() public { @@ -510,9 +514,10 @@ contract VaultMigrationHelperTest is BaseTest { IComplianceVedaTeller.ComplianceData memory compliance_ = _transferComplianceData(from_, to_, shares_, deadline_, COMPLIANCE_SIGNER_KEY); Delegation[] memory firstDelegations_ = _createPremiumTransferChain(users.alice, shares_, 241, address(migrationHelper)); + bytes memory firstUserSignature_ = _userTransferSignature(users.alice, to_, firstDelegations_); vm.prank(address(users.bob.deleGator)); - migrationHelper.premiumTransfer(from_, to_, firstDelegations_, compliance_); + _callPremiumTransfer(from_, to_, firstDelegations_, compliance_, firstUserSignature_); vm.startPrank(PREMIUM_ROLES_AUTHORITY.owner()); PREMIUM_ROLES_AUTHORITY.setUserRole(to_, TRANSFER_ALLOWED_ROLE, true); @@ -520,10 +525,11 @@ contract VaultMigrationHelperTest is BaseTest { vm.prank(to_); PREMIUM_VAULT.transfer(from_, shares_); Delegation[] memory secondDelegations_ = _createPremiumTransferChain(users.alice, shares_, 243, address(migrationHelper)); + bytes memory secondUserSignature_ = _userTransferSignature(users.alice, to_, secondDelegations_); vm.prank(address(users.bob.deleGator)); vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); - migrationHelper.premiumTransfer(from_, to_, secondDelegations_, compliance_); + _callPremiumTransfer(from_, to_, secondDelegations_, compliance_, secondUserSignature_); } function test_premiumTransfer_revertsOnFromMismatch() public { @@ -535,7 +541,7 @@ contract VaultMigrationHelperTest is BaseTest { ); vm.expectRevert(VaultMigrationHelper.DelegatorMismatch.selector); - migrationHelper.premiumTransfer(address(users.carol.deleGator), to_, delegations_, compliance_); + _callPremiumTransfer(address(users.carol.deleGator), to_, delegations_, compliance_, hex""); } function test_premiumTransfer_revertsOnLeafAmountMismatch() public { @@ -547,7 +553,7 @@ contract VaultMigrationHelperTest is BaseTest { _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); vm.expectRevert(VaultMigrationHelper.InvalidTransferAmount.selector); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, hex""); } function test_premiumTransfer_revertsOnShortDelegationChain() public { @@ -559,7 +565,7 @@ contract VaultMigrationHelperTest is BaseTest { _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); vm.expectRevert(VaultMigrationHelper.InvalidDelegationsLength.selector); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, hex""); } function test_premiumTransfer_revertsOnZeroTo() public { @@ -570,7 +576,7 @@ contract VaultMigrationHelperTest is BaseTest { _transferComplianceData(from_, address(0), shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); vm.expectRevert(VaultMigrationHelper.InvalidZeroAddress.selector); - migrationHelper.premiumTransfer(from_, address(0), delegations_, compliance_); + _callPremiumTransfer(from_, address(0), delegations_, compliance_, hex""); } function test_premiumTransfer_revertsWhenHelperNotAllowlisted() public { @@ -580,13 +586,14 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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(); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); } @@ -598,10 +605,11 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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"); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); } @@ -615,10 +623,11 @@ contract VaultMigrationHelperTest is BaseTest { 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.ComplianceCheckFailed.selector); - migrationHelper.premiumTransfer(from_, to_, delegations_, depositCompliance_); + _callPremiumTransfer(from_, to_, delegations_, depositCompliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); } @@ -630,6 +639,7 @@ contract VaultMigrationHelperTest is BaseTest { Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 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), @@ -639,12 +649,60 @@ contract VaultMigrationHelperTest is BaseTest { vm.prank(address(users.bob.deleGator)); vm.expectRevert(VaultMigrationHelper.ComplianceDisabled.selector); - migrationHelper.premiumTransfer(from_, to_, delegations_, compliance_); + _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, shares_, 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_revertsOnUserSignatureForDifferentLeaf() 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, shares_, 361, address(migrationHelper)); + Delegation[] memory usedDelegations_ = _createPremiumTransferChain(users.alice, shares_, 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_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, shares_, 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.ComplianceCheckFailed.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); + } + 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); @@ -858,6 +916,22 @@ contract VaultMigrationHelperTest is BaseTest { }); } + 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, @@ -870,11 +944,13 @@ contract VaultMigrationHelperTest is BaseTest { returns (VaultMigrationHelper.PremiumTransferParams memory) { address from_ = address(_delegator.deleGator); + Delegation[] memory delegations_ = _createPremiumTransferChain(_delegator, _amount, _salt, address(migrationHelper)); return VaultMigrationHelper.PremiumTransferParams({ from: from_, to: _to, - delegations: _createPremiumTransferChain(_delegator, _amount, _salt, address(migrationHelper)), - compliance: _transferComplianceData(from_, _to, _amount, _deadline, COMPLIANCE_SIGNER_KEY) + delegations: delegations_, + compliance: _transferComplianceData(from_, _to, _amount, _deadline, COMPLIANCE_SIGNER_KEY), + userSignature: _userTransferSignature(_delegator, _to, delegations_) }); } @@ -1005,6 +1081,23 @@ contract VaultMigrationHelperTest is BaseTest { delegations_[1] = root_; } + 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[0].signature) + ); + 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, From 0844476ecf376e7846c8a27ee6ed996e93f6adcf Mon Sep 17 00:00:00 2001 From: MoMannn Date: Fri, 25 Sep 2026 15:28:57 +0200 Subject: [PATCH 06/16] add deploy command --- script/DeployVaultMigrationHelper.s.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/DeployVaultMigrationHelper.s.sol b/script/DeployVaultMigrationHelper.s.sol index a296fb24..17c5cc27 100644 --- a/script/DeployVaultMigrationHelper.s.sol +++ b/script/DeployVaultMigrationHelper.s.sol @@ -9,6 +9,9 @@ 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; From 9811b367e668a5961bab23c94654c37cdbc4fc06 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Mon, 5 Oct 2026 11:37:43 +0200 Subject: [PATCH 07/16] verify user signature agains user delegation --- src/helpers/VaultMigrationHelper.sol | 33 ++++++++++++++----------- test/helpers/VaultMigrationHelper.t.sol | 10 ++++++-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index 226af202..533588ab 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -27,7 +27,7 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * * 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 this exact leaf so a caller or backend cannot redirect the second hop. + * 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. @@ -39,10 +39,12 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * (`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[0].signature))`. + * `keccak256(abi.encode(address(this), block.chainid, from, to, delegations[length - 1].signature))`. * Signed payload: `keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", inner))`. - * Verified with ERC-1271 on `from` via SignatureChecker. `delegations[0].signature` is already - * in calldata; binding it ties `to` to this leaf without hashing the full delegation. + * Verified with ERC-1271 on `from` via SignatureChecker. The chain is leaf-to-root, so + * `delegations[length - 1].signature` is the root the share owner signed (the initial transfer + * validation), not the leaf redelegation. Binding that signature ties `to` to this root without + * hashing the full delegation. * 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 @@ -299,9 +301,10 @@ contract VaultMigrationHelper is Ownable2Step { * format: abi.encodePacked(address token, uint256 amount). * @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 this leaf; the compliance signature still includes `to` - * so only a premium-enabled recipient can receive. Delegations are EIP-712; `userSignature` and - * compliance are EIP-191 `personal_sign` over the exact inner hashes documented on the contract. + * binds `to` to this helper, chain, `from`, and the root delegation the user signed; the compliance + * signature still includes `to` so only a premium-enabled recipient can receive. 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. * The redelegation MUST include an `ERC20TransferAmountEnforcer` as its first caveat (`caveats[0]`), * capped to exactly the owner's full premium balance. Reverts when Teller compliance is disabled. @@ -369,7 +372,7 @@ contract VaultMigrationHelper is Ownable2Step { revert InvalidTransferAmount(); } - _verifyUserTransferSignature(from_, to_, _params.delegations[0].signature, _params.userSignature); + _verifyUserTransferSignature(from_, to_, _params.delegations[length_ - 1].signature, _params.userSignature); _verifyTransferCompliance(from_, to_, address(vault_), amount_, _params.compliance); bytes[] memory permissionContexts_ = new bytes[](1); @@ -460,24 +463,26 @@ contract VaultMigrationHelper is Ownable2Step { } /** - * @notice Verifies that `from` authorized this exact destination for this leaf. + * @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, leaf.signature))`. + * Inner: `keccak256(abi.encode(address(this), block.chainid, from, to, root.signature))`. * 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. Binding `_delegations[0].signature` (already in calldata) ties `to` to this - * leaf without hashing the full delegation on-chain. + * the user signed. The chain is sorted leaf to root, so the bound signature is + * `delegations[length - 1].signature`: the user's initial transfer validation, not the + * redelegation at `delegations[0]`. Binding that signature ties `to` to this root without + * hashing the full delegation on-chain. */ function _verifyUserTransferSignature( address _from, address _to, - bytes calldata _leafSignature, + bytes calldata _rootSignature, bytes calldata _userSignature ) private view { - bytes32 messageHash_ = keccak256(abi.encode(address(this), block.chainid, _from, _to, _leafSignature)); + bytes32 messageHash_ = keccak256(abi.encode(address(this), block.chainid, _from, _to, _rootSignature)); bytes32 ethSignedHash_ = MessageHashUtils.toEthSignedMessageHash(messageHash_); if (!SignatureChecker.isValidSignatureNow(_from, ethSignedHash_, _userSignature)) { revert InvalidUserSignature(); diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index 33e9ac29..b5c70bab 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -671,7 +671,7 @@ contract VaultMigrationHelperTest is BaseTest { assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); } - function test_premiumTransfer_revertsOnUserSignatureForDifferentLeaf() public { + 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); @@ -1091,7 +1091,13 @@ contract VaultMigrationHelperTest is BaseTest { returns (bytes memory) { bytes32 messageHash_ = keccak256( - abi.encode(address(migrationHelper), block.chainid, address(_delegator.deleGator), _to, _delegations[0].signature) + abi.encode( + address(migrationHelper), + block.chainid, + address(_delegator.deleGator), + _to, + _delegations[_delegations.length - 1].signature + ) ); 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. From 16962d4f1713e612fe504a9caceeda8a4cd76a24 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Mon, 5 Oct 2026 11:50:42 +0200 Subject: [PATCH 08/16] gas optimization via caveats --- src/helpers/VaultMigrationHelper.sol | 63 +++++++------------- test/helpers/VaultMigrationHelper.t.sol | 77 +++++++++++-------------- 2 files changed, 54 insertions(+), 86 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index 533588ab..46f6ac7c 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -21,9 +21,14 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * @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 chain must pin `transfer` `to` to this contract and name - * this contract as redeemer. 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`). + * 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 root does not use `ERC20TransferAmountEnforcer`. 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. @@ -31,8 +36,10 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * - 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. Amount remains the - * full premium balance at execution (leaf terms). First hop stays helper-only via caveats. + * 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. * * Signing schemes — wallets and CHOMP must implement both. They are not interchangeable: * 1. Delegation chain (root + leaf): EIP-712 typed data through DelegationManager @@ -49,14 +56,6 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * `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. - * - * Leaf Caveat Format: - * - For `premiumTransfer`, the first caveat of the leaf delegation (`_delegations[0].caveats[0]`) must - * follow the ERC20TransferAmountEnforcer terms format: abi.encodePacked(address token, uint256 amount) - * (52 bytes). This helper parses only the amount from these terms; the token address encoded in - * bytes 0–19 is consumed by the enforcer itself and is not read here. A delegation without this - * enforcer as the first caveat (or with an amount other than the owner's full premium balance) - * will revert. */ contract VaultMigrationHelper is Ownable2Step { using SafeERC20 for IERC20; @@ -179,12 +178,6 @@ contract VaultMigrationHelper is Ownable2Step { /// @dev Thrown when the withdrawal and deposit chains have different root delegators. error DelegatorMismatch(); - /// @dev Thrown when the leaf caveat terms are shorter than 52 bytes. - error InvalidTermsLength(); - - /// @dev Thrown when the premium share balance is zero or does not match the leaf caveat amount. - error InvalidTransferAmount(); - /// @dev Thrown when compliance signature verification fails. error ComplianceCheckFailed(); @@ -294,20 +287,18 @@ contract VaultMigrationHelper is Ownable2Step { /** * @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 and must pin `transfer` `to` to this - * helper. Production must grant this helper `transferAllowedRole` on the premium Teller. - * The transfer amount is parsed from the first caveat of the leaf delegation - * (`delegations[0].caveats[0].terms`), which must follow the ERC20TransferAmountEnforcer - * format: abi.encodePacked(address token, uint256 amount). + * @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. * @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 the user signed; the compliance - * signature still includes `to` so only a premium-enabled recipient can receive. Delegations are EIP-712; - * `userSignature` and compliance are EIP-191 `personal_sign` over the exact inner hashes documented - * on the contract. + * 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. - * The redelegation MUST include an `ERC20TransferAmountEnforcer` as its first caveat (`caveats[0]`), - * capped to exactly the owner's full premium balance. Reverts when Teller compliance is disabled. + * Reverts when Teller compliance is disabled. */ function premiumTransfer(PremiumTransferParams calldata _params) external { _premiumTransfer(_params); @@ -368,9 +359,6 @@ contract VaultMigrationHelper is Ownable2Step { IERC20 vault_ = IERC20(premiumAdapter.boringVault()); uint256 amount_ = vault_.balanceOf(from_); - if (amount_ == 0 || amount_ != _parseERC20TransferTerms(_params.delegations[0].caveats[0].terms)) { - revert InvalidTransferAmount(); - } _verifyUserTransferSignature(from_, to_, _params.delegations[length_ - 1].signature, _params.userSignature); _verifyTransferCompliance(from_, to_, address(vault_), amount_, _params.compliance); @@ -451,17 +439,6 @@ contract VaultMigrationHelper is Ownable2Step { if (delegator_ != _depositDelegations[depositLength_ - 1].delegator) revert DelegatorMismatch(); } - /** - * @notice Parses the transfer amount from ERC20TransferAmountEnforcer terms. - * @dev Terms format: abi.encodePacked(address token, uint256 amount) = 52 bytes. - * The token address (bytes 0–19) is validated by the enforcer itself and is not read here. - * Only the amount (bytes 20–51) is returned. - */ - function _parseERC20TransferTerms(bytes calldata _terms) private pure returns (uint256 amount_) { - if (_terms.length < 52) revert InvalidTermsLength(); - amount_ = uint256(bytes32(_terms[20:52])); - } - /** * @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. diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index b5c70bab..997b5d0d 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -444,7 +444,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 201, address(migrationHelper)); + 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_); @@ -463,7 +463,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 211, address(migrationHelper)); + 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_); @@ -480,7 +480,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 221, address(migrationHelper)); + 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_); @@ -496,7 +496,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 231, address(migrationHelper)); + 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_); @@ -513,7 +513,7 @@ contract VaultMigrationHelperTest is BaseTest { uint256 deadline_ = block.timestamp + 30 minutes; IComplianceVedaTeller.ComplianceData memory compliance_ = _transferComplianceData(from_, to_, shares_, deadline_, COMPLIANCE_SIGNER_KEY); - Delegation[] memory firstDelegations_ = _createPremiumTransferChain(users.alice, shares_, 241, address(migrationHelper)); + Delegation[] memory firstDelegations_ = _createPremiumTransferChain(users.alice, 241, address(migrationHelper)); bytes memory firstUserSignature_ = _userTransferSignature(users.alice, to_, firstDelegations_); vm.prank(address(users.bob.deleGator)); @@ -524,7 +524,7 @@ contract VaultMigrationHelperTest is BaseTest { vm.stopPrank(); vm.prank(to_); PREMIUM_VAULT.transfer(from_, shares_); - Delegation[] memory secondDelegations_ = _createPremiumTransferChain(users.alice, shares_, 243, address(migrationHelper)); + Delegation[] memory secondDelegations_ = _createPremiumTransferChain(users.alice, 243, address(migrationHelper)); bytes memory secondUserSignature_ = _userTransferSignature(users.alice, to_, secondDelegations_); vm.prank(address(users.bob.deleGator)); @@ -535,7 +535,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 251, address(migrationHelper)); + 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 ); @@ -544,16 +544,19 @@ contract VaultMigrationHelperTest is BaseTest { _callPremiumTransfer(address(users.carol.deleGator), to_, delegations_, compliance_, hex""); } - function test_premiumTransfer_revertsOnLeafAmountMismatch() public { + 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, shares_ - 1, 261, address(migrationHelper)); + Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, 261, address(migrationHelper)); IComplianceVedaTeller.ComplianceData memory compliance_ = - _transferComplianceData(from_, to_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + _transferComplianceData(from_, to_, shares_ - 1, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); + bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); - vm.expectRevert(VaultMigrationHelper.InvalidTransferAmount.selector); - _callPremiumTransfer(from_, to_, delegations_, compliance_, hex""); + vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); + + assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); } function test_premiumTransfer_revertsOnShortDelegationChain() public { @@ -571,7 +574,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 281, address(migrationHelper)); + 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); @@ -583,7 +586,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 291, address(migrationHelper)); + 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_); @@ -602,7 +605,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 301, to_); + 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_); @@ -619,7 +622,7 @@ contract VaultMigrationHelperTest is BaseTest { address from_ = address(users.alice.deleGator); address to_ = address(users.carol.deleGator); uint256 deadline_ = block.timestamp + 30 minutes; - Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 311, address(migrationHelper)); + 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) }); @@ -636,7 +639,7 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 321, address(migrationHelper)); + 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_); @@ -660,7 +663,7 @@ contract VaultMigrationHelperTest is BaseTest { address from_ = address(users.alice.deleGator); address to_ = address(users.carol.deleGator); address otherTo_ = makeAddr("otherPremiumRecipient"); - Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 351, address(migrationHelper)); + 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_); @@ -675,8 +678,8 @@ contract VaultMigrationHelperTest is BaseTest { 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, shares_, 361, address(migrationHelper)); - Delegation[] memory usedDelegations_ = _createPremiumTransferChain(users.alice, shares_, 362, address(migrationHelper)); + 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_); @@ -692,7 +695,7 @@ contract VaultMigrationHelperTest is BaseTest { address from_ = address(users.alice.deleGator); address to_ = address(users.carol.deleGator); address otherTo_ = makeAddr("unapprovedPremiumRecipient"); - Delegation[] memory delegations_ = _createPremiumTransferChain(users.alice, shares_, 371, address(migrationHelper)); + 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_); @@ -944,7 +947,7 @@ contract VaultMigrationHelperTest is BaseTest { returns (VaultMigrationHelper.PremiumTransferParams memory) { address from_ = address(_delegator.deleGator); - Delegation[] memory delegations_ = _createPremiumTransferChain(_delegator, _amount, _salt, address(migrationHelper)); + Delegation[] memory delegations_ = _createPremiumTransferChain(_delegator, _salt, address(migrationHelper)); return VaultMigrationHelper.PremiumTransferParams({ from: from_, to: _to, @@ -1027,28 +1030,20 @@ contract VaultMigrationHelperTest is BaseTest { return signDelegation(users.bob, delegation_); } - function _createPremiumTransferChain( - TestUser memory _delegator, - uint256 _amount, - uint256 _salt, - address _calldataTo - ) + function _createPremiumTransferChain(TestUser memory _delegator, uint256 _salt, address _calldataTo) internal view returns (Delegation[] memory delegations_) { - Caveat[] memory rootCaveats_ = new Caveat[](4); - rootCaveats_[0] = Caveat({ - args: hex"", - enforcer: address(erc20TransferAmountEnforcer), - terms: abi.encodePacked(address(PREMIUM_VAULT), type(uint256).max) - }); - rootCaveats_[1] = + Caveat[] memory rootCaveats_ = new Caveat[](3); + rootCaveats_[0] = Caveat({ args: hex"", enforcer: address(redeemerEnforcer), terms: abi.encodePacked(address(migrationHelper)) }); - rootCaveats_[2] = Caveat({ - args: hex"", enforcer: address(allowedCalldataEnforcer), terms: abi.encodePacked(uint256(4), abi.encode(_calldataTo)) + rootCaveats_[1] = Caveat({ + args: hex"", + enforcer: address(allowedCalldataEnforcer), + terms: abi.encodePacked(uint256(0), IERC20.transfer.selector, abi.encode(_calldataTo)) }); - rootCaveats_[3] = + rootCaveats_[2] = Caveat({ args: hex"", enforcer: address(allowedTargetsEnforcer), terms: abi.encodePacked(address(PREMIUM_VAULT)) }); Delegation memory root_ = Delegation({ @@ -1061,11 +1056,7 @@ contract VaultMigrationHelperTest is BaseTest { }); root_ = signDelegation(_delegator, root_); - Caveat[] memory leafCaveats_ = new Caveat[](1); - leafCaveats_[0] = Caveat({ - args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: abi.encodePacked(address(PREMIUM_VAULT), _amount) - }); - + Caveat[] memory leafCaveats_ = new Caveat[](0); Delegation memory leaf_ = Delegation({ delegate: address(migrationHelper), delegator: address(users.bob.deleGator), From 6c24a2b97444af8964b6857be3d0c38a14245760 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Mon, 5 Oct 2026 12:42:28 +0200 Subject: [PATCH 09/16] fix tests based on base and premium accountant --- test/helpers/VaultMigrationHelper.t.sol | 54 +++++++++++++++---------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index 997b5d0d..d963f96b 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -41,13 +41,14 @@ 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 ACCOUNTANT = IVedaAccountant(0x7382c5b8B51B8C4f127B3123C1039581BAA5A06B); + 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); @@ -111,7 +112,8 @@ contract VaultMigrationHelperTest is BaseTest { 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(ACCOUNTANT), "Base Accountant"); + 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"); @@ -164,7 +166,7 @@ contract VaultMigrationHelperTest is BaseTest { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 10); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + 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); @@ -182,7 +184,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToBase_movesSharesAndLeavesNoDust() public { uint256 premiumShares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 20, block.timestamp + 30 minutes); - uint256 quotedAssets_ = _quoteAssets(premiumShares_); + uint256 quotedAssets_ = _quotePremiumAssets(premiumShares_); uint256 musdBefore_ = MUSD.balanceOf(address(users.alice.deleGator)); VaultMigrationHelper.ToBaseParams memory params_ = _toBaseParams(users.alice, premiumShares_, 21, quotedAssets_, 22); @@ -200,7 +202,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_isPermissionless() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 30); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); vm.prank(address(users.carol.deleGator)); migrationHelper.migrateToPremiumByDelegation( @@ -216,7 +218,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsBothLegsWhenDepositFails() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 40); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + 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); @@ -234,7 +236,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToBase_revertsOnDelegatorMismatch() public { uint256 aliceShares_ = _depositToPremium(users.alice, DEPOSIT_AMOUNT, 50, block.timestamp + 30 minutes); - uint256 quotedAssets_ = _quoteAssets(aliceShares_); + uint256 quotedAssets_ = _quotePremiumAssets(aliceShares_); VaultMigrationHelper.ToBaseParams memory params_ = VaultMigrationHelper.ToBaseParams({ withdrawalDelegations: _createDelegationChain( @@ -256,7 +258,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsOnShortDelegationChain() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 60); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + 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); @@ -275,8 +277,8 @@ contract VaultMigrationHelperTest is BaseTest { uint256 carolShares_ = _depositToBase(users.carol, carolAmount_, 71); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 aliceAssets_ = _quoteAssets(aliceShares_); - uint256 carolAssets_ = _quoteAssets(carolShares_); + 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); @@ -299,8 +301,8 @@ contract VaultMigrationHelperTest is BaseTest { uint256 aliceShares_ = _depositToPremium(users.alice, aliceAmount_, 80, block.timestamp + 20 minutes); uint256 carolShares_ = _depositToPremium(users.carol, carolAmount_, 81, block.timestamp + 21 minutes); - uint256 aliceAssets_ = _quoteAssets(aliceShares_); - uint256 carolAssets_ = _quoteAssets(carolShares_); + 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); @@ -322,8 +324,8 @@ contract VaultMigrationHelperTest is BaseTest { uint256 carolShares_ = _depositToBase(users.carol, 400e6, 91); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 aliceAssets_ = _quoteAssets(aliceShares_); - uint256 carolAssets_ = _quoteAssets(carolShares_); + 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); @@ -357,7 +359,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsOnExpiredCompliance() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 100); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); VaultMigrationHelper.ToPremiumParams memory params_ = _toPremiumParams(users.alice, baseShares_, quotedAssets_, 101, quotedAssets_, 102, block.timestamp - 1); @@ -372,7 +374,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsOnWrongComplianceSigner() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 110); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); VaultMigrationHelper.ToPremiumParams memory params_ = _toPremiumParams(users.alice, baseShares_, quotedAssets_, 111, quotedAssets_, 112, block.timestamp + 30 minutes); params_.compliance = IComplianceVedaTeller.ComplianceData({ @@ -390,7 +392,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsOnReplayedCompliance() public { uint256 firstShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 120); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 firstAssets_ = _quoteAssets(firstShares_); + uint256 firstAssets_ = _quoteBaseAssets(firstShares_); uint256 deadline_ = block.timestamp + 30 minutes; VaultMigrationHelper.ToPremiumParams memory firstParams_ = _toPremiumParams(users.alice, firstShares_, firstAssets_, 121, firstAssets_, 122, deadline_); @@ -400,7 +402,7 @@ contract VaultMigrationHelperTest is BaseTest { uint256 secondShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 123); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 secondAssets_ = _quoteAssets(secondShares_); + uint256 secondAssets_ = _quoteBaseAssets(secondShares_); VaultMigrationHelper.ToPremiumParams memory replayParams_ = _toPremiumParams(users.alice, secondShares_, secondAssets_, 124, secondAssets_, 125, deadline_ + 1 hours); replayParams_.compliance = firstParams_.compliance; @@ -413,7 +415,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsOnWithdrawSlippage() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 130); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + 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; @@ -428,7 +430,7 @@ contract VaultMigrationHelperTest is BaseTest { function test_migrateToPremium_revertsOnDelegationReplay() public { uint256 baseShares_ = _depositToBase(users.alice, DEPOSIT_AMOUNT, 140); vm.warp(block.timestamp + SHARE_LOCK_SECONDS); - uint256 quotedAssets_ = _quoteAssets(baseShares_); + uint256 quotedAssets_ = _quoteBaseAssets(baseShares_); VaultMigrationHelper.ToPremiumParams memory params_ = _toPremiumParams(users.alice, baseShares_, quotedAssets_, 141, quotedAssets_, 142, block.timestamp + 30 minutes); @@ -813,8 +815,16 @@ contract VaultMigrationHelperTest is BaseTest { require(MUSD.transfer(address(_user.deleGator), INITIAL_MUSD_BALANCE), "mUSD funding failed"); } - function _quoteAssets(uint256 _shares) internal view returns (uint256) { - return _shares * ACCOUNTANT.getRateInQuoteSafe(address(MUSD)) / (10 ** ACCOUNTANT.decimals()); + 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_) { From 61c8a428465505ed7d6652474637000c82a45e51 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Tue, 13 Oct 2026 16:18:46 +0200 Subject: [PATCH 10/16] make sure shares are actually transfered --- src/helpers/VaultMigrationHelper.sol | 8 +++ test/helpers/VaultMigrationHelper.t.sol | 92 ++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index 46f6ac7c..6ac7313e 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -187,6 +187,9 @@ contract VaultMigrationHelper is Ownable2Step { /// @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 ////////////////////////////// /** @@ -347,6 +350,9 @@ contract VaultMigrationHelper is Ownable2Step { /** * @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`. */ function _premiumTransfer(PremiumTransferParams calldata _params) internal { address from_ = _params.from; @@ -373,7 +379,9 @@ contract VaultMigrationHelper is Ownable2Step { 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_); diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index d963f96b..d4c837fc 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -15,7 +15,7 @@ import { ERC20TransferAmountEnforcer } from "../../src/enforcers/ERC20TransferAm import { RedeemerEnforcer } from "../../src/enforcers/RedeemerEnforcer.sol"; import { AllowedCalldataEnforcer } from "../../src/enforcers/AllowedCalldataEnforcer.sol"; import { AllowedTargetsEnforcer } from "../../src/enforcers/AllowedTargetsEnforcer.sol"; -import { Delegation, Caveat } from "../../src/utils/Types.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"; @@ -36,6 +36,17 @@ interface IVedaAccountant { 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); + } +} + /// forge-config: default.evm_version = "cancun" contract VaultMigrationHelperTest is BaseTest { IERC20 internal constant MUSD = IERC20(0xacA92E438df0B2401fF60dA7E4337B687a2435DA); @@ -708,6 +719,37 @@ contract VaultMigrationHelperTest is BaseTest { 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); @@ -967,6 +1009,48 @@ contract VaultMigrationHelperTest is BaseTest { }); } + 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, @@ -1040,7 +1124,11 @@ contract VaultMigrationHelperTest is BaseTest { return signDelegation(users.bob, delegation_); } - function _createPremiumTransferChain(TestUser memory _delegator, uint256 _salt, address _calldataTo) + function _createPremiumTransferChain( + TestUser memory _delegator, + uint256 _salt, + address _calldataTo + ) internal view returns (Delegation[] memory delegations_) From b3012f50a02e00ffc5895bd9c109063ac22488a1 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Wed, 14 Oct 2026 10:58:40 +0200 Subject: [PATCH 11/16] document getPrincipalHistoryPaginated --- src/helpers/VaultMigrationHelper.sol | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index 6ac7313e..095422d1 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -40,6 +40,11 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * 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 @@ -294,6 +299,11 @@ contract VaultMigrationHelper is Ownable2Step { * 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 the user signed; the compliance @@ -311,6 +321,11 @@ contract VaultMigrationHelper is Ownable2Step { * @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. @@ -353,6 +368,11 @@ contract VaultMigrationHelper is Ownable2Step { * 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; From 8b18421f00e62b3c4d35580ad168da1eaec84d43 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Wed, 14 Oct 2026 11:06:37 +0200 Subject: [PATCH 12/16] use delegationHash instead of signature --- src/helpers/VaultMigrationHelper.sol | 30 ++++--- test/helpers/VaultMigrationHelper.t.sol | 101 +++++++++++++++++++++++- 2 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index 095422d1..7ed54cf5 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -51,12 +51,13 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * (`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 - 1].signature))`. + * `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 - 1].signature` is the root the share owner signed (the initial transfer - * validation), not the leaf redelegation. Binding that signature ties `to` to this root without - * hashing the full delegation. + * `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 @@ -306,7 +307,8 @@ contract VaultMigrationHelper is Ownable2Step { * 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 the user signed; the compliance + * 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. @@ -386,7 +388,8 @@ contract VaultMigrationHelper is Ownable2Step { IERC20 vault_ = IERC20(premiumAdapter.boringVault()); uint256 amount_ = vault_.balanceOf(from_); - _verifyUserTransferSignature(from_, to_, _params.delegations[length_ - 1].signature, _params.userSignature); + // 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); @@ -470,24 +473,25 @@ contract VaultMigrationHelper is Ownable2Step { /** * @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, root.signature))`. + * 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 signature is - * `delegations[length - 1].signature`: the user's initial transfer validation, not the - * redelegation at `delegations[0]`. Binding that signature ties `to` to this root without - * hashing the full delegation on-chain. + * 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, - bytes calldata _rootSignature, + bytes32 _rootDelegationHash, bytes calldata _userSignature ) private view { - bytes32 messageHash_ = keccak256(abi.encode(address(this), block.chainid, _from, _to, _rootSignature)); + 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(); diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index d4c837fc..72f15813 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -3,6 +3,7 @@ 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"; @@ -47,6 +48,24 @@ contract NoOpAccount { } } +/// @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); @@ -703,6 +722,37 @@ contract VaultMigrationHelperTest is BaseTest { 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); @@ -1170,6 +1220,55 @@ contract VaultMigrationHelperTest is BaseTest { 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, @@ -1185,7 +1284,7 @@ contract VaultMigrationHelperTest is BaseTest { block.chainid, address(_delegator.deleGator), _to, - _delegations[_delegations.length - 1].signature + _delegations[_delegations.length - 2].authority ) ); bytes32 ethSignedMessageHash_ = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_)); From 96e4012d1e18420fd7ed1f0976f529e5421619f7 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Wed, 14 Oct 2026 11:08:44 +0200 Subject: [PATCH 13/16] use distinct errors --- src/helpers/VaultMigrationHelper.sol | 21 ++++++++++---- test/helpers/VaultMigrationHelper.t.sol | 37 ++++++++++++++++++++----- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index 7ed54cf5..fed85a93 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -184,8 +184,17 @@ contract VaultMigrationHelper is Ownable2Step { /// @dev Thrown when the withdrawal and deposit chains have different root delegators. error DelegatorMismatch(); - /// @dev Thrown when compliance signature verification fails. - error ComplianceCheckFailed(); + /// @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(); @@ -523,18 +532,18 @@ contract VaultMigrationHelper is Ownable2Step { bytes32 messageHash_ = keccak256( abi.encode(address(this), address(teller_), block.chainid, _from, _to, _vault, _amount, _compliance.deadline) ); - if (usedComplianceSignatures[messageHash_]) revert ComplianceCheckFailed(); - if (block.timestamp > _compliance.deadline) revert ComplianceCheckFailed(); + 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 ComplianceCheckFailed(); + revert ComplianceDeadlineExceedsWindow(); } bytes32 ethSignedHash_ = MessageHashUtils.toEthSignedMessageHash(messageHash_); address recovered_ = ECDSA.recover(ethSignedHash_, _compliance.signature); if (!IRolesAuthority(teller_.authority()).doesUserHaveRole(recovered_, complianceSignerRole_)) { - revert ComplianceCheckFailed(); + revert InvalidComplianceSigner(); } usedComplianceSignatures[messageHash_] = true; diff --git a/test/helpers/VaultMigrationHelper.t.sol b/test/helpers/VaultMigrationHelper.t.sol index 72f15813..3f43e38a 100644 --- a/test/helpers/VaultMigrationHelper.t.sol +++ b/test/helpers/VaultMigrationHelper.t.sol @@ -518,12 +518,35 @@ contract VaultMigrationHelperTest is BaseTest { bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); vm.prank(address(users.bob.deleGator)); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + 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); @@ -534,7 +557,7 @@ contract VaultMigrationHelperTest is BaseTest { bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); vm.prank(address(users.bob.deleGator)); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); } @@ -560,7 +583,7 @@ contract VaultMigrationHelperTest is BaseTest { bytes memory secondUserSignature_ = _userTransferSignature(users.alice, to_, secondDelegations_); vm.prank(address(users.bob.deleGator)); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + vm.expectRevert(VaultMigrationHelper.ComplianceSignatureAlreadyUsed.selector); _callPremiumTransfer(from_, to_, secondDelegations_, compliance_, secondUserSignature_); } @@ -585,7 +608,7 @@ contract VaultMigrationHelperTest is BaseTest { _transferComplianceData(from_, to_, shares_ - 1, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); @@ -661,7 +684,7 @@ contract VaultMigrationHelperTest is BaseTest { bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); vm.prank(address(users.bob.deleGator)); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); _callPremiumTransfer(from_, to_, delegations_, depositCompliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); @@ -763,7 +786,7 @@ contract VaultMigrationHelperTest is BaseTest { _transferComplianceData(from_, otherTo_, shares_, block.timestamp + 30 minutes, COMPLIANCE_SIGNER_KEY); bytes memory userSignature_ = _userTransferSignature(users.alice, to_, delegations_); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + vm.expectRevert(VaultMigrationHelper.InvalidComplianceSigner.selector); _callPremiumTransfer(from_, to_, delegations_, compliance_, userSignature_); assertEq(PREMIUM_VAULT.balanceOf(from_), shares_); @@ -834,7 +857,7 @@ contract VaultMigrationHelperTest is BaseTest { params_[1] = _premiumTransferParams(users.carol, carolShares_, 343, recipient_, block.timestamp - 1); vm.prank(address(users.bob.deleGator)); - vm.expectRevert(VaultMigrationHelper.ComplianceCheckFailed.selector); + vm.expectRevert(VaultMigrationHelper.ComplianceSignatureExpired.selector); migrationHelper.premiumTransferBatch(params_); assertEq(PREMIUM_VAULT.balanceOf(alice_), aliceShares_); From 43c721bb15130836a89d2ea492f4dc25b326c98c Mon Sep 17 00:00:00 2001 From: MoMannn Date: Wed, 14 Oct 2026 11:13:32 +0200 Subject: [PATCH 14/16] update natspec --- src/helpers/VaultMigrationHelper.sol | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/helpers/VaultMigrationHelper.sol b/src/helpers/VaultMigrationHelper.sol index fed85a93..98fffdff 100644 --- a/src/helpers/VaultMigrationHelper.sol +++ b/src/helpers/VaultMigrationHelper.sol @@ -27,7 +27,9 @@ import { Delegation, ModeCode } from "../utils/Types.sol"; * `transfer` to this helper and the amount stays unset on the root; * - name the premium vault in `AllowedTargetsEnforcer`; * - name this contract in `RedeemerEnforcer`. - * The root does not use `ERC20TransferAmountEnforcer`. This helper must hold `transferAllowedRole` on the + * 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: @@ -69,8 +71,12 @@ contract VaultMigrationHelper is Ownable2Step { /** * @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. */ @@ -85,8 +91,11 @@ contract VaultMigrationHelper is Ownable2Step { /** * @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 { From 5edf9d8ac42812a22f6ccaa956509b1755836633 Mon Sep 17 00:00:00 2001 From: MoMannn Date: Thu, 15 Oct 2026 10:28:06 +0200 Subject: [PATCH 15/16] add audit report --- audits/cyfrin/cyfrin-10-26.pdf | Bin 0 -> 278826 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 audits/cyfrin/cyfrin-10-26.pdf diff --git a/audits/cyfrin/cyfrin-10-26.pdf b/audits/cyfrin/cyfrin-10-26.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3da3db852fa4babbc5f333c5325d33b4e6c182bc GIT binary patch literal 278826 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

Z5$nX^G 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�xw#BOX;qs}Eq4Rk>@8*qTe*xul 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

P5C?-9*7L+yNP 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

^DVUq#??d`~P^w(!6dQAK9g+AdsTP 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 V99q%2`1pAFf#~$~QYzBu{{a^A>(&4O literal 0 HcmV?d00001 From 0d547e61c81511352757188bf16f8c0ce083d7bd Mon Sep 17 00:00:00 2001 From: MoMannn Date: Fri, 16 Oct 2026 11:08:04 +0200 Subject: [PATCH 16/16] add deployment --- .../143/run-1792141383544.json | 68 +++++++++++++++++++ .../143/run-latest.json | 68 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 broadcast/DeployVaultMigrationHelper.s.sol/143/run-1792141383544.json create mode 100644 broadcast/DeployVaultMigrationHelper.s.sol/143/run-latest.json 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