Skip to content
Open
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
10 changes: 5 additions & 5 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion bridge-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bridge-cli"
version = "0.3.44"
version = "0.3.45"
edition = "2021"
repository = "https://github.com/Near-One/bridge-sdk-rs"
rust-version = "1.88.0"
Expand Down
6 changes: 3 additions & 3 deletions bridge-cli/src/defaults.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// Mainnet
pub const NEAR_RPC_MAINNET: &str = "https://archival-rpc.mainnet.fastnear.com/";
pub const NEAR_TOKEN_LOCKER_ID_MAINNET: &str = "omni.bridge.near";
pub const NEAR_MPC_OMNI_PROVER_ID_MAINNET: &str = "";
pub const NEAR_MPC_OMNI_PROVER_ID_MAINNET: &str = "mpc-prover.bridge.near";
pub const BRIDGE_INDEXER_API_MAINNET: &str = "https://mainnet.api.bridge.nearone.org";
pub const ETH_LIGHT_CLIENT_ID_MAINNET: &str = "client-eth2.bridge.near";
pub const BTC_LIGHT_CLIENT_ID_MAINNET: &str = "btc-client.bridge.near";
Expand Down Expand Up @@ -38,7 +38,7 @@ pub const HYPEREVM_WORMHOLE_ADDRESS_MAINNET: &str = "0x7C0faFc4384551f063e05aee7

pub const ABS_RPC_MAINNET: &str = "https://api.mainnet.abs.xyz";
pub const ABS_BRIDGE_TOKEN_FACTORY_ADDRESS_MAINNET: &str =
"0x0000000000000000000000000000000000000000";
"0xd2490A00bDB97C1EDE4fdf207CFE2664AFB9C20D";

pub const SOLANA_RPC_MAINNET: &str = "https://api.mainnet-beta.solana.com";
pub const SOLANA_BRIDGE_ADDRESS_MAINNET: &str = "dahPEoZGXfyV58JqqH85okdHmpN8U2q8owgPUXSCPxe";
Expand All @@ -61,7 +61,7 @@ pub const ENABLE_ORCHARD_BUNDLE_MAINNET: bool = false;

pub const STARKNET_RPC_MAINNET: &str = "https://starknet-rpc.publicnode.com";
pub const STARKNET_BRIDGE_TOKEN_FACTORY_ADDRESS_MAINNET: &str =
"0x0000000000000000000000000000000000000000000000000000000000000000";
"0x05f9a4a841dfb7bb3cde33073b2450fe45dcd407fb6c0985a274b0e943ad8598";
pub const STARKNET_CHAIN_ID_MAINNET: &str = "SN_MAIN";

/// Testnet
Expand Down
2 changes: 1 addition & 1 deletion bridge-sdk/bridge-clients/evm-bridge-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "evm-bridge-client"
version = "0.3.4"
version = "0.3.5"
edition = "2021"
rust-version = "1.88.0"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,29 @@ impl EvmBridgeClient {
Ok(block.header.number)
}

/// Gets block number for a specific block tag (e.g., Safe, Finalized, Latest).
pub async fn get_block_number_by_tag(&self, tag: alloy::eips::BlockNumberOrTag) -> Result<u64> {
let block = self
.provider
.get_block_by_number(tag)
.await?
.ok_or_else(|| {
EvmBridgeClientError::BlockchainDataError(
"Block not found for the given tag".to_string(),
)
})?;

Ok(block.header.number)
}

/// Gets the transaction receipt, returning `None` if the transaction is not found.
pub async fn get_transaction_receipt(
&self,
tx_hash: TxHash,
) -> Result<Option<alloy::rpc::types::TransactionReceipt>> {
Ok(self.provider.get_transaction_receipt(tx_hash).await?)
}

