Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 2074c63

Browse files
authored
Re-org token-swap (#113)
1 parent 370150c commit 2074c63

File tree

5 files changed

+943
-913
lines changed

5 files changed

+943
-913
lines changed

token-swap/src/error.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//! Error types
2+
3+
use num_derive::FromPrimitive;
4+
use num_traits::FromPrimitive;
5+
#[cfg(target_arch = "bpf")]
6+
use solana_sdk::program::invoke_signed;
7+
use solana_sdk::{
8+
info,
9+
program_error::{PrintProgramError, ProgramError},
10+
program_utils::DecodeError,
11+
};
12+
use thiserror::Error;
13+
14+
/// Errors that may be returned by the TokenSwap program.
15+
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
16+
pub enum Error {
17+
/// The account cannot be initialized because it is already being used.
18+
#[error("AlreadyInUse")]
19+
AlreadyInUse,
20+
/// The program address provided doesn't match the value generated by the program.
21+
#[error("InvalidProgramAddress")]
22+
InvalidProgramAddress,
23+
/// The owner of the input isn't set to the program address generated by the program.
24+
#[error("InvalidOwner")]
25+
InvalidOwner,
26+
/// The deserialization of the Token state returned something besides State::Token.
27+
#[error("ExpectedToken")]
28+
ExpectedToken,
29+
/// The deserialization of the Token state returned something besides State::Account.
30+
#[error("ExpectedAccount")]
31+
ExpectedAccount,
32+
/// The initialized pool had a non zero supply.
33+
#[error("InvalidSupply")]
34+
InvalidSupply,
35+
/// The initialized token has a delegate.
36+
#[error("InvalidDelegate")]
37+
InvalidDelegate,
38+
/// The token swap state is invalid.
39+
#[error("InvalidState")]
40+
InvalidState,
41+
/// The input token is invalid for swap.
42+
#[error("InvalidInput")]
43+
InvalidInput,
44+
/// The output token is invalid for swap.
45+
#[error("InvalidOutput")]
46+
InvalidOutput,
47+
/// The calculation failed.
48+
#[error("CalculationFailure")]
49+
CalculationFailure,
50+
}
51+
impl From<Error> for ProgramError {
52+
fn from(e: Error) -> Self {
53+
ProgramError::Custom(e as u32)
54+
}
55+
}
56+
impl<T> DecodeError<T> for Error {
57+
fn type_of() -> &'static str {
58+
"Swap Error"
59+
}
60+
}
61+
impl PrintProgramError for Error {
62+
fn print<E>(&self)
63+
where
64+
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
65+
{
66+
match self {
67+
Error::AlreadyInUse => info!("Error: AlreadyInUse"),
68+
Error::InvalidProgramAddress => info!("Error: InvalidProgramAddress"),
69+
Error::InvalidOwner => info!("Error: InvalidOwner"),
70+
Error::ExpectedToken => info!("Error: ExpectedToken"),
71+
Error::ExpectedAccount => info!("Error: ExpectedAccount"),
72+
Error::InvalidSupply => info!("Error: InvalidSupply"),
73+
Error::InvalidDelegate => info!("Error: InvalidDelegate"),
74+
Error::InvalidState => info!("Error: InvalidState"),
75+
Error::InvalidInput => info!("Error: InvalidInput"),
76+
Error::InvalidOutput => info!("Error: InvalidOutput"),
77+
Error::CalculationFailure => info!("Error: CalculationFailure"),
78+
}
79+
}
80+
}

