Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
77d54c2
feature/Add Ethereum connector for handling ETH transactions and corr…
hongwei1 Sep 18, 2025
b6173f9
feature/Add Ethereum transaction request handling and models in API v…
hongwei1 Sep 18, 2025
01116eb
feature/Add Ethereum transaction request handling and models in API v…
hongwei1 Sep 18, 2025
c523fb9
test/Comment out payment test in EthereumConnector_vSept2025Test for …
hongwei1 Sep 19, 2025
49daedf
feature/Enhance EthereumConnector_vSept2025 to support eth_sendRawTra…
hongwei1 Sep 23, 2025
07c1b5a
test/Comment out payment test cases in EthereumConnector_vSept2025Tes…
hongwei1 Sep 23, 2025
5db5db2
feature/Initialize SBT build configuration with project settings, dep…
hongwei1 Sep 23, 2025
7ff31a1
refactor/Update Ethereum transaction request structure to simplify JS…
hongwei1 Sep 24, 2025
cc7b925
feature/Add DecodeRawTx utility for decoding Ethereum raw transaction…
hongwei1 Sep 24, 2025
dd07760
feature/Update Ethereum transaction request types to ETH_SEND_TRANSAC…
hongwei1 Sep 24, 2025
b8a4b92
test/Add DecodeRawTxTest for validating JSON output of legacy signed …
hongwei1 Sep 24, 2025
9f84db2
feature/Add TransactionRequestBodyEthSendRawTransactionJsonV600 for h…
hongwei1 Sep 25, 2025
3ff9e90
feature/Enhance DecodeRawTx to return DecodedTxResponse case class, i…
hongwei1 Sep 25, 2025
187c51b
refactor/Update DecodedTxResponse to use Option types for improved sa…
hongwei1 Sep 25, 2025
e5b3b7c
feature/Implement ETH_SEND_RAW_TRANSACTION handling in LocalMappedCon…
hongwei1 Sep 25, 2025
e92890c
refactor/Update Ethereum transaction request structure to replace 'to…
hongwei1 Sep 25, 2025
c28aaf0
test/ commented
hongwei1 Sep 25, 2025
4b3bd24
docfix/ Update release notes and sample properties for Ethereum Conne…
hongwei1 Sep 26, 2025
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
199 changes: 199 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
ThisBuild / version := "1.10.1"
ThisBuild / scalaVersion := "2.12.20"
ThisBuild / organization := "com.tesobe"

// Java version compatibility
ThisBuild / javacOptions ++= Seq("-source", "11", "-target", "11")
ThisBuild / scalacOptions ++= Seq(
"-unchecked",
"-explaintypes",
"-target:jvm-1.8",
"-Yrangepos"
)

// Enable SemanticDB for Metals
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := "4.13.9"

// Fix dependency conflicts
ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always

lazy val liftVersion = "3.5.0"
lazy val akkaVersion = "2.5.32"
lazy val jettyVersion = "9.4.50.v20221201"
lazy val avroVersion = "1.8.2"

lazy val commonSettings = Seq(
resolvers ++= Seq(
"Sonatype OSS Releases" at "https://oss.sonatype.org/content/repositories/releases",
"Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
"Artima Maven Repository" at "https://repo.artima.com/releases",
"OpenBankProject M2 Repository" at "https://raw.githubusercontent.com/OpenBankProject/OBP-M2-REPO/master",
"jitpack.io" at "https://jitpack.io"
)
)

lazy val obpCommons = (project in file("obp-commons"))
.settings(
commonSettings,
name := "obp-commons",
libraryDependencies ++= Seq(
"net.liftweb" %% "lift-common" % liftVersion,
"net.liftweb" %% "lift-util" % liftVersion,
"net.liftweb" %% "lift-mapper" % liftVersion,
"org.scala-lang" % "scala-reflect" % "2.12.20",
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.scalactic" %% "scalactic" % "3.2.15",
"net.liftweb" %% "lift-json" % liftVersion,
"com.alibaba" % "transmittable-thread-local" % "2.11.5",
"org.apache.commons" % "commons-lang3" % "3.12.0",
"org.apache.commons" % "commons-text" % "1.10.0",
"com.google.guava" % "guava" % "32.0.0-jre"
)
)

