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

Solana: fix finalize transfer #60

Merged
merged 6 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bridge-sdk/bridge-clients/near-bridge-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ near-primitives.workspace = true
near-token.workspace = true
near-contract-standards.workspace = true
omni-types.workspace = true
serde.workspace = true
serde_json.workspace = true
tracing.workspace = true
near-rpc-client = { path = "../../near-rpc-client" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ const FIN_TRANSFER_DEPOSIT: u128 = 60_000_000_000_000_000_000_000;
const CLAIM_FEE_GAS: u64 = 300_000_000_000_000;
const CLAIM_FEE_DEPOSIT: u128 = 200_000_000_000_000_000_000_000;

#[derive(serde::Deserialize)]
struct StorageBalanceBounds {
min: NearToken,
}

/// Bridging NEAR-originated NEP-141 tokens
#[derive(Builder, Default, Clone)]
pub struct NearBridgeClient {
Expand Down Expand Up @@ -77,7 +82,7 @@ impl NearBridgeClient {
token_id,
"get_native_token_id".to_string(),
serde_json::json!({
"origin_chain": origin_chain
"chain": origin_chain
}),
)
.await?;
Expand Down Expand Up @@ -132,16 +137,17 @@ impl NearBridgeClient {
let response = near_rpc_client::view(
endpoint,
token_id.clone(),
"storage_minimum_balance".to_string(),
"storage_balance_bounds".to_string(),
serde_json::Value::Null,
)
.await?;

let storage_minimum_balance = serde_json::from_slice::<NearToken>(&response)?;
let storage_balance_bounds = serde_json::from_slice::<StorageBalanceBounds>(&response)?;

let total_balance = NearToken::from_yoctonear(self.get_storage_balance(account_id).await?);

Ok(storage_minimum_balance
Ok(storage_balance_bounds
.min
.saturating_sub(total_balance)
.as_yoctonear())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ use omni_types::{near_events::Nep141LockerEvent, OmniAddress};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
instruction::{AccountMeta, Instruction},
program_option::COption,
program_pack::Pack,
pubkey::Pubkey,
signature::{Keypair, Signature},
signer::Signer,
system_program, sysvar,
transaction::Transaction,
};
use spl_token::state::Mint;

mod error;
mod instructions;
Expand Down Expand Up @@ -39,7 +42,6 @@ pub struct TransferId {
pub struct DepositPayload {
pub destination_nonce: u64,
pub transfer_id: TransferId,
pub token: String,
pub amount: u128,
pub recipient: Pubkey,
pub fee_recipient: Option<String>,
Expand Down Expand Up @@ -273,12 +275,16 @@ impl SolanaBridgeClient {
],
&spl_associated_token_account::ID,
);
let (vault, _) = Pubkey::find_program_address(&[b"vault", token.as_ref()], program_id);

let (wormhole_bridge, wormhole_fee_collector, wormhole_sequence) =
self.get_wormhole_accounts().await?;
let wormhole_message = Keypair::new();

let is_bridged_token = match self.get_token_owner(token).await? {
COption::Some(owner) => owner == authority,
COption::None => false,
};

let instruction_data = InitTransfer {
amount,
recipient,
Expand All @@ -293,7 +299,13 @@ impl SolanaBridgeClient {
AccountMeta::new_readonly(authority, false),
AccountMeta::new(token, false),
AccountMeta::new(from_token_account, false),
AccountMeta::new(vault, false), // Optional
if is_bridged_token {
AccountMeta::new(*program_id, false) // Vault is not present for non-native tokens
} else {
let (vault, _) =
Pubkey::find_program_address(&[b"vault", token.as_ref()], program_id);
AccountMeta::new(vault, false)
},
AccountMeta::new(sol_vault, false),
AccountMeta::new(keypair.pubkey(), true),
AccountMeta::new_readonly(config, false),
Expand Down Expand Up @@ -395,9 +407,6 @@ impl SolanaBridgeClient {
&spl_associated_token_account::ID,
);

let (vault, _) =
Pubkey::find_program_address(&[b"vault", solana_token.as_ref()], program_id);

let (wormhole_bridge, wormhole_fee_collector, wormhole_sequence) =
self.get_wormhole_accounts().await?;
let wormhole_message = Keypair::new();
Expand All @@ -412,6 +421,11 @@ impl SolanaBridgeClient {
signature: data.signature,
};

let is_bridged_token = match self.get_token_owner(solana_token).await? {
COption::Some(owner) => owner == authority,
COption::None => false,
};

let instruction = Instruction::new_with_borsh(
*program_id,
&instruction_data,
Expand All @@ -421,7 +435,15 @@ impl SolanaBridgeClient {
AccountMeta::new(authority, false),
AccountMeta::new_readonly(recipient, false),
AccountMeta::new(solana_token, false),
AccountMeta::new(vault, false), // Optional vault
if is_bridged_token {
AccountMeta::new(*program_id, false) // Vault is not present for non-native tokens
} else {
let (vault, _) = Pubkey::find_program_address(
&[b"vault", solana_token.as_ref()],
program_id,
);
AccountMeta::new(vault, false)
},
AccountMeta::new(token_account, false),
AccountMeta::new_readonly(config, false),
AccountMeta::new(wormhole_bridge, false),
Expand Down Expand Up @@ -531,7 +553,6 @@ impl SolanaBridgeClient {
origin_chain: 1,
origin_nonce: message_payload.transfer_id.origin_nonce,
},
token: "wrap.testnet".to_string(),
amount: message_payload.amount.into(),
recipient: match message_payload.recipient {
OmniAddress::Sol(addr) => Pubkey::new_from_array(addr.0),
Expand Down Expand Up @@ -585,6 +606,17 @@ impl SolanaBridgeClient {
Ok(signature)
}

async fn get_token_owner(&self, token: Pubkey) -> Result<COption<Pubkey>, SolanaClientError> {
let client = self.client()?;

let mint_account = client.get_account(&token).await?;

let mint_data = Mint::unpack(&mint_account.data)
.map_err(|e| SolanaClientError::InvalidAccountData(e.to_string()))?;

Ok(mint_data.mint_authority)
}

pub fn client(&self) -> Result<&RpcClient, SolanaClientError> {
self.client.as_ref().ok_or(SolanaClientError::ConfigError(
"Client not initialized".to_string(),
Expand Down
Loading