Skip to content

Commit

Permalink
Merge pull request #10 from okx/wc/rpc-dev
Browse files Browse the repository at this point in the history
brc20 rpc
  • Loading branch information
cwbhhjl authored May 8, 2023
2 parents f3ee428 + ca5c235 commit 0e36a1f
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 12 deletions.
67 changes: 66 additions & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::brc20::ledger::Ledger;
use crate::brc20::ScriptKey;
use {
self::{
entry::{
Expand Down Expand Up @@ -939,7 +940,71 @@ impl Index {
Ok(bal)
}

pub(crate) fn brc20_get_tx_events_by_txid() {}
pub(crate) fn brc20_get_all_balance_by_address(
&self,
address: &bitcoin::Address,
) -> Result<Vec<(brc20::Tick, brc20::Balance)>> {
let wtx = self.database.begin_write().unwrap();
let brc20_db = crate::okx::BRC20Database::new(&wtx);
let all_balance = brc20_db.get_balances(&brc20::ScriptKey::from_address(address.clone()))?;
Ok(all_balance)
}

pub(crate) fn brc20_get_tx_events_by_txid(
&self,
txid: &bitcoin::Txid,
) -> Result<Vec<brc20::ActionReceipt>> {
let wtx = self.database.begin_write().unwrap();
let brc20_db = crate::okx::BRC20Database::new(&wtx);
let res = brc20_db.get_transaction_receipts(txid)?;
return Ok(res);
}

pub(crate) fn brc20_get_block_events_by_blockhash(
&self,
blockhash: bitcoin::BlockHash,
) -> Result<Vec<(bitcoin::Txid, Vec<brc20::ActionReceipt>)>> {
let wtx = self.database.begin_write().unwrap();
let brc20_db = crate::okx::BRC20Database::new(&wtx);

let block = self.client.get_block(&blockhash)?;
let mut result = Vec::new();

for tx in &block.txdata {
result.push((
tx.txid().clone(),
brc20_db.get_transaction_receipts(&tx.txid())?,
));
}

Ok(result)
}

pub(crate) fn brc20_get_tick_transferable_by_address(
&self,
tick: &str,
address: &bitcoin::Address,
) -> Result<Vec<brc20::TransferableLog>> {
let wtx = self.database.begin_write().unwrap();
let brc20_db = crate::okx::BRC20Database::new(&wtx);
let res = brc20_db.get_transferable_by_tick(
&ScriptKey::from_address(address.clone()),
&brc20::Tick::from_str(tick)?,
)?;

Ok(res)
}

pub(crate) fn brc20_get_all_transferable_by_address(
&self,
address: &bitcoin::Address,
) -> Result<Vec<brc20::TransferableLog>> {
let wtx = self.database.begin_write().unwrap();
let brc20_db = crate::okx::BRC20Database::new(&wtx);
let res = brc20_db.get_transferable(&ScriptKey::from_address(address.clone()))?;

Ok(res)
}
}

#[cfg(test)]
Expand Down
38 changes: 34 additions & 4 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ impl Server {
"/brc20/tick/:tick/address/:address/balance",
get(brc20_balance),
)
.route("/brc20/address/:address/balance", get(brc20_all_balance))
.route(
"/brc20/tick/:tick/address/:address/transferable",
get(brc20_transferable),
)
.route(
"/brc20/address/:address/transferable",
get(brc20_all_transferable),
)
.route("/brc20/tx/:txid", get(brc20_tx_events))
.route("/brc20/block/:block_hash", get(brc20_block_events))
.layer(Extension(index))
Expand Down Expand Up @@ -2515,12 +2524,33 @@ mod tests {
fn brc20_endpoint() {
let test_server = TestServer::new();

let response = test_server.get("/brc20/tick/🍎");
for url in [
"/brc20/tick/🍎",
"/brc20/tick/ordi/address/bc1pjdmfs5lvqfl6qmzpc0e4ewfdgfmdyz2t79scrsaz8ep98374wwnsywz7t4/balance",
"/brc20/address/bc1pjdmfs5lvqfl6qmzpc0e4ewfdgfmdyz2t79scrsaz8ep98374wwnsywz7t4/balance",
"/brc20/tx/b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735",
"/brc20/tick/ordi/address/bc1pjdmfs5lvqfl6qmzpc0e4ewfdgfmdyz2t79scrsaz8ep98374wwnsywz7t4/transferable",
"/brc20/block/00000000000000000003a337a676b0101f3f7ef7dcbc01debb69f85c6da04dcf",
"/brc20/address/bc1pjdmfs5lvqfl6qmzpc0e4ewfdgfmdyz2t79scrsaz8ep98374wwnsywz7t4/transferable"
] {

println!("{:?}", response.status());
println!("{}", url);

let json_text = response.text().unwrap();
let response = test_server.get(url);

println!("{:?}", response.status());

let json_text = response.text().unwrap();

println!("{}", json_text);
}

println!("{}", json_text);
// let response = test_server.get("/brc20/tick/🍎");
//
// println!("{:?}", response.status());
//
// let json_text = response.text().unwrap();
//
// println!("{}", json_text);
}
}
199 changes: 192 additions & 7 deletions src/subcommand/server/api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::error::ApiError;
use super::*;
use crate::brc20::ActionReceipt;
use axum::Json;

const ERR_TICK_LENGTH: &str = "tick must be 4 bytes length";
Expand Down Expand Up @@ -28,13 +29,78 @@ pub struct Balance {
pub overall_balance: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AllBalance {
pub balance: Vec<Balance>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TxEvents {
pub events: Vec<TxEvent>,
pub txid: String,
}

impl From<&brc20::ActionReceipt> for TxEvent {
fn from(event: &ActionReceipt) -> Self {
match &event.result {
Ok(result) => match result {
brc20::BRC20Event::Deploy(deploy_event) => Self::Deploy(DeployEvent {
tick: std::str::from_utf8(deploy_event.tick.as_bytes())
.unwrap()
.to_string(),
inscription_id: event.inscription_id.to_string(),
supply: deploy_event.supply.to_string(),
limit_per_mint: deploy_event.limit_per_mint.to_string(),
decimal: deploy_event.decimal as u64,
deploy_by: deploy_event.deploy.to_string(),
valid: true,
msg: "ok".to_string(),
}),
brc20::BRC20Event::Mint(mint_event) => Self::Mint(MintEvent {
tick: std::str::from_utf8(mint_event.tick.as_bytes())
.unwrap()
.to_string(),
inscription_id: event.inscription_id.to_string(),
amount: mint_event.amount.to_string(),
to: mint_event.to.to_string(),
valid: true,
msg: "ok".to_string(),
}),
brc20::BRC20Event::TransferPhase1(trans1) => {
Self::InscribeTransfer(InscribeTransferEvent {
tick: std::str::from_utf8(trans1.tick.as_bytes())
.unwrap()
.to_string(),
inscription_id: event.inscription_id.to_string(),
amount: trans1.amount.to_string(),
owner: trans1.owner.to_string(),
valid: true,
msg: "ok".to_string(),
})
}
brc20::BRC20Event::TransferPhase2(trans2) => Self::Transfer(TransferEvent {
tick: std::str::from_utf8(trans2.tick.as_bytes())
.unwrap()
.to_string(),
inscription_id: event.inscription_id.to_string(),
amount: trans2.amount.to_string(),
from: trans2.from.to_string(),
to: trans2.to.to_string(),
valid: true,
msg: "ok".to_string(),
}),
},
Err(err) => Self::Error(ErrorEvent {
inscription_id: event.inscription_id.to_string(),
valid: false,
msg: err.to_string(),
}),
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event")]
#[serde(rename_all = "camelCase")]
Expand All @@ -43,6 +109,15 @@ pub enum TxEvent {
Mint(MintEvent),
InscribeTransfer(InscribeTransferEvent),
Transfer(TransferEvent),
Error(ErrorEvent),
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorEvent {
pub inscription_id: String,
pub valid: bool,
pub msg: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -84,6 +159,7 @@ pub struct InscribeTransferEvent {
#[serde(rename_all = "camelCase")]
pub struct TransferEvent {
pub tick: String,
pub inscription_id: String,
pub amount: String,
pub from: String,
pub to: String,
Expand All @@ -108,6 +184,7 @@ pub struct TransferableInscriptions {
pub struct TransferableInscription {
pub id: String,
pub amount: String,
pub tick: String,
}

pub(crate) async fn brc20_tick_info(
Expand All @@ -132,9 +209,9 @@ pub(crate) async fn brc20_tick_info(

let tick_info = tick_info.unwrap();

// if tick_info.tick != tick {
// return Json(ApiResponse::api_err(&ApiError::internal("db: not match")));
// }
if tick_info.tick != brc20::Tick::from_str(&tick).unwrap() {
return Json(ApiResponse::api_err(&ApiError::internal("db: not match")));
}

Json(ApiResponse::ok(TickInfo {
tick,
Expand Down Expand Up @@ -192,23 +269,131 @@ pub(crate) async fn brc20_balance(
}))
}

pub(crate) async fn brc20_all_balance(
Extension(index): Extension<Arc<Index>>,
Path(address): Path<String>,
) -> Json<ApiResponse<AllBalance>> {
let address: bitcoin::Address = match address.parse() {
Ok(address) => address,
Err(err) => return Json(ApiResponse::api_err(&ApiError::BadRequest(err.to_string()))),
};

let all_balance = match index.brc20_get_all_balance_by_address(&address) {
Ok(balance) => balance,
Err(err) => return Json(ApiResponse::api_err(&ApiError::Internal(err.to_string()))),
};

Json(ApiResponse::ok(AllBalance {
balance: all_balance
.iter()
.map(|(tick, bal)| Balance {
tick: std::str::from_utf8(tick.as_bytes()).unwrap().to_string(),
available_balance: (bal.overall_balance - bal.transferable_balance).to_string(),
transferable_balance: bal.transferable_balance.to_string(),
overall_balance: bal.overall_balance.to_string(),
})
.collect(),
}))
}

pub(crate) async fn brc20_tx_events(
Extension(index): Extension<Arc<Index>>,
Path(txid): Path<String>,
) -> Json<ApiResponse<TxEvents>> {
todo!();
let txid = match bitcoin::Txid::from_str(&txid) {
Ok(txid) => txid,
Err(err) => return Json(ApiResponse::api_err(&ApiError::BadRequest(err.to_string()))),
};
let tx_events = match index.brc20_get_tx_events_by_txid(&txid) {
Ok(tx_events) => tx_events,
Err(err) => return Json(ApiResponse::api_err(&ApiError::Internal(err.to_string()))),
};
Json(ApiResponse::ok(TxEvents {
txid: txid.to_string(),
events: tx_events.iter().map(|event| event.into()).collect(),
}))
}

pub(crate) async fn brc20_block_events(
Extension(index): Extension<Arc<Index>>,
Path(block_hash): Path<String>,
) -> Json<ApiResponse<BlockEvents>> {
todo!();
let blockhash = match bitcoin::BlockHash::from_str(&block_hash) {
Ok(blockhash) => blockhash,
Err(err) => return Json(ApiResponse::api_err(&ApiError::BadRequest(err.to_string()))),
};

let block_events = match index.brc20_get_block_events_by_blockhash(blockhash) {
Ok(block_events) => block_events,
Err(err) => return Json(ApiResponse::api_err(&ApiError::Internal(err.to_string()))),
};

Json(ApiResponse::ok(BlockEvents {
block: block_events
.iter()
.map(|(txid, events)| TxEvents {
txid: txid.to_string(),
events: events.iter().map(|event| event.into()).collect(),
})
.collect(),
}))
}

pub(crate) async fn brc20_transferable(
Extension(index): Extension<Arc<Index>>,
Path((tick, addr)): Path<(String, String)>,
Path((tick, address)): Path<(String, String)>,
) -> Json<ApiResponse<TransferableInscriptions>> {
todo!();
if tick.as_bytes().len() != 4 {
return Json(ApiResponse::api_err(&ApiError::BadRequest(
ERR_TICK_LENGTH.to_string(),
)));
}
let tick = tick.to_lowercase();

let address: bitcoin::Address = match address.parse() {
Ok(address) => address,
Err(err) => return Json(ApiResponse::api_err(&ApiError::BadRequest(err.to_string()))),
};

let transferable = match index.brc20_get_tick_transferable_by_address(&tick, &address) {
Ok(balance) => balance,
Err(err) => return Json(ApiResponse::api_err(&ApiError::Internal(err.to_string()))),
};

Json(ApiResponse::ok(TransferableInscriptions {
inscriptions: transferable
.iter()
.map(|i| TransferableInscription {
id: i.inscription_id.to_string(),
amount: i.amount.to_string(),
tick: tick.clone(),
})
.collect(),
}))
}

pub(crate) async fn brc20_all_transferable(
Extension(index): Extension<Arc<Index>>,
Path(address): Path<String>,
) -> Json<ApiResponse<TransferableInscriptions>> {
let address: bitcoin::Address = match address.parse() {
Ok(address) => address,
Err(err) => return Json(ApiResponse::api_err(&ApiError::BadRequest(err.to_string()))),
};

let transferable = match index.brc20_get_all_transferable_by_address(&address) {
Ok(balance) => balance,
Err(err) => return Json(ApiResponse::api_err(&ApiError::Internal(err.to_string()))),
};

Json(ApiResponse::ok(TransferableInscriptions {
inscriptions: transferable
.iter()
.map(|i| TransferableInscription {
id: i.inscription_id.to_string(),
amount: i.amount.to_string(),
tick: std::str::from_utf8(i.tick.as_bytes()).unwrap().to_string(),
})
.collect(),
}))
}

0 comments on commit 0e36a1f

Please sign in to comment.