diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 000000000..f6e5dfe59 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,35 @@ +# EXAMPLE USAGE: +# +# Refer for explanation to following link: +# https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md +# +# pre-push: +# commands: +# packages-audit: +# tags: frontend security +# run: yarn audit +# gems-audit: +# tags: backend security +# run: bundle audit +# +# pre-commit: +# parallel: true +# commands: +# eslint: +# glob: "*.{js,ts,jsx,tsx}" +# run: yarn eslint {staged_files} +# rubocop: +# tags: backend style +# glob: "*.rb" +# exclude: '(^|/)(application|routes)\.rb$' +# run: bundle exec rubocop --force-exclusion {all_files} +# govet: +# tags: backend style +# files: git ls-files -m +# glob: "*.go" +# run: go vet {files} +# scripts: +# "hello.js": +# runner: node +# "any.go": +# runner: go run diff --git a/package-lock.json b/package-lock.json index 41e5a8257..e58e46cbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27566,7 +27566,7 @@ }, "packages/react-native-compat": { "name": "@walletconnect/react-native-compat", - "version": "2.17.2", + "version": "2.17.2-canary-ca-1", "license": "Apache-2.0", "dependencies": { "events": "3.3.0", diff --git a/packages/react-native-compat/android/build.gradle b/packages/react-native-compat/android/build.gradle index 1bbd37ebc..6da10a786 100644 --- a/packages/react-native-compat/android/build.gradle +++ b/packages/react-native-compat/android/build.gradle @@ -5,15 +5,25 @@ buildscript { repositories { google() mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { - classpath "com.android.tools.build:gradle:7.2.1" + classpath "com.android.tools.build:gradle:8.5.1" // noinspection DifferentKotlinGradleVersion classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } +allprojects { + repositories { + google() + mavenCentral() + maven { url 'https://jitpack.io' } + } +} + + def isNewArchitectureEnabled() { return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" } @@ -109,6 +119,11 @@ dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'net.java.dev.jna:jna:5.12.1@aar' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0' // Latest stable version + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0' // For Dispatchers.Main + implementation 'com.google.code.gson:gson:2.9.0' + implementation("com.github.reown-com:yttrium:0.4.11") } if (isNewArchitectureEnabled()) { diff --git a/packages/react-native-compat/android/gradle/wrapper/gradle-wrapper.properties b/packages/react-native-compat/android/gradle/wrapper/gradle-wrapper.properties index 41dfb8790..8406bcde1 100644 --- a/packages/react-native-compat/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/react-native-compat/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Thu Dec 12 15:47:44 EET 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/react-native-compat/android/src/main/java/com/walletconnect/reactnativemodule/RNWalletConnectModuleModule.kt b/packages/react-native-compat/android/src/main/java/com/walletconnect/reactnativemodule/RNWalletConnectModuleModule.kt index dea7399ca..cda691541 100644 --- a/packages/react-native-compat/android/src/main/java/com/walletconnect/reactnativemodule/RNWalletConnectModuleModule.kt +++ b/packages/react-native-compat/android/src/main/java/com/walletconnect/reactnativemodule/RNWalletConnectModuleModule.kt @@ -4,6 +4,14 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.Promise import android.content.pm.PackageManager +import uniffi.uniffi_yttrium.ChainAbstractionClient +import kotlinx.coroutines.* +import com.facebook.react.bridge.ReadableMap +import uniffi.uniffi_yttrium.* +import uniffi.yttrium.* +import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.gson.JsonElement class RNWalletConnectModuleModule internal constructor(context: ReactApplicationContext) : RNWalletConnectModuleSpec(context) { @@ -49,6 +57,215 @@ class RNWalletConnectModuleModule internal constructor(context: ReactApplication } } + + +// ------------------------------ Yttrium Chain Abstraction ------------------------------ + + private var availableResponseMap: MutableMap = mutableMapOf() + + + @ReactMethod + override fun prepare(params: ReadableMap, promise: Promise){ + System.out.println("checkRoute: Hello from YttriumModule") + GlobalScope.launch(Dispatchers.Main) { + try { + var projectId = params.getString("projectId") as String + val transactionMap = params.getMap("transaction") + var client = ChainAbstractionClient(projectId) + + if (transactionMap != null) { + // Extract values from the nested transaction map + val chainId = transactionMap.getString("chainId") ?: "" + val txData = transactionMap.getString("data") ?: "" + val from = transactionMap.getString("from") ?: "" + val to = transactionMap.getString("to") ?: "" + val value = transactionMap.getString("value") ?: "0" + val tx = InitialTransaction(chainId, from, to, value, txData) + val result = client.prepare(tx) + System.out.println("checkRoute: result: ") + System.out.println(result) + when (result) { + is PrepareResponse.Success -> { + when (result.v1) { + is RouteResponseSuccess.Available -> { + val availableResult = (result.v1 as RouteResponseSuccess.Available).v1 + availableResponseMap[availableResult.orchestrationId] = availableResult + val gson = Gson() + val routesJson: JsonElement = gson.toJsonTree(availableResult) + val response = JsonObject() + response.addProperty("status", "available") + response.add("data", routesJson) + promise.resolve(gson.toJson(response)) + } + is RouteResponseSuccess.NotRequired -> { + val response = JsonObject() + response.addProperty("status", "not_required") + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + } + } + is PrepareResponse.Error -> { + System.out.println(result.v1.error.toString()) + when (result.v1.error.toString()) { + "NO_ROUTES_AVAILABLE" -> { + val response = JsonObject() + response.addProperty("status", "error") + response.addProperty("reason", "noRoutesAvailable") + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + "INSUFFICIENT_FUNDS" -> { + val response = JsonObject() + response.addProperty("status", "error") + response.addProperty("reason", "insufficientFunds") + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + "INSUFFICIENT_GAS_FUNDS" -> { + val response = JsonObject() + response.addProperty("status", "error") + response.addProperty("reason", "insufficientGasFunds") + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + } + } + } + + } + // Resolve the promise with the result + } catch (e: Exception) { + // In case of an error, reject the promise + promise.reject("ERROR", "Yttrium checkRoute Error:" + e.message, e) + } + } + } + + @ReactMethod + override fun status(params: ReadableMap, promise: Promise){ + System.out.println("checkStatus: Hello from YttriumModule address") + + GlobalScope.launch(Dispatchers.Main) { + try { + + var projectId = params.getString("projectId") as String + var orchestrationId = params.getString("orchestrationId") as String + var client = ChainAbstractionClient(projectId) + + when (val result = client.status(orchestrationId)) { + is StatusResponse.Completed -> { + when (result.v1) { + is StatusResponseCompleted -> { + val response = JsonObject() + response.addProperty("status", "completed") + response.addProperty("createdAt", result.v1.createdAt.toString()) + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + } + } + + is StatusResponse.Error -> { + when (result.v1) { + is StatusResponseError -> { + val response = JsonObject() + response.addProperty("status", "error") + response.addProperty("createdAt", result.v1.createdAt.toString()) + response.addProperty("reason", result.v1.error.toString()) + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + } + } + + is StatusResponse.Pending -> { + when (result.v1) { + is StatusResponsePending -> { + val response = JsonObject() + response.addProperty("status", "pending") + response.addProperty("createdAt", result.v1.createdAt.toString()) + response.addProperty("checkIn", result.v1.checkIn.toString()) + val gson = Gson() + promise.resolve(gson.toJson(response)) + } + } + } + } + } catch (e: Exception) { + // In case of an error, reject the promise + promise.reject("ERROR", "Yttrium checkStatus Error:" + e.message, e) + } + } + } + + @ReactMethod + override fun getBridgeDetails(params: ReadableMap, promise: Promise){ + System.out.println("getFulfilmentDetails: Hello from YttriumModule address") + + GlobalScope.launch(Dispatchers.Main) { + try { + + val projectId = params.getString("projectId") as String + val orchestrationId = params.getString("orchestrationId") as String + val client = ChainAbstractionClient(projectId) + + val availableResult = availableResponseMap[orchestrationId] as RouteResponseAvailable + val uiFields = client.getUiFields(availableResult, Currency.USD) + val gson = Gson() + val resultJson: JsonElement = gson.toJsonTree(uiFields) + promise.resolve(gson.toJson(resultJson)) + } catch (e: Exception) { + // In case of an error, reject the promise + promise.reject("ERROR", "Yttrium getFulfilmentDetails Error:" + e.message, e) + } + } + } + + @ReactMethod + override fun getERC20Balance(params: ReadableMap, promise: Promise){ + System.out.println("getERC20Balance: Hello from YttriumModule address") + + GlobalScope.launch(Dispatchers.Main) { + try { + + val projectId = params.getString("projectId") as String + val tokenAddress = params.getString("tokenAddress") as String + val ownerAddress = params.getString("ownerAddress") as String + val chainId = params.getString("chainId") as String + val client = ChainAbstractionClient(projectId) + val result = client.erc20TokenBalance(chainId, tokenAddress, ownerAddress) + val gson = Gson() + val resultJson: JsonElement = gson.toJsonTree(result) + promise.resolve(gson.toJson(resultJson)) + } catch (e: Exception) { + // In case of an error, reject the promise + promise.reject("ERROR", "Yttrium getERC20Balance Error:" + e.message, e) + } + } + } + + @ReactMethod + override fun estimateFees(params: ReadableMap, promise: Promise){ + System.out.println("estimateFees: Hello from YttriumModule address") + + GlobalScope.launch(Dispatchers.Main) { + try { + + val projectId = params.getString("projectId") as String + val chainId = params.getString("chainId") as String + val client = ChainAbstractionClient(projectId) + val result = client.estimateFees(chainId) + val gson = Gson() + val resultJson: JsonElement = gson.toJsonTree(result) + promise.resolve(gson.toJson(resultJson)) + } catch (e: Exception) { + // In case of an error, reject the promise + promise.reject("ERROR", "Yttrium estimateFees Error:" + e.message, e) + } + } + } + companion object { const val NAME = "RNWalletConnectModule" } diff --git a/packages/react-native-compat/android/src/oldarch/RNWalletConnectModuleSpec.kt b/packages/react-native-compat/android/src/oldarch/RNWalletConnectModuleSpec.kt index 485d9b51d..5bf2b844c 100644 --- a/packages/react-native-compat/android/src/oldarch/RNWalletConnectModuleSpec.kt +++ b/packages/react-native-compat/android/src/oldarch/RNWalletConnectModuleSpec.kt @@ -3,11 +3,18 @@ package com.walletconnect.reactnativemodule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReadableMap abstract class RNWalletConnectModuleSpec internal constructor(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) { abstract fun isAppInstalled(packageName: String?, promise: Promise); + abstract fun prepare(params: ReadableMap, promise: Promise); + abstract fun status(params: ReadableMap, promise: Promise); + abstract fun getBridgeDetails(params: ReadableMap, promise: Promise); + abstract fun estimateFees(params: ReadableMap, promise: Promise); + abstract fun getERC20Balance(params: ReadableMap, promise: Promise); + protected abstract fun getTypedExportedConstants(): Map override fun getConstants(): Map { diff --git a/packages/react-native-compat/index.js b/packages/react-native-compat/index.js index 89e7f457e..86d812bda 100644 --- a/packages/react-native-compat/index.js +++ b/packages/react-native-compat/index.js @@ -1,3 +1,4 @@ +import { NativeModules } from "react-native"; import { getApplicationModule } from "./module"; // Polyfill TextEncode / TextDecode @@ -75,3 +76,9 @@ if (typeof global?.Application === "undefined") { console.error("react-native-compat: Application module is not available"); } } + +// iOS uses Yttrium, Android uses RNWalletConnectModule +global.yttrium = NativeModules.Yttrium || NativeModules.RNWalletConnectModule; + +// eslint-disable-next-line no-console +console.log("RN yttrium", global.yttrium); diff --git a/packages/react-native-compat/ios/Yttrium-Bridging-Header.h b/packages/react-native-compat/ios/Yttrium-Bridging-Header.h new file mode 100644 index 000000000..dea7ff6bf --- /dev/null +++ b/packages/react-native-compat/ios/Yttrium-Bridging-Header.h @@ -0,0 +1,2 @@ +#import +#import diff --git a/packages/react-native-compat/ios/Yttrium.mm b/packages/react-native-compat/ios/Yttrium.mm new file mode 100644 index 000000000..d0dc4cff8 --- /dev/null +++ b/packages/react-native-compat/ios/Yttrium.mm @@ -0,0 +1,35 @@ +#import + +@interface RCT_EXTERN_MODULE(Yttrium, NSObject) + +//RCT_EXTERN_METHOD(multiply:(float)a withB:(float)b +// withResolver:(RCTPromiseResolveBlock)resolve +// withRejecter:(RCTPromiseRejectBlock)reject) +// +RCT_EXTERN_METHOD(status:(id)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(prepare:(id)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getBridgeDetails:(id)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getERC20Balance:(id)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(estimateFees:(id)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) + + ++ (BOOL)requiresMainQueueSetup +{ + return NO; +} + +@end diff --git a/packages/react-native-compat/ios/Yttrium.swift b/packages/react-native-compat/ios/Yttrium.swift new file mode 100644 index 000000000..b72539939 --- /dev/null +++ b/packages/react-native-compat/ios/Yttrium.swift @@ -0,0 +1,304 @@ +import YttriumWrapper + + +@objc(Yttrium) +class Yttrium: NSObject { + + @objc + func status(_ params: Any, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + print("checkStatus called with", params ) + if let dict = params as? [String: Any], + let projectId = dict["projectId"] as? String, + let orchestrationId = dict["orchestrationId"] as? String { + let client = ChainAbstractionClient.init(projectId: projectId) + Task { + do { + let statusResponse = try await client.status(orchestrationId: orchestrationId) + + switch statusResponse { + case let .completed(statusResponseCompleted): + print("status response completed", statusResponseCompleted) + let responseDict: [String: Any] = [ + "createdAt": statusResponseCompleted.createdAt, + "status": "completed" + ] + resolve(responseDict) + case let .error(statusResponseError): + print("status response error", statusResponseError) + let responseDict: [String: Any] = [ + "createdAt": statusResponseError.createdAt, + "reason": statusResponseError.error, + "status": "error" + ] + resolve(responseDict) + case let .pending(statusResponsePending): + print("status response pending", statusResponsePending) + let responseDict: [String: Any] = [ + "createdAt": statusResponsePending.createdAt, + "checkIn": statusResponsePending.checkIn, + "status": "pending" + ] + resolve(responseDict) + } + } catch { + print("Error occurred: \(error)") + print(error) + reject("checkStatus err", "checkStatus", error) + } + } + } + } + + func convertRouteResponseAvailableToDictionary(_ routeResponse: RouteResponseAvailable) -> [String: Any] { + func transactionToDictionary(_ transaction: YttriumWrapper.Transaction) -> [String: Any] { + return [ + "chainId": transaction.chainId, + "from": transaction.from, + "to": transaction.to, + "value": transaction.value, + "input": transaction.input, + "gasLimit": transaction.gasLimit, + "nonce": transaction.nonce + ] + } + + func fundingMetadataToDictionary(_ metadata: YttriumWrapper.FundingMetadata) -> [String: Any] { + return [ + "chainId": metadata.chainId, + "tokenContract": metadata.tokenContract, + "symbol": metadata.symbol, + "amount": metadata.amount, + "bridgingFee": metadata.bridgingFee, + "decimals": metadata.decimals + ] + } + + func initialTransactionMetadataToDictionary(_ metadata: YttriumWrapper.InitialTransactionMetadata) -> [String: Any] { + return [ + "transferTo": metadata.transferTo, + "amount": metadata.amount, + "tokenContract": metadata.tokenContract, + "symbol": metadata.symbol, + "decimals": metadata.decimals + ] + } + + func metadataToDictionary(_ metadata: YttriumWrapper.Metadata) -> [String: Any] { + return [ + "fundingFrom": metadata.fundingFrom.map { fundingMetadataToDictionary($0) }, + "initialTransaction": initialTransactionMetadataToDictionary(metadata.initialTransaction), + "checkIn": metadata.checkIn + ] + } + + return [ + "orchestrationId": routeResponse.orchestrationId, + "initialTransaction": transactionToDictionary(routeResponse.initialTransaction), + "transactions": routeResponse.transactions.map { transactionToDictionary($0) }, + "metadata": metadataToDictionary(routeResponse.metadata) + ] + } + + private var availableResponseDictionary: [String: RouteResponseAvailable] = [:] + + @objc + func prepare(_ params: Any, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + print("checkRoute called with", params) + let dict = params as? [String: Any] + + if let transactionData = dict?["transaction"] as? [String: String], + let from = transactionData["from"] ?? "" as Optional, + let chainId = transactionData["chainId"] ?? "" as Optional, + let data = transactionData["data"] ?? "" as Optional, + let value = transactionData["value"] ?? "" as Optional, + let to = transactionData["to"] ?? "" as Optional, + let projectId = dict?["projectId"] as? String { + + let client = ChainAbstractionClient.init(projectId: projectId) + print("created client, checking route...") + Task { + do { + let transaction = InitialTransaction.init(chainId: chainId, from: from, to: to, value: value, input: data) + + let routeResponseSuccess = try await client.prepare(initialTransaction: transaction) + print("result", routeResponseSuccess) + + switch routeResponseSuccess { + case let .success(routeResponse): + switch routeResponse { + case let .available(availableResponse): + + availableResponseDictionary[availableResponse.orchestrationId] = availableResponse; + // let uiFields = try await client.getRouteUiFields(routeResponse: availableResponse, initialTransaction: Transaction(from: from, to: to, value: value, gas: gas, data: data, nonce: nonce, chainId: chainId, gasPrice: gasPrice, maxFeePerGas: maxFeePerGas, maxPriorityFeePerGas: maxPriorityFeePerGas), currency: Currency.usd) + // + // let routesDetails = convertRouteUiFieldsToDictionary(uiFields) +// print("available result", availableResponse) + // print("ui_fields_json", routesDetails) + let responseDict = convertRouteResponseAvailableToDictionary(availableResponse) + print("parsed result dictionary", responseDict) + resolve(["status": "available", "data": responseDict]) +// "routesDetails": routesDetails + + case .notRequired(_): + print("not required") + resolve(["status": "not_required"]) + } + case let .error(routeResponse): + switch routeResponse.error { + case BridgingError.insufficientFunds: + let responseDict: [String: Any] = [ + "status": "error", + "reason": "insufficientFunds" + ] + resolve(responseDict) + case BridgingError.insufficientGasFunds: + let responseDict: [String: Any] = [ + "status": "error", + "reason": "insufficientGasFunds" + ] + resolve(responseDict) + case BridgingError.noRoutesAvailable: + let responseDict: [String: Any] = [ + "status": "error", + "reason": "noRoutesAvailable" + ] + resolve(responseDict) + } + print(routeResponse) + print(routeResponse.error) + } + // resolve(result) + } catch { + print("Error occurred: \(error)") + print(error) + reject("yttrium err", "yttrium_err", error) + } + } + } + } + + func convertUiFieldsToDictionary(_ uiFields: UiFields) -> [String: Any] { + func feeEstimatedTransactionToDictionary(_ transaction: YttriumWrapper.FeeEstimatedTransaction) -> [String: Any] { + return [ + "chainId": transaction.chainId, + "from": transaction.from, + "to": transaction.to, + "value": transaction.value, + "input": transaction.input, + "gasLimit": transaction.gasLimit, + "nonce": transaction.nonce, + "maxFeePerGas": transaction.maxFeePerGas, + "maxPriorityFeePerGas": transaction.maxPriorityFeePerGas + ] + } + + func amountToDictionary(_ amount: YttriumWrapper.Amount) -> [String: Any] { + return [ + "symbol": amount.symbol, + "amount": amount.amount, + "unit": amount.unit, + "formatted": amount.formatted, + "formattedAlt": amount.formattedAlt + ] + } + + func transactionFeeToDictionary(_ fee: YttriumWrapper.TransactionFee) -> [String: Any] { + return [ + "fee": amountToDictionary(fee.fee), + "localFee": amountToDictionary(fee.localFee) + ] + } + + func txnDetailsToDictionary(_ txnDetails: YttriumWrapper.TxnDetails) -> [String: Any] { + return [ + "transaction": feeEstimatedTransactionToDictionary(txnDetails.transaction), + "fee": transactionFeeToDictionary(txnDetails.fee) + ] + } + + return [ + "route": uiFields.route.map { txnDetailsToDictionary($0) }, + "localRouteTotal": amountToDictionary(uiFields.localRouteTotal), + "bridge": uiFields.bridge.map { transactionFeeToDictionary($0) }, + "localBridgeTotal": amountToDictionary(uiFields.localBridgeTotal), + "initial": txnDetailsToDictionary(uiFields.initial), + "localTotal": amountToDictionary(uiFields.localTotal) + ] + } + + @objc + func getBridgeDetails(_ params: Any, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + print("getBridgeDetails called with", params) + let dict = params as? [String: String] + + if let orchestrationId = dict?["orchestrationId"] ?? "" as Optional, + let projectId = dict?["projectId"] as? String { + + let client = ChainAbstractionClient.init(projectId: projectId) + print("created client, getting UI fields...") + Task { + do { + + let availableResponse = availableResponseDictionary[orchestrationId]! + let uiFields = try await client.getUiFields(routeResponse: availableResponse, currency: Currency.usd) + let uiFIeldsDict = convertUiFieldsToDictionary(uiFields) + print("getBridgeDetails result", uiFields) + resolve(uiFIeldsDict) + } catch { + print("Error occurred: \(error)") + print(error) + reject("yttrium err", "yttrium_err getBridgeDetails", error) + } + } + } + } + + @objc + func getERC20Balance(_ params: Any, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + print("getERC20Balance called with", params) + let dict = params as? [String: String] + + if let tokenAddress = dict?["tokenAddress"] ?? "" as Optional, + let ownerAddress = dict?["ownerAddress"] ?? "" as Optional, + let chainId = dict?["chainId"] ?? "" as Optional, + let projectId = dict?["projectId"] as? String { + + let client = ChainAbstractionClient.init(projectId: projectId) + Task { + do { + let balance = try await client.erc20TokenBalance(chainId: chainId, token: tokenAddress, owner: ownerAddress) + print("getERC20Balance result", balance) + resolve(balance) + } catch { + print("Error occurred: \(error)") + print(error) + reject("yttrium err", "yttrium_err getERC20Balance", error) + } + } + } + } + + @objc + func estimateFees(_ params: Any, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + print("getERC20Balance called with", params) + let dict = params as? [String: String] + + if let chainId = dict?["chainId"] ?? "" as Optional, + let projectId = dict?["projectId"] as? String { + + let client = ChainAbstractionClient.init(projectId: projectId) + Task { + do { + let fees = try await client.estimateFees(chainId: chainId) + print("estimateFees result", fees) + resolve(["maxFeePerGas": fees.maxFeePerGas, "maxPriorityFeePerGas": fees.maxPriorityFeePerGas]) + } catch { + print("Error occurred: \(error)") + print(error) + reject("yttrium err", "yttrium_err estimateFees", error) + } + } + } + } + +} diff --git a/packages/react-native-compat/package.json b/packages/react-native-compat/package.json index 82cecfbde..925b5ef4e 100644 --- a/packages/react-native-compat/package.json +++ b/packages/react-native-compat/package.json @@ -1,7 +1,7 @@ { "name": "@walletconnect/react-native-compat", "description": "Shims for WalletConnect Protocol in React Native Projects", - "version": "2.17.2", + "version": "2.17.2-canary-ca-1", "author": "WalletConnect, Inc. ", "homepage": "https://github.com/walletconnect/walletconnect-monorepo/", "license": "Apache-2.0", diff --git a/packages/react-native-compat/react-native-compat.podspec b/packages/react-native-compat/react-native-compat.podspec index bf2634a69..32492ec07 100644 --- a/packages/react-native-compat/react-native-compat.podspec +++ b/packages/react-native-compat/react-native-compat.podspec @@ -14,7 +14,8 @@ Pod::Spec.new do |s| s.platforms = { :ios => "11.0", :visionos => "1.0" } s.source = { :git => "https://github.com/walletconnect/walletconnect-monorepo.git", :tag => "#{s.version}" } - s.source_files = "ios/**/*.{h,m,mm}" + s.source_files = "ios/**/*.{h,m,mm,swift}" + s.dependency 'YttriumWrapper' , '0.4.10' # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.