diff --git a/contracts/satoshi-bridge/src/api/token_receiver.rs b/contracts/satoshi-bridge/src/api/token_receiver.rs index 7906b8ec..acbd3c3c 100644 --- a/contracts/satoshi-bridge/src/api/token_receiver.rs +++ b/contracts/satoshi-bridge/src/api/token_receiver.rs @@ -14,6 +14,7 @@ pub enum TokenReceiverMessage { output: Vec, max_gas_fee: Option, chain_specific_data: Option, + external_id: Option, }, } @@ -54,14 +55,16 @@ impl FungibleTokenReceiver for Contract { output, max_gas_fee, chain_specific_data, + external_id, } => self.ft_on_transfer_withdraw_chain_specific( - sender_id, + sender_id.clone(), amount, target_btc_address, input, output, max_gas_fee, chain_specific_data, + external_id.map(|id| format!("{sender_id}:{id}")), ), } } @@ -75,6 +78,7 @@ impl Contract { target_btc_address: String, mut psbt: PsbtWrapper, max_gas_fee: Option, + external_id: Option, ) { let (utxo_storage_keys, vutxos) = self.generate_vutxos(&mut psbt); require!( @@ -128,12 +132,20 @@ impl Contract { .is_none(), "pending info already exist" ); + + if let Some(external_id) = &external_id { + self.data_mut() + .btc_pending_infos_by_external_id + .insert(external_id.clone(), btc_pending_id.clone()); + } + self.internal_unwrap_mut_account(&sender_id) .btc_pending_sign_id = Some(btc_pending_id.clone()); Event::UtxoRemoved { utxo_storage_keys }.emit(); Event::GenerateBtcPendingInfo { account_id: &sender_id, btc_pending_id: &btc_pending_id, + external_id, } .emit(); } diff --git a/contracts/satoshi-bridge/src/bitcoin_utils/contract_methods.rs b/contracts/satoshi-bridge/src/bitcoin_utils/contract_methods.rs index 5a5114c9..af084b8b 100644 --- a/contracts/satoshi-bridge/src/bitcoin_utils/contract_methods.rs +++ b/contracts/satoshi-bridge/src/bitcoin_utils/contract_methods.rs @@ -37,6 +37,7 @@ macro_rules! define_rbf_method { Event::GenerateBtcPendingInfo { account_id: &account_id, btc_pending_id: &btc_pending_id, + external_id: None, } .emit(); } @@ -72,6 +73,7 @@ impl Contract { output: Vec, max_gas_fee: Option, _chain_specific_data: Option, + external_id: Option, ) -> PromiseOrValue { self.create_btc_pending_info( sender_id, @@ -79,6 +81,7 @@ impl Contract { target_btc_address, PsbtWrapper::new(input, output), max_gas_fee, + external_id, ); PromiseOrValue::Value(U128(0)) } diff --git a/contracts/satoshi-bridge/src/event.rs b/contracts/satoshi-bridge/src/event.rs index eeca095b..41b1b93e 100644 --- a/contracts/satoshi-bridge/src/event.rs +++ b/contracts/satoshi-bridge/src/event.rs @@ -43,6 +43,7 @@ pub enum Event<'a> { GenerateBtcPendingInfo { account_id: &'a AccountId, btc_pending_id: &'a String, + external_id: Option, }, BtcInputSignature { account_id: &'a AccountId, diff --git a/contracts/satoshi-bridge/src/legacy.rs b/contracts/satoshi-bridge/src/legacy.rs index f891f7e5..3b0f33e2 100644 --- a/contracts/satoshi-bridge/src/legacy.rs +++ b/contracts/satoshi-bridge/src/legacy.rs @@ -1,3 +1,5 @@ +use near_sdk::store::LookupMap; + use crate::{ env, near, AccountId, BridgeFee, Config, ContractData, HashMap, HashSet, IterableMap, IterableSet, LazyOption, LookupSet, PublicKey, StorageKey, VAccount, VBTCPendingInfo, VUTXO, @@ -470,3 +472,71 @@ impl From for ContractData { } } } + +#[near(serializers = [borsh])] +pub struct ContractDataV3 { + pub config: LazyOption, + pub accounts: IterableMap, + pub utxos: IterableMap, + pub unavailable_utxos: IterableMap, + pub verified_deposit_utxo: LookupSet, + pub btc_pending_infos: IterableMap, + pub rbf_txs: IterableMap>, + pub relayer_white_list: IterableSet, + pub extra_msg_relayer_white_list: IterableSet, + pub post_action_receiver_id_white_list: IterableSet, + pub post_action_msg_templates: IterableMap>, + pub lost_found: IterableMap, + pub acc_collected_protocol_fee: u128, + pub cur_available_protocol_fee: u128, + pub acc_claimed_protocol_fee: u128, + pub cur_reserved_protocol_fee: u128, + pub acc_protocol_fee_for_gas: u128, +} + +impl From for ContractData { + fn from(c: ContractDataV3) -> Self { + let ContractDataV3 { + config, + accounts, + utxos, + unavailable_utxos, + verified_deposit_utxo, + btc_pending_infos, + rbf_txs, + relayer_white_list, + extra_msg_relayer_white_list, + post_action_receiver_id_white_list, + post_action_msg_templates, + lost_found, + acc_collected_protocol_fee, + cur_available_protocol_fee, + acc_claimed_protocol_fee, + cur_reserved_protocol_fee, + acc_protocol_fee_for_gas, + } = c; + + Self { + config, + accounts, + utxos, + unavailable_utxos, + verified_deposit_utxo, + btc_pending_infos, + btc_pending_infos_by_external_id: LookupMap::new( + StorageKey::BTCPendingInfosByExternalId, + ), + rbf_txs, + relayer_white_list, + extra_msg_relayer_white_list, + post_action_receiver_id_white_list, + post_action_msg_templates, + lost_found, + acc_collected_protocol_fee, + cur_available_protocol_fee, + acc_claimed_protocol_fee, + cur_reserved_protocol_fee, + acc_protocol_fee_for_gas, + } + } +} diff --git a/contracts/satoshi-bridge/src/lib.rs b/contracts/satoshi-bridge/src/lib.rs index a0e00d82..270ff284 100644 --- a/contracts/satoshi-bridge/src/lib.rs +++ b/contracts/satoshi-bridge/src/lib.rs @@ -7,7 +7,7 @@ use near_sdk::{ log, near, require, serde::{Deserialize, Serialize}, serde_json::{self, json, Value}, - store::{IterableMap, IterableSet, LazyOption, LookupSet}, + store::{IterableMap, IterableSet, LazyOption, LookupMap, LookupSet}, AccountId, BorshStorageKey, Gas, NearToken, PanicOnDefault, Promise, PromiseOrValue, PublicKey, Timestamp, }; @@ -92,6 +92,7 @@ enum StorageKey { LostFound, PostActionMsgTemplates, ExtraMsgRelayerWhiteList, + BTCPendingInfosByExternalId, } #[derive(AccessControlRole, Deserialize, Serialize, Copy, Clone)] @@ -114,6 +115,7 @@ pub struct ContractData { pub unavailable_utxos: IterableMap, pub verified_deposit_utxo: LookupSet, pub btc_pending_infos: IterableMap, + pub btc_pending_infos_by_external_id: LookupMap, pub rbf_txs: IterableMap>, pub relayer_white_list: IterableSet, pub extra_msg_relayer_white_list: IterableSet, @@ -132,6 +134,7 @@ pub enum VersionedContractData { V0(ContractDataV0), V1(ContractDataV1), V2(ContractDataV2), + V3(ContractDataV3), Current(ContractData), } @@ -176,6 +179,9 @@ impl Contract { unavailable_utxos: IterableMap::new(StorageKey::UnavailableUTXOs), verified_deposit_utxo: LookupSet::new(StorageKey::VerifiedDepositUtxos), btc_pending_infos: IterableMap::new(StorageKey::BTCPendingInfos), + btc_pending_infos_by_external_id: LookupMap::new( + StorageKey::BTCPendingInfosByExternalId, + ), rbf_txs: IterableMap::new(StorageKey::RbfTxs), relayer_white_list: IterableSet::new(StorageKey::RelayerWhiteList), extra_msg_relayer_white_list: IterableSet::new( diff --git a/contracts/satoshi-bridge/src/zcash_utils/contract_methods.rs b/contracts/satoshi-bridge/src/zcash_utils/contract_methods.rs index 64b06280..104e3df7 100644 --- a/contracts/satoshi-bridge/src/zcash_utils/contract_methods.rs +++ b/contracts/satoshi-bridge/src/zcash_utils/contract_methods.rs @@ -113,6 +113,7 @@ impl Contract { output: Vec, max_gas_fee: Option, chain_specific_data: Option, + external_id: Option, #[callback_unwrap] last_block_height: u32, ) -> U128 { let expiry_height = self.get_expiry_height(&chain_specific_data, last_block_height); @@ -128,7 +129,14 @@ impl Contract { self.internal_config(), ); - self.create_btc_pending_info(sender_id, amount.0, target_btc_address, psbt, max_gas_fee); + self.create_btc_pending_info( + sender_id, + amount.0, + target_btc_address, + psbt, + max_gas_fee, + external_id, + ); U128(0) }