Skip to main content

rootchain_rs/
contract.rs

1use crate::client::RpcProvider;
2use crate::error::Error;
3use crate::wallet::Wallet;
4use crate::Result;
5use rootchain_core::types::{Address, TransactionType};
6use serde::Serialize;
7
8pub struct Contract<'a> {
9    provider: &'a RpcProvider,
10    address: Address,
11}
12
13impl<'a> Contract<'a> {
14    pub fn new(provider: &'a RpcProvider, address: Address) -> Self {
15        Self { provider, address }
16    }
17
18    /// Call a read-only method on the contract.
19    pub async fn call<Args, Ret>(&self, method: &str, args: Args) -> Result<Ret>
20    where
21        Args: Serialize,
22        Ret: for<'de> serde::Deserialize<'de>,
23    {
24        let payload = bincode::serialize(&args).map_err(|e| Error::Internal(e.to_string()))?;
25
26        let resp: serde_json::Value = self
27            .provider
28            .call(
29                "rootchain_call",
30                serde_json::json!([self.address, method, hex::encode(payload)]),
31            )
32            .await?;
33
34        let output_hex = resp
35            .get("output")
36            .and_then(|v| v.as_str())
37            .ok_or_else(|| Error::Rpc("Missing output field".to_string()))?;
38
39        let output_bytes = hex::decode(output_hex).map_err(|e| Error::Internal(e.to_string()))?;
40
41        let ret: Ret =
42            bincode::deserialize(&output_bytes).map_err(|e| Error::Internal(e.to_string()))?;
43
44        Ok(ret)
45    }
46
47    /// Send a state-changing transaction to the contract.
48    pub async fn send<Args>(
49        &self,
50        wallet: &Wallet,
51        method: &str,
52        args: Args,
53        fee: u64,
54        nonce: u64,
55    ) -> Result<String>
56    where
57        Args: Serialize,
58    {
59        let mut payload = Vec::new();
60        // Method name prefix (simple ABI)
61        payload.extend_from_slice(method.as_bytes());
62        payload.push(0); // null terminator for name
63
64        let arg_bytes = bincode::serialize(&args).map_err(|e| Error::Internal(e.to_string()))?;
65        payload.extend_from_slice(&arg_bytes);
66
67        let tx = crate::wallet::TransactionBuilder::new()
68            .tx_type(TransactionType::ContractCall)
69            .from(wallet.address())
70            .to(self.address)
71            .amount(0)
72            .fee(fee.into())
73            .nonce(nonce)
74            .payload(payload)
75            .build(wallet)?;
76
77        let tx_hex = hex::encode(bincode::serialize(&tx).unwrap());
78        self.provider.send_raw_transaction(&tx_hex).await
79    }
80}