Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add token programs to server #328

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions auction-server/api-types/src/opportunity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,16 @@ pub enum OpportunityCreateProgramParamsV1Svm {
/// The user wallet address which requested the quote from the wallet.
#[schema(example = "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5", value_type = String)]
#[serde_as(as = "DisplayFromStr")]
user_wallet_address: Pubkey,
user_wallet_address: Pubkey,
/// The referral fee in basis points.
#[schema(example = 10, value_type = u16)]
referral_fee_bps: u16,
referral_fee_bps: u16,
#[schema(example = "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5", value_type = String)]
#[serde_as(as = "DisplayFromStr")]
input_token_program: Pubkey,
anihamde marked this conversation as resolved.
Show resolved Hide resolved
#[schema(example = "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5", value_type = String)]
#[serde_as(as = "DisplayFromStr")]
output_token_program: Pubkey,
},
}

Expand Down Expand Up @@ -266,6 +272,7 @@ pub enum OpportunityCreateSvm {
/// The input type for creating a new opportunity.
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq, Debug)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum OpportunityCreate {
#[schema(title = "evm")]
Evm(OpportunityCreateEvm),
Expand Down Expand Up @@ -345,12 +352,16 @@ pub enum OpportunityParamsV1ProgramSvm {
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq, Debug, ToResponse)]
pub enum QuoteTokens {
InputTokenSpecified {
input_token: TokenAmountSvm,
output_token: Pubkey,
input_token: TokenAmountSvm,
output_token: Pubkey,
input_token_program: Pubkey,
output_token_program: Pubkey,
},
OutputTokenSpecified {
input_token: Pubkey,
output_token: TokenAmountSvm,
input_token: Pubkey,
output_token: TokenAmountSvm,
input_token_program: Pubkey,
output_token_program: Pubkey,
},
}

