Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: RN Yttrium #5550

Draft
wants to merge 11 commits into
base: v2.0
Choose a base branch
from
Draft
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
35 changes: 35 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion packages/react-native-compat/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -49,6 +57,215 @@ class RNWalletConnectModuleModule internal constructor(context: ReactApplication
}
}



// ------------------------------ Yttrium Chain Abstraction ------------------------------

private var availableResponseMap: MutableMap<String, RouteResponseAvailable> = 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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String>

override fun getConstants(): Map<String, String> {
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native-compat/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NativeModules } from "react-native";
import { getApplicationModule } from "./module";

// Polyfill TextEncode / TextDecode
Expand Down Expand Up @@ -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);
2 changes: 2 additions & 0 deletions packages/react-native-compat/ios/Yttrium-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#import <React/RCTBridgeModule.h>
#import <React/RCTViewManager.h>
35 changes: 35 additions & 0 deletions packages/react-native-compat/ios/Yttrium.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#import <React/RCTBridgeModule.h>

@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
Loading