-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
af5d868
commit 6b9b354
Showing
13 changed files
with
4,181 additions
and
244 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,42 @@ | ||
# Evil | ||
|
||
用来跟踪一些邪恶的钱包地址,并记录他的持仓。 | ||
用来跟踪一些邪恶的钱包地址,并记录他的持仓。 | ||
|
||
## 使用 | ||
|
||
```bash | ||
# 1. 安装sqlx | ||
cargo install sqlx-cli --no-default-features --features postgres | ||
# 2. 创建数据库 | ||
mkdir data | ||
touch data/db.sqlite3 | ||
cp env.example .env | ||
sqlx database create | ||
sqlx migrate run | ||
# 3. 编译 | ||
cargo build --release | ||
# 4. 运行 | ||
cp app.example.toml app.toml | ||
## 4.1 起web服务 | ||
./target/release/angel -c app.toml web | ||
## 4.2 启动跟踪 | ||
./target/release/angel -c app.toml daemon | ||
``` | ||
|
||
添加一个地址 | ||
|
||
```bash | ||
curl "http://127.0.0.1:2211/api/v1/add_account?address=9xHxgDbeQDX51Vof7ruAaYjSYgR87BXRp3ZC62jrmJV1" | ||
{"msg":"ok","data":null} | ||
``` | ||
|
||
查询一个币地址 | ||
|
||
```bash | ||
# 有币 | ||
curl "http://127.0.0.1:2211/api/v1/get_coin?token=APAkdwfAyqFsQuD92hURMnfUE2dKkjaZjbttx3oZfniy" | ||
{"msg":"ok","data":{"id":528,"account":"9xHxgDbeQDX51Vof7ruAaYjSYgR87BXRp3ZC62jrmJV1","token":"APAkdwfAyqFsQuD92hURMnfUE2dKkjaZjbttx3oZfniy","created_at":1733293394,"deleted":0}} | ||
# 没币 | ||
curl "http://127.0.0.1:2211/api/v1/get_coin?token=APAkdwfAyqFsQuD92hURMnfUE2dKkjaZjbttx3oZfn1y" | ||
{"msg":"ok","data":null} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use angel::solana_rpc::get_tokens_with_account; | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
let account = "JDLGDgY7jSGkmmRPzQcYtwLQpkrqMgYqY7cFkNb81NTq"; // 当前要有持仓才行 | ||
let solana_rpc_url = "https://api.mainnet-beta.solana.com"; | ||
match get_tokens_with_account(account, solana_rpc_url).await { | ||
Ok(tokens) => { | ||
for token in tokens { | ||
println!("111 {:?}", token); | ||
} | ||
} | ||
Err(e) => { | ||
println!("111 {:?}", e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use std::time::Duration; | ||
|
||
use crate::solana_rpc::get_tokens_with_account; | ||
use tokio::time::sleep; | ||
use tracing::{error, info}; | ||
|
||
use crate::{config::get_global_config, models::get_global_manager}; | ||
|
||
pub async fn daemon() { | ||
// loop and interval | ||
let c = get_global_config().await; | ||
let manager = get_global_manager().await; | ||
info!("daemon start"); | ||
loop { | ||
info!("daemon loop, sleep {}s", c.solana_rpc_curl_interval); | ||
sleep(Duration::from_secs(c.solana_rpc_curl_interval)).await; | ||
// get evil accounts | ||
match manager.get_all_accounts().await { | ||
Ok(accounts) => { | ||
for account in accounts { | ||
match get_tokens_with_account(&account.account, &c.solana_rpc_url).await { | ||
Ok(tokens) => { | ||
for token in &tokens { | ||
if let Err(e) = | ||
manager.add_new_coin(&account.account, &token.mint).await | ||
{ | ||
error!( | ||
"add new coin error: {:?}, account: {}, token: {}", | ||
e, &account.account, &token.mint | ||
); | ||
continue; | ||
} | ||
} | ||
} | ||
Err(e) => { | ||
error!("get tokens with account error: {:?}", e); | ||
} | ||
} | ||
} | ||
} | ||
Err(e) => { | ||
error!("get all accounts error: {:?}", e); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use anyhow::Result; | ||
use serde::{Deserialize, Serialize}; | ||
use solana_account_decoder::UiAccountData; | ||
use solana_client::rpc_client::RpcClient; | ||
use solana_sdk::pubkey::Pubkey; | ||
|
||
pub type TokenAccounts = Vec<TokenAccount>; | ||
#[derive(Debug, Serialize, Deserialize, Clone)] | ||
pub struct TokenAccount { | ||
pub pubkey: String, | ||
pub mint: String, | ||
pub amount: String, | ||
pub ui_amount: f64, | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct Amount { | ||
amount: String, | ||
decimals: u8, | ||
ui_amount: f64, | ||
ui_amount_string: String, | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct TokenInfo { | ||
is_native: bool, | ||
mint: String, | ||
owner: String, | ||
state: String, | ||
token_amount: Amount, | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
struct Parsed { | ||
info: TokenInfo, | ||
#[serde(rename = "type")] | ||
account_type: String, | ||
} | ||
|
||
// rpc https://solana.com/docs/rpc/http/gettokenaccountsbyowner | ||
pub async fn get_tokens_with_account(account: &str, rpc_url: &str) -> Result<Vec<TokenAccount>> { | ||
let client = RpcClient::new(rpc_url); | ||
|
||
let account_pubkey = Pubkey::from_str_const(account); | ||
let token_accounts = client.get_token_accounts_by_owner( | ||
&account_pubkey, | ||
solana_client::rpc_request::TokenAccountsFilter::ProgramId(spl_token::id()), | ||
)?; | ||
|
||
let mut accounts: TokenAccounts = vec![]; | ||
for token_account in token_accounts { | ||
let account_data = token_account.account.data; | ||
match account_data { | ||
UiAccountData::Json(parsed_account) => { | ||
let parsed: Parsed = serde_json::from_value(parsed_account.parsed)?; | ||
accounts.push(TokenAccount { | ||
pubkey: token_account.pubkey.to_string(), | ||
mint: parsed.info.mint, | ||
amount: parsed.info.token_amount.amount, | ||
ui_amount: parsed.info.token_amount.ui_amount, | ||
}); | ||
} | ||
UiAccountData::LegacyBinary(_) | UiAccountData::Binary(_, _) => { | ||
continue; | ||
} | ||
} | ||
} | ||
Ok(accounts) | ||
} |
Oops, something went wrong.