use std::ops::Deref;
use super::{
block::Header,
scalars::{
Address,
Bytes32,
HexString,
MessageId,
Nonce,
TransactionId,
U64,
},
};
use crate::{
fuel_core_graphql_api::service::Database,
query::MessageQueryData,
schema::scalars::{
BlockId,
U32,
},
};
use anyhow::anyhow;
use async_graphql::{
connection::{
Connection,
EmptyFields,
},
Context,
Object,
};
use fuel_core_types::entities;
pub struct Message(pub(crate) entities::message::Message);
#[Object]
impl Message {
async fn amount(&self) -> U64 {
self.0.amount.into()
}
async fn sender(&self) -> Address {
self.0.sender.into()
}
async fn recipient(&self) -> Address {
self.0.recipient.into()
}
async fn nonce(&self) -> Nonce {
self.0.nonce.into()
}
async fn data(&self) -> HexString {
self.0.data.clone().into()
}
async fn da_height(&self) -> U64 {
self.0.da_height.as_u64().into()
}
}
#[derive(Default)]
pub struct MessageQuery {}
#[Object]
impl MessageQuery {
async fn messages(
&self,
ctx: &Context<'_>,
#[graphql(desc = "address of the owner")] owner: Option<Address>,
first: Option<i32>,
after: Option<String>,
last: Option<i32>,
before: Option<String>,
) -> async_graphql::Result<Connection<HexString, Message, EmptyFields, EmptyFields>>
{
let query: &Database = ctx.data_unchecked();
crate::schema::query_pagination(
after,
before,
first,
last,
|start: &Option<HexString>, direction| {
let start = if let Some(start) = start.clone() {
Some(start.try_into().map_err(|err| anyhow!("{}", err))?)
} else {
None
};
let messages = if let Some(owner) = owner {
if matches!(last, Some(last) if last > 0) {
return Err(anyhow!(
"reverse pagination isn't supported for this resource"
)
.into())
}
query.owned_messages(&owner.0, start, direction)
} else {
query.all_messages(start, direction)
};
let messages = messages.map(|result| {
result
.map(|message| (message.nonce.into(), message.into()))
.map_err(Into::into)
});
Ok(messages)
},
)
.await
}
async fn message_proof(
&self,
ctx: &Context<'_>,
transaction_id: TransactionId,
message_id: MessageId,
commit_block_id: Option<BlockId>,
commit_block_height: Option<U32>,
) -> async_graphql::Result<Option<MessageProof>> {
let data: &Database = ctx.data_unchecked();
let block_id = match (commit_block_id, commit_block_height) {
(Some(commit_block_id), None) => commit_block_id.0.into(),
(None, Some(commit_block_height)) => {
let block_height = commit_block_height.0.into();
data.block_id(&block_height)?
}
_ => Err(anyhow::anyhow!(
"Either `commit_block_id` or `commit_block_height` must be provided exclusively"
))?,
};
Ok(crate::query::message_proof(
data.deref(),
transaction_id.into(),
message_id.into(),
block_id,
)?
.map(MessageProof))
}
}
pub struct MerkleProof(pub(crate) entities::message::MerkleProof);
#[Object]
impl MerkleProof {
async fn proof_set(&self) -> Vec<Bytes32> {
self.0
.proof_set
.iter()
.cloned()
.map(|array| Bytes32::from(fuel_core_types::fuel_types::Bytes32::from(array)))
.collect()
}
async fn proof_index(&self) -> U64 {
self.0.proof_index.into()
}
}
pub struct MessageProof(pub(crate) entities::message::MessageProof);
#[Object]
impl MessageProof {
async fn message_proof(&self) -> MerkleProof {
self.0.message_proof.clone().into()
}
async fn block_proof(&self) -> MerkleProof {
self.0.block_proof.clone().into()
}
async fn message_block_header(&self) -> Header {
self.0.message_block_header.clone().into()
}
async fn commit_block_header(&self) -> Header {
self.0.commit_block_header.clone().into()
}
async fn sender(&self) -> Address {
self.0.sender.into()
}
async fn recipient(&self) -> Address {
self.0.recipient.into()
}
async fn nonce(&self) -> Nonce {
self.0.nonce.into()
}
async fn amount(&self) -> U64 {
self.0.amount.into()
}
async fn data(&self) -> HexString {
self.0.data.clone().into()
}
}
impl From<entities::message::Message> for Message {
fn from(message: entities::message::Message) -> Self {
Message(message)
}
}
impl From<entities::message::MerkleProof> for MerkleProof {
fn from(proof: entities::message::MerkleProof) -> Self {
MerkleProof(proof)
}
}