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
2 changes: 1 addition & 1 deletion 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 contracts/satoshi-bridge/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "satoshi-bridge"
version = "0.7.0"
version = "0.8.0"
edition.workspace = true
publish.workspace = true
repository.workspace = true
Expand Down
1 change: 1 addition & 0 deletions contracts/satoshi-bridge/src/api/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ impl Contract {
max_gas_fee: gas_fee,
last_rbf_time_sec: None,
cancel_rbf_reserved: None,
subsidize_amount: 0,
}),
};
require!(
Expand Down
33 changes: 22 additions & 11 deletions contracts/satoshi-bridge/src/api/token_receiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub enum TokenReceiverMessage {
output: Vec<TxOut>,
max_gas_fee: Option<U128>,
},
Rbf {
pending_tx_id: String,
output: Vec<TxOut>,
},
}

#[near]
Expand All @@ -26,10 +30,6 @@ impl FungibleTokenReceiver for Contract {
msg: String,
) -> PromiseOrValue<U128> {
let amount = amount.into();
require!(
amount >= self.internal_config().min_withdraw_amount,
"Invalid amount"
);
let message = serde_json::from_str::<TokenReceiverMessage>(&msg).expect("INVALID MSG");
let token_id = env::predecessor_account_id();
require!(
Expand All @@ -52,14 +52,24 @@ impl FungibleTokenReceiver for Contract {
input,
output,
max_gas_fee,
} => self.ft_on_transfer_withdraw_chain_specific(
sender_id,
amount,
target_btc_address,
input,
} => {
require!(
amount >= self.internal_config().min_withdraw_amount,
"Invalid amount"
);
self.ft_on_transfer_withdraw_chain_specific(
sender_id,
amount,
target_btc_address,
input,
output,
max_gas_fee,
)
}
TokenReceiverMessage::Rbf {
pending_tx_id,
output,
max_gas_fee,
),
} => self.rbf_subsidize_chain_specific(amount, sender_id, pending_tx_id, output),
}
}
}
Expand Down Expand Up @@ -119,6 +129,7 @@ impl Contract {
max_gas_fee: gas_fee,
last_rbf_time_sec: None,
cancel_rbf_reserved: None,
subsidize_amount: 0,
}),
};
require!(
Expand Down
65 changes: 63 additions & 2 deletions contracts/satoshi-bridge/src/bitcoin_utils/contract_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ macro_rules! define_rbf_method {
account_id: AccountId,
original_btc_pending_verify_id: String,
output: Vec<TxOut>,
) {
) -> String {
let original_tx_btc_pending_info =
self.internal_unwrap_btc_pending_info(&original_btc_pending_verify_id);

Expand All @@ -31,6 +31,8 @@ macro_rules! define_rbf_method {
btc_pending_id: &btc_pending_id,
}
.emit();

btc_pending_id
}
};
}
Expand All @@ -45,7 +47,10 @@ impl Contract {
// Ensure that the RBF transaction pays more gas than the previous transaction.
let max_gas_fee = original_tx_btc_pending_info.get_max_gas_fee();
let additional_gas_amount = gas_fee.saturating_sub(max_gas_fee);
require!(additional_gas_amount > 0, "No gas increase.");
require!(
additional_gas_amount > 0,
format!("No gas increase. Old gas fee = {max_gas_fee}, new gas fee = {gas_fee}")
);
}

