Skip to content

Commit

Permalink
Create /contracts/invoke method for firefly
Browse files Browse the repository at this point in the history
  • Loading branch information
SupernaviX committed Nov 4, 2024
1 parent ce88ecf commit 5302d02
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 34 deletions.
8 changes: 4 additions & 4 deletions firefly-cardanoconnect/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::PathBuf;
use anyhow::{bail, Result};
use balius_runtime::{Runtime, Store};
use serde::Deserialize;
use serde_json::json;
use serde_json::{json, Value};
use tokio::{fs, sync::RwLock};

#[derive(Debug, Deserialize, Clone)]
Expand Down Expand Up @@ -36,15 +36,15 @@ impl ContractManager {
}
}

pub async fn invoke(&self, contract: &str, method: &str) -> Result<()> {
pub async fn invoke(&self, contract: &str, method: &str, params: Value) -> Result<Value> {
let Some(rt_lock) = &self.runtime else {
bail!("Contract manager not configured");
};

let runtime = rt_lock.read().await;
runtime.handle_request(contract, method, json!({})).await?;
let result = runtime.handle_request(contract, method, params).await?;

Ok(())
Ok(result)
}

pub async fn connect(&self) -> Result<RuntimeWrapper> {
Expand Down
5 changes: 3 additions & 2 deletions firefly-cardanoconnect/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ use contracts::ContractManager;
use firefly_server::instrumentation;
use routes::{
chain::get_chain_tip,
contracts::invoke_contract,
health::health,
streams::{
create_listener, create_stream, delete_listener, delete_stream, get_listener, get_stream,
list_listeners, list_streams, update_stream,
},
transaction::{submit_transaction, try_balius},
transaction::submit_transaction,
ws::handle_socket_upgrade,
};
use signer::CardanoSigner;
Expand Down Expand Up @@ -87,8 +88,8 @@ async fn main() -> Result<()> {

let router = ApiRouter::new()
.api_route("/health", get(health))
.api_route("/contracts/invoke", post(invoke_contract))
.api_route("/transactions", post(submit_transaction))
.api_route("/transactions/balius", post(try_balius))
.api_route("/eventstreams", post(create_stream).get(list_streams))
.api_route(
"/eventstreams/:streamId",
Expand Down
1 change: 1 addition & 0 deletions firefly-cardanoconnect/src/routes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod chain;
pub mod contracts;
pub mod health;
pub mod streams;
pub mod transaction;
Expand Down
56 changes: 56 additions & 0 deletions firefly-cardanoconnect/src/routes/contracts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use axum::{extract::State, Json};
use firefly_server::apitypes::{ApiError, ApiResult, NoContent};
use reqwest::StatusCode;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::Value;

use crate::AppState;

#[derive(Deserialize, JsonSchema)]
pub struct InvokeRequest {
#[expect(unused)]
/// The FireFly operation ID of this request.
pub id: String,
/// The name of the contract getting invoked
pub address: String,
/// A description of the method getting invoked.
pub method: ABIMethod,
/// Any parameters needed to invoke the method.
pub params: Vec<Value>,
}

#[derive(Deserialize, JsonSchema)]
pub struct ABIMethod {
pub name: String,
pub params: Vec<ABIParameter>,
}

#[derive(Deserialize, JsonSchema)]
pub struct ABIParameter {
pub name: String,
}

pub async fn invoke_contract(
State(AppState { contracts, .. }): State<AppState>,
Json(req): Json<InvokeRequest>,
) -> ApiResult<NoContent> {
let mut params = serde_json::Map::new();
for (schema, value) in req.method.params.iter().zip(req.params.into_iter()) {
params.insert(schema.name.to_string(), value);
}
match contracts
.invoke(&req.address, &req.method.name, params.into())
.await
{
Ok(_res) => {
// TODO: send res to the websocket
Ok(NoContent)
}
Err(error) => {
let err = ApiError::new(StatusCode::BAD_REQUEST, error.to_string())
.with_field("submissionRejected", true);
Err(err)
}
}
}
19 changes: 0 additions & 19 deletions firefly-cardanoconnect/src/routes/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ pub struct SubmitTransactionResponse {
txid: String,
}

#[derive(Deserialize, JsonSchema)]
pub struct TryBaliusRequest {
/// The name of the contract to invoke
contract: String,
/// The method of the contract to invoke
method: String,
}

#[derive(Serialize, JsonSchema)]
pub struct TryBaliusResponse {}

pub async fn submit_transaction(
State(AppState {
blockchain, signer, ..
Expand All @@ -49,11 +38,3 @@ pub async fn submit_transaction(
.context("could not submit transaction")?;
Ok(Json(SubmitTransactionResponse { txid }))
}

pub async fn try_balius(
State(AppState { contracts, .. }): State<AppState>,
Json(req): Json<TryBaliusRequest>,
) -> ApiResult<Json<TryBaliusResponse>> {
contracts.invoke(&req.contract, &req.method).await?;
Ok(Json(TryBaliusResponse {}))
}
21 changes: 12 additions & 9 deletions firefly-server/src/apitypes/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ use axum::{
Json,
};
use reqwest::StatusCode;
use serde::Serialize;

#[derive(Debug)]
pub struct ApiError {
status: StatusCode,
message: String,
fields: serde_json::Map<String, serde_json::Value>,
}

impl ApiError {
pub fn new(status: impl Into<StatusCode>, message: impl Into<String>) -> Self {
Self {
status: status.into(),
message: message.into(),
fields: serde_json::Map::new(),
}
}
pub fn from_reqwest(err: reqwest::Error) -> Self {
Expand All @@ -39,6 +40,11 @@ impl ApiError {
pub fn not_implemented(message: impl Into<String>) -> Self {
Self::new(StatusCode::NOT_IMPLEMENTED, message)
}
pub fn with_field(self, name: &str, value: impl Into<serde_json::Value>) -> Self {
let mut fields = self.fields;
fields.insert(name.into(), value.into());
Self { fields, ..self }
}
}

impl Display for ApiError {
Expand All @@ -47,16 +53,12 @@ impl Display for ApiError {
}
}

#[derive(Serialize)]
struct SerializableApiError {
message: String,
}
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let body = SerializableApiError {
message: self.message,
};
(self.status, Json(body)).into_response()
let mut fields = self.fields;
fields.insert("message".into(), self.message.into());
let value = serde_json::Value::Object(fields);
(self.status, Json(value)).into_response()
}
}

Expand All @@ -68,6 +70,7 @@ where
Self {
status: StatusCode::INTERNAL_SERVER_ERROR,
message: format!("{:#}", value.into()),
fields: serde_json::Map::new(),
}
}
}
Expand Down

0 comments on commit 5302d02

Please sign in to comment.