Error occured during inserting data about Ethereum block data using tokio-postgres.
the original code was:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv().ok();
let api_key = env::var("ALCHEMY_API_KEY")?;
let db_url = env::var("DATABASE_URL")?;
// Connect to PostgreSQL
let (client, connection) = tokio_postgres::connect(&db_url, NoTls).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
// Fetch Ethereum block data
let client_http = Client::new();
let url = format!("https://eth-mainnet.alchemyapi.io/v2/{}", api_key);
let payload = json!({
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": ["latest", true],
"id": 1
});
let response = client_http.post(&url).json(&payload).send().await?;
let response_json: serde_json::Value = response.json().await?;
if let Some(transactions) = response_json["result"]["transactions"].as_array() {
for tx in transactions {
let tx_data = Transaction {
hash: tx["hash"].as_str().unwrap_or_default().to_string(),
from: tx["from"].as_str().unwrap_or_default().to_string(),
to: tx["to"].as_str().unwrap_or_default().to_string(),
value: tx["value"].as_str().unwrap_or_default().to_string(),
gasPrice: tx["gasPrice"].as_str().unwrap_or_default().to_string(),
};
// Save to PostgreSQL
client.execute(
"INSERT INTO transactions (tx_hash, from_address, to_address, value, gas_price)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (tx_hash) DO NOTHING",
&[
&tx_data.hash,
&tx_data.from,
&tx_data.to,
&tx_data.value,
&tx_data.gasPrice,
],
).await?;
}
}
println!("Completed saving transaction!");
Ok(())
}
but the table schema is
transactions (
id SERIAL primary key,
tx_hash text unique not null,
from_address text not null,
to_address text not null,
value numeric not null,
gas_price numeric not null,
timestamp timestamp default now()
)
the data type mismatched(Error Code: E0277). So I tried to change type to i64, but same error occured.
the data type needed to be changed to Decimal to use Numerical type in PostgreSQL, so I tried below:
// ... existing code ...
use rust_decimal::Decimal; // Add this import
struct Transaction {
// ... existing code ...
gas_price: Decimal, // Changed from i64 to Decimal and fixed snake_case
// ... existing code ...
}
// ... existing code ...
// ... existing code ...
[dependencies]
rust_decimal = { version = "1.32", features = ["postgres"] }
// ... existing code ...
// ... existing code ...
value: tx["value"].as_str().unwrap_or_default().parse::<i64>().unwrap_or_default().into(),
gas_price: tx["gasPrice"].as_str().unwrap_or_default().parse::<i64>().unwrap_or_default().into(),
// ... existing code ...
whole src code:
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::env;
use dotenv::dotenv;
use tokio_postgres::{NoTls, Error};
use rust_decimal::Decimal;
#[derive(Debug, Serialize, Deserialize)]
struct Transaction {
hash: String,
from: String,
to: String,
value: Decimal,
gas_price: Decimal,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv().ok();
let api_key = env::var("ALCHEMY_API_KEY")?;
let db_url = env::var("DATABASE_URL")?;
// Connect to PostgreSQL
let (client, connection) = tokio_postgres::connect(&db_url, NoTls).await?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
// Fetch Ethereum block data
let client_http = Client::new();
let url = format!("https://eth-mainnet.alchemyapi.io/v2/{}", api_key);
let payload = json!({
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": ["latest", true],
"id": 1
});
let response = client_http.post(&url).json(&payload).send().await?;
let response_json: serde_json::Value = response.json().await?;
if let Some(transactions) = response_json["result"]["transactions"].as_array() {
for tx in transactions {
let tx_data = Transaction {
hash: tx["hash"].as_str().unwrap_or_default().to_string(),
from: tx["from"].as_str().unwrap_or_default().to_string(),
to: tx["to"].as_str().unwrap_or_default().to_string(),
value: tx["value"].as_str().unwrap_or_default().parse::<i64>().unwrap_or_default().into(),
gas_price: tx["gasPrice"].as_str().unwrap_or_default().parse::<i64>().unwrap_or_default().into(),
};
// Save to PostgreSQL
client.execute(
"INSERT INTO transactions (tx_hash, from_address, to_address, value, gas_price)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (tx_hash) DO NOTHING",
&[
&tx_data.hash,
&tx_data.from,
&tx_data.to,
&tx_data.value,
&tx_data.gas_price,
],
).await?;
}
}
println!("Completed saving transaction!");
Ok(())
}
result from PostgreSQL
(used sql: SELECT * FROM transactions ORDER BY timestamp DESC LIMIT 5;)
