Skip to content

Commit 6cba375

Browse files
authored
feat: support tetra (#349)
* chore: update tetra support * feature: refactor * feature: refactor * chore: cli docs * feat: fix custom network explorer url * chore: bump version
1 parent 3fec080 commit 6cba375

File tree

12 files changed

+70
-62
lines changed

12 files changed

+70
-62
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.44.0] - 2026-03-02
9+
10+
- Support tetra via `tetra` network option
11+
812
## [0.43.0] - 2026-01-19
913

1014
### Added

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ Start by adding the following environment variables to your `.env` file:
136136
**Optional variables:**
137137
* **`WALLET_ID`**: The wallet ID (can be used with versions below `v5r1`).
138138
* **`SUBWALLET_NUMBER`**: The subwallet number used to build the wallet ID (can be used with `v5r1` wallets).
139+
* **`WALLET_NETWORK_ID`**: Network ID used to build the wallet ID (v5 wallets). Defaults: `-3` testnet, `-239` mainnet and other networks (tetra, custom).
139140

140141
Once your environment is set up, you can use the mnemonic wallet for deployment with the appropriate configuration.
141142

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ton/blueprint",
3-
"version": "0.43.0",
3+
"version": "0.44.0",
44
"description": "Framework for development of TON smart contracts",
55
"main": "dist/index.js",
66
"bin": "./dist/cli/cli.js",

src/cli/constants.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@ ${chalk.cyan('--custom')} [api-endpoint] - use a custom API
127127
${chalk.cyan('--custom-version')} - API version (v2, v4)
128128
${chalk.cyan('--custom-key')} - API key (v2 only)
129129
${chalk.cyan('--custom-type')} - network type (custom, mainnet, testnet)
130-
${chalk.cyan('--custom-domain')} - network domain (for custom l2 domain)
131-
${chalk.cyan('--custom-network-id')} - network global ID (for custom network)
130+
${chalk.cyan('--custom-global-id')} - network global ID (for custom l2 domain)
132131
${chalk.cyan('--tonconnect')}, ${chalk.cyan('--deeplink')}, ${chalk.cyan('--mnemonic')} - deployer options
133132
${chalk.cyan('--tonscan')}, ${chalk.cyan('--tonviewer')}, ${chalk.cyan('--toncx')}, ${chalk.cyan('--dton')} - explorer (default: tonviewer)
134133
${chalk.gray('[...args]')} (array of strings, optional) - Arguments passed directly to the script.
@@ -166,7 +165,7 @@ ${chalk.bold('SEE ALSO')}
166165
Verifies a deployed contract on ${chalk.underline('https://verifier.ton.org')}.
167166
168167
${chalk.bold('Flags:')}
169-
${chalk.cyan('--mainnet')}, ${chalk.cyan('--testnet')} - selects network
168+
${chalk.cyan('--mainnet')}, ${chalk.cyan('--testnet')}, ${chalk.cyan('--tetra')} - selects network
170169
${chalk.cyan('--verifier')} - specifies the verifier ID to use (default: ${chalk.cyan('verifier.ton.org')})
171170
${chalk.cyan('--list-verifiers')} - lists all available verifiers for the selected network (or both networks if none selected)
172171
${chalk.cyan('--compiler-version')} - specifies the exact compiler version to use (e.g. ${chalk.cyan('0.4.4-newops.1')}). Note: this does not change the underlying compiler itself.

