> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tac.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Proxy Functions

> Build the core logic that processes cross-chain calls and arguments

Proxy functions are the heart of your TAC contracts - they receive cross-chain calls from TON users and execute your application logic. This guide covers how to implement robust proxy functions that handle different use cases and error conditions.

## Function Signature Requirements

Every proxy function that handles cross-chain calls must follow this exact pattern:

```solidity theme={null}
function <function_name>(bytes calldata, bytes calldata) external _onlyCrossChainLayer;
```

The function above also can be `payable`.

You can name the function as you wish (e.g. `myProxyFunction`, `invokeWithCallback`, `swap`, etc.), but it must accept two bytes arguments:

```solidity theme={null}
function yourFunctionName(bytes calldata tacHeader, bytes calldata arguments)
    external
    _onlyCrossChainLayer
{
    // Your implementation
}
```

**Required Components:**

* `bytes calldata tacHeader` - Contains verified TON user information
* `bytes calldata arguments` - Your custom encoded parameters
* `external` visibility - Functions must be externally callable
* `_onlyCrossChainLayer` modifier - Security requirement

**Important:** Only the Cross-Chain Layer (CCL) contract can call these functions. When a user on TON sends a message, the CCL automatically transfers any bridged tokens to your proxy contract before calling your function.

## Parameter Encoding and Decoding

### Simple Parameters

For basic data types, use straightforward encoding:

```solidity theme={null}
struct SimpleParams {
    address token;
    uint256 amount;
    address recipient;
}

function handleSimpleParams(bytes calldata tacHeader, bytes calldata arguments)
    external
    _onlyCrossChainLayer
{
    // Decode parameters
    SimpleParams memory params = abi.decode(arguments, (SimpleParams));

    // Use the parameters
    IERC20(params.token).transfer(params.recipient, params.amount);
}
```

### Complex Parameters

For complex data with arrays or nested structures:

```solidity theme={null}
struct ComplexParams {
    address[] tokens;
    uint256[] amounts;
    bytes swapData;
    uint256 deadline;
}

function handleComplexParams(bytes calldata tacHeader, bytes calldata arguments)
    external
    _onlyCrossChainLayer
{
    ComplexParams memory params = abi.decode(arguments, (ComplexParams));

    // Validate array lengths match
    require(params.tokens.length == params.amounts.length, "Array length mismatch");

    // Process each token
    for (uint i = 0; i < params.tokens.length; i++) {
        // Your custom logic here
        IERC20(params.tokens[i]).transfer(msg.sender, params.amounts[i]);
    }

    // Use additional data
    if (params.swapData.length > 0) {
        // Your custom swap logic here
    }
}
```

## Complete Implementation Walkthrough

Let's build a complete proxy function step-by-step,
showing external dApp integration - the most common real-world pattern implementing TON->TAC->TON transaction type:

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TacProxyV1 } from "@tonappchain/evm-ccl/contracts/proxies/TacProxyV1.sol";
import { TokenAmount, OutMessageV1, TacHeaderV1, NFTAmount } from "@tonappchain/evm-ccl/contracts/core/Structs.sol";

interface IDappContract {
    function doSomething(address tokenA, address tokenB, uint256 amount)
        external returns (uint256);
}

contract MyProxy is TacProxyV1 {
    IDappContract public dappContract;

    struct MyProxyFunctionArguments {
        address tokenA;
        address tokenB;
        uint256 amount;
    }

    constructor(address _dappContract, address _crossChainLayer)
        TacProxyV1(_crossChainLayer)
    {
        dappContract = IDappContract(_dappContract);
    }

    function myProxyFunction(bytes calldata tacHeader, bytes calldata arguments)
        external
        _onlyCrossChainLayer
    {
        // 1. Decode the custom arguments
        MyProxyFunctionArguments memory args = abi.decode(arguments, (MyProxyFunctionArguments));

        // 2. Approve tokens to your Dapp contract for some action
        IERC20(args.tokenA).approve(address(dappContract), args.amount);

        // 3. Call the Dapp contract
        uint256 tokenBAmount = dappContract.doSomething(
            args.tokenA,
            args.tokenB,
            args.amount
        );

        // 4. Prepare tokens to send back to TON
        TokenAmount[] memory tokensToBridge = new TokenAmount[](1);
        tokensToBridge[0] = TokenAmount(args.tokenB, tokenBAmount);

        // 5. Approve the CrossChainLayer to pull them
        IERC20(tokensToBridge[0].evmAddress).approve(
            _getCrossChainLayerAddress(),
            tokensToBridge[0].amount
        );

        // 6. Decode the TAC header
        TacHeaderV1 memory header = _decodeTacHeader(tacHeader);

        // 7. Form an OutMessage
        OutMessageV1 memory outMsg = OutMessageV1({
            shardsKey: header.shardsKey,           // Use same key for RoundTrip
            tvmTarget: header.tvmCaller,           // Send back to caller
            tvmPayload: "",                        // Must be empty - not supported
            tvmProtocolFee: 0,                     // 0 for RoundTrip - already paid on TON
            tvmExecutorFee: 0,                     // 0 for RoundTrip - already paid on TON
            tvmValidExecutors: new string[](0),    // Empty for RoundTrip - already defined on TON
            toBridge: tokensToBridge,              // Result tokens
            toBridgeNFT: new NFTAmount[](0)        // No NFTs
        });

        // 8. Send message back through CrossChainLayer with zero native
        _sendMessageV1(outMsg, 0); // 0 TACs to send back
    }
}
```

**This example shows the complete flow:**

* External Dapp contract integration (lines 11-13, 19-21, 32-38)
* Token approval for external contracts (line 33)
* Processing and getting results (lines 35-38)
* RoundTrip response pattern (lines 48-58)
* Proper fee handling for RoundTrip messages (lines 52-53)

**Important Notes:**

* Use `_sendMessageV1` for ERC20 token bridging only
* The CCL automatically handles token transfers to your proxy before calling your function
* Always include the `NFTAmount` import when using OutMessageV1

## What's Next?

Now that you understand how to implement proxy functions, learn about managing fees:

<CardGroup cols={1}>
  <Card title="Fee Management" icon="dollar-sign" href="/proxies/custom-proxy/fee-management">
    Understand how to handle protocol fees and executor payments
  </Card>
</CardGroup>
