Axis-Fi / axis-core

Axis Protocol
https://axis.finance
Other
6 stars 1 forks source link

Callbacks: Finalize interface + library and integrate into AuctionHouse #56

Closed Oighty closed 5 months ago

Oighty commented 6 months ago

Objective

Finalize the design of callback contracts in the Axis Protocol.

Rationale

Callbacks allow for additional functionality to be implemented into specific auction flows without requiring these to be known up front or implemented on launch.

Design

Now that we've implemented both Batch and Atomic auction styles we have a better grasp on what this should look like.

I'll start off with a review of the current hooks (pre, mid, post within the purchase function), which don't really make sense in the scheme of a broader auction system. The bond system uses a single callback in the purchase function that mostly serves two purposes:

This idea is useful from an integration and convenience standpoint, though not many protocols wanted to write custom contracts to use.

In the new system, I had originally thought that expanding the options of when in the purchase (or bid) call you allow for external logic would provide more options, but it doesn't really.

To explain, let's consider UniV4, which allows before and after hooks to be performed sandwiching most pool interactions. This is useful because the Hook is actually implementing additional logic that changes the behavior of the pool. We don't necessarily want that to happen with our auction logic (at least right now). In a way, our Auction and Derivative Modules are already "hooks" in the UniV4 sense because they allow custom auction/derivative implementations.

One similarity with UniV4 is the desire to allow for external checks on certain actions. An example is a KYC UniV4 pool that would check if a sender is allowed to use the pool in a beforeSwap hook. We could leverage similar functionality to check permissions without a separate allowlist. However, UniV4 natively handles sending/receiving of funds and doesn't send funds to hooks (at least as far as I can tell). We do want to enable this functionality though and for various functions.

Based on this, I have come up with a revised callback design that takes this into account. I believe it's sufficient to just do one callback per function. The variation detailed below (with conditionally sending/receiving tokens when needed) handles our cases. We stick with the term "callbacks" since, as noted above, the intention with these functions is different from UniV4 hooks.

A key idea is that we can use a similar permissions map as UniV4 to selectively activate certain hooks to be called. Additionally, we can set whether the seller or hook will send the baseTokens when required or if the seller or hook should receive quoteTokens when required. See https://github.com/Uniswap/v4-core/blob/4a13732dc0b9a8c516d3639a78c54af3fc3db8d4/src/libraries/Hooks.sol#L34. Since these hooks will be deployed by individual sellers, it's not a good idea to use the address bits to store the permissions information, but we can pack it tightly into a single bytes1 parameter

We will also use the UniV4 hook call setup so that sellers can't use hooks to brick auctions on settlement or in other places.

Each function takes an arbitrary callbackData parameter that can be sent by the caller. This can be used for allowlist proofs among other things.

Oighty commented 6 months ago
/// @title  ICallbacks
/// @notice Interface for callback contracts use in the Axis system
interface ICallbacks {

    /// @notice Callback configuration. Used by AuctionHouse to know which functions are implemented on this contract.
    /// @dev 8-bit map of which callbacks are implemented on this contract.
    ///     The last two bits designate whether the callback should be expected send base tokens and receive quote tokens.
    ///     If the contract does not send/receive, then the AuctionHouse will expect the tokens to be sent/received directly by the seller wallet.
    ///     Bit 1: onCreate
    ///     Bit 2: onCancel
    ///     Bit 3: onCurate
    ///     Bit 4: onPurchase
    ///     Bit 5: onBid
    ///     Bit 6: onSettle
    ///     Bit 7: Receives quote tokens
    ///     Bit 8: Sends base tokens (and receives them if refunded)
    function CONFIG() external view returns (bytes1);

    // General functions that can be used by all auctions

    /// @notice Called when an auction is created. Reverts if validation fails.
    /// @dev Should register the lot ID on the Hook and validate seller is allowed to use the Hook contract
    /// @dev If the Hook is configured to send tokens and the auction is to be prefunded, then the AuctionHouse will expect the capacity of base tokens to be sent back.
    function onCreate(uint96 lotId, address seller, address baseToken, address quoteToken, uint96 capacity, bool preFund, bytes calldata callbackData) external returns (bytes4);

    /// @notice Called when an auction is cancelled. 
    /// @dev If the Hook is configured to receive tokens and the auction was prefunded, then the refund will be sent prior to the call.
    function onCancel(uint96 lotId, uint96 refund, bool preFunded, bytes calldata callbackData) external returns (bytes4);

    /// @notice Called when curate is called for an auction.
    /// @dev If the Hook is configured to send tokens and the auction is to be prefunded, then the AuctionHouse will expect the curatorFee of base tokens to be sent back.
    function onCurate(uint96 lotId, uint96 curatorFee, bool preFund, bytes calldata callbackData) external returns (bytes4);

    // Atomic Auction hooks

    /// @notice Called when a buyer purchases from an atomic auction. Reverts if validation fails.
    /// @dev If the Hook is configured to receive tokens, then the user purchase amount of quote tokens will be sent prior to this call.
    /// @dev If the Hook is configured to send tokens and the auction wasn't prefunded, then the AuctionHouse will expect the payout of base tokens to be sent back.
    function onPurchase(uint96 lotId, address buyer, uint96 amount, uint96 payout, bool preFunded, bytes calldata callbackData) external returns (bytes4);

    // Batch Auction hooks

    /// @notice Called when a buyer bids on a batch auction. Reverts if validation fails.
    function onBid(uint96 lotId, uint64 bidId, address buyer, uint96 amount, bytes calldata callbackData) external returns (bytes4);

    /// @notice Called when a batch auction is settled.
    /// @dev If the Hook is configured to receive tokens, then the proceeds and/or refund will be sent prior to the call.
    function onSettle(uint96 lotId, uint96 proceeds, uint96 refund, bytes calldata callbackData, bytes memory auctionOutput) external returns (bytes4);
}
Oighty commented 5 months ago

PR #47