use crate::{
AlloyProvider, Eip712PermitData, Ethereum, IntoBlockId, Sender, TryGetReceipt, WVara,
abi::{self, IMirror},
mirror::events::AllEventsBuilder,
};
use alloy::{
contract::CallBuilder,
eips::BlockId,
network,
primitives::{Address as AlloyAddress, Bytes, U256 as AlloyU256},
providers::{PendingTransactionBuilder, Provider, RootProvider, WalletProvider},
rpc::types::TransactionReceipt,
};
use anyhow::{Result, anyhow};
use ethexe_common::{
Address,
events::mirror::{ReplyEvent, StateChangedEvent, ValueClaimedEvent},
};
pub use events::signatures;
use events::{
ExecutableBalanceTopUpRequestedEventBuilder, MessageCallFailedEventBuilder,
MessageEventBuilder, MessageQueueingRequestedEventBuilder,
OwnedBalanceTopUpRequestedEventBuilder, ReplyCallFailedEventBuilder, ReplyEventBuilder,
ReplyQueueingRequestedEventBuilder, ReplyTransferFailedEventBuilder, StateChangedEventBuilder,
TransferLockedValueToInheritorFailedEventBuilder, ValueClaimFailedEventBuilder,
ValueClaimedEventBuilder, ValueClaimingRequestedEventBuilder,
};
use futures::StreamExt;
use gear_core::{ids::prelude::MessageIdExt, rpc::ReplyInfo};
use gprimitives::{ActorId, H256, MessageId, U256};
use serde::Serialize;
pub mod events;
#[derive(Debug, Clone, Serialize)]
pub struct ClaimInfo {
pub message_id: MessageId,
pub actor_id: ActorId,
pub value: u128,
}
type Instance = IMirror::IMirrorInstance<AlloyProvider>;
type QueryInstance = IMirror::IMirrorInstance<RootProvider>;
pub struct Mirror {
instance: Instance,
wvara_address: AlloyAddress,
sender: Sender,
}
impl Mirror {
pub(crate) fn new(
address: AlloyAddress,
wvara_address: AlloyAddress,
sender: Sender,
provider: AlloyProvider,
) -> Self {
Self {
instance: Instance::new(address, provider),
wvara_address,
sender,
}
}
pub fn actor_id(&self) -> ActorId {
let address = Address(*self.instance.address().0);
address.into()
}
pub fn query(&self) -> MirrorQuery {
MirrorQuery(QueryInstance::new(
*self.instance.address(),
self.instance.provider().root().clone(),
))
}
pub fn wvara(&self) -> WVara {
WVara::new(self.wvara_address, self.instance.provider().clone())
}
pub async fn wait_for_state_change(&self) -> Result<H256> {
let mut stream = self.query().events().state_changed().subscribe().await?;
while let Some(result) = stream.next().await {
if let Ok((StateChangedEvent { state_hash }, _)) = result {
return Ok(state_hash);
}
}
Err(anyhow!("Failed to define if state changed"))
}
pub async fn send_message(
&self,
payload: impl AsRef<[u8]>,
value: u128,
) -> Result<(H256, MessageId)> {
self.send_message_pending(payload, value)
.await?
.try_get_message_send_receipt()
.await
}
pub async fn send_message_with_receipt(
&self,
payload: impl AsRef<[u8]>,
value: u128,
) -> Result<(TransactionReceipt, MessageId)> {
let receipt = self
.send_message_pending(payload, value)
.await?
.try_get_receipt_check_reverted()
.await?;
let mut message_id = None;
for log in receipt.inner.logs() {
if log.topic0() == Some(&signatures::MESSAGE_QUEUEING_REQUESTED) {
let event = crate::decode_log::<IMirror::MessageQueueingRequested>(log)?;
message_id = Some((*event.id).into());
break;
}
}
let message_id =
message_id.ok_or_else(|| anyhow!("Couldn't find `MessageQueueingRequested` log"))?;
Ok((receipt, message_id))
}
pub async fn send_message_pending(
&self,
payload: impl AsRef<[u8]>,
value: u128,
) -> Result<PendingTransactionBuilder<network::Ethereum>> {
let call_reply = false;
self.instance
.sendMessage(payload.as_ref().to_vec().into(), call_reply)
.value(AlloyU256::from(value))
.send()
.await
.map_err(Into::into)
}
pub async fn wait_for_reply(&self, message_id: MessageId) -> Result<ReplyInfo> {
let mut stream = self.query().events().reply().subscribe().await?;
while let Some(result) = stream.next().await {
if let Ok((
ReplyEvent {
payload,
value,
reply_to,
reply_code,
},
_,
)) = result
&& reply_to == message_id
{
return Ok(ReplyInfo {
payload,
value,
code: reply_code,
});
}
}
Err(anyhow!("Failed to wait for reply"))
}
pub async fn send_reply(
&self,
replied_to: MessageId,
payload: impl AsRef<[u8]>,
value: u128,
) -> Result<(H256, MessageId)> {
self.send_reply_with_receipt(replied_to, payload, value)
.await
.map(|(receipt, message_id)| ((*receipt.transaction_hash).into(), message_id))
}
pub async fn send_reply_with_receipt(
&self,
replied_to: MessageId,
payload: impl AsRef<[u8]>,
value: u128,
) -> Result<(TransactionReceipt, MessageId)> {
let builder = self
.instance
.sendReply(
replied_to.into_bytes().into(),
payload.as_ref().to_vec().into(),
)
.value(AlloyU256::from(value));
let receipt = builder
.send()
.await?
.try_get_receipt_check_reverted()
.await?;
let message_id = MessageId::generate_reply(replied_to);
Ok((receipt, message_id))
}
pub async fn claim_value(&self, claimed_id: MessageId) -> Result<H256> {
self.claim_value_with_receipt(claimed_id)
.await
.map(|receipt| (*receipt.transaction_hash).into())
}
pub async fn claim_value_with_receipt(
&self,
claimed_id: MessageId,
) -> Result<TransactionReceipt> {
let builder = self.instance.claimValue(claimed_id.into_bytes().into());
let receipt = builder
.send()
.await?
.try_get_receipt_check_reverted()
.await?;
Ok(receipt)
}
pub async fn wait_for_value_claim(&self, message_id: MessageId) -> Result<ClaimInfo> {
let mut stream = self.query().events().value_claimed().subscribe().await?;
while let Some(result) = stream.next().await {
if let Ok((ValueClaimedEvent { claimed_id, value }, _)) = result
&& claimed_id == message_id
{
let actor_id =
Address::from(self.instance.provider().default_signer_address()).into();
return Ok(ClaimInfo {
message_id: claimed_id,
actor_id,
value,
});
}
}
Err(anyhow!("Failed to wait for value claimed"))
}
pub async fn executable_balance_top_up(&self, value: u128) -> Result<H256> {
self.executable_balance_top_up_with_receipt(value)
.await
.map(|receipt| (*receipt.transaction_hash).into())
}
pub async fn executable_balance_top_up_with_receipt(
&self,
value: u128,
) -> Result<TransactionReceipt> {
let builder = self.instance.executableBalanceTopUp(value);
let receipt = builder
.send()
.await?
.try_get_receipt_check_reverted()
.await?;
Ok(receipt)
}
pub async fn executable_balance_top_up_with_permit(&self, value: u128) -> Result<H256> {
self.executable_balance_top_up_with_permit_and_receipt(value)
.await
.map(|receipt| (*receipt.transaction_hash).into())
}
pub async fn executable_balance_top_up_with_permit_and_receipt(
&self,
value: u128,
) -> Result<TransactionReceipt> {
let Eip712PermitData { deadline, v, r, s } = Ethereum::prepare_permit_data(
self.instance.provider(),
self.wvara().query(),
&self.sender,
self.actor_id(),
value,
)
.await?;
let builder = self
.instance
.executableBalanceTopUpWithPermit(value, deadline, v, r, s);
let receipt = builder
.send()
.await?
.try_get_receipt_check_reverted()
.await?;
Ok(receipt)
}
pub async fn transfer_locked_value_to_inheritor(&self) -> Result<H256> {
self.transfer_locked_value_to_inheritor_with_receipt()
.await
.map(|receipt| (*receipt.transaction_hash).into())
}
pub async fn transfer_locked_value_to_inheritor_with_receipt(
&self,
) -> Result<TransactionReceipt> {
let builder = self.instance.transferLockedValueToInheritor();
let receipt = builder
.send()
.await?
.try_get_receipt_check_reverted()
.await?;
Ok(receipt)
}
pub async fn owned_balance_top_up(&self, value: u128) -> Result<H256> {
self.owned_balance_top_up_with_receipt(value)
.await
.map(|receipt| (*receipt.transaction_hash).into())
}
pub async fn owned_balance_top_up_with_receipt(
&self,
value: u128,
) -> Result<TransactionReceipt> {
let builder = CallBuilder::new_raw(self.instance.provider(), Bytes::new())
.to(*self.instance.address())
.value(AlloyU256::from(value));
let receipt = builder
.send()
.await?
.try_get_receipt_check_reverted()
.await?;
Ok(receipt)
}
}
pub struct MirrorQuery(QueryInstance);
impl MirrorQuery {
pub fn new(provider: RootProvider, mirror_address: Address) -> Self {
Self(QueryInstance::new(
AlloyAddress::new(mirror_address.0),
provider,
))
}
pub fn events(&self) -> MirrorEvents<'_> {
MirrorEvents { query: self }
}
pub async fn balance(&self) -> Result<u128> {
self.0
.provider()
.get_balance(*self.0.address())
.await
.map(abi::utils::uint256_to_u128_lossy)
.map_err(Into::into)
}
pub async fn router(&self) -> Result<Address> {
self.0
.router()
.call()
.await
.map(|res| Address(res.into()))
.map_err(Into::into)
}
pub async fn state_hash(&self) -> Result<H256> {
self.state_hash_at(BlockId::latest()).await
}
pub async fn state_hash_at(&self, id: impl IntoBlockId) -> Result<H256> {
self.0
.stateHash()
.block(id.into_block_id())
.call()
.await
.map(|res| H256(res.0))
.map_err(Into::into)
}
pub async fn nonce(&self) -> Result<U256> {
self.0
.nonce()
.call()
.await
.map(abi::utils::uint256_to_u256)
.map_err(Into::into)
}
pub async fn exited(&self) -> Result<bool> {
self.0.exited().call().await.map_err(Into::into)
}
pub async fn inheritor(&self) -> Result<ActorId> {
self.0
.inheritor()
.call()
.await
.map(|res| Address(res.into()).into())
.map_err(Into::into)
}
pub async fn initializer(&self) -> Result<ActorId> {
self.0
.initializer()
.call()
.await
.map(|res| Address(res.into()).into())
.map_err(Into::into)
}
}
pub struct MirrorEvents<'a> {
query: &'a MirrorQuery,
}
impl<'a> MirrorEvents<'a> {
pub fn all(&self) -> AllEventsBuilder<'a> {
AllEventsBuilder::new(self.query)
}
pub fn state_changed(&self) -> StateChangedEventBuilder<'a> {
StateChangedEventBuilder::new(self.query)
}
pub fn message_queueing_requested(&self) -> MessageQueueingRequestedEventBuilder<'a> {
MessageQueueingRequestedEventBuilder::new(self.query)
}
pub fn reply_queueing_requested(&self) -> ReplyQueueingRequestedEventBuilder<'a> {
ReplyQueueingRequestedEventBuilder::new(self.query)
}
pub fn value_claiming_requested(&self) -> ValueClaimingRequestedEventBuilder<'a> {
ValueClaimingRequestedEventBuilder::new(self.query)
}
pub fn owned_balance_top_up_requested(&self) -> OwnedBalanceTopUpRequestedEventBuilder<'a> {
OwnedBalanceTopUpRequestedEventBuilder::new(self.query)
}
pub fn executable_balance_top_up_requested(
&self,
) -> ExecutableBalanceTopUpRequestedEventBuilder<'a> {
ExecutableBalanceTopUpRequestedEventBuilder::new(self.query)
}
pub fn message(&self) -> MessageEventBuilder<'a> {
MessageEventBuilder::new(self.query)
}
pub fn message_call_failed(&self) -> MessageCallFailedEventBuilder<'a> {
MessageCallFailedEventBuilder::new(self.query)
}
pub fn reply(&self) -> ReplyEventBuilder<'a> {
ReplyEventBuilder::new(self.query)
}
pub fn reply_call_failed(&self) -> ReplyCallFailedEventBuilder<'a> {
ReplyCallFailedEventBuilder::new(self.query)
}
pub fn value_claimed(&self) -> ValueClaimedEventBuilder<'a> {
ValueClaimedEventBuilder::new(self.query)
}
pub fn transfer_locked_value_to_inheritor_failed(
&self,
) -> TransferLockedValueToInheritorFailedEventBuilder<'a> {
TransferLockedValueToInheritorFailedEventBuilder::new(self.query)
}
pub fn reply_transfer_failed(&self) -> ReplyTransferFailedEventBuilder<'a> {
ReplyTransferFailedEventBuilder::new(self.query)
}
pub fn value_claim_failed(&self) -> ValueClaimFailedEventBuilder<'a> {
ValueClaimFailedEventBuilder::new(self.query)
}
}