LIP-73: Confluence - Arbitrum One Migration

A few updates I’m planning on making to the current proposal:

  • Support specifying an amount of stake to migrate in the L1 Migrator instead of migrating all stake. This feature can be useful for users that have a large amount of stake and that wish to take precautions while migrating stake to L2 by starting with a smaller amount. An app that is integrated with the L1 Migrator can still set the default amount to an account’s entire stake while also giving the user the option to change the amount.
  • Support authorizing migrations using EIP-712 signatures as a replacement for the opt-out mechanism that is described in the current proposal. This feature makes it easier for orchestrators to use a web UI (i.e. the explorer) for migration without exporting/importing their local keystore files (which store their encrypted private key) since they can sign a message within the CLI and provide the signature in the web UI. A user could either submit a migrate tx directly using their delegator account or they could sign a message with a deadline (the last time at which the signature can be submitted on-chain) and any address could then submit the signature on-chain.

Find below a sketch of the code to support these features which is pretty similar to the OpenZeppelin EIP-2612 Permit minus a few tweaks:

struct MigrateMsg {
    // Address of delegator to migrate stake
    address srcDelegator;
    // Address of delegator to receive migrated stake
    address dstDelegator;
    // Address of orchestrator to delegate migrated stake to
    address dstOrchestrator;

    // Hints for updating the position of dstOrchestrator in the pool
    address dstOrchestratorNewPosPrev;
    address dstOrchestratorNewPosNext;

    // Amount of stake to migrate
    uint256 amount;
    // Deadline for signature to be used for migration
    // Optional: Only required if sig is set
    uint256 deadline; 
    // Signature to authorize migration
    // Optional: Only required if msg.sender != srcDelegator
    bytes sig;
}

mapping (address => uint256) public nonces;

function migrate(MigrateMsg memory _msg) external {
    if (_msg.sig != bytes(0)) {
        // Use nonce to generate MigrateMsg EIP-712 hash
        
        nonces[_srcDelegator] += 1;
        
        // Recover signer from signature
        
        require(
            block.timestamp <= _msg.deadline,
            "expired deadline"
        );
        require(
            signer != _msg.srcDelegator,
            "invalid signer"
        );
    } else {
        require(
            msg.sender != _msg.srcDelegator,
            "invalid msg.sender"
        );
    }
    
    bondingManager.migrateStake(_srcDelegator, _msg.amount);
    
    token.approve(l1Gateway, _msg.amount);
    
    // Call gateway
}

Another realization I had while considering the implications of supporting specifying an amount of stake to migrate is that if the delegator address on L2 already has a delegate then a migration should not modify that delegate. Otherwise, an address on L1 could modify the delegate of another address on L2 during a migration. In practice, we can just ignore the dstOrchestrator argument of a migration message in the L2 Migrator if the dstDelegator already has a delegate in the L2 BondingManager.

Updated the LIP in this PR.

2 Likes