ethereum.rs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. use {
  2. crate::config::EthereumOptions,
  3. anyhow::anyhow,
  4. ethers::{
  5. abi::RawLog,
  6. contract::{
  7. abigen,
  8. EthLogDecode,
  9. },
  10. core::types::Address,
  11. middleware::SignerMiddleware,
  12. providers::{
  13. Http,
  14. Middleware,
  15. Provider,
  16. },
  17. signers::{
  18. LocalWallet,
  19. Signer,
  20. },
  21. },
  22. sha3::{
  23. Digest,
  24. Keccak256,
  25. },
  26. std::{
  27. error::Error,
  28. sync::Arc,
  29. },
  30. };
  31. // TODO: Programatically generate this so we don't have to keep committed ABI in sync with the
  32. // contract in the same repo.
  33. abigen!(PythRandom, "src/abi.json");
  34. pub type PythContract = PythRandom<SignerMiddleware<Provider<Http>, LocalWallet>>;
  35. impl PythContract {
  36. // TODO: this method requires a private key to instantiate the contract. This key
  37. // shouldn't be required for read-only uses (e.g., when the server is running).
  38. pub async fn from_opts(opts: &EthereumOptions) -> Result<PythContract, Box<dyn Error>> {
  39. let provider = Provider::<Http>::try_from(&opts.geth_rpc_addr)?;
  40. let chain_id = provider.get_chainid().await?;
  41. let wallet__ = opts
  42. .private_key
  43. .clone()
  44. .ok_or(anyhow!("No private key specified"))?
  45. .parse::<LocalWallet>()?
  46. .with_chain_id(chain_id.as_u64());
  47. Ok(PythRandom::new(
  48. opts.contract_addr,
  49. Arc::new(SignerMiddleware::new(provider, wallet__)),
  50. ))
  51. }
  52. /// Submit a request for a random number to the contract.
  53. ///
  54. /// This method is a version of the autogenned `request` method that parses the emitted logs
  55. /// to return the sequence number of the created Request.
  56. pub async fn request_wrapper(
  57. &self,
  58. provider: &Address,
  59. user_randomness: &[u8; 32],
  60. use_blockhash: bool,
  61. ) -> Result<u64, Box<dyn Error>> {
  62. let fee = self.get_fee(*provider).call().await?;
  63. let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into();
  64. if let Some(r) = self
  65. .request(*provider, hashed_randomness, use_blockhash)
  66. .value(fee)
  67. .send()
  68. .await?
  69. .await?
  70. {
  71. // Extract Log from TransactionReceipt.
  72. let l: RawLog = r.logs[0].clone().into();
  73. if let PythRandomEvents::RequestedFilter(r) = PythRandomEvents::decode_log(&l)? {
  74. Ok(r.request.sequence_number)
  75. } else {
  76. Err(anyhow!("No log with sequence number").into())
  77. }
  78. } else {
  79. Err(anyhow!("Request failed").into())
  80. }
  81. }
  82. /// Reveal the generated random number to the contract.
  83. ///
  84. /// This method is a version of the autogenned `reveal` method that parses the emitted logs
  85. /// to return the generated random number.
  86. pub async fn reveal_wrapper(
  87. &self,
  88. provider: &Address,
  89. sequence_number: u64,
  90. user_randomness: &[u8; 32],
  91. provider_randomness: &[u8; 32],
  92. ) -> Result<[u8; 32], Box<dyn Error>> {
  93. if let Some(r) = self
  94. .reveal(
  95. *provider,
  96. sequence_number,
  97. *user_randomness,
  98. *provider_randomness,
  99. )
  100. .send()
  101. .await?
  102. .await?
  103. {
  104. if let PythRandomEvents::RevealedFilter(r) =
  105. PythRandomEvents::decode_log(&r.logs[0].clone().into())?
  106. {
  107. Ok(r.random_number)
  108. } else {
  109. Err(anyhow!("No log with randomnumber").into())
  110. }
  111. } else {
  112. Err(anyhow!("Request failed").into())
  113. }
  114. }
  115. }