src/config/Config.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ export interface Config {
3333
*/
3434
network?: 'mainnet' | 'testnet' | 'tetra' | CustomNetwork;
3535

36-
domain?: number;
37-
networkId?: number;
38-
3936
/**
4037
* If true, keeps compilable files (`*.compile.ts`) in a separate directory `compilables`.
4138
* When false or unset, compilables are stored in `wrappers` directory.

src/config/CustomNetwork.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export type CustomNetwork = {
66
version?: NetworkVersion;
77
key?: string;
88
type?: Network;
9+
globalId?: number;
910
};

src/network/Network.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export type Network = 'mainnet' | 'testnet' | 'tetra' | 'custom';
1+
import { AVAILABLE_NETWORKS } from './constants';
2+
3+
export type Network = (typeof AVAILABLE_NETWORKS)[number];

src/network/constants.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const AVAILABLE_NETWORKS = ['mainnet', 'testnet', 'tetra', 'custom'] as const satisfies string[];
2+
3+
export const MAINNET_NETWORK_GLOBAL_ID = -239;
4+
export const TESTNET_NETWORK_GLOBAL_ID = -3;
5+
const TETRA_NETWORK_GLOBAL_ID = 662387;
6+
7+
export const TETRA_DOMAIN = {
8+
type: 'l2',
9+
globalId: TETRA_NETWORK_GLOBAL_ID,
10+
} as const;

src/network/createNetworkProvider.ts

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { CustomNetwork } from '../config/CustomNetwork';
4242
import { Network } from './Network';
4343
import { WalletVersion } from './send/wallets';
4444
import { Explorer } from './Explorer';
45+
import { AVAILABLE_NETWORKS } from './constants';
4546

4647
const INITIAL_DELAY = 400;
4748
const MAX_ATTEMPTS = 4;
@@ -55,8 +56,7 @@ export const argSpec = {
5556
'--custom-type': String,
5657
'--custom-version': String,
5758
'--custom-key': String,
58-
'--custom-domain': Number,
59-
'--custom-network-id': Number,
59+
'--custom-global-id': Number,
6060

6161
'--compiler-version': String,
6262

@@ -403,13 +403,7 @@ function getOptionalNumberEnv(envName: string) {
403403
return value;
404404
}
405405

406-
async function createMnemonicProvider(
407-
client: BlueprintTonClient,
408-
network: Network,
409-
ui: UIProvider,
410-
domain?: number,
411-
networkId?: number,
412-
) {
406+
async function createMnemonicProvider(client: BlueprintTonClient, network: Network, ui: UIProvider, globalId?: number) {
413407
const mnemonic = process.env.WALLET_MNEMONIC ?? '';
414408
const walletVersion = process.env.WALLET_VERSION ?? '';
415409
if (mnemonic.length === 0 || walletVersion.length === 0) {
@@ -419,6 +413,7 @@ async function createMnemonicProvider(
419413
}
420414
const walletId = getOptionalNumberEnv('WALLET_ID');
421415
const subwalletNumber = getOptionalNumberEnv('SUBWALLET_NUMBER');
416+
const walletNetworkId = getOptionalNumberEnv('WALLET_NETWORK_ID');
422417

423418
const keyPair = await mnemonicToPrivateKey(mnemonic.split(' '));
424419
return new MnemonicProvider({
@@ -429,8 +424,8 @@ async function createMnemonicProvider(
429424
walletId,
430425
subwalletNumber,
431426
network,
432-
domain,
433-
networkId,
427+
globalId,
428+
networkId: walletNetworkId,
434429
});
435430
}
436431

@@ -493,11 +488,7 @@ class NetworkProviderBuilder {
493488
return typeof this.config.network === 'string' ? this.config.network : 'custom';
494489
}
495490

496-
network = await this.ui.choose(
497-
'Which network do you want to use?',
498-
['mainnet', 'testnet', 'tetra', 'custom'],
499-
(c) => c,
500-
);
491+
network = await this.ui.choose('Which network do you want to use?', AVAILABLE_NETWORKS, (c) => c);
501492
if (network === 'custom') {
502493
const defaultCustomEndpoint = 'http://localhost:8081/';
503494
this.args['--custom'] = (
@@ -566,15 +557,14 @@ class NetworkProviderBuilder {
566557
this.config?.manifestUrl,
567558
);
568559
break;
569-
case 'mnemonic':
570-
provider = await createMnemonicProvider(
571-
client,
572-
network,
573-
this.ui,
574-
this.config?.domain,
575-
this.config?.networkId,
576-
);
560+
case 'mnemonic': {
561+
let globalId: number | undefined = undefined;
562+
if (typeof this.config?.network === 'object') {
563+
globalId = this.config.network.globalId;
564+
}
565+
provider = await createMnemonicProvider(client, network, this.ui, globalId);
577566
break;
567+
}
578568
default:
579569
throw new Error('Unknown deploy option');
580570
}
@@ -584,7 +574,6 @@ class NetworkProviderBuilder {
584574

585575
async build(): Promise<NetworkProvider> {
586576
let network = await this.chooseNetwork();
587-
const explorer = this.chooseExplorer();
588577

589578
if (
590579
network !== 'custom' &&
@@ -612,21 +601,14 @@ class NetworkProviderBuilder {
612601
if (inputType !== undefined) {
613602
type = inputType as any; // checks come later
614603
}
604+
const globalId = this.args['--custom-global-id'];
615605
configNetwork = {
616606
endpoint: this.args['--custom'],
617607
version,
618608
key: this.args['--custom-key'],
609+
globalId,
619610
type,
620611
};
621-
622-
if (this.config && this.args['--custom-domain']) {
623-
const customDomain = this.args['--custom-domain'];
624-
this.config.domain = customDomain;
625-
}
626-
627-
if (this.config && this.args['--custom-network-id']) {
628-
this.config.networkId = this.args['--custom-network-id'];
629-
}
630612
}
631613
if (configNetwork === undefined) {
632614
throw new Error('Custom network is (somehow) undefined');
@@ -657,8 +639,8 @@ class NetworkProviderBuilder {
657639
}
658640

659641
if (configNetwork.type !== undefined) {
660-
const ct = configNetwork.type.toLowerCase();
661-
if (!['mainnet', 'testnet', 'custom', 'tetra'].includes(ct)) {
642+
const ct = configNetwork.type.toLowerCase() as Network;
643+
if (!AVAILABLE_NETWORKS.includes(ct)) {
662644
throw new Error('Unknown network type: ' + ct);
663645
}
664646
network = ct as Network;
@@ -719,6 +701,7 @@ class NetworkProviderBuilder {
719701

720702
const sender = new SendProviderSender(sendProvider);
721703

704+
const explorer = network === 'tetra' ? 'tonviewer' : this.chooseExplorer();
722705
return new NetworkProviderImpl(tc, sender, network, explorer, this.ui);
723706
}
724707
}

src/network/send/MnemonicProvider.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
openContract,
1010
OpenedContract,
1111
SendMode,
12+
SignatureDomain,
1213
StateInit,
1314
} from '@ton/core';
1415
import { KeyPair, keyPairFromSecretKey } from '@ton/crypto';
@@ -18,7 +19,8 @@ import { UIProvider } from '../../ui/UIProvider';
1819
import { BlueprintTonClient } from '../NetworkProvider';
1920
import { Network } from '../Network';
2021
import { wallets, WalletVersion } from './wallets';
21-
import { getW5NetworkGlobalId, TETRA_DOMAIN } from '../../utils/network.utils';
22+
import { getW5NetworkGlobalId } from '../utils';
23+
import { TETRA_DOMAIN } from '../constants';
2224

2325
interface WalletInstance extends Contract {
2426
getSeqno(provider: ContractProvider): Promise<number>;
@@ -44,7 +46,7 @@ type MnemonicProviderParams = {
4446
client: BlueprintTonClient;
4547
ui: UIProvider;
4648
network: Network;
47-
domain?: number;
49+
globalId?: number;
4850
networkId?: number;
4951
};
5052

@@ -79,12 +81,21 @@ export class MnemonicProvider implements SendProvider {
7981
this.ui = params.ui;
8082
}
8183

84+
private getDomain(params: MnemonicProviderParams): SignatureDomain | undefined {
85+
if (params.globalId !== undefined) {
86+
return { type: 'l2' as const, globalId: params.globalId };
87+
}
88+
if (params.network === 'tetra') {
89+
return TETRA_DOMAIN;
90+
}
91+
return undefined;
92+
}
93+
8294
private createWallet(params: MnemonicProviderParams, kp: KeyPair): WalletInstance {
83-
const networkDomain = params.network === 'tetra' ? TETRA_DOMAIN : undefined;
84-
const domain = params.domain ? { type: 'l2' as const, globalId: params.domain } : networkDomain;
85-
const networkGlobalId = params.networkId ?? getW5NetworkGlobalId(params.network);
95+
const domain = this.getDomain(params);
8696

8797
if (params.version === 'v5r1') {
98+
const networkGlobalId = params.networkId ?? getW5NetworkGlobalId(params.network);
8899
return wallets[params.version].create({
89100
publicKey: kp.publicKey,
90101
walletId: {

0 commit comments

Comments
 (0)