@@ -28,6 +28,7 @@ pub use pallet::*;
28
28
#[ frame_support:: pallet]
29
29
pub mod pallet {
30
30
use codec:: { Decode , Encode , EncodeLike } ;
31
+ use frame_support:: traits:: { Currency , ExistenceRequirement :: AllowDeath , WithdrawReasons } ;
31
32
pub use frame_support:: {
32
33
pallet_prelude:: * , traits:: StorageVersion , weights:: GetDispatchInfo , PalletId , Parameter ,
33
34
} ;
@@ -36,10 +37,9 @@ pub mod pallet {
36
37
{ self as system} ,
37
38
} ;
38
39
use scale_info:: TypeInfo ;
39
- pub use sp_core:: U256 ;
40
40
use sp_runtime:: {
41
41
traits:: { AccountIdConversion , Dispatchable } ,
42
- RuntimeDebug ,
42
+ RuntimeDebug , SaturatedConversion ,
43
43
} ;
44
44
use sp_std:: prelude:: * ;
45
45
@@ -49,6 +49,8 @@ pub mod pallet {
49
49
pub type BridgeChainId = u8 ;
50
50
pub type DepositNonce = u64 ;
51
51
pub type ResourceId = [ u8 ; 32 ] ;
52
+ pub type BalanceOf < T > =
53
+ <<T as Config >:: Currency as Currency < <T as frame_system:: Config >:: AccountId > >:: Balance ;
52
54
53
55
/// Helper function to concatenate a chain ID and some bytes to produce a resource ID.
54
56
/// The common format is (31 bytes unique ID + 1 byte chain ID).
@@ -83,7 +85,7 @@ pub mod pallet {
83
85
84
86
#[ derive( PartialEq , Eq , Clone , Encode , Decode , RuntimeDebug , TypeInfo ) ]
85
87
pub enum BridgeEvent {
86
- FungibleTransfer ( BridgeChainId , DepositNonce , ResourceId , U256 , Vec < u8 > ) ,
88
+ FungibleTransfer ( BridgeChainId , DepositNonce , ResourceId , u128 , Vec < u8 > ) ,
87
89
NonFungibleTransfer ( BridgeChainId , DepositNonce , ResourceId , Vec < u8 > , Vec < u8 > , Vec < u8 > ) ,
88
90
GenericTransfer ( BridgeChainId , DepositNonce , ResourceId , Vec < u8 > ) ,
89
91
}
@@ -154,8 +156,14 @@ pub mod pallet {
154
156
#[ pallet:: constant]
155
157
type BridgeChainId : Get < BridgeChainId > ;
156
158
159
+ /// Currency impl
160
+ type Currency : Currency < Self :: AccountId > ;
161
+
157
162
#[ pallet:: constant]
158
163
type ProposalLifetime : Get < Self :: BlockNumber > ;
164
+
165
+ /// Treasury account to receive assets fee
166
+ type TreasuryAccount : Get < Self :: AccountId > ;
159
167
}
160
168
161
169
#[ pallet:: event]
@@ -171,7 +179,7 @@ pub mod pallet {
171
179
RelayerRemoved ( T :: AccountId ) ,
172
180
/// FungibleTransfer is for relaying fungibles (dest_id, nonce, resource_id, amount,
173
181
/// recipient)
174
- FungibleTransfer ( BridgeChainId , DepositNonce , ResourceId , U256 , Vec < u8 > ) ,
182
+ FungibleTransfer ( BridgeChainId , DepositNonce , ResourceId , u128 , Vec < u8 > ) ,
175
183
/// NonFungibleTransfer is for relaying NFTs (dest_id, nonce, resource_id, token_id,
176
184
/// recipient, metadata)
177
185
NonFungibleTransfer ( BridgeChainId , DepositNonce , ResourceId , Vec < u8 > , Vec < u8 > , Vec < u8 > ) ,
@@ -189,6 +197,8 @@ pub mod pallet {
189
197
ProposalSucceeded ( BridgeChainId , DepositNonce ) ,
190
198
/// Execution of call failed
191
199
ProposalFailed ( BridgeChainId , DepositNonce ) ,
200
+ /// Update bridge transfer fee
201
+ FeeUpdated { dest_id : BridgeChainId , fee : BalanceOf < T > } ,
192
202
}
193
203
194
204
#[ pallet:: error]
@@ -223,6 +233,16 @@ pub mod pallet {
223
233
ProposalAlreadyComplete ,
224
234
/// Lifetime of proposal has been exceeded
225
235
ProposalExpired ,
236
+ /// Too expensive fee for withdrawn asset
237
+ FeeTooExpensive ,
238
+ /// No fee with the ID was found
239
+ FeeDoesNotExist ,
240
+ /// Balance too low to withdraw
241
+ InsufficientBalance ,
242
+
243
+ CannotPayAsFee ,
244
+
245
+ NonceOverFlow ,
226
246
}
227
247
228
248
#[ pallet:: storage]
@@ -261,10 +281,17 @@ pub mod pallet {
261
281
#[ pallet:: getter( fn resources) ]
262
282
pub type Resources < T > = StorageMap < _ , Blake2_256 , ResourceId , Vec < u8 > > ;
263
283
284
+ // ChainBridge Service(https://github.com/litentry/ChainBridge) read this storage for each block,
285
+ // and if this storage has value, it will perform cross-chain transfer.
286
+ // For more details, see at: https://github.com/litentry/ChainBridge/blob/main/chains/substrate/listener.go#L186-L237
264
287
#[ pallet:: storage]
265
288
#[ pallet:: getter( fn bridge_events) ]
266
289
pub type BridgeEvents < T > = StorageValue < _ , Vec < BridgeEvent > , ValueQuery > ;
267
290
291
+ #[ pallet:: storage]
292
+ #[ pallet:: getter( fn bridge_fee) ]
293
+ pub type BridgeFee < T : Config > = StorageMap < _ , Twox64Concat , BridgeChainId , BalanceOf < T > > ;
294
+
268
295
#[ pallet:: hooks]
269
296
impl < T : Config > Hooks < T :: BlockNumber > for Pallet < T > {
270
297
fn on_initialize ( _n : T :: BlockNumber ) -> Weight {
@@ -352,6 +379,23 @@ pub mod pallet {
352
379
Self :: unregister_relayer ( v)
353
380
}
354
381
382
+ /// Change extra bridge transfer fee that user should pay
383
+ ///
384
+ /// # <weight>
385
+ /// - O(1) lookup and insert
386
+ /// # </weight>
387
+ #[ pallet:: weight( 195_000_000 ) ]
388
+ pub fn update_fee (
389
+ origin : OriginFor < T > ,
390
+ dest_id : BridgeChainId ,
391
+ fee : BalanceOf < T > ,
392
+ ) -> DispatchResult {
393
+ T :: BridgeCommitteeOrigin :: ensure_origin ( origin) ?;
394
+ BridgeFee :: < T > :: insert ( dest_id, fee) ;
395
+ Self :: deposit_event ( Event :: FeeUpdated { dest_id, fee } ) ;
396
+ Ok ( ( ) )
397
+ }
398
+
355
399
/// Commits a vote in favour of the provided proposal.
356
400
///
357
401
/// If a proposal with the given nonce and source chain ID does not already exist, it will
@@ -448,6 +492,16 @@ pub mod pallet {
448
492
Self :: chains ( id) . is_some ( )
449
493
}
450
494
495
+ /// Increments the deposit nonce for the specified chain ID
496
+ fn bump_nonce ( id : BridgeChainId ) -> Result < DepositNonce , Error < T > > {
497
+ let nonce = Self :: chains ( id) . unwrap_or_default ( ) ;
498
+ let new_nonce = nonce. checked_add ( 1u64 ) . ok_or ( Error :: < T > :: NonceOverFlow ) ;
499
+ if new_nonce. is_ok ( ) {
500
+ ChainNonces :: < T > :: insert ( id, new_nonce. as_ref ( ) . unwrap ( ) ) ;
501
+ }
502
+ new_nonce
503
+ }
504
+
451
505
// *** Admin methods ***
452
506
453
507
/// Set a new voting threshold
@@ -603,6 +657,50 @@ pub mod pallet {
603
657
Self :: deposit_event ( Event :: ProposalRejected ( src_id, nonce) ) ;
604
658
Ok ( ( ) )
605
659
}
660
+
661
+ /// Initiates a transfer of a fungible asset out of the chain. This should be called by
662
+ /// another pallet.
663
+ pub fn transfer_fungible (
664
+ sender : T :: AccountId ,
665
+ dest_id : BridgeChainId ,
666
+ resource_id : ResourceId ,
667
+ to : Vec < u8 > ,
668
+ amount : BalanceOf < T > ,
669
+ ) -> DispatchResult {
670
+ ensure ! ( Self :: chain_whitelisted( dest_id) , Error :: <T >:: ChainNotWhitelisted ) ;
671
+ let fee: BalanceOf < T > =
672
+ BridgeFee :: < T > :: get ( dest_id) . ok_or ( Error :: < T > :: CannotPayAsFee ) ?;
673
+ // No need to transfer to to dest chains if it's not enough to pay fee.
674
+ ensure ! ( amount > fee, Error :: <T >:: FeeTooExpensive ) ;
675
+
676
+ let actual_amount = amount - fee;
677
+ // Ensure we have sufficient free balance
678
+ let balance: BalanceOf < T > = T :: Currency :: free_balance ( & sender) ;
679
+ ensure ! ( balance >= amount, Error :: <T >:: InsufficientBalance ) ;
680
+
681
+ T :: Currency :: withdraw ( & sender, actual_amount, WithdrawReasons :: TRANSFER , AllowDeath ) ?;
682
+ T :: Currency :: burn ( actual_amount) ;
683
+
684
+ // transfer fee to treasury
685
+ T :: Currency :: transfer ( & sender, & T :: TreasuryAccount :: get ( ) , fee, AllowDeath ) ?;
686
+
687
+ let nonce = Self :: bump_nonce ( dest_id) ?;
688
+ BridgeEvents :: < T > :: append ( BridgeEvent :: FungibleTransfer (
689
+ dest_id,
690
+ nonce,
691
+ resource_id,
692
+ actual_amount. saturated_into :: < u128 > ( ) ,
693
+ to. clone ( ) ,
694
+ ) ) ;
695
+ Self :: deposit_event ( Event :: FungibleTransfer (
696
+ dest_id,
697
+ nonce,
698
+ resource_id,
699
+ actual_amount. saturated_into :: < u128 > ( ) ,
700
+ to,
701
+ ) ) ;
702
+ Ok ( ( ) )
703
+ }
606
704
}
607
705
608
706
/// Simple ensure origin for the bridge account
0 commit comments