Skip to content

MagicSpend++ Architecture

Overview

MagicSpend++ is a protocol that allows users to spend their funds on any chain they need. It consists of two main components: MagicSpendStakeManager and MagicSpendWithdrawalManager. The first one is responsible for managing user stakes, while the second one is responsible for managing the liquidity of the protocol. Each supported chain has deployed instances of these contracts.

MagicSpendStakeManager

MagicSpendStakeManager is a contract that manages user stakes. The stake can be in native tokens (ETH, Matic, etc) or any ERC20 token. Users can add, unlock, and remove their staked balances. Pimlico has no control over the staked balances, and the user can remove them at any time. These funds are not used in fullfilling the user's requests. These stakes can only be claimed by Pimlico if the user explicitly provides a signed Allowance message to authorize Pimlico to do so.

Allowance message

/// @notice Helper struct that represents an allowance for a specific asset.
struct AssetAllowance {
    /// @dev Token that can be claimed.
    address token;
    /// @dev The amount to claim.
    uint128 amount;
    /// @dev The chain id of the network, where the claim will be made.
    uint128 chainId;
}
 
/// @notice Helper struct that represents an allowance.
/// @dev signed by the user it allows Pimlico to claim part of user's stake from the `MagicSpendStakeManager` contract
/// @dev on one or many chains.
struct Allowance {
    /// @dev Address which stake is allowed to be claimed.
    address account;
    /// @dev List of assets, allowed to be claimed.
    /// @dev One allowance per asset, where asset is the combination of (token,chainId)
    AssetAllowance[] assets;
    /// @dev The time in which the allowance is valid until.
    uint48 validUntil;
    /// @dev The time in which the allowance is valid after.
    uint48 validAfter;
    /// @dev The salt of the allowance.
    uint48 salt;
    /// @dev Signer which is allowed to request withdrawals on behalf of this allowance.
    address operator;
}

Allowance is a message that user signs and sends to Pimlico. It contains the details of the stake, such as the asset, amount, chain id, etc. Pimlico can claim the user's stake only if the user provided a signed Allowance message.

Each Allowance can have many AssetAllowance, where each AssetAllowance represents a unique combination of (AssetAllowance.token,AssetAllowance.chainId). It allows users to provide parts of their stakes on different chains and tokens in a single request.

Upgradability

Pimlico can upgrade the contracts to add new features or fix bugs. However, the upgradability is only available with a timelock equal to one week, so users have enough time to withdraw their funds if they don't agree with the changes. There is no timelock on testnets, so the contracts can be upgraded instantly.

MagicSpendWithdrawalManager

MagicSpendWithdrawalManager is a contract that manages the liquidity of the MagicSpend++ protocol. It allows users to request withdrawals on any chain they need. This contract has no user's funds and stores only liquidity, added by Pimlico. Users can request withdrawals of their funds, if they provide a Withdrawal message, signed by Pimlico.

Withdrawal message

/// @notice Helper struct that represents a call to make.
struct Call {
    address to;
    uint256 value;
    bytes data;
}
 
/// @notice This struct represents a withdrawal request.
/// @dev signed by the signer it allows to withdraw funds from the `MagicSpendWithdrawalManager` contract
struct Withdrawal {
    /// @dev Token that will be withdrawn.
    address token;
    /// @dev The requested amount to withdraw.
    uint128 amount;
    /// @dev Chain id of the network, where the withdrawal will be executed.
    uint128 chainId;
    /// @dev Address that will receive the funds.
    address recipient;
    /// @dev Calls that will be made before the funds are sent to the user.
    Call[] preCalls;
    /// @dev Calls that will be made after the funds are sent to the user.
    Call[] postCalls;
    /// @dev The time in which the withdrawal is valid until.
    uint48 validUntil;
    /// @dev The time in which this withdrawal is valid after.
    uint48 validAfter;
    /// @dev The salt of the withdrawal.
    uint48 salt;
}
 

Withdrawal is a message that Pimlico signs and sends to user. It contains the details of the withdrawal, such as the asset, amount, recipient, chain id, etc.

MagicSpend++ Flow

This section describes the flow of the MagicSpend++ protocol. In order to start using MagicSpend++, users have to stake their funds. Read how to do that in the Staking section.

  1. User stakes their funds in the MagicSpendStakeManager contract, on one or many networks.
  2. User sends a pimlico_prepareMagicSpendAllowance call, which returns the Allowance.
  3. User signs the Allowance and sends it with pimlico_grantMagicSpendAllowance call.
  4. Pimlico validates the Allowance and returns it's hash.
  5. User sends a pimlico_sponsorMagicSpendWithdrawal call, where they specify the asset, amount, recipient, chain id, etc.
  6. Pimlico signs the corresponding Withdrawal and returns it with a signature to user
  7. User sends a transaction, which contains MagicSpendWithdrawalManager.withdraw(Withdrawal withdrawal, bytes signature) call and receives the funds.
  8. Once Withdrawal is executed, Pimlico can claim the user's stake by calling MagicSpendStakeManager.claim(Allowance allowance, bytes signature).