@@ -1014,11 +1014,11 @@ func (w *ETHWallet) Connect(ctx context.Context) (_ *sync.WaitGroup, err error)
10141014 w .bridges [acrossBridgeName ] = acrossBridge
10151015 }
10161016
1017- if usdcBridge , err := newUsdcBridge (w .assetID , w .net , w .node .contractBackend (), w .addr , w .node ); err != nil {
1017+ /* if usdcBridge, err := newUsdcBridge(w.assetID, w.net, w.node.contractBackend(), w.addr, w.node); err != nil {
10181018 w.log.Errorf("Failed to initialize USDC bridge: %v", err)
10191019 } else {
10201020 w.bridges[usdcBridgeName] = usdcBridge
1021- }
1021+ } */
10221022
10231023 if w .assetID == ethID {
10241024 if polygonBridge , err := newPolygonBridgeEth (ctx , w .node .contractBackend (), w .net , w .addr , w .node , w .log ); err != nil {
@@ -1034,6 +1034,15 @@ func (w *ETHWallet) Connect(ctx context.Context) (_ *sync.WaitGroup, err error)
10341034 }
10351035 }
10361036
1037+ // Register simnet bridge for testing
1038+ if w .net == dex .Simnet {
1039+ if simnetBridge , err := newSimnetBridge (w .assetID , w .net , w .node .contractBackend (), w .addr , w .node .chainConfig ().ChainID , w .log ); err != nil {
1040+ w .log .Debugf ("Simnet bridge not available: %v" , err )
1041+ } else {
1042+ w .bridges [simnetBridgeName ] = simnetBridge
1043+ }
1044+ }
1045+
10371046 w .bridgeManager , err = newBridgeManager (& bridgeManagerConfig {
10381047 ctx : ctx ,
10391048 baseChainID : w .baseChainID ,
@@ -1454,7 +1463,7 @@ func newBridgeManager(cfg *bridgeManagerConfig) (*bridgeManager, error) {
14541463 monitorInterval : cfg .monitorInterval ,
14551464 }
14561465
1457- pendingBridges , err := cfg .txDB .getPendingBridges ()
1466+ pendingBridges , err := cfg .txDB .getPendingBridges (nil )
14581467 if err != nil {
14591468 return nil , fmt .Errorf ("error getting pending bridges: %v" , err )
14601469 }
@@ -1551,7 +1560,7 @@ func (bm *bridgeManager) checkPendingBridges(ctx context.Context) {
15511560// markBridgeComplete is called when the destination wallet has confirmed the
15521561// completion of a bridge. The pending bridge is removed from the manager and
15531562// the db is updated to reflect the completion.
1554- func (bm * bridgeManager ) markBridgeComplete (initiationTxID string , completionTxIDs []string , amtReceived uint64 , isComplete bool ) {
1563+ func (bm * bridgeManager ) markBridgeComplete (initiationTxID string , completionTxIDs []string , amtReceived , fees uint64 , isComplete bool ) {
15551564 var deletePendingBridge bool
15561565 defer func () {
15571566 if ! deletePendingBridge {
@@ -1582,6 +1591,7 @@ func (bm *bridgeManager) markBridgeComplete(initiationTxID string, completionTxI
15821591 bridgeTx .BridgeCounterpartTx .IDs = completionTxIDs
15831592 bridgeTx .BridgeCounterpartTx .Complete = isComplete
15841593 bridgeTx .BridgeCounterpartTx .AmountReceived = amtReceived
1594+ bridgeTx .BridgeCounterpartTx .Fees = fees
15851595 if err := bm .txDB .storeTx (bridgeTx ); err != nil {
15861596 bm .log .Errorf ("error storing completed bridge tx: %v" , err )
15871597 return
@@ -3446,7 +3456,7 @@ func (w *assetWallet) BridgeContractApprovalStatus(ctx context.Context, bridgeNa
34463456 return w .bridgeContractApprovalStatus (ctx , bridge )
34473457}
34483458
3449- func (w * assetWallet ) approveBridgeContract (ctx context.Context , bridge bridge ) (string , error ) {
3459+ func (w * assetWallet ) approveBridgeContract (ctx context.Context , bridge bridge , onConfirm func () ) (string , error ) {
34503460 approvalStatus , err := w .bridgeContractApprovalStatus (ctx , bridge )
34513461 if err != nil {
34523462 return "" , fmt .Errorf ("error checking approval status: %w" , err )
@@ -3504,7 +3514,7 @@ func (w *assetWallet) approveBridgeContract(ctx context.Context, bridge bridge)
35043514 delete (w .approvalCache , bridgeAddr )
35053515 w .pendingApprovals [bridgeAddr ] = & pendingApproval {
35063516 txHash : tx .Hash (),
3507- onConfirm : func () {} ,
3517+ onConfirm : onConfirm ,
35083518 }
35093519 w .approvalsMtx .Unlock ()
35103520
@@ -3518,15 +3528,15 @@ func (w *assetWallet) approveBridgeContract(ctx context.Context, bridge bridge)
35183528
35193529// ApproveBridgeContract approves the bridge contract to spend tokens on behalf
35203530// of the account handled by the wallet.
3521- func (w * assetWallet ) ApproveBridgeContract (ctx context.Context , bridgeName string ) (string , error ) {
3531+ func (w * assetWallet ) ApproveBridgeContract (ctx context.Context , bridgeName string , onConfirm func () ) (string , error ) {
35223532 bridge , ok := w .bridges [bridgeName ]
35233533 if ! ok {
35243534 return "" , fmt .Errorf ("bridge %s not found" , bridgeName )
35253535 }
3526- return w .approveBridgeContract (ctx , bridge )
3536+ return w .approveBridgeContract (ctx , bridge , onConfirm )
35273537}
35283538
3529- func (w * assetWallet ) unapproveBridgeContract (ctx context.Context , bridge bridge ) (string , error ) {
3539+ func (w * assetWallet ) unapproveBridgeContract (ctx context.Context , bridge bridge , onConfirm func () ) (string , error ) {
35303540 approvalStatus , err := w .bridgeContractApprovalStatus (ctx , bridge )
35313541 if err != nil {
35323542 return "" , fmt .Errorf ("error checking approval status: %w" , err )
@@ -3581,7 +3591,7 @@ func (w *assetWallet) unapproveBridgeContract(ctx context.Context, bridge bridge
35813591 delete (w .approvalCache , bridgeAddr )
35823592 w .pendingApprovals [bridgeAddr ] = & pendingApproval {
35833593 txHash : tx .Hash (),
3584- onConfirm : func () {} ,
3594+ onConfirm : onConfirm ,
35853595 }
35863596 w .approvalsMtx .Unlock ()
35873597
@@ -3594,12 +3604,12 @@ func (w *assetWallet) unapproveBridgeContract(ctx context.Context, bridge bridge
35943604}
35953605
35963606// UnapproveBridgeContract removes the approval for the bridge contract.
3597- func (w * assetWallet ) UnapproveBridgeContract (ctx context.Context , bridgeName string ) (string , error ) {
3607+ func (w * assetWallet ) UnapproveBridgeContract (ctx context.Context , bridgeName string , onConfirm func () ) (string , error ) {
35983608 bridge , ok := w .bridges [bridgeName ]
35993609 if ! ok {
36003610 return "" , fmt .Errorf ("bridge %s not found" , bridgeName )
36013611 }
3602- return w .unapproveBridgeContract (ctx , bridge )
3612+ return w .unapproveBridgeContract (ctx , bridge , onConfirm )
36033613}
36043614
36053615func (w * assetWallet ) initiateBridge (ctx context.Context , amt uint64 , dest uint32 , bridgeName string ) (txID string , err error ) {
@@ -3674,6 +3684,43 @@ func (w *assetWallet) InitiateBridge(ctx context.Context, amt uint64, dest uint3
36743684 return "" , fmt .Errorf ("insufficient balance: %d < %d" , balance .Available , amt )
36753685 }
36763686
3687+ // Calculate the fee for the bridge initiation
3688+ bridge , ok := w .bridges [bridgeName ]
3689+ if ! ok {
3690+ return "" , fmt .Errorf ("bridge %s not found" , bridgeName )
3691+ }
3692+ initiateGas := bridge .initiateBridgeGas (w .assetID )
3693+ maxFeeRateWei , _ , err := w .recommendedMaxFeeRate (ctx )
3694+ if err != nil {
3695+ return "" , fmt .Errorf ("error calculating bridge fee rate: %w" , err )
3696+ }
3697+ maxFeeRateGwei := dexeth .WeiToGweiCeil (maxFeeRateWei )
3698+ fee := initiateGas * maxFeeRateGwei
3699+
3700+ // Check fee balance
3701+ isToken := w .assetID != w .baseChainID
3702+ if isToken {
3703+ // For tokens, the fee is paid in the base asset
3704+ w .walletsMtx .RLock ()
3705+ baseWallet := w .wallets [w .baseChainID ]
3706+ w .walletsMtx .RUnlock ()
3707+ if baseWallet == nil {
3708+ return "" , fmt .Errorf ("base wallet not found" )
3709+ }
3710+ baseBal , err := baseWallet .balance ()
3711+ if err != nil {
3712+ return "" , fmt .Errorf ("error getting base asset balance: %w" , err )
3713+ }
3714+ if baseBal .Available < fee {
3715+ return "" , fmt .Errorf ("insufficient fee balance: required %d, available %d" , fee , baseBal .Available )
3716+ }
3717+ } else {
3718+ // For base assets, the fee comes from the same balance as the amount
3719+ if balance .Available < amt + fee {
3720+ return "" , fmt .Errorf ("insufficient balance for amount + fee: required %d, available %d" , amt + fee , balance .Available )
3721+ }
3722+ }
3723+
36773724 txID , err := w .initiateBridge (ctx , amt , dest , bridgeName )
36783725 if err != nil {
36793726 return "" , err
@@ -3686,12 +3733,12 @@ func (w *assetWallet) InitiateBridge(ctx context.Context, amt uint64, dest uint3
36863733
36873734// MarkBridgeComplete is called when the bridge completion transaction has
36883735// been confirmed on the destination chain.
3689- func (w * assetWallet ) MarkBridgeComplete (initiationTxID string , completionTxIDs []string , amtReceived uint64 , complete bool ) {
3690- w .bridgeManager .markBridgeComplete (initiationTxID , completionTxIDs , amtReceived , complete )
3736+ func (w * assetWallet ) MarkBridgeComplete (initiationTxID string , completionTxIDs []string , amtReceived , fees uint64 , complete bool ) {
3737+ w .bridgeManager .markBridgeComplete (initiationTxID , completionTxIDs , amtReceived , fees , complete )
36913738}
36923739
36933740func (w * assetWallet ) pendingBridges () ([]* asset.WalletTransaction , error ) {
3694- pendingBridges , err := w .txDB .getPendingBridges ()
3741+ pendingBridges , err := w .txDB .getPendingBridges (& w . assetID )
36953742 if err != nil {
36963743 return nil , fmt .Errorf ("error getting pending bridges: %w" , err )
36973744 }
@@ -3715,7 +3762,7 @@ func (w *assetWallet) bridgeHistory(n int, refID *string, past bool) ([]*asset.W
37153762 rh := common .HexToHash (* refID )
37163763 refHash = & rh
37173764 }
3718- return w .txDB .getBridges (n , refHash , past )
3765+ return w .txDB .getBridges (& w . assetID , n , refHash , past )
37193766}
37203767
37213768// BridgeHistory retrieves a record of bridge initiations on the blockchain.
@@ -3875,10 +3922,11 @@ func (w *assetWallet) completeBridgeIfNeeded(ctx context.Context, bridgeTx *asse
38753922 }
38763923
38773924 var completionTxIDs []string
3925+ var fees uint64
38783926 var sendNote , isComplete bool
38793927 defer func () {
38803928 if sendNote {
3881- w .emit .BridgeCompleted (bridgeTx .AssetID , bridgeTx .IDs [0 ], completionTxIDs , amount , isComplete )
3929+ w .emit .BridgeCompleted (bridgeTx .AssetID , bridgeTx .IDs [0 ], completionTxIDs , amount , fees , isComplete )
38823930 }
38833931 }()
38843932
@@ -3913,6 +3961,7 @@ func (w *assetWallet) completeBridgeIfNeeded(ctx context.Context, bridgeTx *asse
39133961 if err != nil {
39143962 return fmt .Errorf ("error completing bridge: %w" , err )
39153963 }
3964+ // Send notification with isComplete=false. Fees are 0 since tx isn't confirmed.
39163965 completionTxIDs = []string {txID }
39173966 sendNote = true
39183967 return nil
@@ -3926,6 +3975,7 @@ func (w *assetWallet) completeBridgeIfNeeded(ctx context.Context, bridgeTx *asse
39263975 if ! bridge .requiresFollowUpCompletion (w .assetID ) {
39273976 isComplete = true
39283977 completionTxIDs = []string {wt .ID }
3978+ fees = wt .Fees
39293979 sendNote = true
39303980 return nil
39313981 }
@@ -3940,6 +3990,10 @@ func (w *assetWallet) completeBridgeIfNeeded(ctx context.Context, bridgeTx *asse
39403990 completionTxIDs = []string {wt .PreviousBridgeCompletionID , wt .ID }
39413991 isComplete = complete
39423992 if isComplete {
3993+ fees = wt .Fees
3994+ if prevTx , err := w .txDB .getTx (common .HexToHash (wt .PreviousBridgeCompletionID )); err == nil && prevTx != nil {
3995+ fees += prevTx .Fees
3996+ }
39433997 sendNote = true
39443998 }
39453999 return nil
@@ -3954,6 +4008,7 @@ func (w *assetWallet) completeBridgeIfNeeded(ctx context.Context, bridgeTx *asse
39544008 }
39554009 if ! required {
39564010 completionTxIDs = []string {wt .ID }
4011+ fees = wt .Fees
39574012 isComplete = true
39584013 sendNote = true
39594014 return nil
@@ -3963,9 +4018,9 @@ func (w *assetWallet) completeBridgeIfNeeded(ctx context.Context, bridgeTx *asse
39634018 if err != nil {
39644019 return fmt .Errorf ("error completing bridge: %w" , err )
39654020 }
4021+ // Send notification with isComplete=false. Fees are 0 since follow-up tx isn't confirmed.
39664022 completionTxIDs = []string {wt .ID , txID }
39674023 sendNote = true
3968-
39694024 return nil
39704025}
39714026
@@ -3976,7 +4031,7 @@ func (w *assetWallet) CompleteBridge(ctx context.Context, bridgeTx *asset.Bridge
39764031}
39774032
39784033// SupportedDestinations returns the list of asset IDs that the wallet can bridge funds to.
3979- func (w * assetWallet ) SupportedDestinations () ( map [uint32 ][]string , error ) {
4034+ func (w * assetWallet ) SupportedDestinations () map [uint32 ][]string {
39804035 allBridgeDestinations := map [uint32 ][]string {}
39814036
39824037 for bridgeName , bridge := range w .bridges {
@@ -3991,7 +4046,7 @@ func (w *assetWallet) SupportedDestinations() (map[uint32][]string, error) {
39914046 allBridgeDestinations [destination ] = append (allBridgeDestinations [destination ], bridgeName )
39924047 }
39934048 }
3994- return allBridgeDestinations , nil
4049+ return allBridgeDestinations
39954050}
39964051
39974052// BridgeInitiationFeesAndLimits returns the estimated fees, limits, and whether the
@@ -4008,7 +4063,7 @@ func (w *assetWallet) BridgeInitiationFeesAndLimits(bridgeName string, destAsset
40084063 if err != nil {
40094064 return 0 , [2 ]uint64 {}, false , fmt .Errorf ("error calculating bridge fee rate: %w" , err )
40104065 }
4011- maxFeeRateGwei := dexeth .WeiToGwei (maxFeeRateWei )
4066+ maxFeeRateGwei := dexeth .WeiToGweiCeil (maxFeeRateWei )
40124067
40134068 minBig , maxBig , hasLimits , err := bridge .bridgeLimits (w .assetID , destAssetID )
40144069 if err != nil {
@@ -4021,23 +4076,50 @@ func (w *assetWallet) BridgeInitiationFeesAndLimits(bridgeName string, destAsset
40214076 return initiateGas * maxFeeRateGwei , [2 ]uint64 {min , max }, hasLimits , nil
40224077}
40234078
4024- // BridgeCompletionFees returns the estimated fees for completing a bridge.
4025- func (w * assetWallet ) BridgeCompletionFees (bridgeName string ) (uint64 , error ) {
4079+ // BridgeCompletionFees returns the estimated fees for completing a bridge,
4080+ // and whether the wallet has sufficient balance available to pay those fees.
4081+ func (w * assetWallet ) BridgeCompletionFees (bridgeName string ) (uint64 , bool , error ) {
40264082 bridge , found := w .bridges [bridgeName ]
40274083 if ! found {
4028- return 0 , fmt .Errorf ("bridge %s not found" , bridgeName )
4084+ return 0 , false , fmt .Errorf ("bridge %s not found" , bridgeName )
40294085 }
40304086
40314087 completeGas := bridge .completeBridgeGas (w .assetID )
40324088 followUpCompleteGas := bridge .followUpCompleteBridgeGas ()
40334089
40344090 maxFeeRateWei , _ , err := w .recommendedMaxFeeRate (w .ctx )
40354091 if err != nil {
4036- return 0 , fmt .Errorf ("error calculating bridge fee rate: %w" , err )
4092+ return 0 , false , fmt .Errorf ("error calculating bridge fee rate: %w" , err )
4093+ }
4094+ maxFeeRateGwei := dexeth .WeiToGweiCeil (maxFeeRateWei )
4095+
4096+ fees := (completeGas + followUpCompleteGas ) * maxFeeRateGwei
4097+
4098+ // Get available fee balance
4099+ var availableFeeBalance uint64
4100+ if w .assetID == w .baseChainID {
4101+ // Base asset - fees come from own balance
4102+ bal , err := w .balance ()
4103+ if err != nil {
4104+ return fees , false , fmt .Errorf ("error getting balance: %w" , err )
4105+ }
4106+ availableFeeBalance = bal .Available
4107+ } else {
4108+ // Token - fees come from parent asset
4109+ w .walletsMtx .RLock ()
4110+ baseWallet := w .wallets [w .baseChainID ]
4111+ w .walletsMtx .RUnlock ()
4112+ if baseWallet == nil {
4113+ return fees , false , fmt .Errorf ("base wallet not found" )
4114+ }
4115+ bal , err := baseWallet .balance ()
4116+ if err != nil {
4117+ return fees , false , fmt .Errorf ("error getting base asset balance: %w" , err )
4118+ }
4119+ availableFeeBalance = bal .Available
40374120 }
4038- maxFeeRateGwei := dexeth .WeiToGwei (maxFeeRateWei )
40394121
4040- return ( completeGas + followUpCompleteGas ) * maxFeeRateGwei , nil
4122+ return fees , availableFeeBalance >= fees , nil
40414123}
40424124
40434125func (w * ETHWallet ) canRedeemWithBundler (lotSize uint64 , gases * dexeth.Gases , n uint64 ) (bool , error ) {
0 commit comments