lazy val obpApi = (project in file("obp-api"))
.dependsOn(obpCommons)
.settings(
commonSettings,
name := "obp-api",
libraryDependencies ++= Seq(
// Core dependencies
"net.liftweb" %% "lift-mapper" % liftVersion,
"net.databinder.dispatch" %% "dispatch-lift-json" % "0.13.1",
"ch.qos.logback" % "logback-classic" % "1.2.13",
"org.slf4j" % "log4j-over-slf4j" % "1.7.26",
"org.slf4j" % "slf4j-ext" % "1.7.26",

// Security
"org.bouncycastle" % "bcpg-jdk15on" % "1.70",
"org.bouncycastle" % "bcpkix-jdk15on" % "1.70",
"com.nimbusds" % "nimbus-jose-jwt" % "9.37.2",
"com.nimbusds" % "oauth2-oidc-sdk" % "9.27",

// Commons
"org.apache.commons" % "commons-lang3" % "3.12.0",
"org.apache.commons" % "commons-text" % "1.10.0",
"org.apache.commons" % "commons-email" % "1.5",
"org.apache.commons" % "commons-compress" % "1.26.0",
"org.apache.commons" % "commons-pool2" % "2.11.1",

// Database
"org.postgresql" % "postgresql" % "42.4.4",
"com.h2database" % "h2" % "2.2.220" % Runtime,
"mysql" % "mysql-connector-java" % "8.0.30",
"com.microsoft.sqlserver" % "mssql-jdbc" % "11.2.0.jre11",

// Web
"javax.servlet" % "javax.servlet-api" % "3.1.0" % Provided,
"org.eclipse.jetty" % "jetty-server" % jettyVersion % Test,
"org.eclipse.jetty" % "jetty-webapp" % jettyVersion % Test,
"org.eclipse.jetty" % "jetty-util" % jettyVersion,

// Akka
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-remote" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"com.typesafe.akka" %% "akka-http-core" % "10.1.6",

// Avro
"com.sksamuel.avro4s" %% "avro4s-core" % avroVersion,

// Twitter
"com.twitter" %% "chill-akka" % "0.9.1",
"com.twitter" %% "chill-bijection" % "0.9.1",

// Cache
"com.github.cb372" %% "scalacache-redis" % "0.9.3",
"com.github.cb372" %% "scalacache-guava" % "0.9.3",

// Utilities
"com.github.dwickern" %% "scala-nameof" % "1.0.3",
"org.javassist" % "javassist" % "3.25.0-GA",
"com.alibaba" % "transmittable-thread-local" % "2.14.2",
"org.clapper" %% "classutil" % "1.4.0",
"com.github.grumlimited" % "geocalc" % "0.5.7",
"com.github.OpenBankProject" % "scala-macros" % "v1.0.0-alpha.3",
"org.scalameta" %% "scalameta" % "3.7.4",

// Akka Adapter - exclude transitive dependency on obp-commons to use local module
"com.github.OpenBankProject.OBP-Adapter-Akka-SpringBoot" % "adapter-akka-commons" % "v1.1.0" exclude("com.github.OpenBankProject.OBP-API", "obp-commons"),

// JSON Schema
"com.github.everit-org.json-schema" % "org.everit.json.schema" % "1.6.1",
"com.networknt" % "json-schema-validator" % "1.0.87",

// Swagger
"io.swagger.parser.v3" % "swagger-parser" % "2.0.13",

// Text processing
"org.atteo" % "evo-inflector" % "1.2.2",

// Payment
"com.stripe" % "stripe-java" % "12.1.0",
"com.twilio.sdk" % "twilio" % "9.2.0",

// gRPC
"com.thesamet.scalapb" %% "scalapb-runtime-grpc" % "0.8.4",
"io.grpc" % "grpc-all" % "1.48.1",
"io.netty" % "netty-tcnative-boringssl-static" % "2.0.27.Final",
"org.asynchttpclient" % "async-http-client" % "2.10.4",

// Database utilities
"org.scalikejdbc" %% "scalikejdbc" % "3.4.0",

// XML
"org.scala-lang.modules" %% "scala-xml" % "1.2.0",

// IBAN
"org.iban4j" % "iban4j" % "3.2.7-RELEASE",

// JavaScript
"org.graalvm.js" % "js" % "22.0.0.2",
"org.graalvm.js" % "js-scriptengine" % "22.0.0.2",
"ch.obermuhlner" % "java-scriptengine" % "2.0.0",

// Hydra
"sh.ory.hydra" % "hydra-client" % "1.7.0",

// HTTP
"com.squareup.okhttp3" % "okhttp" % "4.12.0",
"com.squareup.okhttp3" % "logging-interceptor" % "4.12.0",
"org.apache.httpcomponents" % "httpclient" % "4.5.13",

// RabbitMQ
"com.rabbitmq" % "amqp-client" % "5.22.0",
"net.liftmodules" %% "amqp_3.1" % "1.5.0",

// Elasticsearch
"org.elasticsearch" % "elasticsearch" % "8.14.0",
"com.sksamuel.elastic4s" %% "elastic4s-client-esjava" % "8.5.2",

// OAuth
"oauth.signpost" % "signpost-commonshttp4" % "1.2.1.2",

// Utilities
"cglib" % "cglib" % "3.3.0",
"com.sun.activation" % "jakarta.activation" % "1.2.2",
"com.nulab-inc" % "zxcvbn" % "1.9.0",

// Testing - temporarily disabled due to version incompatibility
// "org.scalatest" %% "scalatest" % "2.2.6" % Test,

// Jackson
"com.fasterxml.jackson.core" % "jackson-databind" % "2.12.7.1",

// Flexmark (markdown processing)
"com.vladsch.flexmark" % "flexmark-profile-pegdown" % "0.40.8",
"com.vladsch.flexmark" % "flexmark-util-options" % "0.64.0",

// Connection pool
"com.zaxxer" % "HikariCP" % "4.0.3",

// Test dependencies
"junit" % "junit" % "4.13.2" % Test,
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.seleniumhq.selenium" % "htmlunit-driver" % "2.36.0" % Test,
"org.testcontainers" % "rabbitmq" % "1.20.3" % Test
)
)
6 changes: 6 additions & 0 deletions obp-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@
<artifactId>flexmark-util-options</artifactId>
<version>0.64.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.web3j/core -->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.9.8</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
Expand Down
12 changes: 12 additions & 0 deletions obp-api/src/main/resources/props/sample.props.template
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,18 @@ regulated_entities = []
# super_admin_email=tom@tesobe.com


