├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── assets └── banner.png └── src ├── account ├── auth │ ├── generate_bearer.rs │ ├── get_session_token.rs │ ├── mod.rs │ └── revoke_session_token.rs └── mod.rs ├── api └── mod.rs ├── assets ├── get_collection_owners.rs ├── get_user_collections.rs └── mod.rs ├── casts ├── cast_builder.rs ├── delete_cast.rs ├── get_casts.rs ├── mod.rs └── publish_cast.rs ├── constants ├── merkle.rs ├── mod.rs └── registry.rs ├── follows ├── follow_user.rs ├── following.rs ├── get_followers.rs ├── mod.rs └── unfollow_user.rs ├── lib.rs ├── misc ├── api_health.rs └── mod.rs ├── notifications ├── get_notifications.rs └── mod.rs ├── reactions ├── delete_like.rs ├── delete_recast.rs ├── get_likes.rs ├── get_recasters.rs ├── like_cast.rs ├── mod.rs └── recast.rs ├── registry ├── json │ ├── IdRegistryV2.json │ └── NameRegistryV2.json ├── mod.rs └── token_to_fname.rs ├── types ├── account │ ├── auth │ │ ├── bearer.rs │ │ ├── mod.rs │ │ ├── revoke.rs │ │ └── secret.rs │ └── mod.rs ├── assets │ ├── collection_owners.rs │ ├── mod.rs │ └── user_collections.rs ├── casts │ ├── cast_builder.rs │ ├── casts.rs │ ├── deleted_cast.rs │ ├── mod.rs │ └── published_cast.rs ├── follows │ ├── follow_user.rs │ ├── followers.rs │ ├── mod.rs │ └── unfollow_user.rs ├── mod.rs ├── notifications │ ├── mod.rs │ └── notifications.rs ├── reactions │ ├── deleted_like.rs │ ├── deleted_recast.rs │ ├── liked_cast.rs │ ├── liked_casts.rs │ ├── mod.rs │ ├── recasted.rs │ └── recasters.rs ├── registry │ └── mod.rs ├── shared │ ├── mod.rs │ ├── pfp.rs │ ├── shared_profile.rs │ └── viewer_context.rs ├── user │ ├── custody_address.rs │ ├── mod.rs │ └── user.rs └── verifications │ ├── mod.rs │ └── verifications.rs ├── users ├── get_custody_address.rs ├── get_user.rs ├── me.rs └── mod.rs └── verifications ├── get_verifications.rs └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Cache 2 | .idea 3 | /Cargo.lock 4 | .DS_Store 5 | 6 | # Build Files 7 | /target -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "farcaster-rs" 3 | version = "1.0.1" 4 | description="A rust crate to interface & interact with the Farcaster.xyz contracts & APIs" 5 | repository="https://github.com/TheLDB/farcaster-rs" 6 | authors=["Landon Boles "] 7 | license="MIT" 8 | edition = "2021" 9 | 10 | [dependencies] 11 | 12 | [dependencies.hex] 13 | version = "0.4.3" 14 | 15 | [dependencies.base64] 16 | version = "0.13.1" 17 | 18 | [dependencies.ethers] 19 | git = "https://github.com/gakonst/ethers-rs" 20 | version = "1.0" 21 | 22 | [dependencies.serde_json] 23 | version = "1.0" 24 | 25 | [dependencies.reqwest] 26 | version = "0.11.12" 27 | 28 | [dependencies.serde] 29 | version = "1.0" 30 | 31 | [dependencies.chrono] 32 | version = "0.4" 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Landon Boles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | farcaster-rs 3 |

farcaster-rs

4 |

🚀 A simple & easy way to interface with Farcaster via Rust 🦀

5 |

Author: Landon Boles

6 |
7 | GitHub 8 |

|

9 | Farcaster 10 |

|

