Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 check rug on contract #8

Merged
merged 4 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions rug_check_res_sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"tokenProgram": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"tokenType": "",
"risks": [
{
"name": "Large Amount of LP Unlocked",
"value": "100.00%",
"description": "A large amount of LP tokens are unlocked, allowing the owner to remove liquidity at any point.",
"score": 10999,
"level": "danger"
},
{
"name": "High holder correlation",
"value": "(19)",
"description": "The top users hold similar supply amounts",
"score": 2900,
"level": "warn"
},
{
"name": "Low amount of LP Providers",
"value": "",
"description": "Only a few users are providing liquidity",
"score": 400,
"level": "warn"
},
{
"name": "Mutable metadata",
"value": "",
"description": "Token metadata can be changed by the owner",
"score": 100,
"level": "warn"
}
],
"score": 14399
}
25 changes: 24 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub database_url: String,
pub telegram_token: String,
Expand All @@ -10,7 +10,17 @@ pub struct Config {

pub program_id: String,
pub private_key: String,
pub liquidility_pool_wsol_pc_mint: String,
pub rug_checker_url: String,
pub rug_check_config: RugCheckConfig,
}

#[derive(Debug, Clone, PartialEq)]
pub struct RugCheckConfig {
pub signal_holder_ownership: f64,
pub not_allowed_risk: Vec<String>,
}

impl Config {
pub fn init() -> Config {
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
Expand All @@ -21,6 +31,9 @@ impl Config {
let helius_api_key = std::env::var("HELIUS_API_KEY").expect("HELIUS_API_KEY must be set");
let program_id = std::env::var("PROGRAM_ID").expect("PROGRAM_ID must be set");
let private_key = std::env::var("PRIVATE_KEY").expect("PRIVATE_KEY must be set");
let liquidility_pool_wsol_pc_mint = std::env::var("LIQUIDILITY_POOL_WSOL_PC_MINT")
.expect("LIQUIDILITY_POOL_WSOL_PC_MINT must be set");
let rug_checker_url = std::env::var("RUG_CHECKER_URL").expect("RUG_CHECKER_URL must be set");

Config {
database_url,
Expand All @@ -31,6 +44,16 @@ impl Config {
private_key,
program_id,
telegram_chat_id: telegram_chat_id.parse::<i64>().unwrap(),
liquidility_pool_wsol_pc_mint,
rug_checker_url,
rug_check_config: RugCheckConfig {
signal_holder_ownership: 0 as f64,
not_allowed_risk: vec![
"Freeze Authority still enabled".to_string(),
"Large Amount of LP Unlocked".to_string(),
"Copycat token".to_string(),
],
},
}
}
}
7 changes: 3 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod config;
mod database;
mod model;
mod price_monitor;
mod rug_checker;
mod telegram;
mod transaction_processor;
mod websocket;
Expand Down Expand Up @@ -41,14 +42,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
while let Some(message) = stream.next().await {
// Process transaction
let signer = Keypair::from_base58_string(&config.private_key);
let processor = transaction_processor::TransactionProcessor::new(&config.helius_rpc_url);
let processor =
transaction_processor::TransactionProcessor::new(config.clone(), &websocket, &notifier);

// let notifier = notifier.clone();
if let Err(e) = processor.process_transaction(&message, &signer).await {
eprintln!("Error processing transaction: {}", e);
notifier
.send_message(&format!("Error processing transaction: {}", e))
.await?;
}
}

Expand Down
138 changes: 138 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,141 @@ pub struct Value {
pub signature: Option<String>,
pub logs: Option<Vec<String>>,
}

pub type TrxDetailRes = Vec<TrxDetailRe>;

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct TrxDetailRe {
pub description: String,

#[serde(rename = "type")]
pub trx_detail_re_type: String,

pub source: String,

pub fee: i64,

pub fee_payer: String,

pub signature: String,

pub slot: i64,

pub timestamp: i64,

pub token_transfers: Vec<TokenTransfer>,

pub native_transfers: Vec<NativeTransfer>,

pub account_data: Vec<AccountDatum>,

pub transaction_error: Option<TransactionError>,
pub instructions: Vec<Instruction>,

pub events: Events,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct AccountDatum {
pub account: String,

pub native_balance_change: i64,

pub token_balance_changes: Vec<Option<serde_json::Value>>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Events {}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Instruction {
pub accounts: Option<Vec<String>>,

pub data: String,

pub program_id: String,

pub inner_instructions: Option<Vec<Instruction>>,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct NativeTransfer {
pub from_user_account: String,

pub to_user_account: String,

pub amount: i64,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct TokenTransfer {
pub from_token_account: String,

pub to_token_account: String,

pub from_user_account: String,

pub to_user_account: String,

pub token_amount: f64,

pub mint: String,

pub token_standard: String,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TransactionError {
pub instruction_error: Vec<InstructionErrorElement>,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum InstructionErrorElement {
InstructionErrorClass(InstructionErrorClass),

Integer(i64),
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct InstructionErrorClass {
pub custom: i64,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DisplayDataItem {
pub token_mint: String,
pub sol_mint: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RugCheckRes {
pub token_program: String,

pub token_type: String,

pub risks: Vec<RugCheckRisk>,

pub score: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RugCheckRisk {
pub name: String,

pub value: String,

pub description: String,

pub score: i64,

pub level: String,
}
76 changes: 76 additions & 0 deletions src/rug_checker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::{config::Config, model::RugCheckRes};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum RugCheckerError {
#[error("Connection error: {0}")]
RugCheckError(String),
}

pub struct RugChecker<'a> {
pub config: &'a Config,
}

impl<'a> RugChecker<'a> {
pub fn new(config: &'a Config) -> Self {
Self { config }
}

pub async fn isvalid_rug_check(&self, token_mint: &str) -> Result<bool, RugCheckerError> {
let client = reqwest::Client::new();
let url = format!(
"{}/tokens/{}/report/summary",
self.config.rug_checker_url, token_mint
);
let res = client
.get(url)
.header("Content-Type", "application/json")
.send()
.await;

match res {
Ok(data) => {
let status = data.status();
let response_txt = data.text().await;
println!("Status: {}", status);

match response_txt {
Ok(txt) => {
println!("Response: {}", txt);
let res_data = serde_json::from_str::<RugCheckRes>(&txt).unwrap();
let mut is_valid = true;
res_data.risks.iter().for_each(|risk| {
println!("Rug: {:?}", risk);
if risk.name.to_lowercase() == "single holder ownership" {
let value = risk.value.replace("%", "");
let numberic_value = value.parse::<f64>().unwrap();
if numberic_value > self.config.rug_check_config.signal_holder_ownership {
is_valid = false;
}
}
});
if !is_valid {
return Ok(false);
}

let res_data = res_data.clone();
let valid = !res_data.risks.iter().any(|r| {
self
.config
.rug_check_config
.not_allowed_risk
.contains(&r.name)
});
return Ok(valid);
}
Err(e) => {
return Err(RugCheckerError::RugCheckError(e.to_string()));
}
}
}
Err(e) => {
return Err(RugCheckerError::RugCheckError(e.to_string()));
}
};
}
}
Loading