ethereum.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. use {
  2. crate::{
  3. api::ChainId,
  4. chain::{
  5. eth_gas_oracle::EthProviderOracle,
  6. nonce_manager::NonceManagerMiddleware,
  7. reader::{self, BlockNumber, BlockStatus, EntropyReader, RequestedWithCallbackEvent},
  8. traced_client::{RpcMetrics, TracedClient},
  9. },
  10. config::EthereumConfig,
  11. },
  12. anyhow::{anyhow, Error, Result},
  13. axum::async_trait,
  14. ethers::{
  15. abi::RawLog,
  16. contract::{abigen, ContractCall, EthLogDecode},
  17. core::types::Address,
  18. middleware::{gas_oracle::GasOracleMiddleware, MiddlewareError, SignerMiddleware},
  19. prelude::{BlockId, JsonRpcClient, PendingTransaction, TransactionRequest},
  20. providers::{Http, Middleware, Provider},
  21. signers::{LocalWallet, Signer},
  22. types::{transaction::eip2718::TypedTransaction, BlockNumber as EthersBlockNumber, U256},
  23. },
  24. sha3::{Digest, Keccak256},
  25. std::sync::Arc,
  26. thiserror::Error,
  27. };
  28. // TODO: Programmatically generate this so we don't have to keep committed ABI in sync with the
  29. // contract in the same repo.
  30. abigen!(
  31. PythRandom,
  32. "../../target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json"
  33. );
  34. pub type MiddlewaresWrapper<T> = LegacyTxMiddleware<
  35. GasOracleMiddleware<
  36. NonceManagerMiddleware<SignerMiddleware<Provider<T>, LocalWallet>>,
  37. EthProviderOracle<Provider<T>>,
  38. >,
  39. >;
  40. pub type SignablePythContractInner<T> = PythRandom<MiddlewaresWrapper<T>>;
  41. pub type SignablePythContract = SignablePythContractInner<Http>;
  42. pub type InstrumentedSignablePythContract = SignablePythContractInner<TracedClient>;
  43. pub type PythContractCall = ContractCall<MiddlewaresWrapper<TracedClient>, ()>;
  44. pub type PythContract = PythRandom<Provider<Http>>;
  45. pub type InstrumentedPythContract = PythRandom<Provider<TracedClient>>;
  46. /// Middleware that converts a transaction into a legacy transaction if use_legacy_tx is true.
  47. /// We can not use TransformerMiddleware because keeper calls fill_transaction first which bypasses
  48. /// the transformer.
  49. #[derive(Clone, Debug)]
  50. pub struct LegacyTxMiddleware<M> {
  51. use_legacy_tx: bool,
  52. inner: M,
  53. }
  54. impl<M> LegacyTxMiddleware<M> {
  55. pub fn new(use_legacy_tx: bool, inner: M) -> Self {
  56. Self {
  57. use_legacy_tx,
  58. inner,
  59. }
  60. }
  61. }
  62. #[derive(Error, Debug)]
  63. pub enum LegacyTxMiddlewareError<M: Middleware> {
  64. #[error("{0}")]
  65. MiddlewareError(M::Error),
  66. }
  67. impl<M: Middleware> MiddlewareError for LegacyTxMiddlewareError<M> {
  68. type Inner = M::Error;
  69. fn from_err(src: M::Error) -> Self {
  70. LegacyTxMiddlewareError::MiddlewareError(src)
  71. }
  72. fn as_inner(&self) -> Option<&Self::Inner> {
  73. match self {
  74. LegacyTxMiddlewareError::MiddlewareError(e) => Some(e),
  75. }
  76. }
  77. }
  78. #[async_trait]
  79. impl<M: Middleware> Middleware for LegacyTxMiddleware<M> {
  80. type Error = LegacyTxMiddlewareError<M>;
  81. type Provider = M::Provider;
  82. type Inner = M;
  83. fn inner(&self) -> &M {
  84. &self.inner
  85. }
  86. async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
  87. &self,
  88. tx: T,
  89. block: Option<BlockId>,
  90. ) -> std::result::Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
  91. let mut tx = tx.into();
  92. if self.use_legacy_tx {
  93. let legacy_request: TransactionRequest = tx.into();
  94. tx = legacy_request.into();
  95. }
  96. self.inner()
  97. .send_transaction(tx, block)
  98. .await
  99. .map_err(MiddlewareError::from_err)
  100. }
  101. async fn fill_transaction(
  102. &self,
  103. tx: &mut TypedTransaction,
  104. block: Option<BlockId>,
  105. ) -> std::result::Result<(), Self::Error> {
  106. if self.use_legacy_tx {
  107. let legacy_request: TransactionRequest = (*tx).clone().into();
  108. *tx = legacy_request.into();
  109. }
  110. self.inner()
  111. .fill_transaction(tx, block)
  112. .await
  113. .map_err(MiddlewareError::from_err)
  114. }
  115. }
  116. impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
  117. /// Get the wallet that signs transactions sent to this contract.
  118. pub fn wallet(&self) -> LocalWallet {
  119. self.client().inner().inner().inner().signer().clone()
  120. }
  121. /// Get the underlying provider that communicates with the blockchain.
  122. pub fn provider(&self) -> Provider<T> {
  123. self.client().inner().inner().inner().provider().clone()
  124. }
  125. /// Submit a request for a random number to the contract.
  126. ///
  127. /// This method is a version of the autogenned `request` method that parses the emitted logs
  128. /// to return the sequence number of the created Request.
  129. pub async fn request_wrapper(
  130. &self,
  131. provider: &Address,
  132. user_randomness: &[u8; 32],
  133. use_blockhash: bool,
  134. ) -> Result<u64> {
  135. let fee = self.get_fee(*provider).call().await?;
  136. let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into();
  137. if let Some(r) = self
  138. .request(*provider, hashed_randomness, use_blockhash)
  139. .value(fee)
  140. .send()
  141. .await?
  142. .await?
  143. {
  144. // Extract Log from TransactionReceipt.
  145. let l: RawLog = r.logs[0].clone().into();
  146. if let PythRandomEvents::RequestedFilter(r) = PythRandomEvents::decode_log(&l)? {
  147. Ok(r.request.sequence_number)
  148. } else {
  149. Err(anyhow!("No log with sequence number"))
  150. }
  151. } else {
  152. Err(anyhow!("Request failed"))
  153. }
  154. }
  155. /// Reveal the generated random number to the contract.
  156. ///
  157. /// This method is a version of the autogenned `reveal` method that parses the emitted logs
  158. /// to return the generated random number.
  159. pub async fn reveal_wrapper(
  160. &self,
  161. provider: &Address,
  162. sequence_number: u64,
  163. user_randomness: &[u8; 32],
  164. provider_randomness: &[u8; 32],
  165. ) -> Result<[u8; 32]> {
  166. if let Some(r) = self
  167. .reveal(
  168. *provider,
  169. sequence_number,
  170. *user_randomness,
  171. *provider_randomness,
  172. )
  173. .send()
  174. .await?
  175. .await?
  176. {
  177. if let PythRandomEvents::RevealedFilter(r) =
  178. PythRandomEvents::decode_log(&r.logs[0].clone().into())?
  179. {
  180. Ok(r.random_number)
  181. } else {
  182. Err(anyhow!("No log with randomnumber"))
  183. }
  184. } else {
  185. Err(anyhow!("Request failed"))
  186. }
  187. }
  188. pub async fn from_config_and_provider(
  189. chain_config: &EthereumConfig,
  190. private_key: &str,
  191. provider: Provider<T>,
  192. ) -> Result<SignablePythContractInner<T>> {
  193. let chain_id = provider.get_chainid().await?;
  194. let gas_oracle = EthProviderOracle::new(provider.clone());
  195. let wallet__ = private_key
  196. .parse::<LocalWallet>()?
  197. .with_chain_id(chain_id.as_u64());
  198. let address = wallet__.address();
  199. Ok(PythRandom::new(
  200. chain_config.contract_addr,
  201. Arc::new(LegacyTxMiddleware::new(
  202. chain_config.legacy_tx,
  203. GasOracleMiddleware::new(
  204. NonceManagerMiddleware::new(SignerMiddleware::new(provider, wallet__), address),
  205. gas_oracle,
  206. ),
  207. )),
  208. ))
  209. }
  210. }
  211. impl SignablePythContract {
  212. pub async fn from_config(chain_config: &EthereumConfig, private_key: &str) -> Result<Self> {
  213. let provider = Provider::<Http>::try_from(&chain_config.geth_rpc_addr)?;
  214. Self::from_config_and_provider(chain_config, private_key, provider).await
  215. }
  216. }
  217. impl InstrumentedSignablePythContract {
  218. pub async fn from_config(
  219. chain_config: &EthereumConfig,
  220. private_key: &str,
  221. chain_id: ChainId,
  222. metrics: Arc<RpcMetrics>,
  223. ) -> Result<Self> {
  224. let provider = TracedClient::new(chain_id, &chain_config.geth_rpc_addr, metrics)?;
  225. Self::from_config_and_provider(chain_config, private_key, provider).await
  226. }
  227. }
  228. impl PythContract {
  229. pub fn from_config(chain_config: &EthereumConfig) -> Result<Self> {
  230. let provider = Provider::<Http>::try_from(&chain_config.geth_rpc_addr)?;
  231. Ok(PythRandom::new(
  232. chain_config.contract_addr,
  233. Arc::new(provider),
  234. ))
  235. }
  236. }
  237. impl InstrumentedPythContract {
  238. pub fn from_config(
  239. chain_config: &EthereumConfig,
  240. chain_id: ChainId,
  241. metrics: Arc<RpcMetrics>,
  242. ) -> Result<Self> {
  243. let provider = TracedClient::new(chain_id, &chain_config.geth_rpc_addr, metrics)?;
  244. Ok(PythRandom::new(
  245. chain_config.contract_addr,
  246. Arc::new(provider),
  247. ))
  248. }
  249. }
  250. #[async_trait]
  251. impl<T: JsonRpcClient + 'static> EntropyReader for PythRandom<Provider<T>> {
  252. async fn get_request(
  253. &self,
  254. provider_address: Address,
  255. sequence_number: u64,
  256. ) -> Result<Option<reader::Request>> {
  257. let r = self
  258. .get_request(provider_address, sequence_number)
  259. // TODO: This doesn't work for lighlink right now. Figure out how to do this in lightlink
  260. // .block(ethers::core::types::BlockNumber::Finalized)
  261. .call()
  262. .await?;
  263. // sequence_number == 0 means the request does not exist.
  264. if r.sequence_number != 0 {
  265. Ok(Some(reader::Request {
  266. provider: r.provider,
  267. sequence_number: r.sequence_number,
  268. block_number: r.block_number,
  269. use_blockhash: r.use_blockhash,
  270. }))
  271. } else {
  272. Ok(None)
  273. }
  274. }
  275. async fn get_block_number(&self, confirmed_block_status: BlockStatus) -> Result<BlockNumber> {
  276. let block_number: EthersBlockNumber = confirmed_block_status.into();
  277. let block = self
  278. .client()
  279. .get_block(block_number)
  280. .await?
  281. .ok_or_else(|| Error::msg("pending block confirmation"))?;
  282. Ok(block
  283. .number
  284. .ok_or_else(|| Error::msg("pending confirmation"))?
  285. .as_u64())
  286. }
  287. async fn get_request_with_callback_events(
  288. &self,
  289. from_block: BlockNumber,
  290. to_block: BlockNumber,
  291. ) -> Result<Vec<RequestedWithCallbackEvent>> {
  292. let mut event = self.requested_with_callback_filter();
  293. event.filter = event.filter.from_block(from_block).to_block(to_block);
  294. let res: Vec<RequestedWithCallbackFilter> = event.query().await?;
  295. Ok(res
  296. .iter()
  297. .map(|r| RequestedWithCallbackEvent {
  298. sequence_number: r.sequence_number,
  299. user_random_number: r.user_random_number,
  300. provider_address: r.request.provider,
  301. })
  302. .collect())
  303. }
  304. async fn estimate_reveal_with_callback_gas(
  305. &self,
  306. sender: Address,
  307. provider: Address,
  308. sequence_number: u64,
  309. user_random_number: [u8; 32],
  310. provider_revelation: [u8; 32],
  311. ) -> Result<U256> {
  312. let result = self
  313. .reveal_with_callback(
  314. provider,
  315. sequence_number,
  316. user_random_number,
  317. provider_revelation,
  318. )
  319. .from(sender)
  320. .estimate_gas()
  321. .await;
  322. result.map_err(|e| e.into())
  323. }
  324. }