/// Checks if the transfer is already finalised on EVM
pub async fn is_transfer_finalised(&self, nonce: u64) -> Result<bool> {
let omni_bridge = self.omni_bridge()?;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "starknet-bridge-client"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
rust-version = "1.88.0"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use error::Result;
use starknet::{
accounts::{Account, SingleOwnerAccount},
core::types::{
BlockId, BlockTag, Call, ExecutionResult, Felt, FunctionCall,
BlockId, BlockTag, Call, ExecutionResult, Felt, FunctionCall, TransactionFinalityStatus,
TransactionReceiptWithBlockInfo,
},
macros::selector,
Expand All @@ -21,9 +21,8 @@ mod builder;
pub mod error;

/// STRK native token contract address on Starknet.
const STRK_TOKEN: Felt = Felt::from_hex_unchecked(
"0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
);
const STRK_TOKEN: Felt =
Felt::from_hex_unchecked("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d");

/// Event data extracted from a Starknet `InitTransfer` receipt.
#[derive(Debug)]
Expand Down Expand Up @@ -252,9 +251,7 @@ impl StarknetBridgeClient {

// Approve transfer token for amount + fee
let token_total: u128 = amount.checked_add(fee).ok_or_else(|| {
StarknetBridgeClientError::InvalidArgument(
"amount + fee overflows u128".to_string(),
)
StarknetBridgeClientError::InvalidArgument("amount + fee overflows u128".to_string())
})?;

if token_total > 0 {
Expand Down Expand Up @@ -381,6 +378,28 @@ impl StarknetBridgeClient {
Ok(!result.is_empty() && result[0] != Felt::ZERO)
}

/// Gets the finality status of a transaction.
/// Returns `None` if the transaction receipt is not found.
pub async fn get_transaction_finality_status(
&self,
tx_hash: Felt,
) -> Result<Option<TransactionFinalityStatus>> {
match self.provider.get_transaction_receipt(tx_hash).await {
Ok(receipt) => Ok(Some(*receipt.receipt.finality_status())),
Err(err) => {
let err_str = err.to_string();
if err_str.contains("not found")
|| err_str.contains("TXN_HASH_NOT_FOUND")
|| err_str.contains("hash_not_found")
{
Ok(None)
} else {
Err(StarknetBridgeClientError::ProviderError(err_str))
}
}
}
}

/// Extract an `InitTransfer` event from a Starknet transaction receipt.
pub async fn get_transfer_event(&self, tx_hash: Felt) -> Result<StarknetInitTransferEvent> {
let log = self.get_init_transfer_log(tx_hash).await?;
Expand Down
2 changes: 1 addition & 1 deletion bridge-sdk/connectors/bridge-connector-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bridge-connector-common"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
rust-version = "1.88.0"

Expand Down
2 changes: 2 additions & 0 deletions bridge-sdk/connectors/bridge-connector-common/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub enum BridgeSdkError {
StarknetRpcError(String),
#[error("Error working with Starknet: {0}")]
StarknetOtherError(String),
#[error("Transaction has not reached the required MPC finality")]
MpcFinalityNotReached,
}

impl From<SolanaBridgeClientError> for BridgeSdkError {
Expand Down
2 changes: 1 addition & 1 deletion bridge-sdk/connectors/omni-connector/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "omni-connector"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
rust-version = "1.88.0"

Expand Down
90 changes: 72 additions & 18 deletions bridge-sdk/connectors/omni-connector/src/omni_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use bridge_connector_common::result::{BridgeSdkError, Result};
use derive_builder::Builder;
use light_client::LightClient;
use near_mpc_contract_interface::types::{
EvmExtractedValue, EvmExtractor, EvmLog, EvmRpcRequest, EvmTxId, ExtractedValue,
EvmExtractedValue, EvmExtractor, EvmFinality, EvmLog, EvmRpcRequest, EvmTxId, ExtractedValue,
ForeignChainRpcRequest, ForeignTxSignPayload, ForeignTxSignPayloadV1, Hash160, Hash256,
StarknetExtractedValue, StarknetExtractor, StarknetFelt, StarknetLog, StarknetRpcRequest,
StarknetTxId,
StarknetExtractedValue, StarknetExtractor, StarknetFelt, StarknetFinality, StarknetLog,
StarknetRpcRequest, StarknetTxId,
};
use near_primitives::hash::CryptoHash;
use near_primitives::types::AccountId;
Expand Down Expand Up @@ -1138,6 +1138,40 @@ impl OmniConnector {
proof_kind: ProofKind,
) -> Result<Vec<u8>> {
let evm_client = self.evm_bridge_client(ChainKind::Abs)?;

let mpc_finalities = self.get_mpc_finalities()?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we had finalities configured at the level of evm_bridge_client, this whole logic could be encapsulated in there which I think would be nice

let Some(MpcFinality::Evm(finality)) = mpc_finalities.get(&ChainKind::Abs).cloned() else {
return Err(BridgeSdkError::ConfigError(
"No mpc finality provided for Abs".to_string(),
));
};

// Check that the transaction has reached the required finality level
let block_tag = match &finality {
EvmFinality::Latest => alloy::eips::BlockNumberOrTag::Latest,
EvmFinality::Safe => alloy::eips::BlockNumberOrTag::Safe,
EvmFinality::Finalized => alloy::eips::BlockNumberOrTag::Finalized,
_ => {
return Err(BridgeSdkError::ConfigError(
"Unsupported EVM finality variant for Abs".to_string(),
));
}
};

let receipt = evm_client
.get_transaction_receipt(tx_hash)
.await?
.ok_or(BridgeSdkError::MpcFinalityNotReached)?;

let tx_block_number = receipt
.block_number
.ok_or(BridgeSdkError::MpcFinalityNotReached)?;

let finalized_block = evm_client.get_block_number_by_tag(block_tag).await?;
if tx_block_number >= finalized_block {
return Err(BridgeSdkError::MpcFinalityNotReached);
}

let rpc_log = match proof_kind {
ProofKind::InitTransfer => evm_client.get_init_transfer_log(tx_hash).await?,
ProofKind::DeployToken => evm_client.get_deploy_token_log(tx_hash).await?,
Expand Down Expand Up @@ -1185,13 +1219,6 @@ impl OmniConnector {
topics: rpc_log.topics().iter().map(|t| Hash256(t.0)).collect(),
};

let mpc_finalities = self.get_mpc_finalities()?;
let Some(MpcFinality::Evm(finality)) = mpc_finalities.get(&ChainKind::Abs).cloned() else {
return Err(BridgeSdkError::ConfigError(
"No mpc finality provided for Abs".to_string(),
));
};

let sign_payload = ForeignTxSignPayload::V1(ForeignTxSignPayloadV1 {
request: ForeignChainRpcRequest::Abstract(EvmRpcRequest {
tx_id: EvmTxId(tx_hash.0),
Expand Down Expand Up @@ -1219,6 +1246,41 @@ impl OmniConnector {
proof_kind: ProofKind,
) -> Result<Vec<u8>> {
let strk_client = self.starknet_bridge_client()?;

let mpc_finalities = self.get_mpc_finalities()?;
let Some(MpcFinality::Starknet(finality)) = mpc_finalities.get(&ChainKind::Strk).cloned()
else {
return Err(BridgeSdkError::ConfigError(
"No mpc finality provided for Strk".to_string(),
));
};

// Check that the transaction has reached the required finality level
let status = strk_client
.get_transaction_finality_status(tx_hash)
.await?
.ok_or(BridgeSdkError::MpcFinalityNotReached)?;

let is_finalized = match &finality {
StarknetFinality::AcceptedOnL2 => matches!(
status,
starknet::core::types::TransactionFinalityStatus::AcceptedOnL2
| starknet::core::types::TransactionFinalityStatus::AcceptedOnL1
),
StarknetFinality::AcceptedOnL1 => matches!(
status,
starknet::core::types::TransactionFinalityStatus::AcceptedOnL1
),
_ => {
return Err(BridgeSdkError::ConfigError(
"Unsupported Starknet finality variant".to_string(),
));
}
};
if !is_finalized {
return Err(BridgeSdkError::MpcFinalityNotReached);
}

let log = match proof_kind {
ProofKind::InitTransfer => strk_client.get_init_transfer_log(tx_hash).await?,
ProofKind::DeployToken => strk_client.get_deploy_token_log(tx_hash).await?,
Expand Down Expand Up @@ -1246,14 +1308,6 @@ impl OmniConnector {
.collect(),
};

let mpc_finalities = self.get_mpc_finalities()?;
let Some(MpcFinality::Starknet(finality)) = mpc_finalities.get(&ChainKind::Strk).cloned()
else {
return Err(BridgeSdkError::ConfigError(
"No mpc finality provided for Abs".to_string(),
));
};

let sign_payload = ForeignTxSignPayload::V1(ForeignTxSignPayloadV1 {
request: ForeignChainRpcRequest::Starknet(StarknetRpcRequest {
tx_id: StarknetTxId(StarknetFelt(tx_hash.to_bytes_be())),
Expand Down
Loading