Sonic Blaze Testnet

Contract Diff Checker

Contract Name:
Wallet

Contract Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IConnection} from "../connection/IConnection.sol";
import {IWalletFactory} from "./IWalletFactory.sol";
import {IWallet} from "./IWallet.sol";

/// @title Wallet Contract
/// @notice A wallet contract that can execute arbitrary contract calls based on verified messages.
/// @dev This contract interacts with a factory and connection for verification and address mapping.
contract Wallet is IWallet {
    /// @notice Factory contract for wallet creation and management.
    IWalletFactory public immutable factory;

    /// @notice Connection contract for cross-chain message verification.
    IConnection public immutable connection;

    /// @notice The address of the Asset Manager allowed to invoke calls on behalf of a user on token transfers.
    address public immutable assetManager;
    /// @notice The address of the XToken Manager allowed to invoke calls on behalf of a user on token transfers.
    address public immutable xTokenManager;

    event CallStored(bytes32 indexed callHash);

    mapping(bytes32 => bool) public storedCalls;

    /// @notice Initializes the wallet contract.
    /// @param _factory The address of the wallet factory contract.
    /// @param _connection The address of the connection contract for message verification.
    /// @param _assetManager The address of the asset manager contract.
    constructor(IWalletFactory _factory, IConnection _connection, address _assetManager, address _xTokenManager) {
        require(address(_factory) != address(0), "Invalid factory address");
        require(address(_connection) != address(0), "Invalid connection address");
        require(_assetManager != address(0), "Invalid asset manager address");
        require(_xTokenManager != address(0), "Invalid xToken manager address");

        factory = _factory;
        connection = _connection;
        assetManager = _assetManager;
        xTokenManager = _xTokenManager;
    }

    /// @notice Receives and processes a verified cross-chain message.
    /// @param srcChainId The chain ID of the originating chain.
    /// @param srcAddress The address of the sender on the originating chain.
    /// @param _connSn The unique identifier for the message.
    /// @param _payload The encoded payload containing call data.
    /// @param signatures An array of signatures for verifying the message.
    function recvMessage(
        uint256 srcChainId,
        bytes calldata srcAddress,
        uint256 _connSn,
        bytes memory _payload,
        bytes[] calldata signatures
    ) external override {
        // Verify the message using the connection contract
        connection.verifyMessage(srcChainId, srcAddress, _connSn, _payload, signatures);

        // Ensure the caller address matches the expected wallet address from the factory
        require(
            address(this) == factory.getWallet(srcChainId, srcAddress),
            "Mismatched address and caller"
        );

        // Execute the calls described in the payload
        try this.executeCalls(_payload) {} catch (bytes memory) {}
    }

    /// @notice Allows the asset manager to execute calls on behalf of the wallet.
    /// @param data The encoded data containing an array of ContractCall structs.
    function assetManagerHook(bytes memory data) external override {
        // Restrict access to the asset manager
        require(msg.sender == assetManager, "Only AssetManager is allowed");

        // Execute the calls described in the data
        this.executeCalls(data);
    }

    /// @notice Allows the xToken manager to execute calls on behalf of the wallet.
    /// @param data The encoded data containing an array of ContractCall structs.
    function xTokenManagerHook(bytes memory data) external override {
        // Restrict access to the asset manager
        require(msg.sender == xTokenManager, "Only XTokenManager is allowed");

        // Execute the calls described in the data
        this.executeCalls(data);
    }

    /// @notice Executes multiple contract calls described in the input data.
    /// @dev Decodes the input data into an array of ContractCall structs and executes them sequentially.
    /// @param data The encoded data containing an array of ContractCall structs.
    function executeCalls(bytes memory data) external {
        require(msg.sender == address(this), "Only this contract can execute calls");
        if (data.length == 32) {
            bytes32 callHash = bytesToBytes32(data);
            storedCalls[callHash] = true;
            emit CallStored(callHash);
            return;
        }

        // Decode the input data into an array of ContractCall structs
        ContractCall[] memory contractCalls = abi.decode(data, (ContractCall[]));

        // Iterate over the array and execute each call
        for (uint256 i = 0; i < contractCalls.length; i++) {
            executeInner(contractCalls[i].addr, contractCalls[i].value, contractCalls[i].data);
        }
    }


    function executeStored(bytes memory calls) external {
        require(storedCalls[keccak256(calls)], "Calls do not match stored calls");
        try this.executeCalls(calls) {} catch (bytes memory) {}
        delete storedCalls[keccak256(calls)];
    }

    /// @notice Performs a single arbitrary call to a specified target.
    /// @param target The address of the contract or account to call.
    /// @param value The amount of Ether to send with the call.
    /// @param data The calldata to send with the call.
    function executeInner(address target, uint256 value, bytes memory data) internal {
        // Perform the call and check the result
        (bool success, ) = target.call{value: value}(data);
        require(success, "External call failed");
    }

    /// @notice Simulates recvMessage without signature verification (view function)
    /// @dev This function will always revert at the end to prevent actual state changes
    /// @param srcChainId The chain ID of the originating chain
    /// @param srcAddress The address of the sender on the originating chain
    /// @param _payload The encoded payload containing call data
    function simulateRecvMessage(
        uint256 srcChainId,
        bytes calldata srcAddress,
        bytes memory _payload
    ) external {
        // Ensure the caller address matches the expected wallet address from the factory
        require(
            address(this) == factory.getWallet(srcChainId, srcAddress),
            "Mismatched address and caller"
        );

        // Execute the calls described in the payload
        this.executeCalls(_payload);

        // Always revert to prevent state changes
        revert("Simulation completed");
    }

    function bytesToBytes32(bytes memory source) private pure returns (bytes32 result) {
        if (source.length == 0) {
            return 0x0;
        }
        assembly {
            result := mload(add(source, 32))
        }
    }

}

// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;

interface IConnection {
    function sendMessage(
        uint256 dstChainId,
        bytes memory dstAddress,
        bytes memory payload
    ) external payable;

    function verifyMessage(
        uint256 srcChainId,
        bytes calldata srcAddress,
        uint256 connSn,
        bytes memory payload,
        bytes[] calldata signatures
    ) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWalletFactory {
    // Public state variables
    function implementation() external view returns (address);
    function owner() external view returns (address);

    // Events
    event Deployed(address indexed deployedAddress, bytes32 indexed salt);

    // Function signatures
    /// @notice Updates the implementation contract address
    /// @param _newImplementation The address of the new implementation contract
    function updateImplementation(address _newImplementation) external;

    /// @notice Transfers ownership to a new address
    /// @param _newOwner The address of the new owner
    function transferOwnership(address _newOwner) external;

    /// @notice Derive the address of a contract deployed with CREATE3
    /// @param chainId chainId of address
    /// @param user User's address on the specified chain
    /// @return computedAddress The derived contract address
    function getWallet(uint256 chainId, bytes calldata user) external returns (address computedAddress);

    /// @notice Deploy a contract deterministically with CREATE3
    /// @param salt Unique salt to differentiate deployments
    /// @return deployedAddress Address of the deployed contract
    function deploy(bytes32 salt) external returns (address deployedAddress);

    /// @notice Derive the address of a contract deployed with CREATE3
    /// @param chainId chainId of address
    /// @param user User's address on the specified chain
    /// @return computedAddress The derived contract address
    function getDeployedAddress(uint256 chainId, bytes calldata user) external view returns (address computedAddress);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title IWallet Interface
/// @notice Interface for the Wallet contract.
interface IWallet {
    /// @notice Represents a contract call to be executed.
    struct ContractCall {
        address addr;     // Target address of the call
        uint256 value;    // Ether value to send
        bytes data;       // Calldata for the call
    }

    /// @notice Receives and processes a verified cross-chain message.
    /// @param srcChainId The chain ID of the originating chain.
    /// @param srcAddress The address of the sender on the originating chain.
    /// @param _connSn The unique identifier for the message.
    /// @param _payload The encoded payload containing call data.
    /// @param signatures An array of signatures for verifying the message.
    function recvMessage(
        uint256 srcChainId,
        bytes calldata srcAddress,
        uint256 _connSn,
        bytes memory _payload,
        bytes[] calldata signatures
    ) external;

    /// @notice Allows the asset manager to execute calls on behalf of the wallet.
    /// @param data The encoded data containing an array of ContractCall structs.
    function assetManagerHook(bytes memory data) external;

    /// @notice Allows the asset manager to execute calls on behalf of the wallet.
    /// @param data The encoded data containing an array of ContractCall structs.
    function xTokenManagerHook(bytes memory data) external;
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):