Skip to content

Commit f91fa87

Browse files
feat: Add near-intents support to liquidator bot
1 parent c9f914a commit f91fa87

File tree

9 files changed

+434
-72
lines changed

9 files changed

+434
-72
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ near-sdk = { version = "5.7", features = ["unstable"] }
2727
near-sdk-contract-tools = { git = "https://github.com/near/near-sdk-contract-tools", branch = "140-nep-245-multitoken-component" }
2828
near-workspaces = { version = "0.16", features = ["unstable"] }
2929
primitive-types = { version = "0.10.1" }
30+
reqwest = "0.12.12"
3031
rstest = { version = "0.24" }
3132
schemars = { version = "0.8" }
3233
templar-common = { path = "./common" }

bots/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ near-jsonrpc-client = { workspace = true }
2424
near-jsonrpc-primitives = { workspace = true }
2525
near-primitives = { workspace = true }
2626
near-sdk = { workspace = true }
27+
reqwest = { workspace = true }
2728
templar-common = { workspace = true }
2829
thiserror = { workspace = true }
2930
tokio = { workspace = true }

bots/src/bin/liquidator-bot.rs

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use near_sdk::{serde_json::json, AccountId};
1212
use templar_bots::{
1313
liquidator::{Args, Liquidator, LiquidatorError, LiquidatorResult},
1414
near::{view, RpcResult},
15-
swap::{RheaSwap, SwapType},
15+
swap::{IntentsSwap, RheaSwap, Swap, SwapType},
1616
};
1717
use tokio::time::sleep;
1818
use tracing::{info, instrument};
@@ -73,30 +73,17 @@ pub async fn list_all_deployments(
7373
Ok(all_markets)
7474
}
7575

76-
#[tokio::main]
77-
async fn main() -> LiquidatorResult {
78-
tracing_subscriber::registry()
79-
.with(fmt::layer())
80-
.with(EnvFilter::from_default_env())
81-
.init();
82-
83-
let args = Args::parse();
84-
let client = JsonRpcClient::connect(args.network.rpc_url());
85-
let signer = Arc::new(InMemorySigner::from_secret_key(
86-
args.signer_account.clone(),
87-
args.signer_key.clone(),
88-
));
89-
let swap = match args.swap {
90-
SwapType::RheaSwap => Arc::new(RheaSwap::new(
91-
args.swap.account_id(args.network),
92-
client.clone(),
93-
signer.clone(),
94-
)),
95-
};
96-
let asset = Arc::new(args.asset);
97-
76+
#[instrument(skip(client, asset, swap), level = "debug")]
77+
async fn run_bot<S: Swap>(
78+
client: JsonRpcClient,
79+
signer: Arc<InMemorySigner>,
80+
asset: Arc<AccountId>,
81+
swap: Arc<S>,
82+
args: &Args,
83+
) -> LiquidatorResult {
9884
let registry_refresh_interval = Duration::from_secs(args.registry_refresh_interval);
9985
let mut next_refresh = Instant::now();
86+
10087
let mut markets = HashMap::<AccountId, Liquidator<_>>::new();
10188

10289
loop {
@@ -139,3 +126,42 @@ async fn main() -> LiquidatorResult {
139126
sleep(Duration::from_secs(args.interval)).await;
140127
}
141128
}
129+
130+
#[tokio::main]
131+
async fn main() -> LiquidatorResult {
132+
tracing_subscriber::registry()
133+
.with(fmt::layer())
134+
.with(EnvFilter::from_default_env())
135+
.init();
136+
137+
let args = Args::parse();
138+
let client = JsonRpcClient::connect(args.network.rpc_url());
139+
let signer = Arc::new(InMemorySigner::from_secret_key(
140+
args.signer_account.clone(),
141+
args.signer_key.clone(),
142+
));
143+
let asset = Arc::new(args.asset.clone());
144+
145+
match args.swap {
146+
SwapType::RheaSwap => {
147+
run_bot(
148+
client.clone(),
149+
signer.clone(),
150+
asset,
151+
Arc::new(RheaSwap::new(args.network, client, signer)),
152+
&args,
153+
)
154+
.await
155+
}
156+
SwapType::NearIntents => {
157+
run_bot(
158+
client.clone(),
159+
signer.clone(),
160+
asset,
161+
Arc::new(IntentsSwap::new(args.network, client, signer)),
162+
&args,
163+
)
164+
.await
165+
}
166+
}
167+
}

bots/src/liquidator.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use tracing::{error, info, instrument, warn};
2323

2424
use crate::{
2525
near::{get_access_key_data, send_tx, serialize_and_encode, view, RpcError},
26-
swap::{Swap, SwapType},
26+
swap::{QuoteOutput, Swap, SwapType},
2727
BorrowPositions, Network, DEFAULT_GAS,
2828
};
2929

@@ -240,24 +240,27 @@ impl<S: Swap> Liquidator<S> {
240240

241241
let available = self.get_asset_balance(self.asset.as_ref().clone()).await?;
242242

243-
if available < swap_amount {
244-
warn!("Insufficient asset balance for liquidation: {available:?} < {swap_amount:?}");
243+
if available < swap_amount.to_u128() {
244+
warn!(
245+
"Insufficient asset balance for liquidation: {available:?} < {:?}",
246+
swap_amount.to_u128()
247+
);
245248
return Ok(());
246249
}
247250

248251
// Implement this function based on your liquidation strategy
249252
if !self
250-
.should_liquidate(swap_amount, liquidation_amount)
253+
.should_liquidate(&swap_amount, liquidation_amount)
251254
.await?
252255
{
253256
info!("Skipping liquidation due to insufficient conditions");
254257
return Ok(());
255258
}
256259

257-
if swap_amount > 0.into() {
260+
if swap_amount.to_u128() > 0.into() {
258261
match self
259262
.swap
260-
.swap(&self.asset, &borrow_asset, swap_amount)
263+
.swap(&self.asset, &borrow_asset, swap_amount.to_u128())
261264
.await
262265
{
263266
Ok(_) => {
@@ -450,10 +453,10 @@ impl<S: Swap> Liquidator<S> {
450453
Ok(())
451454
}
452455

453-
#[instrument(skip(self), level = "debug")]
456+
#[instrument(skip(self, swap_amount), level = "debug", fields(swap_amount = ?swap_amount.to_u128()))]
454457
pub async fn should_liquidate(
455458
&self,
456-
swap_amount: U128,
459+
swap_amount: &S::QuoteOutput,
457460
liquidation_amount: U128,
458461
) -> LiquidatorResult<bool> {
459462
// TODO: Calculate optimal liquidation amount

bots/src/near.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ pub enum RpcError {
4949
/// No outcome for transaction
5050
#[error("No outcome for transaction: {0}")]
5151
NoOutcome(String),
52+
/// Error sending request to Solver RPC
53+
#[error("Error sending request to Solver RPC: {0}")]
54+
SolverRequestError(reqwest::Error),
55+
/// Error deserializing response from Solver RPC
56+
#[error("Error deserializing response from Solver RPC: {0}")]
57+
SolverResponseDeserialization(reqwest::Error),
58+
/// No quote hash received from Solver RPC
59+
#[error("No quote hash received from Solver RPC")]
60+
NoQuoteHashReceived,
61+
/// Error getting system time
62+
#[error("Error getting system time: {0}")]
63+
SystemTimeError(#[from] std::time::SystemTimeError),
5264
}
5365

5466
pub type RpcResult<T = ()> = Result<T, RpcError>;

0 commit comments

Comments
 (0)