Skip to content

Commit

Permalink
Solana: fix finalize transfer (#60)
Browse files Browse the repository at this point in the history
* Solana: fix finalize transfer

* chore: removed `unwrap`

* fix: `get_native_token_id` method arguments

* Fix determining native/bridged

* fix: applied same logic with vault/program_id to `finalize_transfer` method

* fix: used standard method `storage_balance_bounds`

---------

Co-authored-by: Ivan Frolov <[email protected]>
  • Loading branch information
kiseln and frolvanya authored Dec 18, 2024
1 parent 77e0120 commit c1e8149
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 12 deletions.
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

0 comments on commit c1e8149

Please sign in to comment.