## Ethereum Connector Configuration
## ================================
## The Ethereum connector uses JSON-RPC to communicate with Ethereum nodes.
## It supports two transaction modes:
## 1) eth_sendRawTransaction - for pre-signed transactions (recommended for production)
## 2) eth_sendTransaction - for unlocked accounts (development/test environments)
##
## Ethereum RPC endpoint URL
## Default: http://127.0.0.1:8545 (local Ethereum node)
ethereum.rpc.url=http://127.0.0.1:8545


# Note: For secure and http only settings for cookies see resources/web.xml which is mentioned in the README.md


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import com.openbankproject.commons.model._
import com.openbankproject.commons.model.enums.TransactionRequestTypes._
import com.openbankproject.commons.model.enums.{AttributeCategory, CardAttributeType, ChallengeType, TransactionRequestStatus}
import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, ReflectUtils}
import net.liftweb.common.Full
import net.liftweb.json

import java.net.URLEncoder
Expand Down Expand Up @@ -5741,6 +5740,16 @@ object SwaggerDefinitionsJSON {
description = descriptionExample.value,
metadata = Some(Map("202507022319" -> cardanoMetadataStringJsonV600))
)

lazy val transactionRequestBodyEthereumJsonV600 = TransactionRequestBodyEthereumJsonV600(
to = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
value = AmountOfMoneyJsonV121("ETH", "0.01"),
description = descriptionExample.value
)
lazy val transactionRequestBodyEthSendRawTransactionJsonV600 = TransactionRequestBodyEthSendRawTransactionJsonV600(
params = "0xf86b018203e882520894627306090abab3a6e1400e9345bc60c78a8bef57880de0b6b3a764000080820ff6a0d0367709eee090a6ebd74c63db7329372db1966e76d28ce219d1e105c47bcba7a0042d52f7d2436ad96e8714bf0309adaf870ad6fb68cfe53ce958792b3da36c12",
description = descriptionExample.value
)

//The common error or success format.
//Just some helper format to use in Json
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/util/ErrorMessages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ object ErrorMessages {
// General messages (OBP-10XXX)
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
val InvalidNumber = "OBP-10002: Invalid Number. Could not convert value to a number."
val InvalidISOCurrencyCode = """OBP-10003: Invalid Currency Value. Expected a 3-letter ISO Currency Code (e.g., 'USD', 'EUR') or 'lovelace' for Cardano transactions.""".stripMargin
val InvalidISOCurrencyCode = """OBP-10003: Invalid Currency Value. Expected a 3-letter ISO Currency Code (e.g., 'USD', 'EUR'), 'lovelace' (Cardano), or 'ETH' (Ethereum). Refer to ISO 4217 currency codes: https://www.iso.org/iso-4217-currency-codes.html""".stripMargin
val FXCurrencyCodeCombinationsNotSupported = "OBP-10004: ISO Currency code combination not supported for FX. Please modify the FROM_CURRENCY_CODE or TO_CURRENCY_CODE. "
val InvalidDateFormat = "OBP-10005: Invalid Date Format. Could not convert value to a Date."
val InvalidCurrency = "OBP-10006: Invalid Currency Value."
Expand Down
3 changes: 2 additions & 1 deletion obp-api/src/main/scala/code/api/util/NewStyle.scala
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ object NewStyle extends MdcLoggable{
Failure(s"$failMsg. Details: ${e.getMessage}", Full(e), Empty)
}
} map {
x => unboxFullOrFail(x, callContext, failMsg, failCode)
x =>
unboxFullOrFail(x, callContext, failMsg, failCode)
}
}

