Skip to main content
It is often useful to derive an address of an account to interact with it, either on-chain or off-chain. The general algorithm for address derivation is to compose the StateInit of an account, calculate its hash, and build the address in the desired format. The StateInit is distinct from the account’s current state. It is the initial state, which an account stores at deployment. After that, state can be changed by the smart contract itself — for example, by updating the data. Since the address depends on StateInit, to derive the address it is necessary to compose the initial state rather than the current one. In practice, address derivation is mostly about composing the correct StateInit.

On-chain derivation

On-chain — simple

If it’s known how to compose the contract’s initial code and data, it’s enough to follow the general algorithm mentioned above as is. Code and data form a StateInit, and the workchain is usually hardcoded. Below is one of the most common patterns when code is a constant cell known in advance and data is composed at runtime.
// Compose an address builder
fun calculateAddress(
    code: cell,
    data: cell,
    workchain: int
): builder {
    val addr = AutoDeployAddress {
        workchain,
        stateInit: ContractState { code, data }
    };
    return addr.buildAddress();
}

// Placeholder for the code cell encoded as a bag of cells
fun getCode(): cell asm "B{b5ee9c724101010100020000004cacb9cd} B>boc PUSHREF";

// Placeholder for data that is usually assembled at runtime
fun getData(): cell {
    return beginCell()
        .storeUint(123, 123)
        .storeSlice("hello, world")
    .endCell();
}

fun main() {
    val code = getCode();
    val data = getData();
    val workchain = BASECHAIN;

    val builderWithAddress = calculateAddress(code, data, workchain);

    // Example usage if the derived address has to be written in another cell.
    val exampleCell = beginCell()
        .storeBuilder(builderWithAddress)
    .endCell();
    debug.print(exampleCell);
}
The b5ee9c724101010100020000004cacb9cd in the getCode function is a placeholder for a hardcoded code cell in BoC format known at compile-time. The getData function is a placeholder for building a data cell, and the actual implementation depends on the storage layout of the target smart contract. Usually, data is composed in a parametrized way, but this does not alter the rest of the logic — only the getData function. The calculateAddress function uses the AutoDeployAddress built-in that handles all the underlying StateInit and address composing logic. In the Tolk stdlib, the code and data pair is represented by ContractState. The buildAddress method returns a builder containing the resulting address, which can be cheaply stored in another builder — the most common use case.

On-chain — contract sharding

When working with the contract sharding pattern, child contracts usually have a StateInit that depends on the parent. In the example below, the dependence is implemented by adding the parent address to the child StateInit. The same logic from the simple on-chain case works here, and only getData has to be changed.
// Compose an address builder using the AutoDeployAddress built-in.
fun calculateAddress(code: cell, data: cell, workchain: int): builder {
    val addr = AutoDeployAddress {
        workchain,
        stateInit: ContractState { code, data }
    };
    return addr.buildAddress();
}

// Placeholder for the code cell encoded as a BoC.
fun getCode(): cell asm "B{b5ee9c724101010100020000004cacb9cd} B>boc PUSHREF";

// Placeholder for child data that depends on the parent address.
fun getChildData(parentAddress: address): cell {
    return beginCell()
        .storeSlice("hello, world")
        .storeAddress(parentAddress)
    .endCell();
}

fun main() {
    val code = getCode();
    val parentAddress = contract.getAddress();
    val data = getChildData(parentAddress);
    val workchain = BASECHAIN;

    val builderWithAddress = calculateAddress(code, data, workchain);

    // Example usage if the derived address has to be written in another cell.
    val exampleCell = beginCell()
        .storeBuilder(builderWithAddress)
    .endCell();
    debug.print(exampleCell);
}
Child contracts also often have some kind of index or additional data that they depend on, but it is case-specific and up to the implementation. The common pattern is to include at least the parent address in StateInit.

On-chain — vanity

A vanity contract allows customizing the address of a smart contract that is being deployed. It does that by making its own StateInit depend on some salt that is randomly generated many times until a desired address is found. In this case, there is no real correlation between the data the contract holds and its final address. So there is no way to actually derive the address. The only option left is to define a constant with the actual address that is obtained in advance.
// Constant address in raw format.
const VANITY_ADDRESS_RAW: address = address("0:4de24b95c1c3c9b6a94231460716192c7d2b4e444ca6ae9a98bc5c4b3fcdef3f");

// Constant address in user-friendly format.
const VANITY_ADDRESS_USER_FRIENDLY: address = address("EQBN4kuVwcPJtqlCMUYHFhksfStOREymrpqYvFxLP83vP-Ci");

fun main() {
    debug.print(VANITY_ADDRESS_RAW);
    debug.print(VANITY_ADDRESS_USER_FRIENDLY);
}

On-chain — prefixed