Expand Down
41 changes: 28 additions & 13 deletions auction-server/src/opportunity/entities/opportunity_svm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ pub enum FeeToken {

#[derive(Debug, Clone, PartialEq)]
pub struct OpportunitySvmProgramSwap {
pub user_wallet_address: Pubkey,
pub fee_token: FeeToken,
pub referral_fee_bps: u16,
pub user_wallet_address: Pubkey,
pub fee_token: FeeToken,
pub referral_fee_bps: u16,
// TODO*: these really should not live here. they should live in the opportunity core fields, but we don't want to introduce a breaking change. in any case, the need for the token programs is another sign that quotes should be separated from the traditional opportunity struct.
pub input_token_program: Pubkey,
pub output_token_program: Pubkey,
}

#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -108,9 +111,11 @@ impl Opportunity for OpportunitySvm {
OpportunitySvmProgram::SwapKamino(program) => {
repository::OpportunityMetadataSvmProgram::SwapKamino(
repository::OpportunityMetadataSvmProgramSwap {
user_wallet_address: program.user_wallet_address,
fee_token: program.fee_token,
referral_fee_bps: program.referral_fee_bps,
user_wallet_address: program.user_wallet_address,
fee_token: program.fee_token,
referral_fee_bps: program.referral_fee_bps,
input_token_program: program.input_token_program,
output_token_program: program.output_token_program,
},
)
}
Expand Down Expand Up @@ -200,16 +205,20 @@ impl From<OpportunitySvm> for api::OpportunitySvm {
.expect("Failed to get sell token from opportunity svm");
let tokens = if buy_token.amount == 0 {
api::QuoteTokens::OutputTokenSpecified {
input_token: buy_token.token,
output_token: sell_token.clone().into(),
input_token: buy_token.token,
output_token: sell_token.clone().into(),
input_token_program: program.input_token_program,
output_token_program: program.output_token_program,
}
} else {
if sell_token.amount != 0 {
tracing::error!(opportunity = ?val, "Both token amounts are specified for swap opportunity");
}
api::QuoteTokens::InputTokenSpecified {
input_token: buy_token.clone().into(),
output_token: sell_token.token,
input_token: buy_token.clone().into(),
output_token: sell_token.token,
input_token_program: program.input_token_program,
output_token_program: program.output_token_program,
}
};
api::OpportunityParamsV1ProgramSvm::Swap {
Expand Down Expand Up @@ -264,9 +273,11 @@ impl TryFrom<repository::Opportunity<repository::OpportunityMetadataSvm>> for Op
}
repository::OpportunityMetadataSvmProgram::SwapKamino(program) => {
OpportunitySvmProgram::SwapKamino(OpportunitySvmProgramSwap {
user_wallet_address: program.user_wallet_address,
fee_token: program.fee_token,
referral_fee_bps: program.referral_fee_bps,
user_wallet_address: program.user_wallet_address,
fee_token: program.fee_token,
referral_fee_bps: program.referral_fee_bps,
input_token_program: program.input_token_program,
output_token_program: program.output_token_program,
})
}
};
Expand Down Expand Up @@ -303,11 +314,15 @@ impl From<api::OpportunityCreateSvm> for OpportunityCreateSvm {
api::OpportunityCreateProgramParamsV1Svm::Swap {
user_wallet_address,
referral_fee_bps,
input_token_program,
output_token_program,
} => OpportunitySvmProgram::SwapKamino(OpportunitySvmProgramSwap {
user_wallet_address,
// TODO*: see comment above about this arm
fee_token: FeeToken::InputToken,
referral_fee_bps,
input_token_program,
output_token_program,
}),
};

Expand Down
10 changes: 7 additions & 3 deletions auction-server/src/opportunity/repository/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,13 @@ pub struct OpportunityMetadataSvmProgramLimo {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct OpportunityMetadataSvmProgramSwap {
#[serde_as(as = "DisplayFromStr")]
pub user_wallet_address: Pubkey,
pub fee_token: FeeToken,
pub referral_fee_bps: u16,
pub user_wallet_address: Pubkey,
pub fee_token: FeeToken,
pub referral_fee_bps: u16,
#[serde_as(as = "DisplayFromStr")]
pub input_token_program: Pubkey,
#[serde_as(as = "DisplayFromStr")]
pub output_token_program: Pubkey,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
Expand Down
33 changes: 29 additions & 4 deletions auction-server/src/opportunity/service/get_quote.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use {
super::{
ChainTypeSvm,
ConfigSvm,
Service,
},
crate::{
Expand Down Expand Up @@ -99,6 +100,7 @@ impl Service<ChainTypeSvm> {
&self,
quote_create: entities::QuoteCreate,
program: &ProgramSvm,
config: &ConfigSvm,
) -> Result<entities::OpportunityCreateSvm, RestError> {
let router = quote_create.router;
let permission_account = get_quote_permission_key(
Expand Down Expand Up @@ -126,7 +128,7 @@ impl Service<ChainTypeSvm> {
router,
permission_account,
),
chain_id: quote_create.chain_id,
chain_id: quote_create.chain_id.clone(),
sell_tokens: vec![entities::TokenAmountSvm {
token: input_mint,
amount: input_amount,
Expand All @@ -137,13 +139,36 @@ impl Service<ChainTypeSvm> {
}],
};

let chain_store = self
anihamde marked this conversation as resolved.
Show resolved Hide resolved
.store
.chains_svm
.get(&quote_create.chain_id)
.ok_or(RestError::BadParameters("Chain not found".to_string()))?;
// get input token program from cache or else fetch from chain
let input_token_program = chain_store
.get_token_program(input_mint, &config.rpc_client)
.await
.map_err(|err| {
tracing::error!("Failed to get input token program: {:?}", err);
RestError::BadParameters("Input token program not found".to_string())
})?;
let output_token_program = chain_store
.get_token_program(output_mint, &config.rpc_client)
.await
.map_err(|err| {
tracing::error!("Failed to get output token program: {:?}", err);
RestError::BadParameters("Output token program not found".to_string())
})?;

let program_opportunity = match program {
ProgramSvm::SwapKamino => {
entities::OpportunitySvmProgram::SwapKamino(entities::OpportunitySvmProgramSwap {
user_wallet_address: quote_create.user_wallet_address,
// TODO*: we should determine this more intelligently
fee_token: entities::FeeToken::InputToken,
referral_fee_bps: quote_create.referral_fee_bps,
fee_token: entities::FeeToken::InputToken,
referral_fee_bps: quote_create.referral_fee_bps,
input_token_program,
output_token_program,
})
}
_ => {
Expand Down Expand Up @@ -187,7 +212,7 @@ impl Service<ChainTypeSvm> {
tracing::info!(quote_create = ?input.quote_create, "Received request to get quote");

let opportunity_create = self
.get_opportunity_create_for_quote(input.quote_create.clone(), &input.program)
.get_opportunity_create_for_quote(input.quote_create.clone(), &input.program, config)
.await?;
let opportunity = self
.add_opportunity(AddOpportunityInput {
Expand Down
15 changes: 14 additions & 1 deletion auction-server/src/opportunity/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use {
Svm,
},
traced_client::TracedClient,
traced_sender_svm::TracedSenderSvm,
},
state::{
ChainStoreEvm,
Expand All @@ -31,6 +32,11 @@ use {
types::Address,
},
futures::future::try_join_all,
solana_client::{
nonblocking::rpc_client::RpcClient,
rpc_client::RpcClientConfig,
},
solana_sdk::commitment_config::CommitmentConfig,
std::{
collections::HashMap,
sync::Arc,
Expand Down Expand Up @@ -81,6 +87,7 @@ impl ConfigEvm {

// NOTE: Do not implement debug here. it has a circular reference to auction_service
pub struct ConfigSvm {
pub rpc_client: RpcClient,
pub auction_service: RwLock<Option<auction_service::Service<Svm>>>,
}

Expand Down Expand Up @@ -194,10 +201,16 @@ impl ConfigSvm {
) -> anyhow::Result<HashMap<ChainId, Self>> {
Ok(chains
.iter()
.map(|(chain_id, _)| {
.map(|(chain_id, chain_store)| {
(
chain_id.clone(),
Self {
rpc_client: TracedSenderSvm::new_client(
chain_id.clone(),
chain_store.config.rpc_read_url.as_str(),
chain_store.config.rpc_timeout,
RpcClientConfig::with_commitment(CommitmentConfig::processed()),
),
auction_service: RwLock::new(None),
},
)
Expand Down
42 changes: 39 additions & 3 deletions auction-server/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ use {
types::U256,
},
rand::Rng,
solana_client::rpc_response::{
Response,
RpcLogsResponse,
solana_client::{
nonblocking::rpc_client::RpcClient,
rpc_response::{
Response,
RpcLogsResponse,
},
},
solana_sdk::pubkey::Pubkey,
std::{
collections::HashMap,
sync::Arc,
Expand Down Expand Up @@ -107,6 +111,7 @@ pub struct ChainStoreSvm {
// only to avoid closing the channel
pub _dummy_log_receiver: Receiver<Response<RpcLogsResponse>>,
pub config: ConfigSvm,
pub token_program_cache: RwLock<HashMap<Pubkey, Pubkey>>,
}

impl ChainStoreSvm {
Expand All @@ -117,7 +122,38 @@ impl ChainStoreSvm {
log_sender: tx,
_dummy_log_receiver: rx,
config,
token_program_cache: RwLock::new(HashMap::new()),
}
}

pub async fn get_token_program(
anihamde marked this conversation as resolved.
Show resolved Hide resolved
anihamde marked this conversation as resolved.
Show resolved Hide resolved
&self,
mint: Pubkey,
rpc_client: &RpcClient,
) -> anyhow::Result<Pubkey> {
if let Some(program) = self.token_program_cache.read().await.get(&mint) {
return Ok(*program);
}

let program = rpc_client
.get_account(&mint)
.await
.map_err(|err| {
tracing::error!(
"Failed to retrieve owner program for mint account {mint}: {:?}",
err,
mint = mint
);
anyhow!(
"Failed to retrieve owner program for mint account {mint}: {:?}",
err,
mint = mint
)
})?
.owner;

self.token_program_cache.write().await.insert(mint, program);
Ok(program)
}
}

Expand Down
Loading