1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Support for Linera applications that interact with Ethereum or other EVM contracts.

use std::fmt::Debug;

use async_graphql::scalar;
use async_trait::async_trait;
use linera_base::http;
pub use linera_ethereum::{
    client::EthereumQueries,
    common::{EthereumDataType, EthereumEvent},
};
use linera_ethereum::{client::JsonRpcClient, common::EthereumServiceError};
use serde::{Deserialize, Serialize};

use crate::{
    contract::wit::base_runtime_api as contract_wit, service::wit::base_runtime_api as service_wit,
};

// TODO(#3143): Unify the two types into a single `EthereumClient` type.

/// A wrapper for a URL that implements `JsonRpcClient` and uses the JSON oracle to make requests.
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ContractEthereumClient {
    /// The URL of the JSON-RPC server, without the method or parameters.
    pub url: String,
}
scalar!(ContractEthereumClient);

impl ContractEthereumClient {
    /// Creates a new [`ContractEthereumClient`] from an URL.
    pub fn new(url: String) -> Self {
        Self { url }
    }
}

#[async_trait]
impl JsonRpcClient for ContractEthereumClient {
    type Error = EthereumServiceError;

    async fn get_id(&self) -> u64 {
        1
    }

    async fn request_inner(&self, payload: Vec<u8>) -> Result<Vec<u8>, Self::Error> {
        let response = contract_wit::perform_http_request(
            &http::Request {
                method: http::Method::Post,
                url: self.url.clone(),
                headers: Vec::from([http::Header::new("Content-Type", b"application/json")]),
                body: payload,
            }
            .into(),
        );

        Ok(response.body)
    }
}

/// A wrapper for a URL that implements `JsonRpcClient` and uses the JSON oracle to make requests.
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ServiceEthereumClient {
    /// The URL of the JSON-RPC server, without the method or parameters.
    pub url: String,
}
scalar!(ServiceEthereumClient);

impl ServiceEthereumClient {
    /// Creates a new [`ServiceEthereumClient`] from an URL.
    pub fn new(url: String) -> Self {
        Self { url }
    }
}

#[async_trait]
impl JsonRpcClient for ServiceEthereumClient {
    type Error = EthereumServiceError;

    async fn get_id(&self) -> u64 {
        1
    }

    async fn request_inner(&self, payload: Vec<u8>) -> Result<Vec<u8>, Self::Error> {
        let response = service_wit::perform_http_request(
            &http::Request {
                method: http::Method::Post,
                url: self.url.clone(),
                headers: Vec::from([http::Header::new("Content-Type", b"application/json")]),
                body: payload,
            }
            .into(),
        );

        Ok(response.body)
    }
}