# AsyncSwap Hooks (/docs/protocols/v4/guides/hooks/async-swap)

Implement AsyncSwap-style hook behavior in Uniswap v4 using custom accounting and hook permissions.

One feature enabled by [custom accounting](/docs/protocols/v4/guides/custom-accounting) is AsyncSwap. This feature allows hook developers to replace v4 (v3-style) swap logic.

This means developers can replace Uniswap's internal core logic for how to handle swaps. Two emergent use-cases are possible with custom accounting:

1. Asynchronous swaps and swap-ordering. Delay the v4 swap logic for fulfillment at a later time.
2. Custom Curves. Replace the v4 swap logic with different swap logic. The custom logic is flexible and developers can implement symmetric curves, asymmetric curves, or custom quoting.

> AsyncSwap is typically described as taking the full input to replace the internal swap logic, partially taking the input is better described as *custom accounting*

Note: The flexibility of AsyncSwap means hook developers can implement harmful behavior (such as taking all swap amounts for themselves, charging extra fees, etc.). Hooks with AsyncSwap behavior should be examined very closely by both developers and users.

## Configure an AsyncSwap Hook
To enable AsyncSwap, developers will need the hook permission `BEFORE_SWAP_RETURNS_DELTA_FLAG`

```solidity
import {BaseHook} from "v4-periphery/src/utils/BaseHook.sol";
// ...

contract AsyncSwapHook is BaseHook {
    // ...

    function getHookPermissions() public pure virtual override returns (Hooks.Permissions memory) {
        return Hooks.Permissions({
            beforeInitialize: false,
            afterInitialize: false,
            beforeAddLiquidity: false,
            afterAddLiquidity: false,
            beforeRemoveLiquidity: false,
            afterRemoveLiquidity: false,
            beforeSwap: true,
            afterSwap: false,
            beforeDonate: false,
            afterDonate: false,
            beforeSwapReturnDelta: true,
            afterSwapReturnDelta: false,
            afterAddLiquidityReturnDelta: false,
            afterRemoveLiquidityReturnDelta: false
        });
    }

    // ...
}
```

## beforeSwap
AsyncSwap only works on exact-input swaps and the *beforeSwap* **must** take the input currency and return `BeforeSwapDelta`. The hook should `IPoolManager.mint` itself the corresponding tokens equal to the amount of the input (`amountSpecified`). It should then return a `BeforeSwapDelta` where `deltaSpecified = -amountSpecified` (the positive amount).

The funds' movements are as follows:

1. User initiates a swap, specifying -100 tokenA as input
2. The hook's beforeSwap takes 100 tokenA for itself, and returns a value of 100 to PoolManager.
3. The PoolManager accounts the 100 tokens against the swap input, leaving 0 tokens remaining
4. The PoolManager does not execute swap logic, as there are no tokens left to swap
5. The PoolManager transfers the delta from the hook to the swap router, in step 2 the hook created a debt (that must be paid)
6. The swap router pays off the debt using the user's tokens

```solidity
contract AsyncSwapHook is BaseHook {
     // ...

    function _beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)
        internal
        override
        returns (bytes4, BeforeSwapDelta, uint24)
    {
        // AsyncSwap only works on exact-input swaps
        if (params.amountSpecified < 0) {
            // take the input token so that v3-swap is skipped...
            Currency input = params.zeroForOne ? key.currency0 : key.currency1;
            uint256 amountTaken = uint256(-params.amountSpecified);
            poolManager.mint(address(this), input.toId(), amountTaken);

            // to AsyncSwap the exact input, we return the amount that's taken by the hook
            return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(amountTaken.toInt128(), 0), 0);
        }
        else {
            return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
        }

    }
}
```

## Testing
To verify the AsyncSwap behaved properly, developers should test the swap and that token balances match expected behavior.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import {Deployers} from "v4-core/test/utils/Deployers.sol";
// ...

contract AsyncSwapTest is Test, Deployers {
    // ...

    function setUp() public {
        // ... 
    }

    function test_asyncSwap() public {
        assertEq(hook.beforeSwapCount(poolId), 0);

        uint256 balance0Before = currency0.balanceOfSelf();
        uint256 balance1Before = currency1.balanceOfSelf();

        // Perform a test swap //
        int256 amount = -1e18;
        bool zeroForOne = true;
        BalanceDelta swapDelta = swap(poolKey, zeroForOne, amount, ZERO_BYTES);
        // ------------------- //

        uint256 balance0After = currency0.balanceOfSelf();
        uint256 balance1After = currency1.balanceOfSelf();

        // user paid token0
        assertEq(balance0Before - balance0After, 1e18);

        // user did not recieve token1 (AsyncSwap)
        assertEq(balance1Before, balance1After);
    }
}
```