pub(crate) fn ft_on_transfer_withdraw_chain_specific(
Expand Down Expand Up @@ -97,4 +102,60 @@ impl Contract {
let original_psbt = original_tx_btc_pending_info.get_psbt();
PsbtWrapper::from_original_psbt(original_psbt, output)
}

pub(crate) fn rbf_subsidize_chain_specific(
&mut self,
amount: u128,
sender_id: AccountId,
pending_tx_id: String,
output: Vec<TxOut>,
) -> PromiseOrValue<U128> {
let origin_tx_btc_pending_info = self.internal_unwrap_btc_pending_info(&pending_tx_id);
let user_account_id = origin_tx_btc_pending_info.account_id.clone();
require!(
self.internal_unwrap_account(&user_account_id)
.btc_pending_sign_id
.is_none(),
"Assisted user previous btc tx has not been signed"
);
let full_subsidy_amount = self
.internal_unwrap_btc_pending_info(&pending_tx_id)
.get_subsidize_amount()
+ amount;
self.internal_unwrap_mut_btc_pending_info(&pending_tx_id)
.update_subsidize_amount(full_subsidy_amount);

let new_pending_info_id = self.withdraw_rbf_chain_specific(
user_account_id.clone(),
pending_tx_id.clone(),
output,
);

let origin_tx_btc_pending_info = self.internal_unwrap_btc_pending_info(&pending_tx_id);
let new_tx_btc_pending_info = self.internal_unwrap_btc_pending_info(&new_pending_info_id);

require!(
new_tx_btc_pending_info.actual_received_amount
== origin_tx_btc_pending_info.actual_received_amount,
"Actual received amount has been changed."
);
let gas_fee_diff = new_tx_btc_pending_info
.gas_fee
.saturating_sub(origin_tx_btc_pending_info.gas_fee);
require!(
gas_fee_diff == full_subsidy_amount,
"Gas fee diff is not equal to subsidy amount."
);

Event::SubsidizeRbf {
origin_btc_pending_id: &pending_tx_id,
subsidy_amount: U128(amount),
full_subsidy_amount: U128(full_subsidy_amount),
subsidizer: &sender_id,
beneficiary: &user_account_id,
}
.emit();

PromiseOrValue::Value(U128(0))
}
}
2 changes: 1 addition & 1 deletion contracts/satoshi-bridge/src/bitcoin_utils/psbt_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl PsbtWrapper {
original_psbt: crate::psbt_wrapper::PsbtWrapper,
output: Vec<TxOut>,
) -> Self {
let sequence = bitcoin::Sequence::MAX;
let sequence = bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME;

let transaction = BtcTransaction {
version: Version::TWO,
Expand Down
42 changes: 39 additions & 3 deletions contracts/satoshi-bridge/src/btc_pending_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub struct OriginalState {
pub max_gas_fee: u128,
pub last_rbf_time_sec: Option<u32>,
pub cancel_rbf_reserved: Option<U128>,
#[serde(with = "u128_dec_format")]
pub subsidize_amount: u128,
}

impl OriginalState {
Expand Down Expand Up @@ -199,6 +201,28 @@ impl BTCPendingInfo {
_ => env::panic_str("Not original tx"),
}
}

pub fn get_subsidize_amount(&self) -> u128 {
match self.state.borrow() {
PendingInfoState::WithdrawOriginal(state) => state.subsidize_amount,
PendingInfoState::ActiveUtxoManagementOriginal(state) => state.subsidize_amount,
_ => env::panic_str("Not original tx"),
}
}

pub fn update_subsidize_amount(&mut self, subsidize_amount: u128) {
match self.state.borrow_mut() {
PendingInfoState::WithdrawOriginal(state) => {
state.subsidize_amount = subsidize_amount;
state.last_rbf_time_sec = Some(nano_to_sec(env::block_timestamp()));
}
PendingInfoState::ActiveUtxoManagementOriginal(state) => {
state.subsidize_amount = subsidize_amount;
state.last_rbf_time_sec = Some(nano_to_sec(env::block_timestamp()));
}
_ => env::panic_str("Not original tx"),
}
}

pub fn to_pending_verify_stage(&mut self) {
match self.state.borrow_mut() {
Expand Down Expand Up @@ -293,12 +317,14 @@ impl BTCPendingInfo {

#[near(serializers = [borsh])]
pub enum VBTCPendingInfo {
V0(BTCPendingInfoV0),
Current(BTCPendingInfo),
}

impl From<VBTCPendingInfo> for BTCPendingInfo {
fn from(v: VBTCPendingInfo) -> Self {
match v {
VBTCPendingInfo::V0(c) => c.into(),
VBTCPendingInfo::Current(c) => c,
}
}
Expand All @@ -307,6 +333,7 @@ impl From<VBTCPendingInfo> for BTCPendingInfo {
impl From<&VBTCPendingInfo> for BTCPendingInfo {
fn from(v: &VBTCPendingInfo) -> Self {
match v {
VBTCPendingInfo::V0(c) => c.clone().into(),
VBTCPendingInfo::Current(c) => c.clone(),
}
}
Expand All @@ -315,6 +342,7 @@ impl From<&VBTCPendingInfo> for BTCPendingInfo {
impl<'a> From<&'a VBTCPendingInfo> for &'a BTCPendingInfo {
fn from(v: &'a VBTCPendingInfo) -> Self {
match v {
VBTCPendingInfo::V0(_) => unreachable!(),
VBTCPendingInfo::Current(c) => c,
}
}
Expand All @@ -323,6 +351,7 @@ impl<'a> From<&'a VBTCPendingInfo> for &'a BTCPendingInfo {
impl<'a> From<&'a mut VBTCPendingInfo> for &'a mut BTCPendingInfo {
fn from(v: &'a mut VBTCPendingInfo) -> Self {
match v {
VBTCPendingInfo::V0(_) => unreachable!(),
VBTCPendingInfo::Current(c) => c,
}
}
Expand Down Expand Up @@ -361,11 +390,18 @@ impl Contract {
&mut self,
btc_pending_id: &String,
) -> &mut BTCPendingInfo {
self.data_mut()
let btc_pending_info = self
.data_mut()
.btc_pending_infos
.get_mut(btc_pending_id)
.map(|o| o.into())
.expect("BTC pending info not exist")
.expect("BTC pending info not exist");

if let VBTCPendingInfo::V0(old) = &btc_pending_info {
let new_current = BTCPendingInfo::from(old.clone());
*btc_pending_info = VBTCPendingInfo::Current(new_current);
}

btc_pending_info.into()
}

pub fn internal_remove_btc_pending_info(&mut self, btc_pending_id: &String) -> BTCPendingInfo {
Expand Down
7 changes: 7 additions & 0 deletions contracts/satoshi-bridge/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ pub enum Event<'a> {
account_id: &'a AccountId,
btc_pending_id: &'a String,
},
SubsidizeRbf {
origin_btc_pending_id: &'a String,
subsidy_amount: U128,
full_subsidy_amount: U128,
subsidizer: &'a AccountId,
beneficiary: &'a AccountId,
},
BtcInputSignature {
account_id: &'a AccountId,
btc_pending_id: &'a String,
Expand Down
Loading