The toShard field in AutoDeployAddress allows smart contracts to have a specific prefix in the address for the purpose of deploying it to a certain shardchain. This can be useful for cross-contract latency optimization, as in Jetton 2.0. This may seem similar to the vanity case, but it serves a different purpose. A vanity generator finds such a StateInit that an address composed from its hash will have certain letters in a prefix or suffix. The toShard does not alter the whole address and only tells the blockchain to replace the first few bits of the address to which the contract is being deployed. This optimization is used in Jetton 2.0. The logic here is more similar to the simple case. The only difference is the additional toShard field.
// Compose an address builder using the AutoDeployAddress built-in.
fun deriveAddress(target: address, prefixLength: int): builder {
    val code = getCode();
    val data = getData();
    val workchain = BASECHAIN;
    val addr = AutoDeployAddress {
        workchain,
        stateInit: ContractState { code, data },
        toShard: {
            fixedPrefixLength: prefixLength,
            closeTo: target
        }
    };
    return addr.buildAddress();
}

// Placeholder for the code cell encoded as a BoC.
fun getCode(): cell asm "B{b5ee9c724101010100020000004cacb9cd} B>boc PUSHREF";

// Placeholder for data cell composition.
fun getData(): cell {
    return beginCell().endCell();
}

fun main() {
    // Using a constant as a target.
    val constTarget: address = address("0:AA00000000000000000000000000000000000000000000000000000000000000");
    val constBuilder = deriveAddress(constTarget, 8);

    // Example usage if the derived address has to be written in another cell.
    val constCell = beginCell()
        .storeBuilder(constBuilder)
        .endCell();
    debug.print(constCell);

    // Using contract's own address as a target.
    val runtimeTarget = contract.getAddress();
    val runtimeBuilder = deriveAddress(runtimeTarget, 8);

    // Example usage if the derived address has to be written in another cell.
    val runtimeCell = beginCell()
        .storeBuilder(runtimeBuilder)
        .endCell();
    debug.print(runtimeCell);
}
Prefix length limitThe max possible prefix length is defined in the blockchain config’s param 43 as max_acc_fixed_prefix_length.

Off-chain derivation

Off-chain — simple

The logic mirrors the on-chain example. The @ton/core library has the contractAddress function that handles StateInit hash calculation and composes the address.
import { contractAddress, Cell, beginCell } from "@ton/core";

// constant code from a BoC
const code = Cell.fromBoc(
  Buffer.from("b5ee9c724101010100020000004cacb9cd", "hex"),
)[0];

// data composing example
const data = beginCell()
  .storeUint(123, 123)
  .storeStringTail("hello, world!")
  .endCell();

const init = {
  code,
  data,
};

const addr = contractAddress(0, init);

console.log(addr); // EQC235M1tplyIg2OUQZQgG8D3BNF6_TZJ1932iaIV26XBVCH

Off-chain — contract sharding

The logic mirrors the on-chain example. The parent address is hardcoded in this example but could just as well be derived on its own.
import { contractAddress, Cell, beginCell, Address } from "@ton/core";

// constant code from a BoC
const code = Cell.fromBoc(
  Buffer.from("b5ee9c724101010100020000004cacb9cd", "hex"),
)[0];

const parentAddress = Address.parse(
  "EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN",
);
const data = beginCell()
  .storeStringTail("hello, world!")
  // include the parent address in the child StateInit
  .storeAddress(parentAddress)
  .endCell();

const init = {
  code,
  data,
};

const addr = contractAddress(0, init);

console.log(addr);
// EQAE74NUORgCfJQqcKy6GTCEs1hlZ--um6PWEmWX25fgDuuZ

Off-chain — vanity

Similarly to the on-chain case, there is no way to derive the address. Define a constant with the predetermined address obtained in advance.
import { contractAddress, Cell, beginCell, Address } from "@ton/core";

const addr1 = Address.parse("EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN");

console.log(addr1);
// EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN

Off-chain — prefixed

The logic mirrors the on-chain example. Since @ton/core does not define helpers for prefixed addresses, the prefix must be replaced manually.
import { contractAddress, Cell, beginCell } from "@ton/core";

// constant code from a BoC
const code = Cell.fromBoc(
  Buffer.from("b5ee9c724101010100020000004cacb9cd", "hex"),
)[0];

// data composing example
const data = beginCell()
  .storeUint(123, 123)
  .storeStringTail("hello, world!")
  .endCell();

const init = {
  splitDepth: 8, // prefix length
  code,
  data,
};

const addr = contractAddress(0, init); // 0 - basechain
addr.hash[0] = parseInt("00000000", 2); // replace first byte with 0x00

console.log(addr); // EQAAO5BKgRAfcMawxnHYUkZ2Ga74iZMA7uPoD_JkwWfyOwMA