-
Notifications
You must be signed in to change notification settings - Fork 3
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
e18ee50
commit 2214252
Showing
9 changed files
with
909 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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
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,61 @@ | ||
use axum::extract::{Path, State}; | ||
use axum::routing::get; | ||
use axum::Json; | ||
use everscale_types::models::StdAddr; | ||
|
||
use super::error::Error; | ||
use super::responses::{status_to_string, GetAccountResponse}; | ||
use crate::endpoint::tonapi::error::Result; | ||
use crate::state::LoadedAccountState; | ||
use crate::RpcState; | ||
|
||
pub fn router() -> axum::Router<RpcState> { | ||
axum::Router::new().route("/accounts/:account", get(get_account)) | ||
} | ||
|
||
async fn get_account( | ||
Path(address): Path<StdAddr>, | ||
State(state): State<RpcState>, | ||
) -> Result<Json<GetAccountResponse>> { | ||
let item = match state.get_account_state(&address) { | ||
Ok(item) => item, | ||
Err(e) => return Err(Error::AnyhowText(e.to_string())), | ||
}; | ||
|
||
match &item { | ||
&LoadedAccountState::NotFound { .. } => Err(Error::NotFound("account not found")), | ||
LoadedAccountState::Found { state, .. } => { | ||
match state.load_account() { | ||
Ok(Some(loaded)) => { | ||
Ok(Json(GetAccountResponse { | ||
address: address.to_string(), | ||
balance: loaded.balance.tokens.into_inner() as u64, | ||
extra_balance: None, // TODO: fill with correct extra balance | ||
status: status_to_string(loaded.state.status()), | ||
currencies_balance: Some( | ||
loaded | ||
.balance | ||
.other | ||
.as_dict() | ||
.iter() | ||
.filter_map(|res| res.ok()) | ||
.map(|(id, balance)| (id, balance.as_usize() as u64)) | ||
.collect(), | ||
), | ||
last_activity: loaded.last_trans_lt, | ||
interfaces: None, // TODO: fill with correct interfaces, | ||
name: None, // TODO: fill with correct name, | ||
is_scam: None, // TODO: fill with correct is_scam, | ||
icon: None, // TODO: fill with correct icon, | ||
memo_required: None, // TODO: fill with correct memo_required, | ||
get_methods: vec![], // TODO: fill with correct get_methods, | ||
is_suspended: None, // TODO: fill with correct is_suspended, | ||
is_wallet: true, // TODO: fill with correct is_wallet, | ||
})) | ||
} | ||
Ok(None) => Err(Error::NotFound("account not found")), | ||
Err(e) => Err(Error::AnyhowText(e.to_string())), | ||
} | ||
} | ||
} | ||
} |
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,246 @@ | ||
use axum::extract::{Path, Query, State}; | ||
use axum::routing::{get, post}; | ||
use axum::Json; | ||
use everscale_types::boc::Boc; | ||
use everscale_types::models::{ | ||
AccountState, ComputePhase, MsgInfo, OwnedMessage, StateInit, StdAddr, Transaction, TxInfo, | ||
}; | ||
|
||
use super::error::Error; | ||
use super::requests::{ExecMethodArgs, Pagination, SendMessageRequest}; | ||
use super::responses::{ | ||
bounce_phase_to_string, status_to_string, AccountResponse, ExecGetMethodResponse, | ||
LibraryResponse, TransactionAccountResponse, TransactionResponse, TransactionsResponse, | ||
}; | ||
use crate::endpoint::tonapi::error::Result; | ||
use crate::state::LoadedAccountState; | ||
use crate::RpcState; | ||
|
||
pub fn router() -> axum::Router<RpcState> { | ||
axum::Router::new() | ||
.route("/accounts/:account", get(get_blockchain_raw_account)) | ||
.route( | ||
"/accounts/:account/transactions", | ||
get(get_blockchain_account_transactions), | ||
) | ||
.route( | ||
"/accounts/:account/methods/:method_name", | ||
get(exec_get_method_for_blockchain_account), | ||
) | ||
.route("/message", post(send_blockchain_message)) | ||
} | ||
|
||
async fn get_blockchain_raw_account( | ||
Path(address): Path<StdAddr>, | ||
State(state): State<RpcState>, | ||
) -> Result<Json<AccountResponse>> { | ||
let item = state.get_account_state(&address)?; | ||
|
||
match &item { | ||
&LoadedAccountState::NotFound { .. } => Err(Error::NotFound("account not found")), | ||
LoadedAccountState::Found { state, .. } => { | ||
let account = state.load_account()?; | ||
match account { | ||
Some(loaded) => { | ||
let status = loaded.state.status(); | ||
let (code, data, libraries, frozen_hash) = match loaded.state { | ||
AccountState::Active(StateInit { | ||
code, | ||
data, | ||
libraries, | ||
.. | ||
}) => ( | ||
code.map(Boc::encode_hex), | ||
data.map(Boc::encode_hex), | ||
Some( | ||
libraries | ||
.iter() | ||
.filter_map(|res| res.ok()) | ||
.map(|(_, lib)| LibraryResponse { | ||
public: lib.public, | ||
root: Boc::encode_hex(lib.root), | ||
}) | ||
.collect(), | ||
), | ||
None, | ||
), | ||
AccountState::Uninit => (None, None, None, None), | ||
AccountState::Frozen(hash_bytes) => { | ||
(None, None, None, Some(hash_bytes.to_string())) | ||
} | ||
}; | ||
|
||
Ok(Json(AccountResponse { | ||
address: address.to_string(), | ||
balance: loaded.balance.tokens.into_inner() as u64, | ||
extra_balance: None, // TODO: fill with correct extra balance | ||
status: status_to_string(status), | ||
code, | ||
data, | ||
last_transaction_lt: loaded.last_trans_lt, | ||
last_transaction_hash: Some(state.last_trans_hash.to_string()), | ||
frozen_hash, | ||
libraries, | ||
storage: loaded.storage_stat.into(), | ||
})) | ||
} | ||
None => Err(Error::NotFound("account not found")), | ||
} | ||
} | ||
} | ||
} | ||
|
||
async fn get_blockchain_account_transactions( | ||
Path(address): Path<StdAddr>, | ||
Query(pagination): Query<Pagination>, | ||
State(state): State<RpcState>, | ||
) -> Result<Json<TransactionsResponse>> { | ||
let limit = pagination.limit.unwrap_or(TransactionsResponse::MAX_LIMIT); | ||
|
||
if limit == 0 { | ||
return Ok(Json(TransactionsResponse { | ||
transactions: vec![], | ||
})); | ||
} | ||
|
||
if limit > TransactionsResponse::MAX_LIMIT { | ||
return Err(Error::BadRequest("limit is too large")); | ||
} | ||
|
||
let list = state.get_transactions(&address, pagination.after_lt)?; | ||
let transactions = list.map(|item| { | ||
match Boc::decode(item) { | ||
Ok(root) => { | ||
let hash = *root.repr_hash(); | ||
let t = root.parse::<Transaction>()?; | ||
|
||
let in_msg = if let Some(in_msg) = &t.in_msg { | ||
let hash = *in_msg.repr_hash(); | ||
let in_msg = in_msg.parse::<OwnedMessage>()?; | ||
Some((in_msg.info,Some(in_msg.body.0), hash).into()) | ||
} else { | ||
None | ||
}; | ||
|
||
let mut out_msgs = vec![]; | ||
|
||
for out_msg_cell in t.out_msgs.values() { | ||
let out_msg_cell = out_msg_cell?; | ||
let out_msg_hash = *out_msg_cell.repr_hash(); | ||
let out_msg_info = out_msg_cell.parse::<MsgInfo>()?; | ||
out_msgs.push((out_msg_info, None, out_msg_hash).into()); | ||
} | ||
|
||
let info = t.load_info().map_err(|e| Error::AnyhowText(e.to_string()))?; | ||
|
||
let transaction_type; | ||
let compute_phase; | ||
let credit_phase; | ||
let storage_phase; | ||
let action_phase; | ||
let aborted; | ||
let bounce_phase; | ||
let destroyed; | ||
let mut success = true; | ||
|
||
match info { | ||
TxInfo::Ordinary(ordinary_tx_info) => { | ||
transaction_type = "TransOrd".to_string(); | ||
|
||
compute_phase = match ordinary_tx_info.compute_phase { | ||
ComputePhase::Skipped(skipped_compute_phase) => { | ||
Some(skipped_compute_phase.into()) | ||
}, | ||
ComputePhase::Executed(executed_compute_phase) => { | ||
success = executed_compute_phase.success; | ||
Some(executed_compute_phase.into()) | ||
}, | ||
}; | ||
storage_phase = ordinary_tx_info.storage_phase.map(From::from); | ||
credit_phase = ordinary_tx_info.credit_phase.map(From::from); | ||
action_phase = ordinary_tx_info.action_phase.map(From::from); | ||
aborted = ordinary_tx_info.aborted; | ||
bounce_phase = ordinary_tx_info.bounce_phase.map(bounce_phase_to_string); | ||
destroyed = ordinary_tx_info.destroyed; | ||
}, | ||
TxInfo::TickTock(tick_tock_tx_info) => { | ||
transaction_type = "TransTickTock".to_string(); | ||
compute_phase = match tick_tock_tx_info.compute_phase { | ||
ComputePhase::Skipped(skipped_compute_phase) => { | ||
Some(skipped_compute_phase.into()) | ||
}, | ||
ComputePhase::Executed(executed_compute_phase) => { | ||
success = executed_compute_phase.success; | ||
Some(executed_compute_phase.into()) | ||
}, | ||
}; | ||
storage_phase = Some(tick_tock_tx_info.storage_phase.into()); | ||
credit_phase = None; | ||
action_phase = tick_tock_tx_info.action_phase.map(From::from); | ||
aborted = tick_tock_tx_info.aborted; | ||
bounce_phase = None; | ||
destroyed = tick_tock_tx_info.destroyed; | ||
}, | ||
} | ||
|
||
let state_update = t.state_update.load()?; | ||
|
||
Ok(TransactionResponse { | ||
hash: hash.to_string(), | ||
lt: t.lt, | ||
account: TransactionAccountResponse { | ||
address: address.to_string(), | ||
name: None, | ||
is_scam: false, // TODO: fill with correct is_scam | ||
icon: None, | ||
is_wallet: true,// TODO: fill with correct is_wallet | ||
}, | ||
success, | ||
utime: t.now, | ||
orig_status: status_to_string(t.orig_status), | ||
end_status: status_to_string(t.end_status), | ||
total_fees: t.total_fees.tokens.into_inner() as u64, | ||
end_balance: 0, // TODO: fill with correct end_balance | ||
transaction_type, | ||
state_update_old: state_update.old.to_string(), | ||
state_update_new: state_update.new.to_string(), | ||
in_msg, | ||
out_msgs, | ||
block: "(-1,4234234,8000000000000000)".to_string(), // TODO: fill with correct block | ||
prev_trans_hash: Some(t.prev_trans_hash.to_string()), | ||
prev_trans_lt: Some(t.prev_trans_lt), | ||
compute_phase, | ||
storage_phase, | ||
credit_phase, | ||
action_phase, | ||
bounce_phase, | ||
aborted, | ||
destroyed, | ||
raw: Boc::encode_hex( root), | ||
}) | ||
}, | ||
Err(e) => Err(Error::AnyhowText(e.to_string())), | ||
} | ||
}) | ||
.take(limit as _) | ||
.collect::<Result<Vec<TransactionResponse>, _>>()?; | ||
|
||
Ok(Json(TransactionsResponse { transactions })) | ||
} | ||
|
||
async fn exec_get_method_for_blockchain_account( | ||
Path((_address, _method_name)): Path<(StdAddr, String)>, | ||
Query(_args): Query<ExecMethodArgs>, | ||
State(_state): State<RpcState>, | ||
) -> Result<Json<ExecGetMethodResponse>> { | ||
Err(Error::BadRequest("Unimplemented")) | ||
} | ||
|
||
async fn send_blockchain_message( | ||
State(state): State<RpcState>, | ||
Json(input): Json<SendMessageRequest>, | ||
) -> Result<Json<()>> { | ||
let data = hex::decode(input.boc)?; | ||
state.broadcast_external_message(&data).await; | ||
Ok(Json(())) | ||
} |
Oops, something went wrong.