Expand Down
81 changes: 80 additions & 1 deletion obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import code.bankconnectors.LocalMappedConnectorInternal._
import code.entitlement.Entitlement
import code.views.Views
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model._
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import net.liftweb.common.Full
import net.liftweb.http.rest.RestHelper
import com.openbankproject.commons.ExecutionContext.Implicits.global

import scala.collection.immutable.{List, Nil}
import scala.collection.mutable.ArrayBuffer
Expand Down Expand Up @@ -116,6 +116,85 @@ trait APIMethods600 {
val transactionRequestType = TransactionRequestType("CARDANO")
LocalMappedConnectorInternal.createTransactionRequest(bankId, accountId, viewId , transactionRequestType, json)
}

staticResourceDocs += ResourceDoc(
createTransactionRequestEthereumeSendTransaction,
implementedInApiVersion,
nameOf(createTransactionRequestEthereumeSendTransaction),
"POST",
"/banks/BANK_ID/accounts/ACCOUNT_ID/owner/transaction-request-types/ETH_SEND_TRANSACTION/transaction-requests",
"Create Transaction Request (ETH_SEND_TRANSACTION)",
s"""
|
|Send ETH via Ethereum JSON-RPC.
|AccountId should hold the 0x address for now.
|
|${transactionRequestGeneralText}
|
""".stripMargin,
transactionRequestBodyEthereumJsonV600,
transactionRequestWithChargeJSON400,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
InsufficientAuthorisationToCreateTransactionRequest,
InvalidTransactionRequestType,
InvalidJsonFormat,
NotPositiveAmount,
InvalidTransactionRequestCurrency,
TransactionDisabled,
UnknownError
),
List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2)
)

lazy val createTransactionRequestEthereumeSendTransaction: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-request-types" ::
"ETH_SEND_TRANSACTION" :: "transaction-requests" :: Nil JsonPost json -> _ =>
cc => implicit val ec = EndpointContext(Some(cc))
val transactionRequestType = TransactionRequestType("ETH_SEND_TRANSACTION")
LocalMappedConnectorInternal.createTransactionRequest(bankId, accountId, viewId , transactionRequestType, json)
}
staticResourceDocs += ResourceDoc(
createTransactionRequestEthSendRawTransaction,
implementedInApiVersion,
nameOf(createTransactionRequestEthSendRawTransaction),
"POST",
"/banks/BANK_ID/accounts/ACCOUNT_ID/owner/transaction-request-types/ETH_SEND_RAW_TRANSACTION/transaction-requests",
"CREATE TRANSACTION REQUEST (ETH_SEND_RAW_TRANSACTION )",
s"""
|
|Send ETH via Ethereum JSON-RPC.
|AccountId should hold the 0x address for now.
|
|${transactionRequestGeneralText}
|
""".stripMargin,
transactionRequestBodyEthSendRawTransactionJsonV600,
transactionRequestWithChargeJSON400,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
InsufficientAuthorisationToCreateTransactionRequest,
InvalidTransactionRequestType,
InvalidJsonFormat,
NotPositiveAmount,
InvalidTransactionRequestCurrency,
TransactionDisabled,
UnknownError
),
List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2)
)

lazy val createTransactionRequestEthSendRawTransaction: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-request-types" ::
"ETH_SEND_RAW_TRANSACTION" :: "transaction-requests" :: Nil JsonPost json -> _ =>
cc => implicit val ec = EndpointContext(Some(cc))
val transactionRequestType = TransactionRequestType("ETH_SEND_RAW_TRANSACTION")
LocalMappedConnectorInternal.createTransactionRequest(bankId, accountId, viewId , transactionRequestType, json)
}

}
}
Expand Down
14 changes: 14 additions & 0 deletions obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ case class TransactionRequestBodyCardanoJsonV600(
metadata: Option[Map[String, CardanoMetadataStringJsonV600]] = None
) extends TransactionRequestCommonBodyJSON

// ---------------- Ethereum models (V600) ----------------
case class TransactionRequestBodyEthereumJsonV600(
params: Option[String] = None,// This is for eth_sendRawTransaction
to: String, // this is for eth_sendTransaction eg: 0x addressk
value: AmountOfMoneyJsonV121, // currency should be "ETH"; amount string (decimal)
description: String
) extends TransactionRequestCommonBodyJSON

// This is only for the request JSON body; we will construct `TransactionRequestBodyEthereumJsonV600` for OBP.
case class TransactionRequestBodyEthSendRawTransactionJsonV600(
params: String, // eth_sendRawTransaction params field.
description: String
)

case class UserJsonV600(
user_id: String,
email : String,
Expand Down
Loading