Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package io.ton.walletkit.demo.core
import android.util.Log
import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.TESTNET
import io.ton.walletkit.api.TETRA
import io.ton.walletkit.api.generated.TONGetMethodResult
import io.ton.walletkit.api.generated.TONMasterchainInfo
import io.ton.walletkit.api.generated.TONNetwork
Expand Down Expand Up @@ -272,6 +273,7 @@ class TonAPIClient(
private val baseUrl: String = when (network) {
TONNetwork.MAINNET -> "https://tonapi.io"
TONNetwork.TESTNET -> "https://testnet.tonapi.io"
TONNetwork.TETRA -> "https://tetra.tonapi.io"
else -> "https://tonapi.io"
}

Expand Down Expand Up @@ -332,5 +334,6 @@ class TonAPIClient(
companion object {
fun mainnet(apiKey: String = "") = TonAPIClient(TONNetwork.MAINNET, apiKey)
fun testnet(apiKey: String = "") = TonAPIClient(TONNetwork.TESTNET, apiKey)
fun tetra(apiKey: String = "") = TonAPIClient(TONNetwork.TETRA, apiKey)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ import io.ton.walletkit.ITONWalletKit
import io.ton.walletkit.api.ChainIds
import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.TESTNET
import io.ton.walletkit.api.TETRA
import io.ton.walletkit.api.WalletVersions
import io.ton.walletkit.api.generated.TONNetwork
import io.ton.walletkit.api.generated.TONSignatureDomain
import io.ton.walletkit.api.isTetra
import io.ton.walletkit.config.SignDataType
import io.ton.walletkit.config.TONWalletKitConfiguration
import io.ton.walletkit.demo.data.storage.DemoAppStorage
Expand Down Expand Up @@ -161,17 +164,21 @@ class WalletKitDemoApp :
val mnemonicWords = walletRecord.mnemonic.split(" ").filter { it.isNotBlank() }

// Convert network string to TONNetwork enum
val network = when (walletRecord.network.lowercase()) {
val network = when (walletRecord.network) {
ChainIds.MAINNET -> TONNetwork.MAINNET
ChainIds.TESTNET -> TONNetwork.TESTNET
ChainIds.TETRA -> TONNetwork.TETRA
else -> TONNetwork.MAINNET
}

// Tetra (L2) wallets require an L2 signature domain
val domain = if (network.isTetra) TONSignatureDomain.L2(value = 662387) else null

// Use 3-step wallet creation pattern
val signer = kit.createSignerFromMnemonic(mnemonicWords)
val adapter = when (walletRecord.version) {
WalletVersions.V4R2 -> kit.createV4R2Adapter(signer, network)
WalletVersions.V5R1 -> kit.createV5R1Adapter(signer, network)
WalletVersions.V4R2 -> kit.createV4R2Adapter(signer, network, domain = domain)
WalletVersions.V5R1 -> kit.createV5R1Adapter(signer, network, domain = domain)
else -> {
Log.w(TAG, "Unsupported wallet version: ${walletRecord.version}, skipping")
continue
Expand Down Expand Up @@ -303,6 +310,10 @@ object TONWalletKitHelper {
network = TONNetwork.TESTNET,
apiClient = TonAPIClient.testnet(),
),
TONWalletKitConfiguration.NetworkConfiguration(
network = TONNetwork.TETRA,
apiClient = TonAPIClient.tetra(),
),
)
} else {
// Use SDK's built-in API client with default configuration
Expand All @@ -319,6 +330,13 @@ object TONWalletKitHelper {
key = "", // Empty key uses default behavior
),
),
TONWalletKitConfiguration.NetworkConfiguration(
network = TONNetwork.TETRA,
apiClientConfiguration = TONWalletKitConfiguration.APIClientConfiguration(
key = "",
),
apiClientType = TONWalletKitConfiguration.APIClientType.TONAPI,
),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.TESTNET
import io.ton.walletkit.api.TETRA
import io.ton.walletkit.api.generated.TONNetwork
import io.ton.walletkit.demo.R

Expand All @@ -41,11 +42,13 @@ fun NetworkBadge(network: TONNetwork) {
val color = when (network.chainId) {
TONNetwork.MAINNET.chainId -> MAINNET_COLOR
TONNetwork.TESTNET.chainId -> TESTNET_COLOR
TONNetwork.TETRA.chainId -> TETRA_COLOR
else -> MAINNET_COLOR
}
val label = when (network.chainId) {
TONNetwork.MAINNET.chainId -> stringResource(R.string.network_mainnet)
TONNetwork.TESTNET.chainId -> stringResource(R.string.network_testnet)
TONNetwork.TETRA.chainId -> stringResource(R.string.network_tetra)
else -> "Unknown"
}
Surface(shape = MaterialTheme.shapes.medium, color = color.copy(alpha = 0.12f)) {
Expand All @@ -62,6 +65,7 @@ private val BADGE_HORIZONTAL_PADDING = 10.dp
private val BADGE_VERTICAL_PADDING = 4.dp
private val MAINNET_COLOR = Color(0xFF2E7D32)
private val TESTNET_COLOR = Color(0xFFF57C00)
private val TETRA_COLOR = Color(0xFF1565C0)

@Preview
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import androidx.compose.ui.unit.dp
import io.ton.walletkit.api.ChainIds
import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.TESTNET
import io.ton.walletkit.api.TETRA
import io.ton.walletkit.api.WalletVersions
import io.ton.walletkit.api.generated.TONNetwork
import io.ton.walletkit.demo.R
Expand Down Expand Up @@ -153,7 +154,7 @@ fun AddWalletSheet(

Text(stringResource(R.string.label_network), style = MaterialTheme.typography.titleSmall)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
listOf(TONNetwork.MAINNET, TONNetwork.TESTNET).forEach { option ->
listOf(TONNetwork.MAINNET, TONNetwork.TESTNET, TONNetwork.TETRA).forEach { option ->
FilterChip(
selected = network == option,
onClick = { network = option },
Expand All @@ -162,6 +163,7 @@ fun AddWalletSheet(
when (option.chainId) {
ChainIds.MAINNET -> stringResource(R.string.network_mainnet)
ChainIds.TESTNET -> stringResource(R.string.network_testnet)
ChainIds.TETRA -> stringResource(R.string.network_tetra)
else -> "Unknown"
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import io.ton.walletkit.ITONWallet
import io.ton.walletkit.WalletKitUtils
import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.TESTNET
import io.ton.walletkit.api.WalletVersions
import io.ton.walletkit.api.generated.TONNetwork
import io.ton.walletkit.api.generated.TONSignatureDomain
import io.ton.walletkit.api.isTetra
import io.ton.walletkit.demo.R
import io.ton.walletkit.demo.core.RequestErrorTracker
import io.ton.walletkit.demo.data.storage.DemoAppStorage
Expand Down Expand Up @@ -639,14 +639,17 @@ class WalletKitViewModel @Inject constructor(
}
}

// Tetra (L2) wallets require an L2 signature domain
val domain = if (network.isTetra) TONSignatureDomain.L2(value = 662387) else null

// Create adapter based on wallet version
// Note: You can optionally specify workchain and walletId parameters:
// - workchain: 0 (basechain, default) or -1 (masterchain)
// - walletId: unique ID for multiple wallets from same signer
// Example: kit.createV5R1Adapter(signerInfo, network, workchain = 0, walletId = WalletKitConstants.DEFAULT_WALLET_ID_V5R1)
val adapter = when (version) {
WalletVersions.V4R2 -> kit.createV4R2Adapter(signerInfo, network)
WalletVersions.V5R1 -> kit.createV5R1Adapter(signerInfo, network)
WalletVersions.V4R2 -> kit.createV4R2Adapter(signerInfo, network, domain = domain)
WalletVersions.V5R1 -> kit.createV5R1Adapter(signerInfo, network, domain = domain)
else -> throw IllegalArgumentException("Unsupported wallet version: $version")
}

Expand Down Expand Up @@ -740,10 +743,11 @@ class WalletKitViewModel @Inject constructor(
val kit = getKit()
// Generate a new TON mnemonic explicitly (matches JS docs pattern)
val mnemonic = kit.createTonMnemonic()
val domain = if (network.isTetra) TONSignatureDomain.L2(value = 662387) else null
val signer = kit.createSignerFromMnemonic(mnemonic)
val adapter = when (version) {
WalletVersions.V4R2 -> kit.createV4R2Adapter(signer, network)
WalletVersions.V5R1 -> kit.createV5R1Adapter(signer, network)
WalletVersions.V4R2 -> kit.createV4R2Adapter(signer, network, domain = domain)
WalletVersions.V5R1 -> kit.createV5R1Adapter(signer, network, domain = domain)
else -> throw IllegalArgumentException("Unsupported wallet version: $version")
}
kit.addWallet(adapter)
Expand Down Expand Up @@ -1587,10 +1591,8 @@ class WalletKitViewModel @Inject constructor(
private const val TRANSACTION_FETCH_LIMIT = 20
private val DEFAULT_NETWORK = TONNetwork.MAINNET
private const val LOG_TAG = "WalletKitVM"
private const val ERROR_REQUEST_OBJECT_NOT_AVAILABLE = "Request object not available"
private const val DEFAULT_REJECTION_REASON = "User declined the connection"
private const val SIGNER_CONFIRMATION_CANCEL_REASON = "User cancelled signer confirmation"
private const val ERROR_DIRECT_SIGNING_UNSUPPORTED = "Direct signing not supported - use SDK's transaction/signData methods"

// TonConnect error codes (from @tonconnect/protocol)
private const val BAD_REQUEST_ERROR_CODE = 1 // Used for validation errors like insufficient balance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.TESTNET
import io.ton.walletkit.api.WalletVersions
import io.ton.walletkit.api.generated.TONNetwork
import io.ton.walletkit.api.generated.TONSignatureDomain
import io.ton.walletkit.api.isTetra
import io.ton.walletkit.demo.data.cache.TransactionCache
import io.ton.walletkit.demo.data.storage.DemoAppStorage
import io.ton.walletkit.demo.data.storage.UserPreferences
Expand Down Expand Up @@ -215,20 +217,26 @@ class WalletLifecycleManager(
continue
}

if (record.mnemonic.isEmpty()) {
Log.w(LOG_TAG, "rehydrate: skipping $storedAddress (mnemonic is empty)")
continue
}

val networkEnum = parseNetworkString(record.network, currentNetwork)
val version = record.version.ifBlank { defaultWalletVersion }
val name = record.name.ifBlank { defaultWalletNameProvider(restoredCount) }
val domain = if (networkEnum.isTetra) TONSignatureDomain.L2(value = 662387) else null

val result = runCatching {
when (version) {
WalletVersions.V4R2 -> {
val signer = kit.createSignerFromMnemonic(record.mnemonic)
val adapter = kit.createV4R2Adapter(signer, networkEnum)
val adapter = kit.createV4R2Adapter(signer, networkEnum, domain = domain)
kit.addWallet(adapter)
}
WalletVersions.V5R1 -> {
val signer = kit.createSignerFromMnemonic(record.mnemonic)
val adapter = kit.createV5R1Adapter(signer, networkEnum)
val adapter = kit.createV5R1Adapter(signer, networkEnum, domain = domain)
kit.addWallet(adapter)
}
else -> {
Expand Down
1 change: 1 addition & 0 deletions AndroidDemo/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<!-- Network names -->
<string name="network_mainnet">Mainnet</string>
<string name="network_testnet">Testnet</string>
<string name="network_tetra">Tetra</string>

<!-- Quick actions card -->
<string name="quick_actions_title">Quick Actions</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package io.ton.walletkit
import android.content.Context
import io.ton.walletkit.api.MAINNET
import io.ton.walletkit.api.generated.TONNetwork
import io.ton.walletkit.api.generated.TONSignatureDomain
import io.ton.walletkit.config.TONWalletKitConfiguration
import io.ton.walletkit.internal.TONWalletKitFactory
import io.ton.walletkit.listener.TONBridgeEventsHandler
Expand Down Expand Up @@ -66,7 +67,9 @@ interface ITONWalletKit {
/**
* Create a signer from a 32-byte secret key.
*/
suspend fun createSignerFromSecretKey(secretKey: ByteArray): WalletSignerInfo
suspend fun createSignerFromSecretKey(
secretKey: ByteArray,
): WalletSignerInfo

/**
* Create a signer from a custom [WalletSigner] (e.g. hardware wallet).
Expand All @@ -77,22 +80,28 @@ interface ITONWalletKit {

/**
* Create a V5R1 wallet adapter.
*
* @param domain Optional signature domain for L2 chains (e.g. Tetra).
*/
suspend fun createV5R1Adapter(
signer: WalletSignerInfo,
network: TONNetwork = TONNetwork.MAINNET,
workchain: Int = WalletKitConstants.DEFAULT_WORKCHAIN,
walletId: Long = WalletKitConstants.DEFAULT_WALLET_ID_V5R1,
domain: TONSignatureDomain? = null,
): TONWalletAdapter

/**
* Create a V4R2 wallet adapter.
*
* @param domain Optional signature domain for L2 chains (e.g. Tetra).
*/
suspend fun createV4R2Adapter(
signer: WalletSignerInfo,
network: TONNetwork = TONNetwork.MAINNET,
workchain: Int = WalletKitConstants.DEFAULT_WORKCHAIN,
walletId: Long = WalletKitConstants.DEFAULT_WALLET_ID_V4R2,
domain: TONSignatureDomain? = null,
): TONWalletAdapter

// ── Add wallet ──
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ package io.ton.walletkit.api
object ChainIds {
const val MAINNET = "-239"
const val TESTNET = "-3"
const val TETRA = "662387"
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,14 @@ val TONNetwork.Companion.MAINNET: TONNetwork
val TONNetwork.Companion.TESTNET: TONNetwork
get() = TONNetwork(chainId = ChainIds.TESTNET)

val TONNetwork.Companion.TETRA: TONNetwork
get() = TONNetwork(chainId = ChainIds.TETRA)

val TONNetwork.isTestnet: Boolean
get() = this == TONNetwork.TESTNET

val TONNetwork.isMainnet: Boolean
get() = this == TONNetwork.MAINNET

val TONNetwork.isTetra: Boolean
get() = chainId == ChainIds.TETRA
Loading
Loading