Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@
"pages": [
"foundations/addresses/overview",
"foundations/addresses/formats",
"foundations/addresses/serialize"
"foundations/addresses/serialize",
"foundations/addresses/derive"
]
},
{
Expand Down
292 changes: 292 additions & 0 deletions foundations/addresses/derive.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
---
title: "Deriving addresses"
sidebarTitle: "Deriving"
---

import { Aside } from '/snippets/aside.jsx';

It is often useful to derive an [address](/foundations/addresses/overview) of an account to interact with it, either on-chain or off-chain. The general algorithm for address derivation is to compose the [`StateInit`](/foundations/addresses/overview#account-id) of an account, calculate its hash, and build the address in the desired [format](/foundations/addresses/overview#internal-addresses).

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](/foundations/addresses/overview#workchain-id) 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.

```tolk expandable
// 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](/foundations/serialization/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`](/languages/tolk/from-func/create-message#stateinit-and-workchains) built-in that handles all the underlying `StateInit` and address composing logic. In the [Tolk stdlib](/languages/tolk/from-func/stdlib), the code and data pair is represented by [`ContractState`](/languages/tolk/from-func/create-message#dont-confuse-stateinit-and-codedata-they-are-different). 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](/contract-dev/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](#on-chain-simple) case works here, and only `getData` has to be changed.

```tolk expandable
// 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](/contract-dev/vanity) 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.

```tolk
// 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](/standard/tokens/jettons/comparison).

This may seem similar to the [vanity](#on-chain-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](/standard/tokens/jettons/comparison).

The logic here is more similar to the [simple](#on-chain-simple) case. The only difference is the additional `toShard` field.

```tolk expandable
// 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);
}
```

<Aside
type="tip"
title="Prefix length limit"
>
The max possible prefix length is defined in the blockchain config's
[param 43](/foundations/config#param-43-account-and-message-limits)
as `max_acc_fixed_prefix_length`.
</Aside>

## Off-chain derivation

### Off-chain — simple

The logic mirrors the [on-chain](#on-chain-simple) example. The `@ton/core` library has the `contractAddress` function that handles `StateInit` hash calculation and composes the address.

```ts expandable
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](#on-chain-contract-sharding) example. The parent address is hardcoded in this example but could just as well be derived on its own.

```ts
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](#on-chain-vanity) case, there is no way to derive the address. Define a constant with the predetermined address obtained in advance.

```ts
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](#on-chain-prefixed) example. Since `@ton/core` does not define helpers for prefixed addresses, the prefix must be replaced manually.

```ts
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
```