Skip to main content

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.

TAC provides TacProxyV1Upgradeable for contracts that need to be upgraded after deployment. Use this base contract when you need upgrade functionality.

Basic Upgradeable Structure

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

import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { TacProxyV1Upgradeable } from "@tonappchain/evm-ccl/contracts/proxies/TacProxyV1Upgradeable.sol";
import { TacHeaderV1 } from "@tonappchain/evm-ccl/contracts/core/Structs.sol";

contract MyUpgradeableProxy is
    Initializable,
    OwnableUpgradeable,
    UUPSUpgradeable,
    TacProxyV1Upgradeable
{
    // Your state variables go here
    mapping(string => uint256) public userBalances;
    uint256 public totalProcessed;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address owner, address crossChainLayer)
        public
        initializer
    {
        __UUPSUpgradeable_init();
        __Ownable_init(owner);
        __TacProxyV1Upgradeable_init(crossChainLayer);

        // Initialize your contract state
        totalProcessed = 0;
    }

    function _authorizeUpgrade(address newImplementation)
        internal
        override
        onlyOwner
    {}

    // Your proxy functions go here
    function processRequest(bytes calldata tacHeader, bytes calldata arguments)
        external
        _onlyCrossChainLayer
    {
        TacHeaderV1 memory header = _decodeTacHeader(tacHeader);
        uint256 amount = abi.decode(arguments, (uint256));

        userBalances[header.tvmCaller] += amount;
        totalProcessed += amount;

        emit RequestProcessed(header.tvmCaller, amount);
    }

    event RequestProcessed(string indexed user, uint256 amount);
}

Key Difference from Basic Contracts

The inheritance order is critical and must follow OpenZeppelin’s recommended pattern.
contract MyUpgradeableProxy is
    Initializable,           // Must be first
    OwnableUpgradeable,      // Access control
    UUPSUpgradeable,         // Upgrade mechanism
    TacProxyV1Upgradeable    // TAC functionality (last)

Deployment

Deployment differs from the basic example. UUPS pattern is used in the example below:
import { Signer } from "ethers";
import { MyUpgradeableProxy } from "../../typechain-types";
import { deployUpgradable } from '@tonappchain/evm-ccl'
import { DeployProxyOptions } from "@openzeppelin/hardhat-upgrades/dist/utils";
import hre from 'hardhat';

const proxyOptsUUPS: DeployProxyOptions = {
    kind: "uups",
    unsafeAllow: ["constructor"]
};

export async function deployMyUpgradeableProxy(
    deployer: Signer,
    crossChainLayerAddress: string,
    owner?: string,
): Promise<MyUpgradeableProxy> {
    const myUpgradeableProxy = await deployUpgradable<MyUpgradeableProxy>(
        deployer,
        hre.artifacts.readArtifactSync('MyUpgradeableProxy'),
        [owner || await deployer.getAddress(), crossChainLayerAddress],
        proxyOptsUUPS,
        undefined,
        true // verbose
    );

    await myUpgradeableProxy.waitForDeployment();
    return myUpgradeableProxy;
}

main().catch(console.error);

What’s Next?

Advanced Custom Proxy

Learn even more sophisticated development patterns