Skip to content
Open
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import org.json.JSONArray
import org.json.JSONObject
import java.lang.ref.WeakReference
Expand Down Expand Up @@ -608,7 +613,7 @@ internal class TonConnectInjector(
val appName: String,
val appVersion: String,
val maxProtocolVersion: Int,
val features: List<String>,
val features: List<JsonElement>,
)

@Serializable
Expand Down Expand Up @@ -657,30 +662,39 @@ internal class TonConnectInjector(
return Json.encodeToString(options)
}

private fun buildFeaturesList(features: List<TONWalletKitConfiguration.Feature>?): List<String> {
if (features.isNullOrEmpty()) return listOf("SendTransaction")
private fun buildFeaturesList(features: List<TONWalletKitConfiguration.Feature>?): List<JsonElement> {
if (features.isNullOrEmpty()) return listOf(JsonPrimitive("SendTransaction"))

val result = mutableListOf<String>()
val result = mutableListOf<JsonElement>()
for (feature in features) {
when (feature) {
is TONWalletKitConfiguration.SendTransactionFeature -> {
if (feature.maxMessages != null) {
val optionsJson = Json.encodeToString(mapOf("maxMessages" to feature.maxMessages))
result.add("SendTransaction")
result.add("SendTransaction:$optionsJson")
} else {
result.add("SendTransaction")
}
result.add(
buildJsonObject {
put("name", "SendTransaction")
feature.maxMessages?.let { put("maxMessages", it) }
feature.extraCurrencySupported?.let { put("extraCurrencySupported", it) }
},
)
result.add(JsonPrimitive("SendTransaction")) // legacy string required by TonConnect protocol
}
is TONWalletKitConfiguration.SignDataFeature -> {
if (feature.types.isNotEmpty()) {
val types = feature.types.map { it.name.lowercase() }
val typesJson = Json.encodeToString(mapOf("types" to types))
result.add("SignData:$typesJson")
}
result.add(
buildJsonObject {
put("name", "SignData")
put(
"types",
buildJsonArray {
for (type in feature.types) {
add(JsonPrimitive(type.name.lowercase()))
}
},
)
},
)
}
}
}
return result.distinct()
return result
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ package io.ton.walletkit.engine.infrastructure

import android.os.Handler
import io.ton.walletkit.WalletKitBridgeException
import io.ton.walletkit.api.generated.TONPreparedSignData
import io.ton.walletkit.api.generated.TONProofMessage
import io.ton.walletkit.api.generated.TONTransactionRequest
import io.ton.walletkit.browser.TonConnectInjector
import io.ton.walletkit.engine.parsing.EventParser
import io.ton.walletkit.engine.state.AdapterManager
Expand All @@ -35,6 +38,7 @@ import io.ton.walletkit.internal.constants.LogConstants
import io.ton.walletkit.internal.constants.ResponseConstants
import io.ton.walletkit.internal.constants.WebViewConstants
import io.ton.walletkit.internal.util.Logger
import io.ton.walletkit.model.TONWalletAdapter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -140,6 +144,41 @@ internal class MessageDispatcher(
// delivered back via window.__walletkitResponse(id, resultJson, errorJson).
// ──────────────────────────────────────────────────────────────────────────

private val requestHandlers = mapOf<String, suspend (params: JSONObject) -> String>(
REQUEST_METHOD_SIGN_WITH_CUSTOM_SIGNER to { params ->
val signerId = params.getString(ResponseConstants.KEY_SIGNER_ID)
val signer = signerManager.getSigner(signerId)
?: throw IllegalArgumentException("Custom signer not found: $signerId")
val dataArray = params.getJSONArray("data")
val bytes = ByteArray(dataArray.length()) { dataArray.getInt(it).toByte() }
signer.sign(bytes).value
},
REQUEST_METHOD_ADAPTER_GET_STATE_INIT to { params ->
params.requireAdapter().stateInit().value
},
REQUEST_METHOD_ADAPTER_SIGN_TRANSACTION to { params ->
val adapter = params.requireAdapter()
val request = json.decodeFromString<TONTransactionRequest>(params.getString("input"))
adapter.signedSendTransaction(request, params.optBoolean("fakeSignature", false)).value
},
REQUEST_METHOD_ADAPTER_SIGN_DATA to { params ->
val adapter = params.requireAdapter()
val request = json.decodeFromString<TONPreparedSignData>(params.getString("input"))
adapter.signedSignData(request, params.optBoolean("fakeSignature", false)).value
},
REQUEST_METHOD_ADAPTER_SIGN_TON_PROOF to { params ->
val adapter = params.requireAdapter()
val request = json.decodeFromString<TONProofMessage>(params.getString("input"))
adapter.signedTonProof(request, params.optBoolean("fakeSignature", false)).value
},
)

private fun JSONObject.requireAdapter(): TONWalletAdapter {
val adapterId = getString("adapterId")
return adapterManager.getAdapter(adapterId)
?: throw IllegalArgumentException("Adapter not found: $adapterId")
}

private fun handleRequest(payload: JSONObject) {
val id = payload.optString(ResponseConstants.KEY_ID)
val method = payload.optString(ResponseConstants.KEY_METHOD)
Expand All @@ -162,60 +201,14 @@ internal class MessageDispatcher(
}

/**
* Dispatches a reverse-RPC method to the appropriate native manager.
* Dispatches a reverse-RPC method to the appropriate native handler.
*
* @return The result as a raw string (already a JSON-safe value).
*/
private suspend fun executeNativeRequest(method: String, params: JSONObject): String {
return when (method) {
REQUEST_METHOD_SIGN_WITH_CUSTOM_SIGNER -> {
val signerId = params.getString(ResponseConstants.KEY_SIGNER_ID)
val dataArray = params.getJSONArray("data")
val bytes = ByteArray(dataArray.length()) { dataArray.getInt(it).toByte() }
val signer = signerManager.getSigner(signerId)
?: throw IllegalArgumentException("Custom signer not found: $signerId")
signer.sign(bytes).value
}

REQUEST_METHOD_ADAPTER_GET_STATE_INIT -> {
val adapterId = params.getString("adapterId")
val adapter = adapterManager.getAdapter(adapterId)
?: throw IllegalArgumentException("Adapter not found: $adapterId")
adapter.stateInit().value
}

REQUEST_METHOD_ADAPTER_SIGN_TRANSACTION -> {
val adapterId = params.getString("adapterId")
val inputJson = params.getString("input")
val fakeSignature = params.optBoolean("fakeSignature", false)
val adapter = adapterManager.getAdapter(adapterId)
?: throw IllegalArgumentException("Adapter not found: $adapterId")
val request = json.decodeFromString<io.ton.walletkit.api.generated.TONTransactionRequest>(inputJson)
adapter.signedSendTransaction(request, fakeSignature).value
}

REQUEST_METHOD_ADAPTER_SIGN_DATA -> {
val adapterId = params.getString("adapterId")
val inputJson = params.getString("input")
val fakeSignature = params.optBoolean("fakeSignature", false)
val adapter = adapterManager.getAdapter(adapterId)
?: throw IllegalArgumentException("Adapter not found: $adapterId")
val request = json.decodeFromString<io.ton.walletkit.api.generated.TONPreparedSignData>(inputJson)
adapter.signedSignData(request, fakeSignature).value
}

REQUEST_METHOD_ADAPTER_SIGN_TON_PROOF -> {
val adapterId = params.getString("adapterId")
val inputJson = params.getString("input")
val fakeSignature = params.optBoolean("fakeSignature", false)
val adapter = adapterManager.getAdapter(adapterId)
?: throw IllegalArgumentException("Adapter not found: $adapterId")
val request = json.decodeFromString<io.ton.walletkit.api.generated.TONProofMessage>(inputJson)
adapter.signedTonProof(request, fakeSignature).value
}

else -> throw IllegalArgumentException("Unknown reverse-RPC method: $method")
}
val handler = requestHandlers[method]
?: throw IllegalArgumentException("Unknown reverse-RPC method: $method")
return handler(params)
}

/**
Expand Down Expand Up @@ -276,11 +269,9 @@ internal class MessageDispatcher(
data.put(key, payload.get(key))
}
}
val readyEvent =
JSONObject().apply {
put(ResponseConstants.KEY_TYPE, ResponseConstants.VALUE_KIND_READY)
put(ResponseConstants.KEY_DATA, data)
}
val readyEvent = JSONObject()
.put(ResponseConstants.KEY_TYPE, ResponseConstants.VALUE_KIND_READY)
.put(ResponseConstants.KEY_DATA, data)
handleEvent(readyEvent)
}

Expand Down Expand Up @@ -360,16 +351,10 @@ internal class MessageDispatcher(

if (callId != null) {
// Create error response for this specific call
val errorResponse = JSONObject().apply {
put(ResponseConstants.KEY_KIND, ResponseConstants.VALUE_KIND_RESPONSE)
put(ResponseConstants.KEY_ID, callId)
put(
ResponseConstants.KEY_ERROR,
JSONObject().apply {
put(ResponseConstants.KEY_MESSAGE, exception.message ?: "Bridge error")
},
)
}
val errorResponse = JSONObject()
.put(ResponseConstants.KEY_KIND, ResponseConstants.VALUE_KIND_RESPONSE)
.put(ResponseConstants.KEY_ID, callId)
.put(ResponseConstants.KEY_ERROR, JSONObject().put(ResponseConstants.KEY_MESSAGE, exception.message ?: "Bridge error"))

// Dispatch the error response to fail the specific call
rpcClient.handleResponse(callId, errorResponse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import io.ton.walletkit.internal.constants.WebViewConstants
import io.ton.walletkit.internal.util.Logger
import io.ton.walletkit.model.TONBase64
import io.ton.walletkit.model.TONUserFriendlyAddress
import io.ton.walletkit.model.TONWalletAdapter
import io.ton.walletkit.session.SessionFilter
import io.ton.walletkit.session.TONConnectSessionManager
import kotlinx.coroutines.CompletableDeferred
Expand Down Expand Up @@ -278,6 +279,19 @@ internal class WebViewManager(
}

private inner class JsBinding {
private val adapterSyncHandlers =
mapOf<String, suspend (adapter: TONWalletAdapter, params: JSONObject) -> String>(
"getPublicKey" to { adapter, _ -> adapter.publicKey().value },
"getNetwork" to { adapter, _ ->
JSONObject().apply { put("chainId", adapter.network().chainId) }.toString()
},
"getAddress" to { adapter, _ -> adapter.address(adapter.network().isTestnet).value },
"getWalletId" to { adapter, _ -> adapter.identifier() },
"getSupportedFeatures" to { adapter, _ ->
adapter.supportedFeatures()?.let { featuresToJson(it).toString() } ?: "null"
},
)

@JavascriptInterface
fun postMessage(json: String) {
try {
Expand Down Expand Up @@ -327,18 +341,9 @@ internal class WebViewManager(
val adapterId = params.getString("adapterId")
val adapter = adapterManager.getAdapter(adapterId)
?: throw IllegalArgumentException("Adapter not found: $adapterId")
when (method) {
"getPublicKey" -> adapter.publicKey().value
"getNetwork" -> JSONObject().apply { put("chainId", adapter.network().chainId) }.toString()
"getAddress" -> adapter.address(adapter.network().isTestnet).value
"getWalletId" -> adapter.identifier()
"getSupportedFeatures" -> {
val features = adapter.supportedFeatures()
?: return@withTimeout "null"
featuresToJson(features).toString()
}
else -> throw IllegalArgumentException("Unknown sync adapter method: $method")
}
val handler = adapterSyncHandlers[method]
?: throw IllegalArgumentException("Unknown sync adapter method: $method")
handler(adapter, params)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import io.ton.walletkit.exceptions.JSValueConversionException
import io.ton.walletkit.internal.constants.BridgeMethodConstants
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import org.json.JSONObject
import kotlinx.serialization.json.encodeToJsonElement

/**
* Contains NFT and Jetton related bridge calls such as listing assets and building
Expand Down Expand Up @@ -120,16 +120,16 @@ internal class AssetOperations(
): String {
ensureInitialized()

val messageJson = json.encodeToString(io.ton.walletkit.api.generated.TONNFTRawTransferRequestMessage.serializer(), params.message)
val request = CreateTransferNftRawRequest(
walletId = walletId,
nftAddress = params.nftAddress.value,
transferAmount = params.transferAmount,
message = messageJson,
message = json.encodeToJsonElement(
io.ton.walletkit.api.generated.TONNFTRawTransferRequestMessage.serializer(),
params.message,
),
)
val requestObj = json.toJSONObject(request)
requestObj.put("message", JSONObject(messageJson))
val result = rpcClient.call(BridgeMethodConstants.METHOD_CREATE_TRANSFER_NFT_RAW_TRANSACTION, requestObj)
val result = rpcClient.call(BridgeMethodConstants.METHOD_CREATE_TRANSFER_NFT_RAW_TRANSACTION, json.toJSONObject(request))
return result.toString()
}

Expand Down
Loading