token-swap/src/instruction.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#![allow(clippy::too_many_arguments)]
2+
3+
//! Instruction types
4+
5+
#[cfg(target_arch = "bpf")]
6+
use solana_sdk::program::invoke_signed;
7+
use solana_sdk::{
8+
instruction::{AccountMeta, Instruction},
9+
program_error::ProgramError,
10+
pubkey::Pubkey,
11+
};
12+
use std::mem::size_of;
13+
14+
/// fee rate as a ratio
15+
#[repr(C)]
16+
#[derive(Clone, Copy, Debug, Default, PartialEq)]
17+
pub struct Fee {
18+
/// denominator of the fee ratio
19+
pub denominator: u64,
20+
/// numerator of the fee ratio
21+
pub numerator: u64,
22+
}
23+
24+
/// Instructions supported by the SwapInfo program.
25+
#[repr(C)]
26+
#[derive(Clone, Debug, PartialEq)]
27+
pub enum SwapInstruction {
28+
/// Initializes a new SwapInfo.
29+
///
30+
/// 0. `[writable, signer]` New Token-swap to create.
31+
/// 1. `[]` $authority derived from `create_program_address(&[Token-swap account])`
32+
/// 2. `[]` token_a Account. Must be non zero, owned by $authority.
33+
/// 3. `[]` token_b Account. Must be non zero, owned by $authority.
34+
/// 4. `[writable]` pool Token. Must be empty, owned by $authority.
35+
/// 5. `[writable]` Pool Account to deposit the generated tokens, user is the owner.
36+
/// 6. '[]` Token program id
37+
/// userdata: fee rate as a ratio
38+
Initialize(Fee),
39+
40+
/// Swap the tokens in the pool.
41+
///
42+
/// 0. `[]` Token-swap
43+
/// 1. `[]` $authority
44+
/// 2. `[writable]` token_(A|B) SOURCE Account, amount is transferable by $authority,
45+
/// 4. `[writable]` token_(A|B) Base Account to swap INTO. Must be the SOURCE token.
46+
/// 5. `[writable]` token_(A|B) Base Account to swap FROM. Must be the DEST token.
47+
/// 6. `[writable]` token_(A|B) DEST Account assigned to USER as the owner.
48+
/// 7. '[]` Token program id
49+
/// userdata: SOURCE amount to transfer, output to DEST is based on the exchange rate
50+
Swap(u64),
51+
52+
/// Deposit some tokens into the pool. The output is a "pool" token representing ownership
53+
/// into the pool. Inputs are converted to the current ratio.
54+
///
55+
/// 0. `[]` Token-swap
56+
/// 1. `[]` $authority
57+
/// 2. `[writable]` token_a $authority can transfer amount,
58+
/// 4. `[writable]` token_b $authority can transfer amount,
59+
/// 6. `[writable]` token_a Base Account to deposit into.
60+
/// 7. `[writable]` token_b Base Account to deposit into.
61+
/// 8. `[writable]` Pool MINT account, $authority is the owner.
62+
/// 9. `[writable]` Pool Account to deposit the generated tokens, user is the owner.
63+
/// 10. '[]` Token program id
64+
/// userdata: token_a amount to transfer. token_b amount is set by the current exchange rate.
65+
Deposit(u64),
66+
67+
/// Withdraw the token from the pool at the current ratio.
68+
///
69+
/// 0. `[]` Token-swap
70+
/// 1. `[]` $authority
71+
/// 2. `[writable]` SOURCE Pool account, amount is transferable by $authority.
72+
/// 5. `[writable]` token_a Account to withdraw FROM.
73+
/// 6. `[writable]` token_b Account to withdraw FROM.
74+
/// 7. `[writable]` token_a user Account.
75+
/// 8. `[writable]` token_b user Account.
76+
/// 9. '[]` Token program id
77+
/// userdata: SOURCE amount of pool tokens to transfer. User receives an output based on the
78+
/// percentage of the pool tokens that are returned.
79+
Withdraw(u64),
80+
}
81+
impl SwapInstruction {
82+
/// Deserializes a byte buffer into an [SwapInstruction](enum.SwapInstruction.html).
83+
pub fn deserialize(input: &[u8]) -> Result<Self, ProgramError> {
84+
if input.len() < size_of::<u8>() {
85+
return Err(ProgramError::InvalidAccountData);
86+
}
87+
Ok(match input[0] {
88+
0 => {
89+
let fee: &Fee = unpack(input)?;
90+
Self::Initialize(*fee)
91+
}
92+
1 => {
93+
let fee: &u64 = unpack(input)?;
94+
Self::Swap(*fee)
95+
}
96+
2 => {
97+
let fee: &u64 = unpack(input)?;
98+
Self::Deposit(*fee)
99+
}
100+
3 => {
101+
let fee: &u64 = unpack(input)?;
102+
Self::Withdraw(*fee)
103+
}
104+
_ => return Err(ProgramError::InvalidAccountData),
105+
})
106+
}
107+
108+
/// Serializes an [SwapInstruction](enum.SwapInstruction.html) into a byte buffer.
109+
pub fn serialize(self: &Self) -> Result<Vec<u8>, ProgramError> {
110+
let mut output = vec![0u8; size_of::<SwapInstruction>()];
111+
match self {
112+
Self::Initialize(fees) => {
113+
output[0] = 0;
114+
#[allow(clippy::cast_ptr_alignment)]
115+
let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut Fee) };
116+
*value = *fees;
117+
}
118+
Self::Swap(amount) => {
119+
output[0] = 1;
120+
#[allow(clippy::cast_ptr_alignment)]
121+
let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) };
122+
*value = *amount;
123+
}
124+
Self::Deposit(amount) => {
125+
output[0] = 2;
126+
#[allow(clippy::cast_ptr_alignment)]
127+
let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) };
128+
*value = *amount;
129+
}
130+
Self::Withdraw(amount) => {
131+
output[0] = 3;
132+
#[allow(clippy::cast_ptr_alignment)]
133+
let value = unsafe { &mut *(&mut output[1] as *mut u8 as *mut u64) };
134+
*value = *amount;
135+
}
136+
}
137+
Ok(output)
138+
}
139+
}
140+
141+
/// Creates an 'initialize' instruction.
142+
pub fn initialize(
143+
program_id: &Pubkey,
144+
token_program_id: &Pubkey,
145+
swap_pubkey: &Pubkey,
146+
authority_pubkey: &Pubkey,
147+
token_a_pubkey: &Pubkey,
148+
token_b_pubkey: &Pubkey,
149+
pool_pubkey: &Pubkey,
150+
user_output_pubkey: &Pubkey,
151+
fee: Fee,
152+
) -> Result<Instruction, ProgramError> {
153+
let data = SwapInstruction::Initialize(fee).serialize()?;
154+
155+
let accounts = vec![
156+
AccountMeta::new(*swap_pubkey, true),
157+
AccountMeta::new(*authority_pubkey, false),
158+
AccountMeta::new(*token_a_pubkey, false),
159+
AccountMeta::new(*token_b_pubkey, false),
160+
AccountMeta::new(*pool_pubkey, false),
161+
AccountMeta::new(*user_output_pubkey, false),
162+
AccountMeta::new(*token_program_id, false),
163+
];
164+
165+
Ok(Instruction {
166+
program_id: *program_id,
167+
accounts,
168+
data,
169+
})
170+
}
171+
172+
/// Unpacks a reference from a bytes buffer.
173+
pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> {
174+
if input.len() < size_of::<u8>() + size_of::<T>() {
175+
return Err(ProgramError::InvalidAccountData);
176+
}
177+
#[allow(clippy::cast_ptr_alignment)]
178+
let val: &T = unsafe { &*(&input[1] as *const u8 as *const T) };
179+
Ok(val)
180+
}

0 commit comments

Comments
 (0)