# 概述
本项目实现了一个基于Jupiter的限价单系统,用户可实时挂单、撤单,当代币价格达到挂单价格时进行交易。
# 技术
本项目需求较为简单,但是涵盖了以下知识点:
1. **jupiter api使用:** 怎么使用jupiter实现最佳的swap路由?
2. **jito api使用:** 怎么通过jito发送bundle或transaction以实现更快的上链速度?
3. **solana交易构造:** 怎么通过rust构造一笔solana的交易?
4. **rust的tokio、rocket框架的使用**
# 执行流程
## 挂单
用户给定以下数据进行挂单。
```rust!
#[derive(Deserialize)]
pub struct PlaceOrderRequest {
/// 输出代币
pub input_mint: String,
/// 输出代币
pub output_mint: String,
/// 卖出价格
pub price: f32,
/// 数量
pub amount: u64,
/// 滑点
pub slippage_bps: u16,
/// 是否有小费给jito
pub tip_amount: Option<u64>,
/// 加密后的pk
pub encrypt_pk: String,
}
```
注意:此处用户需要给定加密后的private_key,那么前后端之间的加密手段需要协调。如果无法托管私钥,也可以通过用户直接转账至中心化交易账户(用户撤单时转回即可)。
以下是一个http的挂单请求示例:
```jsonld!
curl -X POST \
http://localhost:8000/place_order \
-H 'Content-Type: application/json' \
-d '{
"input_mint": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
"output_mint": "So11111111111111111111111111111111111111112",
"price":0.738401,
"amount": 1000,
"slippage_bps": 50,
"encrypt_pk": "xxx",
"tip_amount":1000
}'
```
此处,若指定了timp_amount则说明发交易时启用jito,如果不使用jito则把tip_amount去掉即可。
若执行成功,则返回一个order_id,可凭借order_id进行撤单。
## 撤单
```jsonld!
curl -X POST \
http://localhost:8000/cancel_order \
-H 'Content-Type: application/json' \
-d '{
"order_id": "3e702c25-9c50-422d-a9dd-949df32b26c5"
}'
```
# 内部实现
## 核心逻辑设计

## 如何成交?
为了实现最佳价格,使用Jupiter作为交易平台。Jupiter提供http请求进行交易构造,对于限价单场景来说,由于对性能的要求不高(可以挂限价单的代币其波动不应过大),因此使用Jupiter完全可满足。
Jupiter提供两种构造交易的方式:
1. 直接构造swap交易
2. 仅构造swap指令
此处为了实现抽佣的功能,选择构造swap指令的方式,而后将swap指令包含在交易内部。
## 什么时候成交?
虽然说限价单是价格等于指定价格时执行买入卖出,由于价格不可能完全相等,因此还需要存在一定的误差阈值,此处有两种选择:
1. 价值误差:(当前价格*代币数量)与(挂单价格*代币数量)之间的绝对值小于阈值,则成交。
2. 价格误差:当前价格与挂单价格之间的绝对值小于阈值,则成交。
个人认为使用价值误差会更合适,因为此时还考虑了挂单的代币数量。试想如果价格差值为0.0001则成交,如果挂单大额代币,其损失数量也不小。
## 如何抽佣?
抽佣即为用户每笔交易都需要抽取一定份额的佣金,对于swap过程中的抽佣,可分为以下情况:
1. **sol -> token**:在交易执行前进行佣金的抽取。
2. **token -> sol**:在交易执行后进行佣金的抽取。
3. **token -> token**:在交易时进行佣金的抽取。
如何理解呢?
在抽取佣金时,我们不希望佣金的形式是token,因为这种情况过于不稳定(代币价格波动),所以佣金必须是sol。
那么情况1和情况2就容易理解了,但是对于情况3,我的方式如下:
在用户的交易触发之间,抽取一部分佣金token,而后构造token -> sol的指令,再将这笔指令插入用户swap的指令当中。这么做的好处是:用户把所有交易费用都承担了😂。
## 如何撤单?
这里利用`tokio select!`以及`oneshot channel`来完成。
当用户在挂单的过程中,构造一个oneshot channel(该通道设计用于一次性的通信),并且将该channel的sender存储下来(用户撤单时,使用sender往channel中写入消息)。而后,我们使用tokio select同时监听两个事件:`价格监听`和`oneshot channel是否收到消息`。当`oneshot channel receiver`收到消息时,说明需要撤单,此时将订单从订单簿中去除即可。
## 如何对私钥进行加解密
加解密有两种方案:
1. 对称加密
2. 非对称加密
对称加密一般使用AES,非对称加密一般使用RSA。此处可自行调整。
# github:
https://github.com/sasiyaluba/Jup-limit-order