11 | Bird App 12 |
13 |
14 | 15 |
16 |
17 |
18 | 19 | # Credits 20 | 21 | - [MistApproach](https://github.com/MistApproach) 22 | - Tons of various improvements & suggestions to build on top of the crate & prepare it for the hubs launch. 23 | 24 | # 📜 Documentation 25 | 26 | ## For extensive documentation, visit our [docs.rs page](https://docs.rs/farcaster-rs/1.0.1/farcaster_rs/) 27 | 28 |
29 | 30 | # 🚀 Getting Started 31 | 32 | ## Installation 33 | 34 | To get started, add the farcaster_rs crate to your `Cargo.toml` file 35 | 36 | ```toml 37 | farcaster_rs = "1.0.1" 38 | ``` 39 | 40 | Once you have the crate installed, you can start using the crate! 41 | 42 | ## Usage 43 | 44 | To connect to and use Farcaster API you need Ethereum provider HTTP endpoint along with mnemonic phrase 45 | or private key of an existing Farcaster account. 46 | 47 | ```rust 48 | use farcaster_rs::{ 49 | Farcaster, 50 | Account 51 | }; 52 | 53 | #[tokio::main] 54 | async fn main() -> Result<(), Box> { 55 | // Initialize a new Account with a phrase/private key and an optional key duration (defaults to 1 hour) 56 | let account = Account::from_mnemonic("mnemonic phrase", None).await?; 57 | 58 | // Create a Farcaster connection 59 | let farcaster = Farcaster::new("eth node", account).await?; 60 | 61 | let casts = farcaster.get_casts_by_username("lndnnft", None, None).await?; 62 | 63 | println!("{:#?}", casts); 64 | 65 | Ok(()) 66 | } 67 | ``` 68 | 69 |
70 | 71 | # 🙏 Contributing 72 | 73 | To start, I appreciate any and all contributions to the farcaster-rs repository! 74 | 75 | There are 3 prefered things I'd like if you decide to contribute, however. 76 | 77 | ## 1. Ensure the issue/contribution is needed 78 | 79 | If you spend your time building something, please ensure it's actually wanted/needed, this is best done by using the [Issues](https://github.com/TheLDB/farcaster-rs/issues) tab, and either viewing other discussions, or opening a new issue/discussion 80 | 81 | ## 2. Create a new branch for your contribution 82 | 83 | Once you have validated the contribution, and forked the repo to your own GitHub account, please create a new branch to commit your code onto. 84 | 85 | This can be done via the git CLI pretty easily: 86 | 87 | ```sh 88 | $ git switch -c my_cool_feature 89 | ``` 90 | 91 | ## 3. Create a detailed pull request, with documentation 92 | 93 | I'd like to keep everything documented to make it as easy as possible for people looking to use the crate. 94 | 95 | When opening a pull request, please ensure your function/contribution has been properly documented, and include good information about it in the PR. (use common sense) 96 | 97 | Thanks so much! 98 | -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLDB/farcaster-rs/9a0d3825cd48e6e978126c57a1c9ed12c3003697/assets/banner.png -------------------------------------------------------------------------------- /src/account/auth/generate_bearer.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use ethers::{ 3 | prelude::k256::ecdsa::SigningKey, 4 | signers::{Signer, Wallet}, 5 | types::Signature, 6 | }; 7 | 8 | use crate::{ 9 | types::account::auth::bearer::{Bearer, Params, Payload}, 10 | Farcaster, 11 | }; 12 | 13 | use serde_json::{json, Value}; 14 | 15 | impl Farcaster { 16 | /// # Best not to use this function directly 17 | /// Instead, use the [Account struct](../types/account/struct.Account.html) methods, such as from_mnemonic, and from_private_key, as that generates these automatically. 18 | /// 19 | /// However, if you'd really like to use these, go ahead. 20 | pub async fn generate_bearer( 21 | wallet: &Wallet, 22 | duration_secs: Option, 23 | ) -> Result> { 24 | // Get the current unix timestamp (non-leap seconds since January 1, 1970 00:00:00 UTC) 25 | let dt: DateTime = Utc::now(); 26 | let timestamp = dt.timestamp_millis(); 27 | 28 | // fill in bearer token parameters 29 | let params = Params { 30 | timestamp, 31 | expires_at: match duration_secs { 32 | // with expiration time 33 | Some(secs) => Some(timestamp + (secs * 1000)), 34 | // without expiration time 35 | None => None, 36 | }, 37 | }; 38 | 39 | // Initialize a bearer payload using serde_json 40 | let payload: Value = json!({ 41 | "method": "generateToken", 42 | "params": params, 43 | }); 44 | 45 | // Sign the payload using our ethers wallet 46 | let signature: Signature = wallet.sign_message(payload.to_string()).await?; 47 | 48 | // Convert our signature to a Vec 49 | let arrayify: Vec = hex::decode(signature.to_string())?; 50 | 51 | // Encode the signature to a base64 format 52 | let base64_signature: String = base64::encode(arrayify); 53 | 54 | // Format our signature 55 | let bearer = format!("Bearer eip191:{}", base64_signature); 56 | 57 | let payload = Payload { 58 | method: "generateToken".to_string(), 59 | params, 60 | }; 61 | 62 | let bearer = Bearer { bearer, payload }; 63 | 64 | Ok(bearer) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/account/auth/get_session_token.rs: -------------------------------------------------------------------------------- 1 | use serde_json::{json, Value}; 2 | 3 | use crate::{ 4 | constants::merkle::API_ROOT, 5 | types::account::auth::bearer::Bearer, 6 | types::account::auth::secret::{Secret, SecretToken}, 7 | Farcaster, 8 | }; 9 | 10 | impl Farcaster { 11 | /// # Best not to use this function directly 12 | /// Instead, use the [Account struct](../types/account/struct.Account.html) methods, such as from_mnemonic, and from_private_key, as that generates these automatically. 13 | /// 14 | /// However, if you'd really like to use these, go ahead. 15 | pub async fn get_session_token( 16 | bearer: &Bearer, 17 | ) -> Result> { 18 | let payload: Value = json!(bearer.payload); 19 | 20 | let session_reqwest: String = reqwest::Client::new() 21 | .put(format!("{}/v2/auth", API_ROOT)) 22 | .header("Content-Type", "application/json") 23 | .header("Authorization", &bearer.bearer) 24 | .json(&payload) 25 | .send() 26 | .await? 27 | .text() 28 | .await?; 29 | 30 | let mut secret: Secret = serde_json::from_str(&session_reqwest)?; 31 | secret.result.token.secret = format!("Bearer {}", secret.result.token.secret); 32 | 33 | Ok(secret.result.token) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/account/auth/mod.rs: -------------------------------------------------------------------------------- 1 | //! Contains internal functions used by the Account struct and functions above 2 | 3 | pub mod generate_bearer; 4 | pub mod get_session_token; 5 | pub mod revoke_session_token; 6 | -------------------------------------------------------------------------------- /src/account/auth/revoke_session_token.rs: -------------------------------------------------------------------------------- 1 | use serde_json::{json, Value}; 2 | 3 | use crate::types::account::auth::revoke::RevokedKeyRoot; 4 | use crate::{constants::merkle::API_ROOT, types::account::auth::secret::SecretToken, Farcaster}; 5 | use chrono::Utc; 6 | use std::error::Error; 7 | 8 | impl Farcaster { 9 | /// Best not to use this function on its own, as the Account struct has a method that does this in a clean way 10 | /// i.e. ``farcaster.account.revoke_auth_token().await?;`` 11 | pub async fn revoke_session_token( 12 | token: &SecretToken, 13 | ) -> Result> { 14 | let payload: Value = json!({ 15 | "method": "revokeToken", 16 | "params": { 17 | "timestamp": Utc::now().timestamp_millis() 18 | } 19 | }); 20 | 21 | let revoke_reqwest = reqwest::Client::new() 22 | .delete(format!("{}/v2/auth", API_ROOT)) 23 | .header("Content-Type", "application/json") 24 | .header("Authorization", &token.secret) 25 | .json(&payload) 26 | .send() 27 | .await? 28 | .text() 29 | .await?; 30 | 31 | let revoke: RevokedKeyRoot = serde_json::from_str(&revoke_reqwest)?; 32 | 33 | Ok(revoke) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/account/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Account module that handles authentication and tokens 2 | //! 3 | //! # Quickstart with the Account module 4 | //! 5 | //! 1. Use the from_mnemonic function as defined in the [Account struct](../types/account/struct.Account.html) 6 | //! ```no_run 7 | //! use farcaster_rs::Account; 8 | //! // Takes a mnemonic phrase and a token duration (defaults to 1 hour) 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! ``` 11 | //! 12 | //! 2. Use the from_private_key function as defined in the [Account struct](../types/account/struct.Account.html) 13 | //! ```no_run 14 | //! use farcaster_rs::Account; 15 | //! // Takes a mnemonic phrase and a token duration (defaults to 1 hour) 16 | //! let account: Account = Account::from_private_key("private key", None).await?; 17 | //! ``` 18 | //! 19 | //! 3. Regenerate your session token as defined in the [Account struct](../types/account/struct.Account.html) 20 | //! ```no_run 21 | //! use farcaster_rs::{Account, Farcaster}; 22 | //! // Takes a mnemonic phrase and a token duration (defaults to 1 hour) 23 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 24 | //! let mut farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 25 | //! 26 | //! // Regenerate your session token with an optional new token duration (defaults to either your previously set token duration, or the default 1 hour) 27 | //! farcaster.account.regen_session_token(None); 28 | //! ``` 29 | 30 | pub mod auth; 31 | 32 | use crate::constants::merkle::AUTH_TOKEN_DEFAULT_DURATION_SECS; 33 | use crate::types::account::auth::revoke::RevokedKeyRoot; 34 | use crate::types::account::Account; 35 | use chrono::Utc; 36 | use ethers::signers::coins_bip39::English; 37 | use ethers::signers::{LocalWallet, MnemonicBuilder}; 38 | use std::error::Error; 39 | 40 | impl Account { 41 | /// Initialize an account with a mnemonic phrase 42 | /// 43 | /// # Params 44 | /// mnemonic_phrase: &str, 45 | /// token_duration: Option - Defaults to 1hr 46 | /// 47 | /// # Example 48 | /// ```no_run 49 | /// let account = farcaster_rs::Account::from_mnemonic("phrase", None).await?; 50 | /// ``` 51 | pub async fn from_mnemonic( 52 | mnemonic_phrase: &str, 53 | token_duration: Option, 54 | ) -> Result> { 55 | let wallet = MnemonicBuilder::::default() 56 | .phrase(mnemonic_phrase) 57 | .build() 58 | .expect("Wallet creation using mnemonic phrase failed"); 59 | 60 | let token_duration_secs = token_duration.unwrap_or(AUTH_TOKEN_DEFAULT_DURATION_SECS); 61 | 62 | let mut account = Self { 63 | wallet, 64 | bearer_token: None, 65 | session_token: None, 66 | token_duration_secs: Some(token_duration_secs), 67 | }; 68 | 69 | // generate Bearer Token 70 | account.generate_bearer().await?; 71 | 72 | // generate Auth Token 73 | account.get_session_token().await?; 74 | 75 | Ok(account) 76 | } 77 | 78 | /// Initialize an account with a private key 79 | /// 80 | /// # Params: 81 | /// key: &str, 82 | /// token_duration: Option - Defaults to 1hr 83 | /// 84 | /// # Example 85 | /// ```no_run 86 | /// let account = farcaster_rs::Account::from_private_key("private key", None).await?; 87 | /// ``` 88 | pub async fn from_private_key( 89 | key: &str, 90 | token_duration: Option, 91 | ) -> Result> { 92 | let wallet = key 93 | .parse::() 94 | .expect("Wallet creation using private key failed"); 95 | 96 | let token_duration_secs = token_duration.unwrap_or(AUTH_TOKEN_DEFAULT_DURATION_SECS); 97 | 98 | let mut account = Self { 99 | wallet, 100 | bearer_token: None, 101 | session_token: None, 102 | token_duration_secs: Some(token_duration_secs), 103 | }; 104 | 105 | // generate Bearer Token 106 | account.generate_bearer().await?; 107 | 108 | // generate Auth Token 109 | account.get_session_token().await?; 110 | 111 | Ok(account) 112 | } 113 | 114 | /// Get your authentication token 115 | pub fn get_auth_token(&self) -> Result<&str, Box> { 116 | // prepare session token, if not ready 117 | if self.session_token.is_none() { 118 | return Err(Box::from("Auth Token not ready")); 119 | } 120 | 121 | // check if token has expired 122 | let timestamp = Utc::now().timestamp_millis(); 123 | if timestamp > self.session_token.as_ref().unwrap().expires_at { 124 | return Err(Box::from("Auth Token expired")); 125 | } 126 | 127 | Ok(&self.session_token.as_ref().unwrap().secret) 128 | } 129 | 130 | /// generate Merkle v2 API bearer token 131 | async fn generate_bearer(&mut self) -> Result<(), Box> { 132 | self.bearer_token = 133 | Some(crate::Farcaster::generate_bearer(&self.wallet, self.token_duration_secs).await?); 134 | 135 | Ok(()) 136 | } 137 | 138 | /// get Merkle v2 API session token 139 | async fn get_session_token(&mut self) -> Result<(), Box> { 140 | // generate bearer token, if not ready 141 | if self.bearer_token.is_none() { 142 | self.generate_bearer().await?; 143 | } 144 | 145 | self.session_token = 146 | Some(crate::Farcaster::get_session_token(&self.bearer_token.as_ref().unwrap()).await?); 147 | 148 | Ok(()) 149 | } 150 | 151 | /// Function to regenete your session token. 152 | /// Ideal for bots. Regenerate every hour to secure your token. 153 | pub async fn regen_session_token( 154 | &mut self, 155 | token_duration: Option, 156 | ) -> Result<(), Box> { 157 | let token_duration_secs = token_duration.unwrap_or( 158 | self.token_duration_secs 159 | .unwrap_or(AUTH_TOKEN_DEFAULT_DURATION_SECS), 160 | ); 161 | 162 | self.bearer_token = 163 | Some(crate::Farcaster::generate_bearer(&self.wallet, Some(token_duration_secs)).await?); 164 | 165 | self.session_token = 166 | Some(crate::Farcaster::get_session_token(&self.bearer_token.as_ref().unwrap()).await?); 167 | 168 | Ok(()) 169 | } 170 | 171 | /// Revokes a specific session token 172 | pub async fn revoke_auth_token(&mut self) -> Result> { 173 | // Return error if no session token is present 174 | if self.session_token.is_none() { 175 | return Err(Box::from("No session token present")); 176 | } 177 | 178 | let revoke = 179 | crate::Farcaster::revoke_session_token(&self.session_token.as_ref().unwrap()).await?; 180 | 181 | Ok(revoke) 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/api/mod.rs: -------------------------------------------------------------------------------- 1 | //! The API module that has internal functions for wrappers and such 2 | 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// bridge to reqwest get call 8 | pub(crate) async fn reqwest_get(&self, url: &str) -> Result> { 9 | let req_builder = reqwest::Client::new().get(url); 10 | let response = req_builder 11 | .header("Content-Type", "application/json") 12 | .header("Authorization", self.account.get_auth_token()?) 13 | .send() 14 | .await? 15 | .text() 16 | .await?; 17 | 18 | Ok(response) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/get_collection_owners.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::assets::collection_owners::CollectionOwnersRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | pub async fn get_collection_owners( 8 | &self, 9 | collection_id: &str, 10 | limit: Option, 11 | cursor: Option, 12 | ) -> Result> { 13 | let mut url = format!( 14 | "{}/v2/collection-owners?collectionid={}", 15 | API_ROOT, collection_id 16 | ); 17 | 18 | if limit.is_some() { 19 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 20 | } 21 | 22 | if cursor.is_some() { 23 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 24 | } 25 | 26 | let collection_reqwest = &self.reqwest_get(&url).await?; 27 | 28 | let collections: CollectionOwnersRoot = serde_json::from_str(&collection_reqwest)?; 29 | 30 | Ok(collections) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/get_user_collections.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::assets::user_collections::UserCollectionsRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get all collections an FID owns 8 | /// ```no_run 9 | /// use farcaster_rs::{Account, Farcaster}; 10 | /// 11 | /// let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 12 | /// let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 13 | /// 14 | /// // Returns all Farcaster collection owners of the ID specified 15 | /// let collections = farcaster.get_collections_by_fid(5, None, None).await?; 16 | /// ``` 17 | pub async fn get_collections_by_fid( 18 | &self, 19 | fid: i64, 20 | limit: Option, 21 | cursor: Option, 22 | ) -> Result> { 23 | let mut url = format!("{}/v2/user-collections?ownerFid={}", API_ROOT, fid); 24 | 25 | if limit.is_some() { 26 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 27 | } 28 | 29 | if cursor.is_some() { 30 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 31 | } 32 | 33 | let collections_reqwest = &self.reqwest_get(&url).await?; 34 | 35 | let collections: UserCollectionsRoot = serde_json::from_str(&collections_reqwest)?; 36 | 37 | Ok(collections) 38 | } 39 | 40 | /// Get all collections a username owns 41 | /// ```no_run 42 | /// use farcaster_rs::{Account, Farcaster}; 43 | /// 44 | /// let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 45 | /// let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 46 | /// 47 | /// // Returns all Farcaster collection owners of the ID specified 48 | /// let collections = farcaster.get_collections_by_username("ace", None, None).await?; 49 | /// ``` 50 | pub async fn get_collections_by_username( 51 | &self, 52 | username: &str, 53 | limit: Option, 54 | cursor: Option, 55 | ) -> Result> { 56 | let fid = &self.get_user_by_username(username).await?; 57 | 58 | let collection = &self.get_collections_by_fid(fid.fid, limit, cursor).await?; 59 | 60 | Ok(collection.clone()) 61 | } 62 | 63 | /// Get all collections an address owns 64 | /// ```no_run 65 | /// use farcaster_rs::{Account, Farcaster}; 66 | /// 67 | /// let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 68 | /// let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 69 | /// 70 | /// // Returns all Farcaster collection owners of the ID specified 71 | /// let collections = farcaster.get_collections_by_address("0x000.....", None, None).await?; 72 | /// ``` 73 | pub async fn get_collections_by_address( 74 | &self, 75 | address: &str, 76 | limit: Option, 77 | cursor: Option, 78 | ) -> Result> { 79 | let fid = &self.get_user_by_address(address).await?; 80 | 81 | let collection = &self.get_collections_by_fid(fid.fid, limit, cursor).await?; 82 | 83 | Ok(collection.clone()) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/assets/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Assets module that handles getting related NFT assets 2 | //! 3 | //! # Quickstart with the Assets module 4 | //! 5 | //! 1. Use the get_user_collections function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // All three functions return the same info, just conveninent ways of fetching them 13 | //! // Fetch the collections a user owns 14 | //! let collections_username = farcaster.get_collections_by_username("lndnnft", None, None).await?; 15 | //! let collections_fid = farcaster.get_collections_by_fid(2, None, None).await?; 16 | //! let collections_address = farcaster.get_collections_by_address("0xef......", None, None).await?; 17 | //! ``` 18 | //! 19 | //! 2. Use the get_collection_owners function 20 | //! ```no_run 21 | //! use farcaster_rs::{Account, Farcaster}; 22 | //! 23 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 24 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 25 | //! 26 | //! // Returns all Farcaster collection owners of the ID specified 27 | //! let collections_owners = farcaster.get_collection_owners("collectionID", None, None).await?; 28 | //! ``` 29 | 30 | pub mod get_collection_owners; 31 | pub mod get_user_collections; 32 | -------------------------------------------------------------------------------- /src/casts/cast_builder.rs: -------------------------------------------------------------------------------- 1 | // use crate::constants::merkle::API_ROOT; 2 | // use crate::types::casts::published_cast::PublishedCast; 3 | // use crate::{Farcaster}; 4 | // use chrono::offset::Utc; 5 | // use serde_json::{json, Value}; 6 | // use std::error::Error; 7 | 8 | // Made all functions private, the new docs deem this irrelevant. Keeping it in case it can be used one day though 9 | // impl CastBuilder { 10 | // fn new() -> Self { 11 | // Self { 12 | // content: "".to_string(), 13 | // embeds: Vec::new(), 14 | // mentions: Vec::new(), 15 | // parent_cast_hash: "".to_string(), 16 | // } 17 | // } 18 | // 19 | // fn content(&mut self, content: &str) -> &mut Self { 20 | // self.content = content.to_string(); 21 | // 22 | // self 23 | // } 24 | // 25 | // fn embed<'a>(&'a mut self, embed: &str) -> Result<&'a mut Self, Box> { 26 | // if self.embeds.len() > 1 { 27 | // return Err(Box::from("You cannot have more than two embeds in a cast")); 28 | // } 29 | // 30 | // self.embeds.push(embed.to_string()); 31 | // 32 | // Ok(self) 33 | // } 34 | // 35 | // fn mention<'a>(&'a mut self, mention: &str) -> Result<&'a mut Self, Box> { 36 | // if self.mentions.len() > 4 { 37 | // return Err(Box::from( 38 | // "You cannot have more than four mentions in a cast", 39 | // )); 40 | // } 41 | // 42 | // self.mentions.push(mention.to_string()); 43 | // 44 | // Ok(self) 45 | // } 46 | // 47 | // fn parent_cast_hash(&mut self, hash: &str) -> &mut Self { 48 | // self.parent_cast_hash = hash.to_string(); 49 | // 50 | // self 51 | // } 52 | // 53 | // async fn cast(&self, farcaster: &Farcaster) -> Result> { 54 | // let payload: Value = json!({ 55 | // "timestamp": Utc::now().timestamp_millis(), 56 | // "text": self.content, 57 | // "embeds": self.embeds, 58 | // "mentions": self.mentions, 59 | // "parentCastHash": self.parent_cast_hash 60 | // }); 61 | // 62 | // let publish_cast_reqwest: String = reqwest::Client::new() 63 | // .post(format!("{}/v2/casts", API_ROOT)) 64 | // .header("Content-Type", "application/json") 65 | // .header( 66 | // "Authorization", 67 | // &farcaster.account.session_token.as_ref().unwrap().secret, 68 | // ) 69 | // .json(&payload) 70 | // .send() 71 | // .await? 72 | // .text() 73 | // .await?; 74 | // 75 | // let published_cast: PublishedCast = serde_json::from_str(&publish_cast_reqwest)?; 76 | // 77 | // Ok(published_cast) 78 | // } 79 | // } 80 | -------------------------------------------------------------------------------- /src/casts/delete_cast.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::casts::deleted_cast::DeletedCastRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Delete a cast via its hash 9 | /// 10 | /// ## Params 11 | /// cast_hash: &str 12 | /// 13 | /// ## Example 14 | /// ```no_run 15 | /// let casts = farcaster.delete_cast_by_cast_hash("cast_hash").await?; 16 | /// ``` 17 | pub async fn delete_cast_by_cast_hash( 18 | &self, 19 | cast_hash: &str, 20 | ) -> Result> { 21 | let payload: Value = json!({ "castHash": cast_hash }); 22 | 23 | let delete_reqwest = reqwest::Client::new() 24 | .delete(format!("{}/v2/casts", API_ROOT)) 25 | .header("Content-Type", "application/json") 26 | .header( 27 | "Authorization", 28 | &self.account.session_token.as_ref().unwrap().secret, 29 | ) 30 | .json(&payload) 31 | .send() 32 | .await? 33 | .text() 34 | .await?; 35 | 36 | let deleted_cast: DeletedCastRoot = serde_json::from_str(&delete_reqwest)?; 37 | 38 | Ok(deleted_cast) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/casts/get_casts.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::casts::casts::CastRoot; 3 | use crate::types::user::user::UserInfo; 4 | use crate::Farcaster; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Get a users casts by their FID 9 | /// 10 | /// ## Params 11 | /// fid: i64 12 | /// limit: Option 13 | /// cursor: Option<&str> 14 | /// 15 | /// ## Example 16 | /// ```no_run 17 | /// let casts = farcaster.get_casts_by_fid(0, None, None).await?; 18 | /// ``` 19 | pub async fn get_casts_by_fid( 20 | &self, 21 | fid: i64, 22 | limit: Option, 23 | cursor: Option<&str>, 24 | ) -> Result> { 25 | let mut url = format!("{}/v2/casts?fid={}", API_ROOT, fid); 26 | if limit.is_some() { 27 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 28 | } 29 | 30 | if cursor.is_some() { 31 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 32 | } 33 | 34 | println!("{}", url); 35 | 36 | let casts_reqwest = &self.reqwest_get(url.as_str()).await?; 37 | 38 | let casts: CastRoot = serde_json::from_str(casts_reqwest)?; 39 | 40 | Ok(casts) 41 | } 42 | 43 | /// Get a users casts by their Username 44 | /// 45 | /// ## Params 46 | /// username: &str 47 | /// limit: Option 48 | /// cursor: Option<&str> 49 | /// 50 | /// ## Example 51 | /// ```no_run 52 | /// let casts = farcaster.get_casts_by_username("cassie", None, None).await?; 53 | /// ``` 54 | pub async fn get_casts_by_username( 55 | &self, 56 | username: &str, 57 | limit: Option, 58 | cursor: Option<&str>, 59 | ) -> Result> { 60 | let fid = &self.get_user_by_username(username).await?; 61 | 62 | let casts = &self.get_casts_by_fid(fid.fid, limit, cursor).await?; 63 | 64 | Ok(casts.clone()) 65 | } 66 | 67 | /// Get a users casts by their Address 68 | /// 69 | /// ## Params 70 | /// address: &str 71 | /// limit: Option 72 | /// cursor: Option<&str> 73 | /// 74 | /// ## Example 75 | /// ```no_run 76 | /// let casts = farcaster.get_casts_by_address("0x0000....", None, None).await?; 77 | /// ``` 78 | pub async fn get_casts_by_address( 79 | &self, 80 | address: &str, 81 | limit: Option, 82 | cursor: Option<&str>, 83 | ) -> Result> { 84 | let address: &UserInfo = &self.get_user_by_address(address).await?; 85 | 86 | let casts = &self.get_casts_by_fid(address.fid, limit, cursor).await?; 87 | 88 | Ok(casts.clone()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/casts/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Casts module that handles protocol casts 2 | //! 3 | //! # Quickstart with the Casts module 4 | //! 5 | //! 1. Use the publish_cast function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // Publish a cast. 13 | //! // 1st Param: Content 14 | //! // 2nd & 3rd Param: Parent Cast Hash & Parent User FID 15 | //! // Used for replying to a cast 16 | //! let cast = farcaster.publish_cast("cast content", Some("optional parent hash to reply to"), Some(0)).await?; 17 | //! ``` 18 | //! 19 | //! //! 2. Use the get_casts function 20 | //! ```no_run 21 | //! use farcaster_rs::{Account, Farcaster}; 22 | //! 23 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 24 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 25 | //! 26 | //! // Get the casts of a user. 27 | //! // All three functions return the same thing, just different methods of fetching 28 | //! // 1st Param: FID/username/address 29 | //! // 2nd Param: Optional Limit of how many to fetch (max 100)//! 30 | //! // 3rd Param: Optional cursor for pagination 31 | //! let casts = farcaster.get_casts_by_fid(0, None, None).await?; 32 | //! let casts = farcaster.get_casts_by_username("dwr", None, None).await?; 33 | //! let casts = farcaster.get_casts_by_address("0x000...", None, None).await?; 34 | //! ``` 35 | //! 36 | //! //! //! 3. Use the delete_cast function 37 | //! ```no_run 38 | //! use farcaster_rs::{Account, Farcaster}; 39 | //! 40 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 41 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 42 | //! 43 | //! let delete_cast = farcaster.delete_cast_by_cast_hash("cast hash").await?; 44 | //! ``` 45 | 46 | pub mod delete_cast; 47 | pub mod get_casts; 48 | /// Publish a cast to the protocol 49 | /// ```no_run 50 | /// use farcaster_rs::{Account, Farcaster}; 51 | /// 52 | /// let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 53 | /// let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 54 | /// 55 | /// // Publish a cast. 56 | /// // 1st Param: Content 57 | /// // 2nd & 3rd Param: Parent Cast Hash & Parent User FID 58 | /// // Used for replying to a cast 59 | /// let cast = farcaster.publish_cast("cast content", Some("optional parent hash to reply to"), Some(0)).await?; 60 | /// ``` 61 | pub mod publish_cast; 62 | -------------------------------------------------------------------------------- /src/casts/publish_cast.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::casts::published_cast::PublishedCast; 3 | use crate::Farcaster; 4 | 5 | use serde_json::{json, Value}; 6 | 7 | impl Farcaster { 8 | /// Publish a cast to the protocol 9 | /// ```no_run 10 | /// use farcaster_rs::{Account, Farcaster}; 11 | /// 12 | /// let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 13 | /// let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 14 | /// 15 | /// // Publish a cast. 16 | /// // 1st Param: Content 17 | /// // 2nd & 3rd Param: Parent Cast Hash & Parent User FID 18 | /// // Used for replying to a cast 19 | /// let cast = farcaster.publish_cast("cast content", Some("optional parent hash to reply to"), Some(0)).await?; 20 | /// ``` 21 | pub async fn publish_cast( 22 | &self, 23 | content: &str, 24 | reply_to_hash: Option<&str>, 25 | reply_to_fid: Option, 26 | ) -> Result> { 27 | let payload: Value; 28 | 29 | if reply_to_hash.is_some() && reply_to_fid.is_some() { 30 | let parent_hash = reply_to_hash.unwrap(); 31 | let parent_fid = reply_to_fid.unwrap(); 32 | 33 | payload = json!({ 34 | "parent": { 35 | "hash": parent_hash, 36 | "fid": parent_fid 37 | }, 38 | "text": content 39 | }) 40 | } else { 41 | payload = json!({ "text": content }) 42 | } 43 | 44 | let publish_cast_reqwest = reqwest::Client::new() 45 | .post(format!("{}/v2/casts", API_ROOT)) 46 | .header("Content-Type", "application/json") 47 | .header( 48 | "Authorization", 49 | &self.account.session_token.as_ref().unwrap().secret, 50 | ) 51 | .json(&payload) 52 | .send() 53 | .await? 54 | .text() 55 | .await?; 56 | 57 | let published_cast: PublishedCast = serde_json::from_str(&publish_cast_reqwest)?; 58 | 59 | Ok(published_cast) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/constants/merkle.rs: -------------------------------------------------------------------------------- 1 | /// Merkle API v2 constants 2 | 3 | /// Merkle API root 4 | pub static API_ROOT: &str = "https://api.warpcast.com"; 5 | 6 | /// Merkle API Authentication 7 | /// default duration of authentication token in seconds 8 | pub const AUTH_TOKEN_DEFAULT_DURATION_SECS: i64 = 60 * 60; // 1 hour 9 | -------------------------------------------------------------------------------- /src/constants/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod merkle; 2 | pub mod registry; 3 | -------------------------------------------------------------------------------- /src/constants/registry.rs: -------------------------------------------------------------------------------- 1 | /// Farcaster ID Registry 2 | pub static FIR_CONTRACT_ADDRESS: &str = "0xda107a1caf36d198b12c16c7b6a1d1c795978c42"; 3 | pub static FIR_DEPLOYMENT_BLOCK: u64 = 7_648_795; 4 | 5 | /// Farcaster Name Registry 6 | pub static FNR_CONTRACT_ADDRESS: &str = "0xe3be01d99baa8db9905b33a3ca391238234b79d1"; 7 | pub static FNR_DEPLOYMENT_BLOCK: u64 = 7_648_795; 8 | -------------------------------------------------------------------------------- /src/follows/follow_user.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::follows::follow_user::FollowUserRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Get a users followers via their FID 9 | /// 10 | /// # Params 11 | /// fid: i64 12 | /// 13 | /// # Example 14 | /// ```no_run 15 | /// let follow = farcaster.follow_user_by_fid(0).await?; 16 | /// ``` 17 | pub async fn follow_user_by_fid(&self, fid: i64) -> Result> { 18 | let payload: Value = json!({ "targetFid": fid }); 19 | 20 | let follow_reqwest = reqwest::Client::new() 21 | .put(format!("{}/v2/follows", API_ROOT)) 22 | .header("Content-Type", "application/json") 23 | .header( 24 | "Authorization", 25 | &self.account.session_token.as_ref().unwrap().secret, 26 | ) 27 | .json(&payload) 28 | .send() 29 | .await? 30 | .text() 31 | .await?; 32 | 33 | let follow: FollowUserRoot = serde_json::from_str(&follow_reqwest)?; 34 | 35 | Ok(follow) 36 | } 37 | 38 | /// Get a users followers via their username 39 | /// 40 | /// # Params 41 | /// username: &str 42 | /// 43 | /// # Example 44 | /// ```no_run 45 | /// let follow = farcaster.follow_user_by_username("abc").await?; 46 | /// ``` 47 | pub async fn follow_user_by_username( 48 | &self, 49 | username: &str, 50 | ) -> Result> { 51 | let fid = &self.get_user_by_username(username).await?; 52 | 53 | let follow = &self.follow_user_by_fid(fid.fid).await?; 54 | 55 | Ok(follow.clone()) 56 | } 57 | 58 | /// Get a users followers via their address 59 | /// 60 | /// # Params 61 | /// address: &str 62 | /// 63 | /// # Example 64 | /// ```no_run 65 | /// let follow = farcaster.follow_user_by_address("0x000....").await?; 66 | /// ``` 67 | pub async fn follow_user_by_address( 68 | &self, 69 | address: &str, 70 | ) -> Result> { 71 | let fid = &self.get_user_by_address(address).await?; 72 | 73 | let follow = &self.follow_user_by_fid(fid.fid).await?; 74 | 75 | Ok(follow.clone()) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/follows/following.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::follows::followers::FollowersRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | pub async fn get_following_by_fid( 8 | &self, 9 | fid: i64, 10 | limit: Option, 11 | cursor: Option<&str>, 12 | ) -> Result> { 13 | let mut url = format!("{}/v2/following?fid={}", API_ROOT, fid); 14 | 15 | if limit.is_some() { 16 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()); 17 | } 18 | 19 | if cursor.is_some() { 20 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()); 21 | } 22 | 23 | let following_reqwest = &self.reqwest_get(&url).await?; 24 | 25 | let following: FollowersRoot = serde_json::from_str(&following_reqwest)?; 26 | 27 | Ok(following) 28 | } 29 | 30 | pub async fn get_following_by_username( 31 | &self, 32 | username: &str, 33 | limit: Option, 34 | cursor: Option<&str>, 35 | ) -> Result> { 36 | let fid = &self.get_user_by_username(username).await?; 37 | 38 | let following = &self.get_following_by_fid(fid.fid, limit, cursor).await?; 39 | 40 | Ok(following.clone()) 41 | } 42 | 43 | pub async fn get_following_by_address( 44 | &self, 45 | address: &str, 46 | limit: Option, 47 | cursor: Option<&str>, 48 | ) -> Result> { 49 | let fid = &self.get_user_by_address(address).await?; 50 | 51 | let following = &self.get_following_by_fid(fid.fid, limit, cursor).await?; 52 | 53 | Ok(following.clone()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/follows/get_followers.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::follows::followers::FollowersRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | pub async fn get_followers_by_fid( 8 | &self, 9 | fid: i64, 10 | limit: Option, 11 | cursor: Option<&str>, 12 | ) -> Result> { 13 | let mut url = format!("{}/v2/followers?fid={}", API_ROOT, fid); 14 | 15 | if limit.is_some() { 16 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 17 | } 18 | 19 | if cursor.is_some() { 20 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 21 | } 22 | 23 | let followers_reqwest = &self.reqwest_get(&url).await?; 24 | 25 | let followers: FollowersRoot = serde_json::from_str(&followers_reqwest)?; 26 | 27 | Ok(followers) 28 | } 29 | 30 | pub async fn get_followers_by_username( 31 | &self, 32 | username: &str, 33 | limit: Option, 34 | cursor: Option<&str>, 35 | ) -> Result> { 36 | let fid = &self.get_user_by_username(username).await?; 37 | 38 | let followers = &self.get_followers_by_fid(fid.fid, limit, cursor).await?; 39 | 40 | Ok(followers.clone()) 41 | } 42 | 43 | pub async fn get_followers_by_address( 44 | &self, 45 | address: &str, 46 | limit: Option, 47 | cursor: Option<&str>, 48 | ) -> Result> { 49 | let fid = &self.get_user_by_address(address).await?; 50 | 51 | let followers = &self.get_followers_by_fid(fid.fid, limit, cursor).await?; 52 | 53 | Ok(followers.clone()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/follows/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Follows module that handles followers and follows and following 2 | //! 3 | //! # Quickstart with the Following module 4 | //! 5 | //! 1. Use the follow_user function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // Follow a user 13 | //! let follow = farcaster.follow_user_by_username("dwr").await?; 14 | //! let follow = farcaster.follow_user_by_fid(0).await?; 15 | //! let follow = farcaster.follow_user_by_address("0x0000....").await?; 16 | //! ``` 17 | //! 18 | //! 2. Use the unfollow_user function 19 | //! ```no_run 20 | //! use farcaster_rs::{Account, Farcaster}; 21 | //! 22 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 23 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 24 | //! 25 | //! // Unfollow a user 26 | //! let unfollow = farcaster.unfollow_user_by_username("dwr").await?; 27 | //! let unfollow = farcaster.unfollow_user_by_fid(0).await?; 28 | //! let unfollow = farcaster.unfollow_user_by_address("0x0000....").await?; 29 | //! ``` 30 | //! 31 | //! //! 3. Use the get_followers function 32 | //! ```no_run 33 | //! use farcaster_rs::{Account, Farcaster}; 34 | //! 35 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 36 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 37 | //! 38 | //! // Get followers 39 | //! let followers = farcaster.get_followers_by_address("juan", None, None).await?; 40 | //! let followers = farcaster.get_followers_by_fid(0, None, None).await?; 41 | //! let followers = farcaster.get_followers_by_address("0x0000....", None, None).await?; 42 | //! ``` 43 | //! 44 | //! //! //! 4. Use the following function 45 | //! ```no_run 46 | //! use farcaster_rs::{Account, Farcaster}; 47 | //! 48 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 49 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 50 | //! 51 | //! // Get following 52 | //! let following = farcaster.get_following_by_username("jayme", None, None).await?; 53 | //! let following = farcaster.get_following_by_fid(0, None, None).await?; 54 | //! let following = farcaster.get_following_by_address("0x000....", None, None).await?; 55 | //! ``` 56 | //! 57 | 58 | /// Follow a user 59 | pub mod follow_user; 60 | /// Get following 61 | pub mod following; 62 | /// Get followers 63 | pub mod get_followers; 64 | /// Unfollow a user 65 | pub mod unfollow_user; 66 | -------------------------------------------------------------------------------- /src/follows/unfollow_user.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::follows::unfollow_user::UnfollowUserRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | pub async fn unfollow_user_by_fid(&self, fid: i64) -> Result> { 9 | let payload: Value = json!({ "targetFid": fid }); 10 | 11 | let unfollow_reqwest = reqwest::Client::new() 12 | .delete(format!("{}/v2/follows", API_ROOT)) 13 | .header("Content-Type", "application/json") 14 | .header( 15 | "Authorization", 16 | &self.account.session_token.as_ref().unwrap().secret, 17 | ) 18 | .json(&payload) 19 | .send() 20 | .await? 21 | .text() 22 | .await?; 23 | 24 | let unfollow: UnfollowUserRoot = serde_json::from_str(&unfollow_reqwest)?; 25 | 26 | Ok(unfollow) 27 | } 28 | 29 | pub async fn unfollow_user_by_username( 30 | &self, 31 | username: &str, 32 | ) -> Result> { 33 | let fid = &self.get_user_by_username(username).await?; 34 | 35 | let unfollow = &self.unfollow_user_by_fid(fid.fid).await?; 36 | 37 | Ok(unfollow.clone()) 38 | } 39 | 40 | pub async fn unfollow_user_by_address( 41 | &self, 42 | address: &str, 43 | ) -> Result> { 44 | let fid = &self.get_user_by_address(address).await?; 45 | 46 | let unfollow = &self.unfollow_user_by_fid(fid.fid).await?; 47 | 48 | Ok(unfollow.clone()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # farcaster-rs 2 | //! A Rust crate to interact with the [Farcaster](https://farcaster.xyz) smart contracrts & merkle APIs 3 | //! 4 | //! # Quickstart 5 | //! To get started with the farcaster-rs crate, this should serve as a simple example of how to use it 6 | //! 7 | //! ```no_run 8 | //! use farcaster_rs::{ 9 | //! Account, 10 | //! Farcaster 11 | //! }; 12 | //! use std::error::Error; 13 | //! 14 | //! #[tokio::main] 15 | //! async fn main() -> Result<(), Box> { 16 | //! // Initialize a new Account w/ your key and an optional token duration (milliseconds) 17 | //! let account: Account = Account::from_mnemonic("super top secret phrase", None).await?; 18 | //! 19 | //! // Connect to an ETH node 20 | //! let farcaster: Farcaster = Farcaster::new("node url", account).await?; 21 | //! 22 | //! // Get Dan Romero's Casts with his Username, while specifying a limit, and a cursor 23 | //! let casts = farcaster.get_casts_by_username("dwr", Some(5), None).await?; 24 | //! 25 | //! println!("{:#?}", casts); 26 | //! 27 | //! Ok(()) 28 | //! } 29 | //! ``` 30 | //! Quick explanation of all folders/modules in farcaster-rs 31 | //! 32 | //! 33 | //! ## If you're looking for authentication 34 | //! It's best to look in the [Account Struct](./types/account/struct.Account.html) 35 | //! 36 | //! ## If you're looking for most protocol-related functions 37 | //! It's best to look in the [Farcaster Struct](./struct.Farcaster.html) 38 | //! 39 | //! ## If you're looking to convert stuff like Username -> FID, FID -> Address, etc.... 40 | //! It's best to look in the [Registry Struct](./types/registry/struct.Registry.html) 41 | //! 42 | //! --------------- 43 | //! ## `account` 44 | //! The account module handles authentication w/ the Farcaster Merkle APIs. Generating tokens, revoking tokens, etc.... 45 | //! View documentation [here](./account/index.html) 46 | //! 47 | //! ## `api` 48 | //! The API module has room for growth, but for now holds some reqwest wrappers to make my life easier 49 | //! View documentation [here](./api/index.html) 50 | //! 51 | //! ## `assets` 52 | //! The assets module holds functions to get NFT collection related data. 53 | //! View documentation [here](./assets/index.html) 54 | //! 55 | //! ## `casts` 56 | //! The casts module holds functions related to casts on Farcaster. Getting casts, publishing casts, etc... 57 | //! View documentation [here](./casts/index.html) 58 | //! 59 | //! ## `follows` 60 | //! The follows module holds function related to followers and following. Follow users, unfollow, view followers, etc.. 61 | //! View documentation [here](./follows/index.html) 62 | //! 63 | //! ## `misc` 64 | //! The misc module holds functions that don't entirely have a place. The only one for now is API Health 65 | //! View documentation [here](./misc/index.html) 66 | //! 67 | //! ## `notifications` 68 | //! The notifications module holds functions relating to notifications, which right now allows you to fetch notifications 69 | //! //! View documentation [here](./notifications/index.html) 70 | //! 71 | //! ## `reactions` 72 | //! The reactions module allows you to view and modify your reactions. Like, recast, list likes/recasts, etc... 73 | //! View documentation [here](./reactions/index.html) 74 | //! 75 | //! ## `registry` 76 | //! The registry module holds important functions most importantly relating to getting users. 77 | //! - Convert addres/username to FID, get users via FID, etc...... 78 | //! View documentation [here](./registry/index.html) 79 | //! ## `users` 80 | //! The users module holds functions relating to users on Farcaster. Get users, get custody adress', etc... 81 | //! //! View documentation [here](./users/index.html) 82 | //! 83 | //! ## `verifications` 84 | //! The verifications module holds functions relating to cryptographic proofs such as getting verified address' 85 | //! //! View documentation [here](./verifications/index.html) 86 | 87 | pub mod account; 88 | pub mod api; 89 | pub mod assets; 90 | pub mod casts; 91 | pub mod constants; 92 | pub mod follows; 93 | pub mod misc; 94 | pub mod notifications; 95 | pub mod reactions; 96 | pub mod registry; 97 | pub mod types; 98 | pub mod users; 99 | pub mod verifications; 100 | 101 | pub use types::account::Account; 102 | pub use types::registry::Registry; 103 | 104 | use std::error::Error; 105 | 106 | /// The Farcaster type that holds the keys to the castle - so to speak :) 107 | #[derive(Debug)] 108 | pub struct Farcaster { 109 | #[allow(dead_code)] 110 | pub account: Account, 111 | pub registry: Registry, 112 | } 113 | 114 | impl Farcaster { 115 | pub async fn new(ethereum_provider: &str, account: Account) -> Result> { 116 | let registry = Registry::new(ethereum_provider).await?; 117 | 118 | Ok(Farcaster { account, registry }) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/misc/api_health.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::Farcaster; 3 | use std::error::Error; 4 | 5 | impl Farcaster { 6 | pub async fn get_api_health() -> Result> { 7 | let api_health = reqwest::Client::new() 8 | .get(format!("{}/healthcheck", API_ROOT)) 9 | .header("Accept", "application/json") 10 | .send() 11 | .await? 12 | .text() 13 | .await?; 14 | 15 | Ok(api_health) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/misc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod api_health; 2 | -------------------------------------------------------------------------------- /src/notifications/get_notifications.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::notifications::notifications::NotificationsRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Fetch your notifications 8 | /// 9 | /// # Params 10 | /// limit: Option - How many casts to get (max 100) 11 | /// cursor: Option<&str) - For pagination 12 | /// 13 | /// # Example 14 | /// ```no_run 15 | /// let notifications = farcaster.get_notifications(None, None).await?; 16 | /// ``` 17 | pub async fn get_notifications( 18 | &self, 19 | limit: Option, 20 | cursor: Option<&str>, 21 | ) -> Result> { 22 | let mut url = format!("{}/v2/mention-and-reply-notifications", API_ROOT); 23 | 24 | if limit.is_some() { 25 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 26 | } 27 | 28 | if cursor.is_some() { 29 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 30 | } 31 | 32 | let notifications_reqwest = &self.reqwest_get(&url).await?; 33 | 34 | let notifications: NotificationsRoot = serde_json::from_str(¬ifications_reqwest)?; 35 | 36 | Ok(notifications) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/notifications/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Notifications module that handles notifications 2 | //! 3 | //! # Quickstart with the Notification module 4 | //! 5 | //! 1. Use the get_notifications function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // Get Notifications 13 | //! let notifications = farcaster.get_notifications(None, None).await?; 14 | //! ``` 15 | 16 | /// Fetch your notifications 17 | pub mod get_notifications; 18 | -------------------------------------------------------------------------------- /src/reactions/delete_like.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::reactions::deleted_like::DeletedLikeRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Unlike a cast 9 | /// 10 | /// # Params 11 | /// cast_hash: &str 12 | /// 13 | /// # Example 14 | /// ```no_run 15 | /// farcaster.delete_like_by_cast_hash("cast hash").await?; 16 | /// ``` 17 | pub async fn delete_like_by_cast_hash( 18 | &self, 19 | cast_hash: &str, 20 | ) -> Result> { 21 | let payload: Value = json!({ "castHash": cast_hash }); 22 | 23 | let deleted_like_reqwest = reqwest::Client::new() 24 | .delete(format!("{}/v2/cast-likes", API_ROOT)) 25 | .header("Content-Type", "application/json") 26 | .header( 27 | "Authorization", 28 | &self.account.session_token.as_ref().unwrap().secret, 29 | ) 30 | .json(&payload) 31 | .send() 32 | .await? 33 | .text() 34 | .await?; 35 | 36 | println!("{}", deleted_like_reqwest); 37 | 38 | let deleted_like: DeletedLikeRoot = serde_json::from_str(&deleted_like_reqwest)?; 39 | 40 | Ok(deleted_like) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/reactions/delete_recast.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::reactions::deleted_recast::DeletedRecastRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Delete a recast by the parent cast's hash 9 | /// 10 | /// # Params 11 | /// cast_hash: &str 12 | /// 13 | /// # Example 14 | /// ```no_run 15 | /// let delete = farcaster.delete_recast_by_cast_hash("cast hash").await?; 16 | /// ``` 17 | pub async fn delete_recast_by_cast_hash( 18 | &self, 19 | cast_hash: &str, 20 | ) -> Result> { 21 | let payload: Value = json!({ "castHash": cast_hash }); 22 | 23 | let delete_recast_reqwest = reqwest::Client::new() 24 | .delete(format!("{}/v2/recasts", API_ROOT)) 25 | .header("Content-Type", "application/json") 26 | .header( 27 | "Authorization", 28 | &self.account.session_token.as_ref().unwrap().secret, 29 | ) 30 | .json(&payload) 31 | .send() 32 | .await? 33 | .text() 34 | .await?; 35 | 36 | let deleted_recast: DeletedRecastRoot = serde_json::from_str(&delete_recast_reqwest)?; 37 | 38 | Ok(deleted_recast) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/reactions/get_likes.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::reactions::liked_casts::ManyLikedCastsRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get likes by the cast hash 8 | /// 9 | /// # Params 10 | /// cast_hash: &str 11 | /// limit: Option - Optional # of likes to get (max 100) 12 | /// cursor: Option<&str> - Optional cursor for pagination 13 | /// 14 | /// # Example 15 | /// ```no_run 16 | /// farcaster.get_likes_by_cast_hash("cast hash", None, None).await?; 17 | /// ``` 18 | pub async fn get_likes_by_cast_hash( 19 | &self, 20 | cast_hash: &str, 21 | limit: Option, 22 | cursor: Option<&str>, 23 | ) -> Result> { 24 | let mut url = format!("{}/v2/cast-likes?castHash={}", API_ROOT, cast_hash); 25 | 26 | if limit.is_some() { 27 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 28 | } 29 | 30 | if cursor.is_some() { 31 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 32 | } 33 | 34 | let likes_reqwest = &self.reqwest_get(&url).await?; 35 | 36 | let likes: ManyLikedCastsRoot = serde_json::from_str(&likes_reqwest)?; 37 | 38 | Ok(likes) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/reactions/get_recasters.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::reactions::recasters::RecastersRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get recasters by cast hash 8 | /// 9 | /// # Params 10 | /// cast_hash: &str, 11 | /// limit: Option - Optional # of recasters to get (max 100) 12 | /// cursor: Option<&str) - Optional cursor for pagination 13 | /// 14 | /// # Example 15 | /// ```no_run 16 | /// let recasters = farcaster.get_recasters.by_cast_hash("cast hash", None, None).await?; 17 | /// ``` 18 | pub async fn get_recasters_by_cast_hash( 19 | &self, 20 | cast_hash: &str, 21 | limit: Option, 22 | cursor: Option<&str>, 23 | ) -> Result> { 24 | let mut url = format!("{}/v2/cast-recasters?castHash={}", API_ROOT, cast_hash); 25 | 26 | if limit.is_some() { 27 | url.push_str(format!("&limit={}", limit.unwrap()).as_str()) 28 | } 29 | 30 | if cursor.is_some() { 31 | url.push_str(format!("&cursor={}", cursor.unwrap()).as_str()) 32 | } 33 | 34 | let recasters_reqwest = reqwest::Client::new() 35 | .delete(url) 36 | .header("Content-Type", "application/json ") 37 | .header( 38 | "Authorization", 39 | &self.account.session_token.as_ref().unwrap().secret, 40 | ) 41 | .send() 42 | .await? 43 | .text() 44 | .await?; 45 | 46 | let recasters: RecastersRoot = serde_json::from_str(&recasters_reqwest)?; 47 | 48 | Ok(recasters) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/reactions/like_cast.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::reactions::liked_cast::LikedCastRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Like cast by the cast hash 9 | /// 10 | /// # Params 11 | /// cast_hash: &str 12 | /// 13 | /// # Example 14 | /// ```no_run 15 | /// farcaster.like_cast_by_cast_hash("cast hash").await?; 16 | /// ``` 17 | pub async fn like_cast_by_cast_hash( 18 | &self, 19 | cast_hash: &str, 20 | ) -> Result> { 21 | let payload: Value = json!({ "castHash": cast_hash }); 22 | 23 | let liked_cast_reqwest = reqwest::Client::new() 24 | .put(format!("{}/v2/cast-likes", API_ROOT)) 25 | .header("Content-Type", "application/json") 26 | .header( 27 | "Authorization", 28 | &self.account.session_token.as_ref().unwrap().secret, 29 | ) 30 | .json(&payload) 31 | .send() 32 | .await? 33 | .text() 34 | .await?; 35 | 36 | let liked_cast: LikedCastRoot = serde_json::from_str(&liked_cast_reqwest)?; 37 | 38 | Ok(liked_cast) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/reactions/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Reactions module that handles likes and recasts 2 | //! 3 | //! # Quickstart with the Reactions module 4 | //! 5 | //! 1. Use the like_cast function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // Like Cast 13 | //! let like = farcaster.like_cast_by_cast_hash("cast hash").await?; 14 | //! ``` 15 | //! 16 | //! 2. Use the get_likes function 17 | //! ```no_run 18 | //! use farcaster_rs::{Account, Farcaster}; 19 | //! 20 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 21 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 22 | //! 23 | //! // Get likes 24 | //! let likes = farcaster.get_likes_by_cast_hash("cast_hash", None, None).await?; 25 | //! ``` 26 | //! 27 | //! 3. Use the delete_like function 28 | //! ```no_run 29 | //! use farcaster_rs::{Account, Farcaster}; 30 | //! 31 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 32 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 33 | //! 34 | //! // Delete like on a cast 35 | //! let likes = farcaster.delete_like_by_cast_hash("cast hash").await?; 36 | //! ``` 37 | //! 38 | //! 4. Use the get_recasters function 39 | //! ```no_run 40 | //! use farcaster_rs::{Account, Farcaster}; 41 | //! 42 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 43 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 44 | //! 45 | //! // Get recasters 46 | //! let recasters = farcaster.get_recasters_by_cast_hash("cast hash", None, None).await?; 47 | //! ``` 48 | //! 49 | //! 2. Use the recast function 50 | //! ```no_run 51 | //! use farcaster_rs::{Account, Farcaster}; 52 | //! 53 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 54 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 55 | //! 56 | //! // Recast 57 | //! let recast = farcaster.recast_by_cast_hash("cast hash").await?; 58 | //! ``` 59 | //! 60 | //! 5. Use the delete_recast function 61 | //! ```no_run 62 | //! use farcaster_rs::{Account, Farcaster}; 63 | //! 64 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 65 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 66 | //! 67 | //! // Delete recast 68 | //! let delete = farcaster.delete_recast_by_cast_hash("cast hash").await?; 69 | //! ``` 70 | //! 71 | pub mod delete_like; 72 | pub mod delete_recast; 73 | pub mod get_likes; 74 | pub mod get_recasters; 75 | pub mod like_cast; 76 | pub mod recast; 77 | -------------------------------------------------------------------------------- /src/reactions/recast.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::reactions::recasted::RecastedRoot; 3 | use crate::Farcaster; 4 | use serde_json::{json, Value}; 5 | use std::error::Error; 6 | 7 | impl Farcaster { 8 | /// Recast a cast by its hash 9 | /// 10 | /// # Params 11 | /// cast_hash: &str 12 | /// 13 | /// # Example 14 | /// ```no_run 15 | /// let recast = farcaster.recast_by_cast_hash("cast hash").await?; 16 | /// ``` 17 | pub async fn recast_by_cast_hash( 18 | &self, 19 | cast_hash: &str, 20 | ) -> Result> { 21 | let payload: Value = json!({ "castHash": cast_hash }); 22 | 23 | let recast_reqwest = reqwest::Client::new() 24 | .put(format!("{}/v2/recasts", API_ROOT)) 25 | .header("Content-Type", "application/json") 26 | .header( 27 | "Authorization", 28 | &self.account.session_token.as_ref().unwrap().secret, 29 | ) 30 | .json(&payload) 31 | .send() 32 | .await? 33 | .text() 34 | .await?; 35 | 36 | let recast: RecastedRoot = serde_json::from_str(&recast_reqwest)?; 37 | 38 | Ok(recast) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/registry/json/IdRegistryV2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_forwarder", 7 | "type": "address" 8 | } 9 | ], 10 | "stateMutability": "nonpayable", 11 | "type": "constructor" 12 | }, 13 | { 14 | "inputs": [], 15 | "name": "Escrow", 16 | "type": "error" 17 | }, 18 | { 19 | "inputs": [], 20 | "name": "HasId", 21 | "type": "error" 22 | }, 23 | { 24 | "inputs": [], 25 | "name": "HasNoId", 26 | "type": "error" 27 | }, 28 | { 29 | "inputs": [], 30 | "name": "Invitable", 31 | "type": "error" 32 | }, 33 | { 34 | "inputs": [], 35 | "name": "NoRecovery", 36 | "type": "error" 37 | }, 38 | { 39 | "inputs": [], 40 | "name": "Registrable", 41 | "type": "error" 42 | }, 43 | { 44 | "inputs": [], 45 | "name": "Unauthorized", 46 | "type": "error" 47 | }, 48 | { 49 | "anonymous": false, 50 | "inputs": [ 51 | { 52 | "indexed": true, 53 | "internalType": "address", 54 | "name": "by", 55 | "type": "address" 56 | }, 57 | { 58 | "indexed": true, 59 | "internalType": "uint256", 60 | "name": "id", 61 | "type": "uint256" 62 | } 63 | ], 64 | "name": "CancelRecovery", 65 | "type": "event" 66 | }, 67 | { 68 | "anonymous": false, 69 | "inputs": [ 70 | { 71 | "indexed": true, 72 | "internalType": "uint256", 73 | "name": "id", 74 | "type": "uint256" 75 | }, 76 | { 77 | "indexed": false, 78 | "internalType": "string", 79 | "name": "url", 80 | "type": "string" 81 | } 82 | ], 83 | "name": "ChangeHome", 84 | "type": "event" 85 | }, 86 | { 87 | "anonymous": false, 88 | "inputs": [ 89 | { 90 | "indexed": true, 91 | "internalType": "uint256", 92 | "name": "id", 93 | "type": "uint256" 94 | }, 95 | { 96 | "indexed": true, 97 | "internalType": "address", 98 | "name": "recovery", 99 | "type": "address" 100 | } 101 | ], 102 | "name": "ChangeRecoveryAddress", 103 | "type": "event" 104 | }, 105 | { 106 | "anonymous": false, 107 | "inputs": [ 108 | { 109 | "indexed": true, 110 | "internalType": "address", 111 | "name": "trustedCaller", 112 | "type": "address" 113 | } 114 | ], 115 | "name": "ChangeTrustedCaller", 116 | "type": "event" 117 | }, 118 | { 119 | "anonymous": false, 120 | "inputs": [], 121 | "name": "DisableTrustedOnly", 122 | "type": "event" 123 | }, 124 | { 125 | "anonymous": false, 126 | "inputs": [ 127 | { 128 | "indexed": true, 129 | "internalType": "address", 130 | "name": "previousOwner", 131 | "type": "address" 132 | }, 133 | { 134 | "indexed": true, 135 | "internalType": "address", 136 | "name": "newOwner", 137 | "type": "address" 138 | } 139 | ], 140 | "name": "OwnershipTransferred", 141 | "type": "event" 142 | }, 143 | { 144 | "anonymous": false, 145 | "inputs": [ 146 | { 147 | "indexed": true, 148 | "internalType": "address", 149 | "name": "to", 150 | "type": "address" 151 | }, 152 | { 153 | "indexed": true, 154 | "internalType": "uint256", 155 | "name": "id", 156 | "type": "uint256" 157 | }, 158 | { 159 | "indexed": false, 160 | "internalType": "address", 161 | "name": "recovery", 162 | "type": "address" 163 | }, 164 | { 165 | "indexed": false, 166 | "internalType": "string", 167 | "name": "url", 168 | "type": "string" 169 | } 170 | ], 171 | "name": "Register", 172 | "type": "event" 173 | }, 174 | { 175 | "anonymous": false, 176 | "inputs": [ 177 | { 178 | "indexed": true, 179 | "internalType": "address", 180 | "name": "from", 181 | "type": "address" 182 | }, 183 | { 184 | "indexed": true, 185 | "internalType": "address", 186 | "name": "to", 187 | "type": "address" 188 | }, 189 | { 190 | "indexed": true, 191 | "internalType": "uint256", 192 | "name": "id", 193 | "type": "uint256" 194 | } 195 | ], 196 | "name": "RequestRecovery", 197 | "type": "event" 198 | }, 199 | { 200 | "anonymous": false, 201 | "inputs": [ 202 | { 203 | "indexed": true, 204 | "internalType": "address", 205 | "name": "from", 206 | "type": "address" 207 | }, 208 | { 209 | "indexed": true, 210 | "internalType": "address", 211 | "name": "to", 212 | "type": "address" 213 | }, 214 | { 215 | "indexed": true, 216 | "internalType": "uint256", 217 | "name": "id", 218 | "type": "uint256" 219 | } 220 | ], 221 | "name": "Transfer", 222 | "type": "event" 223 | }, 224 | { 225 | "inputs": [ 226 | { 227 | "internalType": "address", 228 | "name": "from", 229 | "type": "address" 230 | } 231 | ], 232 | "name": "cancelRecovery", 233 | "outputs": [], 234 | "stateMutability": "payable", 235 | "type": "function" 236 | }, 237 | { 238 | "inputs": [ 239 | { 240 | "internalType": "string", 241 | "name": "url", 242 | "type": "string" 243 | } 244 | ], 245 | "name": "changeHome", 246 | "outputs": [], 247 | "stateMutability": "payable", 248 | "type": "function" 249 | }, 250 | { 251 | "inputs": [ 252 | { 253 | "internalType": "address", 254 | "name": "recovery", 255 | "type": "address" 256 | } 257 | ], 258 | "name": "changeRecoveryAddress", 259 | "outputs": [], 260 | "stateMutability": "payable", 261 | "type": "function" 262 | }, 263 | { 264 | "inputs": [ 265 | { 266 | "internalType": "address", 267 | "name": "_trustedCaller", 268 | "type": "address" 269 | } 270 | ], 271 | "name": "changeTrustedCaller", 272 | "outputs": [], 273 | "stateMutability": "payable", 274 | "type": "function" 275 | }, 276 | { 277 | "inputs": [ 278 | { 279 | "internalType": "address", 280 | "name": "from", 281 | "type": "address" 282 | } 283 | ], 284 | "name": "completeRecovery", 285 | "outputs": [], 286 | "stateMutability": "payable", 287 | "type": "function" 288 | }, 289 | { 290 | "inputs": [], 291 | "name": "completeTransferOwnership", 292 | "outputs": [], 293 | "stateMutability": "nonpayable", 294 | "type": "function" 295 | }, 296 | { 297 | "inputs": [], 298 | "name": "disableTrustedOnly", 299 | "outputs": [], 300 | "stateMutability": "payable", 301 | "type": "function" 302 | }, 303 | { 304 | "inputs": [ 305 | { 306 | "internalType": "address", 307 | "name": "", 308 | "type": "address" 309 | } 310 | ], 311 | "name": "idOf", 312 | "outputs": [ 313 | { 314 | "internalType": "uint256", 315 | "name": "", 316 | "type": "uint256" 317 | } 318 | ], 319 | "stateMutability": "view", 320 | "type": "function" 321 | }, 322 | { 323 | "inputs": [ 324 | { 325 | "internalType": "address", 326 | "name": "forwarder", 327 | "type": "address" 328 | } 329 | ], 330 | "name": "isTrustedForwarder", 331 | "outputs": [ 332 | { 333 | "internalType": "bool", 334 | "name": "", 335 | "type": "bool" 336 | } 337 | ], 338 | "stateMutability": "view", 339 | "type": "function" 340 | }, 341 | { 342 | "inputs": [], 343 | "name": "owner", 344 | "outputs": [ 345 | { 346 | "internalType": "address", 347 | "name": "", 348 | "type": "address" 349 | } 350 | ], 351 | "stateMutability": "view", 352 | "type": "function" 353 | }, 354 | { 355 | "inputs": [ 356 | { 357 | "internalType": "address", 358 | "name": "to", 359 | "type": "address" 360 | }, 361 | { 362 | "internalType": "address", 363 | "name": "recovery", 364 | "type": "address" 365 | }, 366 | { 367 | "internalType": "string", 368 | "name": "url", 369 | "type": "string" 370 | } 371 | ], 372 | "name": "register", 373 | "outputs": [], 374 | "stateMutability": "payable", 375 | "type": "function" 376 | }, 377 | { 378 | "inputs": [], 379 | "name": "renounceOwnership", 380 | "outputs": [], 381 | "stateMutability": "nonpayable", 382 | "type": "function" 383 | }, 384 | { 385 | "inputs": [ 386 | { 387 | "internalType": "address", 388 | "name": "from", 389 | "type": "address" 390 | }, 391 | { 392 | "internalType": "address", 393 | "name": "to", 394 | "type": "address" 395 | } 396 | ], 397 | "name": "requestRecovery", 398 | "outputs": [], 399 | "stateMutability": "payable", 400 | "type": "function" 401 | }, 402 | { 403 | "inputs": [ 404 | { 405 | "internalType": "address", 406 | "name": "newOwner", 407 | "type": "address" 408 | } 409 | ], 410 | "name": "requestTransferOwnership", 411 | "outputs": [], 412 | "stateMutability": "nonpayable", 413 | "type": "function" 414 | }, 415 | { 416 | "inputs": [ 417 | { 418 | "internalType": "address", 419 | "name": "to", 420 | "type": "address" 421 | } 422 | ], 423 | "name": "transfer", 424 | "outputs": [], 425 | "stateMutability": "payable", 426 | "type": "function" 427 | }, 428 | { 429 | "inputs": [ 430 | { 431 | "internalType": "address", 432 | "name": "", 433 | "type": "address" 434 | } 435 | ], 436 | "name": "transferOwnership", 437 | "outputs": [], 438 | "stateMutability": "view", 439 | "type": "function" 440 | }, 441 | { 442 | "inputs": [ 443 | { 444 | "internalType": "address", 445 | "name": "to", 446 | "type": "address" 447 | }, 448 | { 449 | "internalType": "address", 450 | "name": "recovery", 451 | "type": "address" 452 | }, 453 | { 454 | "internalType": "string", 455 | "name": "url", 456 | "type": "string" 457 | } 458 | ], 459 | "name": "trustedRegister", 460 | "outputs": [], 461 | "stateMutability": "payable", 462 | "type": "function" 463 | } 464 | ] -------------------------------------------------------------------------------- /src/registry/json/NameRegistryV2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_forwarder", 7 | "type": "address" 8 | } 9 | ], 10 | "stateMutability": "nonpayable", 11 | "type": "constructor" 12 | }, 13 | { 14 | "inputs": [], 15 | "name": "CallFailed", 16 | "type": "error" 17 | }, 18 | { 19 | "inputs": [], 20 | "name": "CommitReplay", 21 | "type": "error" 22 | }, 23 | { 24 | "inputs": [], 25 | "name": "Escrow", 26 | "type": "error" 27 | }, 28 | { 29 | "inputs": [], 30 | "name": "Expired", 31 | "type": "error" 32 | }, 33 | { 34 | "inputs": [], 35 | "name": "InsufficientFunds", 36 | "type": "error" 37 | }, 38 | { 39 | "inputs": [], 40 | "name": "InvalidCommit", 41 | "type": "error" 42 | }, 43 | { 44 | "inputs": [], 45 | "name": "InvalidName", 46 | "type": "error" 47 | }, 48 | { 49 | "inputs": [], 50 | "name": "InvalidRecovery", 51 | "type": "error" 52 | }, 53 | { 54 | "inputs": [], 55 | "name": "Invitable", 56 | "type": "error" 57 | }, 58 | { 59 | "inputs": [], 60 | "name": "NoRecovery", 61 | "type": "error" 62 | }, 63 | { 64 | "inputs": [], 65 | "name": "NotAdmin", 66 | "type": "error" 67 | }, 68 | { 69 | "inputs": [], 70 | "name": "NotBiddable", 71 | "type": "error" 72 | }, 73 | { 74 | "inputs": [], 75 | "name": "NotInvitable", 76 | "type": "error" 77 | }, 78 | { 79 | "inputs": [], 80 | "name": "NotModerator", 81 | "type": "error" 82 | }, 83 | { 84 | "inputs": [], 85 | "name": "NotOperator", 86 | "type": "error" 87 | }, 88 | { 89 | "inputs": [], 90 | "name": "NotRenewable", 91 | "type": "error" 92 | }, 93 | { 94 | "inputs": [], 95 | "name": "NotTreasurer", 96 | "type": "error" 97 | }, 98 | { 99 | "inputs": [], 100 | "name": "Registered", 101 | "type": "error" 102 | }, 103 | { 104 | "inputs": [], 105 | "name": "Registrable", 106 | "type": "error" 107 | }, 108 | { 109 | "inputs": [], 110 | "name": "Unauthorized", 111 | "type": "error" 112 | }, 113 | { 114 | "anonymous": false, 115 | "inputs": [ 116 | { 117 | "indexed": false, 118 | "internalType": "address", 119 | "name": "previousAdmin", 120 | "type": "address" 121 | }, 122 | { 123 | "indexed": false, 124 | "internalType": "address", 125 | "name": "newAdmin", 126 | "type": "address" 127 | } 128 | ], 129 | "name": "AdminChanged", 130 | "type": "event" 131 | }, 132 | { 133 | "anonymous": false, 134 | "inputs": [ 135 | { 136 | "indexed": true, 137 | "internalType": "address", 138 | "name": "owner", 139 | "type": "address" 140 | }, 141 | { 142 | "indexed": true, 143 | "internalType": "address", 144 | "name": "approved", 145 | "type": "address" 146 | }, 147 | { 148 | "indexed": true, 149 | "internalType": "uint256", 150 | "name": "tokenId", 151 | "type": "uint256" 152 | } 153 | ], 154 | "name": "Approval", 155 | "type": "event" 156 | }, 157 | { 158 | "anonymous": false, 159 | "inputs": [ 160 | { 161 | "indexed": true, 162 | "internalType": "address", 163 | "name": "owner", 164 | "type": "address" 165 | }, 166 | { 167 | "indexed": true, 168 | "internalType": "address", 169 | "name": "operator", 170 | "type": "address" 171 | }, 172 | { 173 | "indexed": false, 174 | "internalType": "bool", 175 | "name": "approved", 176 | "type": "bool" 177 | } 178 | ], 179 | "name": "ApprovalForAll", 180 | "type": "event" 181 | }, 182 | { 183 | "anonymous": false, 184 | "inputs": [ 185 | { 186 | "indexed": true, 187 | "internalType": "address", 188 | "name": "beacon", 189 | "type": "address" 190 | } 191 | ], 192 | "name": "BeaconUpgraded", 193 | "type": "event" 194 | }, 195 | { 196 | "anonymous": false, 197 | "inputs": [ 198 | { 199 | "indexed": true, 200 | "internalType": "address", 201 | "name": "by", 202 | "type": "address" 203 | }, 204 | { 205 | "indexed": true, 206 | "internalType": "uint256", 207 | "name": "tokenId", 208 | "type": "uint256" 209 | } 210 | ], 211 | "name": "CancelRecovery", 212 | "type": "event" 213 | }, 214 | { 215 | "anonymous": false, 216 | "inputs": [ 217 | { 218 | "indexed": false, 219 | "internalType": "uint256", 220 | "name": "fee", 221 | "type": "uint256" 222 | } 223 | ], 224 | "name": "ChangeFee", 225 | "type": "event" 226 | }, 227 | { 228 | "anonymous": false, 229 | "inputs": [ 230 | { 231 | "indexed": true, 232 | "internalType": "address", 233 | "name": "pool", 234 | "type": "address" 235 | } 236 | ], 237 | "name": "ChangePool", 238 | "type": "event" 239 | }, 240 | { 241 | "anonymous": false, 242 | "inputs": [ 243 | { 244 | "indexed": true, 245 | "internalType": "uint256", 246 | "name": "tokenId", 247 | "type": "uint256" 248 | }, 249 | { 250 | "indexed": true, 251 | "internalType": "address", 252 | "name": "recovery", 253 | "type": "address" 254 | } 255 | ], 256 | "name": "ChangeRecoveryAddress", 257 | "type": "event" 258 | }, 259 | { 260 | "anonymous": false, 261 | "inputs": [ 262 | { 263 | "indexed": true, 264 | "internalType": "address", 265 | "name": "trustedCaller", 266 | "type": "address" 267 | } 268 | ], 269 | "name": "ChangeTrustedCaller", 270 | "type": "event" 271 | }, 272 | { 273 | "anonymous": false, 274 | "inputs": [ 275 | { 276 | "indexed": true, 277 | "internalType": "address", 278 | "name": "vault", 279 | "type": "address" 280 | } 281 | ], 282 | "name": "ChangeVault", 283 | "type": "event" 284 | }, 285 | { 286 | "anonymous": false, 287 | "inputs": [], 288 | "name": "DisableTrustedOnly", 289 | "type": "event" 290 | }, 291 | { 292 | "anonymous": false, 293 | "inputs": [ 294 | { 295 | "indexed": false, 296 | "internalType": "uint8", 297 | "name": "version", 298 | "type": "uint8" 299 | } 300 | ], 301 | "name": "Initialized", 302 | "type": "event" 303 | }, 304 | { 305 | "anonymous": false, 306 | "inputs": [ 307 | { 308 | "indexed": true, 309 | "internalType": "uint256", 310 | "name": "inviterId", 311 | "type": "uint256" 312 | }, 313 | { 314 | "indexed": true, 315 | "internalType": "uint256", 316 | "name": "inviteeId", 317 | "type": "uint256" 318 | }, 319 | { 320 | "indexed": true, 321 | "internalType": "bytes16", 322 | "name": "fname", 323 | "type": "bytes16" 324 | } 325 | ], 326 | "name": "Invite", 327 | "type": "event" 328 | }, 329 | { 330 | "anonymous": false, 331 | "inputs": [ 332 | { 333 | "indexed": false, 334 | "internalType": "address", 335 | "name": "account", 336 | "type": "address" 337 | } 338 | ], 339 | "name": "Paused", 340 | "type": "event" 341 | }, 342 | { 343 | "anonymous": false, 344 | "inputs": [ 345 | { 346 | "indexed": true, 347 | "internalType": "uint256", 348 | "name": "tokenId", 349 | "type": "uint256" 350 | }, 351 | { 352 | "indexed": false, 353 | "internalType": "uint256", 354 | "name": "expiry", 355 | "type": "uint256" 356 | } 357 | ], 358 | "name": "Renew", 359 | "type": "event" 360 | }, 361 | { 362 | "anonymous": false, 363 | "inputs": [ 364 | { 365 | "indexed": true, 366 | "internalType": "address", 367 | "name": "from", 368 | "type": "address" 369 | }, 370 | { 371 | "indexed": true, 372 | "internalType": "address", 373 | "name": "to", 374 | "type": "address" 375 | }, 376 | { 377 | "indexed": true, 378 | "internalType": "uint256", 379 | "name": "tokenId", 380 | "type": "uint256" 381 | } 382 | ], 383 | "name": "RequestRecovery", 384 | "type": "event" 385 | }, 386 | { 387 | "anonymous": false, 388 | "inputs": [ 389 | { 390 | "indexed": true, 391 | "internalType": "bytes32", 392 | "name": "role", 393 | "type": "bytes32" 394 | }, 395 | { 396 | "indexed": true, 397 | "internalType": "bytes32", 398 | "name": "previousAdminRole", 399 | "type": "bytes32" 400 | }, 401 | { 402 | "indexed": true, 403 | "internalType": "bytes32", 404 | "name": "newAdminRole", 405 | "type": "bytes32" 406 | } 407 | ], 408 | "name": "RoleAdminChanged", 409 | "type": "event" 410 | }, 411 | { 412 | "anonymous": false, 413 | "inputs": [ 414 | { 415 | "indexed": true, 416 | "internalType": "bytes32", 417 | "name": "role", 418 | "type": "bytes32" 419 | }, 420 | { 421 | "indexed": true, 422 | "internalType": "address", 423 | "name": "account", 424 | "type": "address" 425 | }, 426 | { 427 | "indexed": true, 428 | "internalType": "address", 429 | "name": "sender", 430 | "type": "address" 431 | } 432 | ], 433 | "name": "RoleGranted", 434 | "type": "event" 435 | }, 436 | { 437 | "anonymous": false, 438 | "inputs": [ 439 | { 440 | "indexed": true, 441 | "internalType": "bytes32", 442 | "name": "role", 443 | "type": "bytes32" 444 | }, 445 | { 446 | "indexed": true, 447 | "internalType": "address", 448 | "name": "account", 449 | "type": "address" 450 | }, 451 | { 452 | "indexed": true, 453 | "internalType": "address", 454 | "name": "sender", 455 | "type": "address" 456 | } 457 | ], 458 | "name": "RoleRevoked", 459 | "type": "event" 460 | }, 461 | { 462 | "anonymous": false, 463 | "inputs": [ 464 | { 465 | "indexed": true, 466 | "internalType": "address", 467 | "name": "from", 468 | "type": "address" 469 | }, 470 | { 471 | "indexed": true, 472 | "internalType": "address", 473 | "name": "to", 474 | "type": "address" 475 | }, 476 | { 477 | "indexed": true, 478 | "internalType": "uint256", 479 | "name": "tokenId", 480 | "type": "uint256" 481 | } 482 | ], 483 | "name": "Transfer", 484 | "type": "event" 485 | }, 486 | { 487 | "anonymous": false, 488 | "inputs": [ 489 | { 490 | "indexed": false, 491 | "internalType": "address", 492 | "name": "account", 493 | "type": "address" 494 | } 495 | ], 496 | "name": "Unpaused", 497 | "type": "event" 498 | }, 499 | { 500 | "anonymous": false, 501 | "inputs": [ 502 | { 503 | "indexed": true, 504 | "internalType": "address", 505 | "name": "implementation", 506 | "type": "address" 507 | } 508 | ], 509 | "name": "Upgraded", 510 | "type": "event" 511 | }, 512 | { 513 | "inputs": [], 514 | "name": "DEFAULT_ADMIN_ROLE", 515 | "outputs": [ 516 | { 517 | "internalType": "bytes32", 518 | "name": "", 519 | "type": "bytes32" 520 | } 521 | ], 522 | "stateMutability": "view", 523 | "type": "function" 524 | }, 525 | { 526 | "inputs": [ 527 | { 528 | "internalType": "address", 529 | "name": "to", 530 | "type": "address" 531 | }, 532 | { 533 | "internalType": "uint256", 534 | "name": "tokenId", 535 | "type": "uint256" 536 | } 537 | ], 538 | "name": "approve", 539 | "outputs": [], 540 | "stateMutability": "nonpayable", 541 | "type": "function" 542 | }, 543 | { 544 | "inputs": [ 545 | { 546 | "internalType": "address", 547 | "name": "owner", 548 | "type": "address" 549 | } 550 | ], 551 | "name": "balanceOf", 552 | "outputs": [ 553 | { 554 | "internalType": "uint256", 555 | "name": "", 556 | "type": "uint256" 557 | } 558 | ], 559 | "stateMutability": "view", 560 | "type": "function" 561 | }, 562 | { 563 | "inputs": [ 564 | { 565 | "internalType": "address", 566 | "name": "to", 567 | "type": "address" 568 | }, 569 | { 570 | "internalType": "uint256", 571 | "name": "tokenId", 572 | "type": "uint256" 573 | }, 574 | { 575 | "internalType": "address", 576 | "name": "recovery", 577 | "type": "address" 578 | } 579 | ], 580 | "name": "bid", 581 | "outputs": [], 582 | "stateMutability": "payable", 583 | "type": "function" 584 | }, 585 | { 586 | "inputs": [ 587 | { 588 | "internalType": "uint256", 589 | "name": "tokenId", 590 | "type": "uint256" 591 | } 592 | ], 593 | "name": "cancelRecovery", 594 | "outputs": [], 595 | "stateMutability": "payable", 596 | "type": "function" 597 | }, 598 | { 599 | "inputs": [ 600 | { 601 | "internalType": "uint256", 602 | "name": "_fee", 603 | "type": "uint256" 604 | } 605 | ], 606 | "name": "changeFee", 607 | "outputs": [], 608 | "stateMutability": "payable", 609 | "type": "function" 610 | }, 611 | { 612 | "inputs": [ 613 | { 614 | "internalType": "address", 615 | "name": "_pool", 616 | "type": "address" 617 | } 618 | ], 619 | "name": "changePool", 620 | "outputs": [], 621 | "stateMutability": "payable", 622 | "type": "function" 623 | }, 624 | { 625 | "inputs": [ 626 | { 627 | "internalType": "uint256", 628 | "name": "tokenId", 629 | "type": "uint256" 630 | }, 631 | { 632 | "internalType": "address", 633 | "name": "recovery", 634 | "type": "address" 635 | } 636 | ], 637 | "name": "changeRecoveryAddress", 638 | "outputs": [], 639 | "stateMutability": "payable", 640 | "type": "function" 641 | }, 642 | { 643 | "inputs": [ 644 | { 645 | "internalType": "address", 646 | "name": "_trustedCaller", 647 | "type": "address" 648 | } 649 | ], 650 | "name": "changeTrustedCaller", 651 | "outputs": [], 652 | "stateMutability": "payable", 653 | "type": "function" 654 | }, 655 | { 656 | "inputs": [ 657 | { 658 | "internalType": "address", 659 | "name": "_vault", 660 | "type": "address" 661 | } 662 | ], 663 | "name": "changeVault", 664 | "outputs": [], 665 | "stateMutability": "payable", 666 | "type": "function" 667 | }, 668 | { 669 | "inputs": [ 670 | { 671 | "internalType": "uint256", 672 | "name": "tokenId", 673 | "type": "uint256" 674 | } 675 | ], 676 | "name": "completeRecovery", 677 | "outputs": [], 678 | "stateMutability": "payable", 679 | "type": "function" 680 | }, 681 | { 682 | "inputs": [], 683 | "name": "disableTrustedOnly", 684 | "outputs": [], 685 | "stateMutability": "payable", 686 | "type": "function" 687 | }, 688 | { 689 | "inputs": [ 690 | { 691 | "internalType": "uint256", 692 | "name": "", 693 | "type": "uint256" 694 | } 695 | ], 696 | "name": "expiryOf", 697 | "outputs": [ 698 | { 699 | "internalType": "uint256", 700 | "name": "", 701 | "type": "uint256" 702 | } 703 | ], 704 | "stateMutability": "view", 705 | "type": "function" 706 | }, 707 | { 708 | "inputs": [], 709 | "name": "fee", 710 | "outputs": [ 711 | { 712 | "internalType": "uint256", 713 | "name": "", 714 | "type": "uint256" 715 | } 716 | ], 717 | "stateMutability": "view", 718 | "type": "function" 719 | }, 720 | { 721 | "inputs": [ 722 | { 723 | "internalType": "bytes16", 724 | "name": "fname", 725 | "type": "bytes16" 726 | }, 727 | { 728 | "internalType": "address", 729 | "name": "to", 730 | "type": "address" 731 | }, 732 | { 733 | "internalType": "bytes32", 734 | "name": "secret", 735 | "type": "bytes32" 736 | }, 737 | { 738 | "internalType": "address", 739 | "name": "recovery", 740 | "type": "address" 741 | } 742 | ], 743 | "name": "generateCommit", 744 | "outputs": [ 745 | { 746 | "internalType": "bytes32", 747 | "name": "commit", 748 | "type": "bytes32" 749 | } 750 | ], 751 | "stateMutability": "pure", 752 | "type": "function" 753 | }, 754 | { 755 | "inputs": [ 756 | { 757 | "internalType": "uint256", 758 | "name": "tokenId", 759 | "type": "uint256" 760 | } 761 | ], 762 | "name": "getApproved", 763 | "outputs": [ 764 | { 765 | "internalType": "address", 766 | "name": "", 767 | "type": "address" 768 | } 769 | ], 770 | "stateMutability": "view", 771 | "type": "function" 772 | }, 773 | { 774 | "inputs": [ 775 | { 776 | "internalType": "bytes32", 777 | "name": "role", 778 | "type": "bytes32" 779 | } 780 | ], 781 | "name": "getRoleAdmin", 782 | "outputs": [ 783 | { 784 | "internalType": "bytes32", 785 | "name": "", 786 | "type": "bytes32" 787 | } 788 | ], 789 | "stateMutability": "view", 790 | "type": "function" 791 | }, 792 | { 793 | "inputs": [ 794 | { 795 | "internalType": "bytes32", 796 | "name": "role", 797 | "type": "bytes32" 798 | }, 799 | { 800 | "internalType": "address", 801 | "name": "account", 802 | "type": "address" 803 | } 804 | ], 805 | "name": "grantRole", 806 | "outputs": [], 807 | "stateMutability": "nonpayable", 808 | "type": "function" 809 | }, 810 | { 811 | "inputs": [ 812 | { 813 | "internalType": "bytes32", 814 | "name": "role", 815 | "type": "bytes32" 816 | }, 817 | { 818 | "internalType": "address", 819 | "name": "account", 820 | "type": "address" 821 | } 822 | ], 823 | "name": "hasRole", 824 | "outputs": [ 825 | { 826 | "internalType": "bool", 827 | "name": "", 828 | "type": "bool" 829 | } 830 | ], 831 | "stateMutability": "view", 832 | "type": "function" 833 | }, 834 | { 835 | "inputs": [ 836 | { 837 | "internalType": "string", 838 | "name": "_tokenName", 839 | "type": "string" 840 | }, 841 | { 842 | "internalType": "string", 843 | "name": "_tokenSymbol", 844 | "type": "string" 845 | }, 846 | { 847 | "internalType": "address", 848 | "name": "_vault", 849 | "type": "address" 850 | }, 851 | { 852 | "internalType": "address", 853 | "name": "_pool", 854 | "type": "address" 855 | } 856 | ], 857 | "name": "initialize", 858 | "outputs": [], 859 | "stateMutability": "nonpayable", 860 | "type": "function" 861 | }, 862 | { 863 | "inputs": [ 864 | { 865 | "internalType": "address", 866 | "name": "owner", 867 | "type": "address" 868 | }, 869 | { 870 | "internalType": "address", 871 | "name": "operator", 872 | "type": "address" 873 | } 874 | ], 875 | "name": "isApprovedForAll", 876 | "outputs": [ 877 | { 878 | "internalType": "bool", 879 | "name": "", 880 | "type": "bool" 881 | } 882 | ], 883 | "stateMutability": "view", 884 | "type": "function" 885 | }, 886 | { 887 | "inputs": [ 888 | { 889 | "internalType": "address", 890 | "name": "forwarder", 891 | "type": "address" 892 | } 893 | ], 894 | "name": "isTrustedForwarder", 895 | "outputs": [ 896 | { 897 | "internalType": "bool", 898 | "name": "", 899 | "type": "bool" 900 | } 901 | ], 902 | "stateMutability": "view", 903 | "type": "function" 904 | }, 905 | { 906 | "inputs": [ 907 | { 908 | "internalType": "bytes32", 909 | "name": "commit", 910 | "type": "bytes32" 911 | } 912 | ], 913 | "name": "makeCommit", 914 | "outputs": [], 915 | "stateMutability": "payable", 916 | "type": "function" 917 | }, 918 | { 919 | "inputs": [], 920 | "name": "name", 921 | "outputs": [ 922 | { 923 | "internalType": "string", 924 | "name": "", 925 | "type": "string" 926 | } 927 | ], 928 | "stateMutability": "view", 929 | "type": "function" 930 | }, 931 | { 932 | "inputs": [ 933 | { 934 | "internalType": "uint256", 935 | "name": "tokenId", 936 | "type": "uint256" 937 | } 938 | ], 939 | "name": "ownerOf", 940 | "outputs": [ 941 | { 942 | "internalType": "address", 943 | "name": "", 944 | "type": "address" 945 | } 946 | ], 947 | "stateMutability": "view", 948 | "type": "function" 949 | }, 950 | { 951 | "inputs": [], 952 | "name": "pause", 953 | "outputs": [], 954 | "stateMutability": "payable", 955 | "type": "function" 956 | }, 957 | { 958 | "inputs": [], 959 | "name": "paused", 960 | "outputs": [ 961 | { 962 | "internalType": "bool", 963 | "name": "", 964 | "type": "bool" 965 | } 966 | ], 967 | "stateMutability": "view", 968 | "type": "function" 969 | }, 970 | { 971 | "inputs": [], 972 | "name": "pool", 973 | "outputs": [ 974 | { 975 | "internalType": "address", 976 | "name": "", 977 | "type": "address" 978 | } 979 | ], 980 | "stateMutability": "view", 981 | "type": "function" 982 | }, 983 | { 984 | "inputs": [], 985 | "name": "proxiableUUID", 986 | "outputs": [ 987 | { 988 | "internalType": "bytes32", 989 | "name": "", 990 | "type": "bytes32" 991 | } 992 | ], 993 | "stateMutability": "view", 994 | "type": "function" 995 | }, 996 | { 997 | "inputs": [ 998 | { 999 | "internalType": "uint256", 1000 | "name": "tokenId", 1001 | "type": "uint256" 1002 | } 1003 | ], 1004 | "name": "reclaim", 1005 | "outputs": [], 1006 | "stateMutability": "payable", 1007 | "type": "function" 1008 | }, 1009 | { 1010 | "inputs": [ 1011 | { 1012 | "internalType": "uint256", 1013 | "name": "", 1014 | "type": "uint256" 1015 | } 1016 | ], 1017 | "name": "recoveryClockOf", 1018 | "outputs": [ 1019 | { 1020 | "internalType": "uint256", 1021 | "name": "", 1022 | "type": "uint256" 1023 | } 1024 | ], 1025 | "stateMutability": "view", 1026 | "type": "function" 1027 | }, 1028 | { 1029 | "inputs": [ 1030 | { 1031 | "internalType": "uint256", 1032 | "name": "", 1033 | "type": "uint256" 1034 | } 1035 | ], 1036 | "name": "recoveryDestinationOf", 1037 | "outputs": [ 1038 | { 1039 | "internalType": "address", 1040 | "name": "", 1041 | "type": "address" 1042 | } 1043 | ], 1044 | "stateMutability": "view", 1045 | "type": "function" 1046 | }, 1047 | { 1048 | "inputs": [ 1049 | { 1050 | "internalType": "uint256", 1051 | "name": "", 1052 | "type": "uint256" 1053 | } 1054 | ], 1055 | "name": "recoveryOf", 1056 | "outputs": [ 1057 | { 1058 | "internalType": "address", 1059 | "name": "", 1060 | "type": "address" 1061 | } 1062 | ], 1063 | "stateMutability": "view", 1064 | "type": "function" 1065 | }, 1066 | { 1067 | "inputs": [ 1068 | { 1069 | "internalType": "bytes16", 1070 | "name": "fname", 1071 | "type": "bytes16" 1072 | }, 1073 | { 1074 | "internalType": "address", 1075 | "name": "to", 1076 | "type": "address" 1077 | }, 1078 | { 1079 | "internalType": "bytes32", 1080 | "name": "secret", 1081 | "type": "bytes32" 1082 | }, 1083 | { 1084 | "internalType": "address", 1085 | "name": "recovery", 1086 | "type": "address" 1087 | } 1088 | ], 1089 | "name": "register", 1090 | "outputs": [], 1091 | "stateMutability": "payable", 1092 | "type": "function" 1093 | }, 1094 | { 1095 | "inputs": [ 1096 | { 1097 | "internalType": "uint256", 1098 | "name": "tokenId", 1099 | "type": "uint256" 1100 | } 1101 | ], 1102 | "name": "renew", 1103 | "outputs": [], 1104 | "stateMutability": "payable", 1105 | "type": "function" 1106 | }, 1107 | { 1108 | "inputs": [ 1109 | { 1110 | "internalType": "bytes32", 1111 | "name": "role", 1112 | "type": "bytes32" 1113 | }, 1114 | { 1115 | "internalType": "address", 1116 | "name": "account", 1117 | "type": "address" 1118 | } 1119 | ], 1120 | "name": "renounceRole", 1121 | "outputs": [], 1122 | "stateMutability": "nonpayable", 1123 | "type": "function" 1124 | }, 1125 | { 1126 | "inputs": [ 1127 | { 1128 | "internalType": "uint256", 1129 | "name": "tokenId", 1130 | "type": "uint256" 1131 | }, 1132 | { 1133 | "internalType": "address", 1134 | "name": "to", 1135 | "type": "address" 1136 | } 1137 | ], 1138 | "name": "requestRecovery", 1139 | "outputs": [], 1140 | "stateMutability": "payable", 1141 | "type": "function" 1142 | }, 1143 | { 1144 | "inputs": [ 1145 | { 1146 | "internalType": "bytes32", 1147 | "name": "role", 1148 | "type": "bytes32" 1149 | }, 1150 | { 1151 | "internalType": "address", 1152 | "name": "account", 1153 | "type": "address" 1154 | } 1155 | ], 1156 | "name": "revokeRole", 1157 | "outputs": [], 1158 | "stateMutability": "nonpayable", 1159 | "type": "function" 1160 | }, 1161 | { 1162 | "inputs": [ 1163 | { 1164 | "internalType": "address", 1165 | "name": "from", 1166 | "type": "address" 1167 | }, 1168 | { 1169 | "internalType": "address", 1170 | "name": "to", 1171 | "type": "address" 1172 | }, 1173 | { 1174 | "internalType": "uint256", 1175 | "name": "tokenId", 1176 | "type": "uint256" 1177 | } 1178 | ], 1179 | "name": "safeTransferFrom", 1180 | "outputs": [], 1181 | "stateMutability": "nonpayable", 1182 | "type": "function" 1183 | }, 1184 | { 1185 | "inputs": [ 1186 | { 1187 | "internalType": "address", 1188 | "name": "from", 1189 | "type": "address" 1190 | }, 1191 | { 1192 | "internalType": "address", 1193 | "name": "to", 1194 | "type": "address" 1195 | }, 1196 | { 1197 | "internalType": "uint256", 1198 | "name": "tokenId", 1199 | "type": "uint256" 1200 | }, 1201 | { 1202 | "internalType": "bytes", 1203 | "name": "data", 1204 | "type": "bytes" 1205 | } 1206 | ], 1207 | "name": "safeTransferFrom", 1208 | "outputs": [], 1209 | "stateMutability": "nonpayable", 1210 | "type": "function" 1211 | }, 1212 | { 1213 | "inputs": [ 1214 | { 1215 | "internalType": "address", 1216 | "name": "operator", 1217 | "type": "address" 1218 | }, 1219 | { 1220 | "internalType": "bool", 1221 | "name": "approved", 1222 | "type": "bool" 1223 | } 1224 | ], 1225 | "name": "setApprovalForAll", 1226 | "outputs": [], 1227 | "stateMutability": "nonpayable", 1228 | "type": "function" 1229 | }, 1230 | { 1231 | "inputs": [ 1232 | { 1233 | "internalType": "bytes4", 1234 | "name": "interfaceId", 1235 | "type": "bytes4" 1236 | } 1237 | ], 1238 | "name": "supportsInterface", 1239 | "outputs": [ 1240 | { 1241 | "internalType": "bool", 1242 | "name": "", 1243 | "type": "bool" 1244 | } 1245 | ], 1246 | "stateMutability": "view", 1247 | "type": "function" 1248 | }, 1249 | { 1250 | "inputs": [], 1251 | "name": "symbol", 1252 | "outputs": [ 1253 | { 1254 | "internalType": "string", 1255 | "name": "", 1256 | "type": "string" 1257 | } 1258 | ], 1259 | "stateMutability": "view", 1260 | "type": "function" 1261 | }, 1262 | { 1263 | "inputs": [ 1264 | { 1265 | "internalType": "bytes32", 1266 | "name": "", 1267 | "type": "bytes32" 1268 | } 1269 | ], 1270 | "name": "timestampOf", 1271 | "outputs": [ 1272 | { 1273 | "internalType": "uint256", 1274 | "name": "", 1275 | "type": "uint256" 1276 | } 1277 | ], 1278 | "stateMutability": "view", 1279 | "type": "function" 1280 | }, 1281 | { 1282 | "inputs": [ 1283 | { 1284 | "internalType": "uint256", 1285 | "name": "tokenId", 1286 | "type": "uint256" 1287 | } 1288 | ], 1289 | "name": "tokenURI", 1290 | "outputs": [ 1291 | { 1292 | "internalType": "string", 1293 | "name": "", 1294 | "type": "string" 1295 | } 1296 | ], 1297 | "stateMutability": "pure", 1298 | "type": "function" 1299 | }, 1300 | { 1301 | "inputs": [ 1302 | { 1303 | "internalType": "address", 1304 | "name": "from", 1305 | "type": "address" 1306 | }, 1307 | { 1308 | "internalType": "address", 1309 | "name": "to", 1310 | "type": "address" 1311 | }, 1312 | { 1313 | "internalType": "uint256", 1314 | "name": "tokenId", 1315 | "type": "uint256" 1316 | } 1317 | ], 1318 | "name": "transferFrom", 1319 | "outputs": [], 1320 | "stateMutability": "nonpayable", 1321 | "type": "function" 1322 | }, 1323 | { 1324 | "inputs": [], 1325 | "name": "trustedCaller", 1326 | "outputs": [ 1327 | { 1328 | "internalType": "address", 1329 | "name": "", 1330 | "type": "address" 1331 | } 1332 | ], 1333 | "stateMutability": "view", 1334 | "type": "function" 1335 | }, 1336 | { 1337 | "inputs": [], 1338 | "name": "trustedOnly", 1339 | "outputs": [ 1340 | { 1341 | "internalType": "uint256", 1342 | "name": "", 1343 | "type": "uint256" 1344 | } 1345 | ], 1346 | "stateMutability": "view", 1347 | "type": "function" 1348 | }, 1349 | { 1350 | "inputs": [ 1351 | { 1352 | "internalType": "bytes16", 1353 | "name": "fname", 1354 | "type": "bytes16" 1355 | }, 1356 | { 1357 | "internalType": "address", 1358 | "name": "to", 1359 | "type": "address" 1360 | }, 1361 | { 1362 | "internalType": "address", 1363 | "name": "recovery", 1364 | "type": "address" 1365 | }, 1366 | { 1367 | "internalType": "uint256", 1368 | "name": "inviter", 1369 | "type": "uint256" 1370 | }, 1371 | { 1372 | "internalType": "uint256", 1373 | "name": "invitee", 1374 | "type": "uint256" 1375 | } 1376 | ], 1377 | "name": "trustedRegister", 1378 | "outputs": [], 1379 | "stateMutability": "payable", 1380 | "type": "function" 1381 | }, 1382 | { 1383 | "inputs": [], 1384 | "name": "unpause", 1385 | "outputs": [], 1386 | "stateMutability": "payable", 1387 | "type": "function" 1388 | }, 1389 | { 1390 | "inputs": [ 1391 | { 1392 | "internalType": "address", 1393 | "name": "newImplementation", 1394 | "type": "address" 1395 | } 1396 | ], 1397 | "name": "upgradeTo", 1398 | "outputs": [], 1399 | "stateMutability": "nonpayable", 1400 | "type": "function" 1401 | }, 1402 | { 1403 | "inputs": [ 1404 | { 1405 | "internalType": "address", 1406 | "name": "newImplementation", 1407 | "type": "address" 1408 | }, 1409 | { 1410 | "internalType": "bytes", 1411 | "name": "data", 1412 | "type": "bytes" 1413 | } 1414 | ], 1415 | "name": "upgradeToAndCall", 1416 | "outputs": [], 1417 | "stateMutability": "payable", 1418 | "type": "function" 1419 | }, 1420 | { 1421 | "inputs": [], 1422 | "name": "vault", 1423 | "outputs": [ 1424 | { 1425 | "internalType": "address", 1426 | "name": "", 1427 | "type": "address" 1428 | } 1429 | ], 1430 | "stateMutability": "view", 1431 | "type": "function" 1432 | }, 1433 | { 1434 | "inputs": [ 1435 | { 1436 | "internalType": "uint256", 1437 | "name": "amount", 1438 | "type": "uint256" 1439 | } 1440 | ], 1441 | "name": "withdraw", 1442 | "outputs": [], 1443 | "stateMutability": "payable", 1444 | "type": "function" 1445 | } 1446 | ] -------------------------------------------------------------------------------- /src/registry/mod.rs: -------------------------------------------------------------------------------- 1 | mod token_to_fname; 2 | 3 | use crate::constants::registry::{ 4 | FIR_CONTRACT_ADDRESS, FIR_DEPLOYMENT_BLOCK, FNR_CONTRACT_ADDRESS, FNR_DEPLOYMENT_BLOCK, 5 | }; 6 | use crate::types::registry::{FIDInfo, Registry}; 7 | use ethers::abi::RawLog; 8 | use ethers::providers::Middleware; 9 | use ethers::providers::{Http, Provider}; 10 | use ethers::types::{Address, Filter, Log, H256}; 11 | use std::collections::HashMap; 12 | use std::error::Error; 13 | use token_to_fname::token_to_fname; 14 | 15 | impl Registry { 16 | /// create new Registry 17 | /// provider: Ethereum HTTP provider 18 | pub async fn new(provider: &str) -> Result> { 19 | let mut registry = Self { 20 | fir_abi: serde_json::from_str(include_str!("json/IdRegistryV2.json")) 21 | .expect("Farcaster ID Registry ABI parse error"), 22 | fir_block: FIR_DEPLOYMENT_BLOCK, 23 | fir: HashMap::new(), 24 | fnr_abi: serde_json::from_str(include_str!("json/NameRegistryV2.json")) 25 | .expect("Farcaster Name Registry ABI parse error"), 26 | fnr_block: FNR_DEPLOYMENT_BLOCK, 27 | fnr: HashMap::new(), 28 | provider: Provider::::try_from(provider).expect("Ethereum provider error"), 29 | }; 30 | 31 | // commence initial sync 32 | registry.sync().await?; 33 | 34 | Ok(registry) 35 | } 36 | 37 | /// get fid assigned to `address` 38 | /// return None if not found 39 | pub fn get_fid_by_address(&self, address: &str) -> Option { 40 | for (k, v) in &self.fir { 41 | if v.address == address { 42 | return Some(*k); 43 | } 44 | } 45 | 46 | None 47 | } 48 | 49 | /// get fid assigned to `username` 50 | /// return None if not found 51 | pub fn get_fid_by_username(&self, username: &str) -> Option { 52 | if let Some(address) = self.get_address_by_username(username) { 53 | return self.get_fid_by_address(address); 54 | } 55 | 56 | None 57 | } 58 | 59 | /// get address assigned to `username` 60 | /// returns None if not found 61 | pub fn get_address_by_username(&self, username: &str) -> Option<&str> { 62 | if let Some(address) = self.fnr.get(username) { 63 | return Some(address); 64 | } 65 | 66 | None 67 | } 68 | 69 | /// get address assigned to `fid` 70 | /// returns None if not found 71 | pub fn get_address_by_fid(&self, fid: u64) -> Option<&str> { 72 | if let Some(fid_info) = self.fir.get(&fid) { 73 | return Some(&fid_info.address); 74 | } 75 | 76 | None 77 | } 78 | 79 | /// get username assigned to `fid` 80 | /// returns None if not found 81 | pub fn get_username_by_fid(&self, fid: u64) -> Option<&str> { 82 | if let Some(address) = self.get_address_by_fid(fid) { 83 | return self.get_username_by_address(address); 84 | } 85 | 86 | None 87 | } 88 | 89 | /// get username assigned to `address` 90 | /// returns None if not found 91 | pub fn get_username_by_address(&self, address: &str) -> Option<&str> { 92 | for (k, v) in &self.fnr { 93 | if v == address { 94 | return Some(k); 95 | } 96 | } 97 | 98 | None 99 | } 100 | 101 | /// sync Farcaster ID/Name Registry 102 | pub async fn sync(&mut self) -> Result<(), Box> { 103 | // sync FIR 104 | self.sync_fir_logs().await?; 105 | 106 | // sync FNR 107 | self.sync_fnr_logs().await?; 108 | 109 | Ok(()) 110 | } 111 | 112 | /// [private] sync Farcaster ID Registry logs 113 | /// TODO: parse "Transfer" logs 114 | async fn sync_fir_logs(&mut self) -> Result<(), Box> { 115 | // get latest event logs 116 | let logs = self.get_fir_register_logs(self.fir_block).await?; 117 | 118 | // iterate over logs 119 | for log in logs { 120 | // update stored block number 121 | let block_number = u64::try_from(log.block_number.unwrap()).unwrap(); 122 | if block_number > self.fir_block { 123 | self.fir_block = block_number; 124 | } 125 | 126 | // parse log 127 | let raw_log = RawLog { 128 | topics: log.topics, 129 | data: log.data.to_vec(), 130 | }; 131 | let log_desc = self.fir_abi.event("Register")?.parse_log(raw_log)?; 132 | 133 | // extract address, fid, recovery and url 134 | let address = log_desc.params.get(0).unwrap().value.to_string(); 135 | let fid = log_desc 136 | .params 137 | .get(1) 138 | .unwrap() 139 | .value 140 | .clone() 141 | .into_uint() 142 | .unwrap() 143 | .as_u64(); 144 | let recovery = log_desc.params.get(2).unwrap().value.to_string(); 145 | let url = log_desc.params.get(3).unwrap().value.to_string(); 146 | 147 | self.fir.insert( 148 | fid, 149 | FIDInfo { 150 | address, 151 | recovery, 152 | url, 153 | }, 154 | ); 155 | } 156 | 157 | Ok(()) 158 | } 159 | 160 | /// [private] sync Farcaster Name Registry logs 161 | async fn sync_fnr_logs(&mut self) -> Result<(), Box> { 162 | // get latest event logs 163 | let logs = self.get_fnr_transfer_logs(self.fnr_block).await?; 164 | 165 | // iterate over logs 166 | for log in logs { 167 | // update stored block number 168 | let block_number = u64::try_from(log.block_number.unwrap()).unwrap(); 169 | if block_number > self.fnr_block { 170 | self.fnr_block = block_number; 171 | } 172 | 173 | // parse log 174 | let raw_log = RawLog { 175 | topics: log.topics, 176 | data: log.data.to_vec(), 177 | }; 178 | let log_desc = self.fnr_abi.event("Transfer")?.parse_log(raw_log)?; 179 | 180 | // extract address and username 181 | let address = log_desc.params.get(1).unwrap().value.to_string(); 182 | let username = token_to_fname(log_desc.params.get(2).unwrap().value.clone())?; 183 | 184 | // insert (or update) FNR HashMap 185 | self.fnr.insert(username, address); 186 | } 187 | 188 | Ok(()) 189 | } 190 | 191 | /// [private] get Farcaster ID Registry Register logs starting at block `from_block` 192 | async fn get_fir_register_logs(&mut self, from_block: u64) -> Result, Box> { 193 | // prepare contract address and log topic 194 | let contract_address = FIR_CONTRACT_ADDRESS.parse::
()?; 195 | let register_topic = 196 | "0x3cd6a0ffcc37406d9958e09bba79ff19d8237819eb2e1911f9edbce656499c87".parse::()?; 197 | 198 | // prepare filter 199 | let register_filter = Filter::new() 200 | .select(from_block..) 201 | .address(contract_address) 202 | .topic0(register_topic); 203 | 204 | // get event logs 205 | let logs = self.provider.get_logs(®ister_filter).await?; 206 | 207 | Ok(logs) 208 | } 209 | 210 | /// [private] get Farcaster ID Registry Transfer logs starting at block `from_block` 211 | #[allow(dead_code)] 212 | async fn get_fir_transfer_logs(&mut self, from_block: u64) -> Result, Box> { 213 | // prepare contract address and log topic 214 | let contract_address = FIR_CONTRACT_ADDRESS.parse::
()?; 215 | let transfer_topic = 216 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".parse::()?; 217 | 218 | // prepare filter 219 | let transfer_filter = Filter::new() 220 | .select(from_block..) 221 | .address(contract_address) 222 | .topic0(transfer_topic); 223 | 224 | // get event logs 225 | let logs = self.provider.get_logs(&transfer_filter).await?; 226 | 227 | Ok(logs) 228 | } 229 | 230 | /// [private] get Farcaster Name Registry Transfer logs starting at block `from_block` 231 | async fn get_fnr_transfer_logs(&mut self, from_block: u64) -> Result, Box> { 232 | // prepare contract address and log topic 233 | let contract_address = FNR_CONTRACT_ADDRESS.parse::
()?; 234 | let transfer_topic = 235 | "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".parse::()?; 236 | 237 | // prepare transfer filter 238 | let transfer_filter = Filter::new() 239 | .select(from_block..) 240 | .address(contract_address) 241 | .topic0(transfer_topic); 242 | 243 | // get event logs 244 | let logs = self.provider.get_logs(&transfer_filter).await?; 245 | 246 | Ok(logs) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/registry/token_to_fname.rs: -------------------------------------------------------------------------------- 1 | use ethers::{ 2 | abi::{AbiEncode, Token, Tokenizable}, 3 | types::U256, 4 | utils::parse_bytes32_string, 5 | }; 6 | 7 | /// # Convert a FC V2 token to an fname (username) 8 | /// 9 | /// ## Arguments 10 | /// 11 | /// * `token: Token` 12 | /// - Token is a type of ethers::abi::Token 13 | /// - Retrieveable by parsing a log from the logs functions 14 | /// 15 | pub fn token_to_fname(token: Token) -> Result> { 16 | let u256_token = U256::from_token(token)?; 17 | let u256_token = u256_token.encode(); 18 | let byte_u256_token: &[u8] = &&u256_token; 19 | let byte_u256_token: &[u8; 32] = byte_u256_token[0..32].try_into()?; 20 | let fname = parse_bytes32_string(byte_u256_token)?; 21 | 22 | Ok(fname.to_string()) 23 | } 24 | -------------------------------------------------------------------------------- /src/types/account/auth/bearer.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | pub struct Bearer { 5 | pub bearer: String, 6 | pub payload: Payload, 7 | } 8 | 9 | #[derive(Debug, Deserialize, Serialize)] 10 | pub struct Payload { 11 | pub method: String, 12 | pub params: Params, 13 | } 14 | 15 | #[derive(Debug, Deserialize, Serialize)] 16 | pub struct Params { 17 | pub timestamp: i64, 18 | #[serde(rename = "expiresAt")] 19 | #[serde(skip_serializing_if = "Option::is_none")] 20 | pub expires_at: Option, 21 | } 22 | -------------------------------------------------------------------------------- /src/types/account/auth/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bearer; 2 | pub mod revoke; 3 | pub mod secret; 4 | -------------------------------------------------------------------------------- /src/types/account/auth/revoke.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | pub struct RevokedKeyRoot { 5 | pub result: RevokedKeyResult, 6 | } 7 | 8 | #[derive(Debug, Deserialize, Serialize)] 9 | pub struct RevokedKeyResult { 10 | pub success: bool, 11 | } 12 | -------------------------------------------------------------------------------- /src/types/account/auth/secret.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | pub struct Secret { 5 | pub result: SecretResult, 6 | } 7 | 8 | #[derive(Debug, Deserialize, Serialize)] 9 | pub struct SecretResult { 10 | pub token: SecretToken, 11 | } 12 | 13 | #[derive(Debug, Deserialize, Serialize)] 14 | pub struct SecretToken { 15 | pub secret: String, 16 | #[serde(rename = "expiresAt")] 17 | pub expires_at: i64, 18 | } 19 | -------------------------------------------------------------------------------- /src/types/account/mod.rs: -------------------------------------------------------------------------------- 1 | /// ## Auth 2 | /// Holds authentication structs 3 | pub mod auth; 4 | 5 | use auth::bearer::Bearer; 6 | use auth::secret::SecretToken; 7 | use ethers::prelude::k256::ecdsa::SigningKey; 8 | use ethers::signers::Wallet; 9 | 10 | /// Farcaster account 11 | /// holds account keypair and Merkle v2 API token 12 | #[derive(Debug)] 13 | pub struct Account { 14 | pub(crate) wallet: Wallet, 15 | pub(crate) bearer_token: Option, 16 | pub(crate) session_token: Option, 17 | pub(crate) token_duration_secs: Option, 18 | } 19 | -------------------------------------------------------------------------------- /src/types/assets/collection_owners.rs: -------------------------------------------------------------------------------- 1 | use crate::types::shared::pfp::SharedPfp; 2 | use crate::types::shared::shared_profile::SharedProfile; 3 | use crate::types::shared::viewer_context::SharedViewerContext; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct CollectionOwnersRoot { 8 | pub result: CollectionOwnersResult, 9 | pub next: Option, 10 | } 11 | 12 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 13 | pub struct CollectionOwnersResult { 14 | pub users: Vec, 15 | } 16 | 17 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 18 | pub struct CollectionOwnersUser { 19 | pub fid: i64, 20 | pub username: String, 21 | #[serde(rename = "displayName")] 22 | pub display_name: String, 23 | pub pfp: SharedPfp, 24 | pub profile: SharedProfile, 25 | #[serde(rename = "followerCount")] 26 | pub follower_count: i64, 27 | #[serde(rename = "followingCount")] 28 | pub following_count: i64, 29 | #[serde(rename = "viewerContext")] 30 | pub viewer_context: SharedViewerContext, 31 | } 32 | 33 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 34 | pub struct CollectionOwnersNext { 35 | pub cursor: Option, 36 | } 37 | -------------------------------------------------------------------------------- /src/types/assets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod collection_owners; 2 | pub mod user_collections; 3 | -------------------------------------------------------------------------------- /src/types/assets/user_collections.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct UserCollectionsRoot { 5 | pub result: UserCollectionsResult, 6 | pub next: Option, 7 | } 8 | 9 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 10 | pub struct UserCollectionsResult { 11 | pub collections: Vec, 12 | } 13 | 14 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 15 | pub struct Collection { 16 | pub id: String, 17 | pub name: String, 18 | pub description: String, 19 | #[serde(rename = "itemCount")] 20 | pub item_count: i64, 21 | #[serde(rename = "ownerCount")] 22 | pub owner_count: i64, 23 | #[serde(rename = "farcasterOwnerCount")] 24 | pub fc_owner_count: i64, 25 | #[serde(rename = "imageUrl")] 26 | pub image_url: String, 27 | #[serde(rename = "volumeTraded")] 28 | pub volume_traded: String, 29 | #[serde(rename = "externalUrl")] 30 | pub external_url: Option, 31 | #[serde(rename = "openSeaUrl")] 32 | pub opensea_url: Option, 33 | #[serde(rename = "twitterUsername")] 34 | pub twitter_username: Option, 35 | #[serde(rename = "schemaName")] 36 | pub schema_name: Option, 37 | } 38 | 39 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 40 | pub struct UserCollectionsNext { 41 | pub cursor: Option, 42 | } 43 | -------------------------------------------------------------------------------- /src/types/casts/cast_builder.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize, Debug)] 4 | pub struct CastBuilder { 5 | pub(crate) content: String, 6 | pub(crate) embeds: Vec, 7 | pub(crate) mentions: Vec, 8 | pub(crate) parent_cast_hash: String, 9 | } 10 | -------------------------------------------------------------------------------- /src/types/casts/casts.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct CastRoot { 5 | pub result: Result, 6 | pub next: Option, 7 | } 8 | 9 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 10 | pub struct Result { 11 | pub casts: Vec, 12 | } 13 | 14 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 15 | pub struct Cast { 16 | pub hash: String, 17 | #[serde(rename = "threadHash")] 18 | pub thread_hash: String, 19 | pub author: Author, 20 | pub text: String, 21 | pub timestamp: i64, 22 | pub replies: Replies, 23 | pub reactions: Reactions, 24 | pub recasts: Recasts, 25 | pub watches: Watches, 26 | pub recast: Option, 27 | #[serde(rename = "viewerContext")] 28 | pub viewer_context: ViewerContext, 29 | } 30 | 31 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 32 | pub struct Author { 33 | pub fid: i64, 34 | pub username: String, 35 | #[serde(rename = "displayName")] 36 | pub display_name: String, 37 | pub pfp: PFP, 38 | pub profile: Profile, 39 | #[serde(rename = "followerCount")] 40 | pub follower_count: i64, 41 | #[serde(rename = "followingCount")] 42 | pub following_count: i64, 43 | } 44 | 45 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 46 | pub struct PFP { 47 | pub url: String, 48 | pub verified: bool, 49 | } 50 | 51 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 52 | pub struct Profile { 53 | pub bio: Bio, 54 | } 55 | 56 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 57 | pub struct Bio { 58 | pub text: String, 59 | pub mentions: Option>, 60 | } 61 | 62 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 63 | pub struct Replies { 64 | pub count: i32, 65 | } 66 | 67 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 68 | pub struct Reactions { 69 | pub count: i32, 70 | } 71 | 72 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 73 | pub struct Recasts { 74 | pub count: i32, 75 | pub recasters: Vec, 76 | } 77 | 78 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 79 | pub struct Recasters { 80 | pub fid: i64, 81 | pub username: String, 82 | #[serde(rename = "displayName")] 83 | pub display_name: String, 84 | #[serde(rename = "recastHash")] 85 | pub recast_hash: String, 86 | } 87 | 88 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 89 | pub struct Watches { 90 | count: i32, 91 | } 92 | 93 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 94 | pub struct ViewerContext { 95 | pub reacted: bool, 96 | pub recast: bool, 97 | pub watched: bool, 98 | } 99 | 100 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 101 | #[serde(rename_all = "camelCase")] 102 | pub struct Next { 103 | pub cursor: Option, 104 | } 105 | -------------------------------------------------------------------------------- /src/types/casts/deleted_cast.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct DeletedCastRoot { 5 | pub result: DeletedCastResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct DeletedCastResult { 10 | pub success: bool, 11 | } 12 | -------------------------------------------------------------------------------- /src/types/casts/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cast_builder; 2 | pub mod casts; 3 | pub mod deleted_cast; 4 | pub mod published_cast; 5 | -------------------------------------------------------------------------------- /src/types/casts/published_cast.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_json::Value; 3 | 4 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 5 | pub struct PublishedCast { 6 | pub result: Result, 7 | } 8 | 9 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 10 | pub struct Result { 11 | pub cast: Cast, 12 | } 13 | 14 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 15 | #[serde(rename_all = "camelCase")] 16 | pub struct Cast { 17 | pub hash: String, 18 | pub thread_hash: String, 19 | pub author: Author, 20 | pub text: String, 21 | pub timestamp: i64, 22 | pub replies: Replies, 23 | pub reactions: Reactions, 24 | pub recasts: Recasts, 25 | pub watches: Watches, 26 | pub viewer_context: ViewerContext, 27 | } 28 | 29 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 30 | #[serde(rename_all = "camelCase")] 31 | pub struct Author { 32 | pub fid: i64, 33 | pub username: String, 34 | pub display_name: String, 35 | pub pfp: Pfp, 36 | pub follower_count: i64, 37 | pub following_count: i64, 38 | } 39 | 40 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 41 | #[serde(rename_all = "camelCase")] 42 | pub struct Pfp { 43 | pub url: String, 44 | pub verified: bool, 45 | } 46 | 47 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 48 | #[serde(rename_all = "camelCase")] 49 | pub struct Replies { 50 | pub count: i64, 51 | } 52 | 53 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 54 | #[serde(rename_all = "camelCase")] 55 | pub struct Reactions { 56 | pub count: i64, 57 | } 58 | 59 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 60 | #[serde(rename_all = "camelCase")] 61 | pub struct Recasts { 62 | pub count: i64, 63 | pub recasters: Vec, 64 | } 65 | 66 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 67 | #[serde(rename_all = "camelCase")] 68 | pub struct Watches { 69 | pub count: i64, 70 | } 71 | 72 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 73 | #[serde(rename_all = "camelCase")] 74 | pub struct ViewerContext { 75 | pub reacted: bool, 76 | pub recast: bool, 77 | pub watched: bool, 78 | } 79 | -------------------------------------------------------------------------------- /src/types/follows/follow_user.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct FollowUserRoot { 5 | pub result: FollowUserResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct FollowUserResult { 10 | pub success: bool, 11 | } 12 | -------------------------------------------------------------------------------- /src/types/follows/followers.rs: -------------------------------------------------------------------------------- 1 | use crate::types::shared::pfp::SharedPfp; 2 | use crate::types::shared::shared_profile::SharedProfile; 3 | use crate::types::shared::viewer_context::SharedViewerContext; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct FollowersRoot { 8 | pub result: FollowersUsersVec, 9 | pub next: Option, 10 | } 11 | 12 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 13 | pub struct FollowersUsersVec { 14 | pub users: Vec, 15 | } 16 | 17 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 18 | pub struct Follower { 19 | pub fid: i64, 20 | pub username: String, 21 | #[serde(rename = "displayName")] 22 | pub display_name: String, 23 | pub pfp: SharedPfp, 24 | pub profile: SharedProfile, 25 | #[serde(rename = "followerCount")] 26 | pub follower_count: i64, 27 | #[serde(rename = "followingCount")] 28 | pub following_count: i64, 29 | #[serde(rename = "viewerContext")] 30 | pub viewer_context: SharedViewerContext, 31 | } 32 | 33 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 34 | pub struct FollowersNext { 35 | pub cursor: Option, 36 | } 37 | -------------------------------------------------------------------------------- /src/types/follows/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod follow_user; 2 | pub mod followers; 3 | pub mod unfollow_user; 4 | -------------------------------------------------------------------------------- /src/types/follows/unfollow_user.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct UnfollowUserRoot { 5 | pub result: UnfollowUserResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct UnfollowUserResult { 10 | pub success: bool, 11 | } 12 | -------------------------------------------------------------------------------- /src/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! Contains all of the types used throughout farcaster-rs 2 | 3 | /// ## Casts 4 | /// Holds structs for cast info, casts, etc... 5 | pub mod casts; 6 | /// ## User 7 | /// Holds structs for user info, notifications, etc... 8 | pub mod user; 9 | 10 | /// ## Account 11 | /// Holds Farcaster Account 12 | pub mod account; 13 | /// ## Registry 14 | /// Holds Farcaster ID/Name Registry 15 | pub mod registry; 16 | 17 | /// ## Reactions 18 | /// Holds types for reaction events 19 | pub mod reactions; 20 | 21 | /// ## Follows 22 | /// Holds types for followers/following 23 | pub mod follows; 24 | 25 | /// ## Shared 26 | /// Contains shared types i.e. Profile, Pfp, etc.... 27 | pub mod shared; 28 | 29 | /// ## Verifications 30 | /// Contains types relating to cryptographic proofs & verifications 31 | pub mod verifications; 32 | 33 | /// ## Notifications 34 | /// Contains types relating to notifications 35 | pub mod notifications; 36 | 37 | /// ## Assets 38 | /// Contains types relating to NFT assets 39 | pub mod assets; 40 | -------------------------------------------------------------------------------- /src/types/notifications/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod notifications; 2 | -------------------------------------------------------------------------------- /src/types/notifications/notifications.rs: -------------------------------------------------------------------------------- 1 | use crate::types::casts::published_cast::{Reactions, Recasts, Replies, ViewerContext, Watches}; 2 | use crate::types::shared::pfp::SharedPfp; 3 | use crate::types::shared::shared_profile::SharedProfile; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct NotificationsRoot { 8 | pub result: NotificationsResult, 9 | pub next: Option, 10 | } 11 | 12 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 13 | pub struct NotificationsResult { 14 | pub notifications: Vec, 15 | } 16 | 17 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 18 | pub struct Notification { 19 | #[serde(rename = "type")] 20 | pub notif_type: String, 21 | pub id: String, 22 | pub timestamp: i64, 23 | pub actor: NotificationActor, 24 | pub content: NotificationContent, 25 | } 26 | 27 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 28 | pub struct NotificationActor { 29 | pub fid: i64, 30 | pub username: String, 31 | #[serde(rename = "displayName")] 32 | pub display_name: String, 33 | pub pfp: SharedPfp, 34 | pub profile: SharedProfile, 35 | #[serde(rename = "followerCount")] 36 | pub follower_count: i64, 37 | #[serde(rename = "followingCount")] 38 | pub following_name: i64, 39 | } 40 | 41 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 42 | pub struct NotificationContent { 43 | pub cast: NotificationCast, 44 | } 45 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 46 | pub struct NotificationCast { 47 | pub hash: String, 48 | #[serde(rename = "threadHash")] 49 | pub thread_hash: String, 50 | #[serde(rename = "parentHash")] 51 | pub parent_hash: String, 52 | #[serde(rename = "parentAuthor")] 53 | pub parent_author: NotificationParentAuthor, 54 | pub author: NotificationParentAuthor, 55 | pub text: String, 56 | pub timestamp: i64, 57 | pub replies: Replies, 58 | pub reactions: Reactions, 59 | pub recasts: Recasts, 60 | pub watches: Watches, 61 | #[serde(rename = "viewerContext")] 62 | pub viewer_context: ViewerContext, 63 | } 64 | 65 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 66 | pub struct NotificationParentAuthor { 67 | pub fid: i64, 68 | pub username: String, 69 | #[serde(rename = "displayName")] 70 | pub display_name: String, 71 | pub pfp: SharedPfp, 72 | pub profile: SharedProfile, 73 | #[serde(rename = "followerCount")] 74 | pub follower_count: i64, 75 | #[serde(rename = "followingCount")] 76 | pub following_count: i64, 77 | } 78 | 79 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 80 | pub struct NotificationsNext { 81 | pub cursor: Option, 82 | } 83 | -------------------------------------------------------------------------------- /src/types/reactions/deleted_like.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct DeletedLikeRoot { 5 | pub result: DeletedLikeResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct DeletedLikeResult { 10 | pub success: bool, 11 | } 12 | -------------------------------------------------------------------------------- /src/types/reactions/deleted_recast.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct DeletedRecastRoot { 5 | pub result: DeletedRecastResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct DeletedRecastResult { 10 | pub success: bool, 11 | } 12 | -------------------------------------------------------------------------------- /src/types/reactions/liked_cast.rs: -------------------------------------------------------------------------------- 1 | use crate::types::shared::pfp::SharedPfp; 2 | use crate::types::shared::shared_profile::SharedProfile; 3 | use crate::types::shared::viewer_context::SharedViewerContext; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct LikedCastRoot { 8 | pub result: LikedCastResult, 9 | } 10 | 11 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 12 | pub struct LikedCastResult { 13 | pub like: Like, 14 | } 15 | 16 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 17 | pub struct Like { 18 | #[serde(rename = "type")] 19 | pub like_type: String, 20 | pub hash: String, 21 | pub reactor: Reactor, 22 | pub timestamp: i64, 23 | #[serde(rename = "castHash")] 24 | pub cast_hash: String, 25 | } 26 | 27 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 28 | pub struct Reactor { 29 | pub fid: i64, 30 | pub username: String, 31 | #[serde(rename = "displayName")] 32 | pub display_name: String, 33 | pub pfp: SharedPfp, 34 | pub profile: SharedProfile, 35 | #[serde(rename = "followerCount")] 36 | pub follower_count: i64, 37 | #[serde(rename = "followingCount")] 38 | pub following_count: i64, 39 | #[serde(rename = "referrerUsername")] 40 | pub referrer_username: Option, 41 | #[serde(rename = "viewerContext")] 42 | pub viewer_context: SharedViewerContext, 43 | } 44 | -------------------------------------------------------------------------------- /src/types/reactions/liked_casts.rs: -------------------------------------------------------------------------------- 1 | use crate::types::shared::pfp::SharedPfp; 2 | use crate::types::shared::shared_profile::SharedProfile; 3 | use crate::types::shared::viewer_context::SharedViewerContext; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct ManyLikedCastsRoot { 8 | pub result: LikedCastResult, 9 | } 10 | 11 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 12 | pub struct LikedCastResult { 13 | pub likes: Vec, 14 | } 15 | 16 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 17 | pub struct Like { 18 | #[serde(rename = "type")] 19 | pub like_type: String, 20 | pub hash: String, 21 | pub reactor: Reactor, 22 | pub timestamp: i64, 23 | #[serde(rename = "castHash")] 24 | pub cast_hash: String, 25 | } 26 | 27 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 28 | pub struct Reactor { 29 | pub fid: i64, 30 | pub username: String, 31 | #[serde(rename = "displayName")] 32 | pub display_name: String, 33 | pub pfp: SharedPfp, 34 | pub profile: SharedProfile, 35 | #[serde(rename = "followerCount")] 36 | pub follower_count: i64, 37 | #[serde(rename = "followingCount")] 38 | pub following_count: i64, 39 | #[serde(rename = "referrerUsername")] 40 | pub referrer_username: Option, 41 | #[serde(rename = "viewerContext")] 42 | pub viewer_context: SharedViewerContext, 43 | } 44 | -------------------------------------------------------------------------------- /src/types/reactions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod deleted_like; 2 | pub mod deleted_recast; 3 | pub mod liked_cast; 4 | pub mod liked_casts; 5 | pub mod recasted; 6 | pub mod recasters; 7 | -------------------------------------------------------------------------------- /src/types/reactions/recasted.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct RecastedRoot { 5 | pub result: RecastedResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct RecastedResult { 10 | #[serde(rename = "castHash")] 11 | cast_hash: String, 12 | } 13 | -------------------------------------------------------------------------------- /src/types/reactions/recasters.rs: -------------------------------------------------------------------------------- 1 | use crate::types::shared::pfp::SharedPfp; 2 | use crate::types::shared::shared_profile::SharedProfile; 3 | use crate::types::shared::viewer_context::SharedViewerContext; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct RecastersRoot { 8 | pub result: RecastersResult, 9 | } 10 | 11 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 12 | pub struct RecastersResult { 13 | pub users: Vec, 14 | } 15 | 16 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 17 | pub struct Recaster { 18 | pub fid: i64, 19 | pub username: String, 20 | #[serde(rename = "displayName")] 21 | pub display_name: String, 22 | pub pfp: SharedPfp, 23 | pub profile: SharedProfile, 24 | #[serde(rename = "followerCount")] 25 | pub follower_count: i64, 26 | #[serde(rename = "followingCount")] 27 | pub following_count: i64, 28 | #[serde(rename = "ViewerContext")] 29 | pub viewer_context: SharedViewerContext, 30 | } 31 | -------------------------------------------------------------------------------- /src/types/registry/mod.rs: -------------------------------------------------------------------------------- 1 | use ethers::core::abi::Abi; 2 | use ethers::providers::{Http, Provider}; 3 | use std::collections::HashMap; 4 | 5 | /// interface to Farcaster ID/Name Registry 6 | #[derive(Debug)] 7 | pub struct Registry { 8 | pub(crate) fir_abi: Abi, // FIR JSON ABI 9 | pub(crate) fir_block: u64, // FIR block to fetch from 10 | pub(crate) fir: HashMap, // FIR fid to FIDInfo mapping 11 | pub(crate) fnr_abi: Abi, // FNR JSON ABI 12 | pub(crate) fnr_block: u64, // FNR block to fetch from 13 | pub(crate) fnr: HashMap, // FNR name to address mapping 14 | pub(crate) provider: Provider, // Ethereum HTTP provider 15 | } 16 | 17 | /// holds Farcaster ID info from FIR 18 | #[derive(Debug)] 19 | pub struct FIDInfo { 20 | pub address: String, 21 | pub recovery: String, 22 | pub url: String, 23 | } 24 | -------------------------------------------------------------------------------- /src/types/shared/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pfp; 2 | pub mod shared_profile; 3 | pub mod viewer_context; 4 | -------------------------------------------------------------------------------- /src/types/shared/pfp.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct SharedPfp { 5 | pub url: String, 6 | pub verified: bool, 7 | } 8 | -------------------------------------------------------------------------------- /src/types/shared/shared_profile.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct SharedProfile { 5 | pub bio: SharedBio, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct SharedBio { 10 | pub text: String, 11 | pub mentions: Option>, 12 | } 13 | -------------------------------------------------------------------------------- /src/types/shared/viewer_context.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct SharedViewerContext { 5 | pub following: bool, 6 | #[serde(rename = "followedBy")] 7 | pub followed_by: bool, 8 | } 9 | -------------------------------------------------------------------------------- /src/types/user/custody_address.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | pub struct CustodyAddressRoot { 5 | pub result: CustodyAddressResult, 6 | } 7 | 8 | #[derive(Debug, Deserialize, Serialize)] 9 | pub struct CustodyAddressResult { 10 | #[serde(rename = "custodyAddress")] 11 | pub custody_address: String, 12 | } 13 | -------------------------------------------------------------------------------- /src/types/user/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod custody_address; 2 | pub mod user; 3 | -------------------------------------------------------------------------------- /src/types/user/user.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | pub struct UserRoot { 5 | pub result: Result, 6 | } 7 | 8 | #[derive(Debug, Deserialize, Serialize)] 9 | pub struct Result { 10 | pub user: UserInfo, 11 | } 12 | 13 | #[derive(Debug, Deserialize, Serialize)] 14 | pub struct UserInfo { 15 | pub fid: i64, 16 | pub username: String, 17 | #[serde(rename = "displayName")] 18 | pub display_name: String, 19 | pub pfp: Pfp, 20 | #[serde(rename = "followerCount")] 21 | pub follower_count: i64, 22 | #[serde(rename = "followingCount")] 23 | pub following_count: i64, 24 | #[serde(rename = "referrerUsername")] 25 | pub referrer_username: Option, 26 | #[serde(rename = "viewerContext")] 27 | pub viewer_content: ViewerContext, 28 | } 29 | 30 | #[derive(Debug, Deserialize, Serialize)] 31 | pub struct Pfp { 32 | pub url: String, 33 | pub verified: bool, 34 | } 35 | 36 | #[derive(Debug, Deserialize, Serialize)] 37 | pub struct ViewerContext { 38 | pub following: bool, 39 | #[serde(rename = "followedBy")] 40 | pub followed_by: bool, 41 | #[serde(rename = "canSendDirectCasts")] 42 | pub can_send_direct_casts: bool, 43 | } 44 | -------------------------------------------------------------------------------- /src/types/verifications/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod verifications; 2 | -------------------------------------------------------------------------------- /src/types/verifications/verifications.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 4 | pub struct VerificationsRoot { 5 | pub result: VerificationsResult, 6 | } 7 | 8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 9 | pub struct VerificationsResult { 10 | pub verifications: Vec, 11 | } 12 | 13 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 14 | pub struct Verification { 15 | pub fid: i64, 16 | pub address: String, 17 | pub timestamp: i64, 18 | } 19 | -------------------------------------------------------------------------------- /src/users/get_custody_address.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::user::custody_address::CustodyAddressRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get a custody address by FID 8 | pub async fn get_custody_address_by_fid( 9 | &self, 10 | fid: i64, 11 | ) -> Result> { 12 | let url = format!("{}/v2/custody-address?fid={}", API_ROOT, fid); 13 | 14 | let custody_address_reqwest = &self.reqwest_get(&url).await?; 15 | 16 | let custody_address: CustodyAddressRoot = serde_json::from_str(&custody_address_reqwest)?; 17 | 18 | Ok(custody_address) 19 | } 20 | 21 | /// Get a custody address by FID 22 | pub async fn get_custody_address_by_username( 23 | &self, 24 | username: &str, 25 | ) -> Result> { 26 | let url = format!("{}/v2/custody-address?fname={}", API_ROOT, username); 27 | 28 | let custody_address_reqwest = &self.reqwest_get(&url).await?; 29 | 30 | let custody_address: CustodyAddressRoot = serde_json::from_str(&custody_address_reqwest)?; 31 | 32 | Ok(custody_address) 33 | } 34 | 35 | /// Get a custody address by FID 36 | pub async fn get_custody_address_by_address( 37 | &self, 38 | address: &str, 39 | ) -> Result> { 40 | let fid = &self.get_user_by_address(address).await?; 41 | 42 | let url = format!("{}/v2/custody-address?fid={}", API_ROOT, fid.fid); 43 | 44 | let custody_address_reqwest = &self.reqwest_get(&url).await?; 45 | 46 | let custody_address: CustodyAddressRoot = serde_json::from_str(&custody_address_reqwest)?; 47 | 48 | Ok(custody_address) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/users/get_user.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::user::user::{UserInfo, UserRoot}; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get information about a user via their Farcaster ID (more commonly shortened to FID throughout this codebase 8 | pub async fn get_user_by_fid(&self, fid: u64) -> Result> { 9 | // make sure fid exists 10 | if let Some(_addr) = self.registry.get_address_by_fid(fid) { 11 | let response = self 12 | .reqwest_get(&format!("{}/v2/user?fid={}", API_ROOT, fid)) 13 | .await?; 14 | let user_root: UserRoot = serde_json::from_str(&response)?; 15 | 16 | return Ok(user_root.result.user); 17 | } 18 | 19 | Err(Box::from(format!( 20 | "FID '{}' not found in Farcaster ID Registry", 21 | fid 22 | ))) 23 | } 24 | 25 | /// Get information about a user via their Farcaster Username 26 | pub async fn get_user_by_username(&self, username: &str) -> Result> { 27 | if let Some(fid) = self.registry.get_fid_by_username(username) { 28 | return self.get_user_by_fid(fid).await; 29 | } 30 | 31 | Err(Box::from(format!( 32 | "User '{}' not found in Farcaster Name Registry", 33 | username 34 | ))) 35 | } 36 | 37 | /// Get information about a user via their Ethereum address 38 | pub async fn get_user_by_address(&self, address: &str) -> Result> { 39 | if let Some(fid) = self.registry.get_fid_by_address(address) { 40 | return self.get_user_by_fid(fid).await; 41 | } 42 | 43 | Err(Box::from(format!( 44 | "Address '{}' not found in Farcaster ID Registry", 45 | address 46 | ))) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/users/me.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::user::user::UserRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get information about the authenticated user 8 | pub async fn get_me(&self) -> Result> { 9 | let me_reqwest = &self 10 | .reqwest_get(format!("{}/v2/me", API_ROOT).as_str()) 11 | .await?; 12 | 13 | let me: UserRoot = serde_json::from_str(&me_reqwest)?; 14 | 15 | Ok(me) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/users/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Users module that handles users 2 | //! 3 | //! # Quickstart with the Users module 4 | //! 5 | //! 1. Use the get_user function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // Get a user 13 | //! let user = farcaster.get_user_by_username("ace").await?; 14 | //! let user = farcaster.get_user_by_address("0X000...").await?; 15 | //! let user = farcaster.get_user_by_fid(0).await?; 16 | //! ``` 17 | //! 18 | //! 2. Use the get_custody_address function 19 | //! ```no_run 20 | //! use farcaster_rs::{Account, Farcaster}; 21 | //! 22 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 23 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 24 | //! 25 | //! // Get custody address 26 | //! let custody_address = farcaster.get_custody_address_by_username("v").await?; 27 | //! let custody_address = farcaster.get_custody_address_by_fid(0).await?; 28 | //! let custody_address = farcaster.get_custody_address_by_address("0x0000").await?; 29 | //! ``` 30 | //! 31 | //! Use the me function 32 | //! ```no_run 33 | //! use farcaster_rs::{Account, Farcaster}; 34 | //! 35 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 36 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 37 | //! 38 | //! // Get me 39 | //! let me = farcaster.get_me().await?; 40 | //! ``` 41 | 42 | /// Get a custody address by FID, Address, or Username 43 | pub mod get_custody_address; 44 | /// Get a user by FID, Address, or Username 45 | pub mod get_user; 46 | /// Get info about the authenticated user 47 | pub mod me; 48 | -------------------------------------------------------------------------------- /src/verifications/get_verifications.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::merkle::API_ROOT; 2 | use crate::types::verifications::verifications::VerificationsRoot; 3 | use crate::Farcaster; 4 | use std::error::Error; 5 | 6 | impl Farcaster { 7 | /// Get verifications of a user by their FID 8 | pub async fn get_verifications_by_fid( 9 | &self, 10 | fid: i64, 11 | ) -> Result> { 12 | let verifications_reqwest = &self 13 | .reqwest_get(format!("{}/v2/verifications?fid={}", API_ROOT, fid).as_str()) 14 | .await?; 15 | 16 | let verifications: VerificationsRoot = serde_json::from_str(&verifications_reqwest)?; 17 | 18 | Ok(verifications) 19 | } 20 | 21 | /// Get verifications of a user by their username 22 | pub async fn get_verifications_by_username( 23 | &self, 24 | username: &str, 25 | ) -> Result> { 26 | let fid = &self.get_user_by_username(username).await?; 27 | 28 | let verifications = &self.get_verifications_by_fid(fid.fid).await?; 29 | 30 | Ok(verifications.clone()) 31 | } 32 | 33 | /// Get verifications of a user by their address 34 | pub async fn get_verifications_by_address( 35 | &self, 36 | address: &str, 37 | ) -> Result> { 38 | let fid = &self.get_user_by_address(address).await?; 39 | 40 | let verifications = &self.get_verifications_by_fid(fid.fid).await?; 41 | 42 | Ok(verifications.clone()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/verifications/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Verifications module that handles proof verifications 2 | //! 3 | //! # Quickstart with the Verifications module 4 | //! 5 | //! 1. Use the get_verifications function 6 | //! ```no_run 7 | //! use farcaster_rs::{Account, Farcaster}; 8 | //! 9 | //! let account: Account = Account::from_mnemonic("mnemonic phrase", None).await?; 10 | //! let farcaster: Farcaster = Farcaster::new("eth provider", account).await?; 11 | //! 12 | //! // Get verifications 13 | //! let verif = farcaster.get_verifications_by_username("giu").await?; 14 | //! let verif = farcaster.get_verifications_by_address("0x000...").await?; 15 | //! let verif = farcaster.get_verifications_by_fid(0).await?; 16 | //! ``` 17 | //! 18 | //! 19 | 20 | /// Get verifications of a user 21 | pub mod get_verifications; 22 | --------------------------------------------------------------------------------