• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

decentraland / marketplace / 9999902307

18 Jul 2024 11:10PM UTC coverage: 66.468% (-0.2%) from 66.682%
9999902307

Pull #2268

github

meelrossi
fix isBid trade calculation
Pull Request #2268: feat: add accept trade flow

2635 of 5152 branches covered (51.15%)

Branch coverage included in aggregate %.

71 of 128 new or added lines in 11 files covered. (55.47%)

3 existing lines in 2 files now uncovered.

7714 of 10418 relevant lines covered (74.04%)

77.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.52
/webapp/src/modules/bid/sagas.ts
1
import { History } from 'history'
2
import { takeEvery, put, select, call, all, getContext } from 'redux-saga/effects'
32✔
3
import { Bid, RentalStatus, Trade, TradeCreation } from '@dcl/schemas'
32✔
4
import { showToast } from 'decentraland-dapps/dist/modules/toast/actions'
32✔
5
import { waitForTx } from 'decentraland-dapps/dist/modules/transaction/utils'
32✔
6
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
32✔
7
import { sendTransaction } from 'decentraland-dapps/dist/modules/wallet/utils'
32✔
8
import { ContractData, ContractName, getContract as getDCLContract } from 'decentraland-transactions'
32✔
9
import { isErrorWithMessage } from '../../lib/error'
32✔
10
import * as tradeUtils from '../../utils/trades'
32✔
11
import { isNFT } from '../asset/utils'
32✔
12
import { getContract } from '../contract/selectors'
32✔
13
import { getIsBidsOffChainEnabled } from '../features/selectors'
32✔
14
import { getCurrentNFT } from '../nft/selectors'
32✔
15
import { getRentalById } from '../rental/selectors'
32✔
16
import { isRentalListingOpen, waitUntilRentalChangesStatus } from '../rental/utils'
32✔
17
import { locations } from '../routing/locations'
32✔
18
import { getBidPlacedSuccessToast } from '../toast/toasts'
32✔
19
import { MarketplaceAPI } from '../vendor/decentraland/marketplace/api'
20
import { VendorName } from '../vendor/types'
32✔
21
import { VendorFactory } from '../vendor/VendorFactory'
32✔
22
import { getWallet } from '../wallet/selectors'
32✔
23
import {
24
  PLACE_BID_REQUEST,
25
  PlaceBidRequestAction,
26
  placeBidFailure,
27
  placeBidSuccess,
28
  FETCH_BIDS_BY_ADDRESS_REQUEST,
29
  fetchBidsByAddressSuccess,
30
  fetchBidsByAddressFailure,
31
  FetchBidsByAddressRequestAction,
32
  ACCEPT_BID_REQUEST,
33
  CANCEL_BID_REQUEST,
34
  AcceptBidRequestAction,
35
  acceptBidSuccess,
36
  acceptBidFailure,
37
  CancelBidRequestAction,
38
  cancelBidSuccess,
39
  cancelBidFailure,
40
  FETCH_BIDS_BY_NFT_REQUEST,
41
  FetchBidsByNFTRequestAction,
42
  fetchBidsByNFTSuccess,
43
  fetchBidsByNFTFailure,
44
  acceptBidtransactionSubmitted
45
} from './actions'
32✔
46
import * as bidUtils from './utils'
32✔
47

48
export function* bidSaga(marketplaceAPI: MarketplaceAPI) {
264✔
49
  yield takeEvery(PLACE_BID_REQUEST, handlePlaceBidRequest)
264✔
50
  yield takeEvery(ACCEPT_BID_REQUEST, handleAcceptBidRequest)
264✔
51
  yield takeEvery(CANCEL_BID_REQUEST, handleCancelBidRequest)
264✔
52
  yield takeEvery(FETCH_BIDS_BY_ADDRESS_REQUEST, handleFetchBidsByAddressRequest)
264✔
53
  yield takeEvery(FETCH_BIDS_BY_NFT_REQUEST, handleFetchBidsByNFTRequest)
264✔
54

55
  function* handlePlaceBidRequest(action: PlaceBidRequestAction) {
56
    const { asset, price, expiresAt, fingerprint } = action.payload
6✔
57
    try {
6✔
58
      const wallet = (yield select(getWallet)) as ReturnType<typeof getWallet>
6✔
59
      if (!wallet) {
6!
60
        throw new Error("Can't place a bid without a wallet")
×
61
      }
62

63
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
6✔
64

65
      if (isBidsOffchainEnabled) {
6✔
66
        const history: History = yield getContext('history')
4✔
67
        const trade: TradeCreation = yield call([bidUtils, 'createBidTrade'], asset, price, expiresAt, fingerprint)
4✔
68
        yield call([marketplaceAPI, 'addTrade'], trade)
4✔
69
        yield put(placeBidSuccess(asset, price, expiresAt, asset.chainId, wallet.address, fingerprint))
2✔
70
        yield put(showToast(getBidPlacedSuccessToast(asset)))
2✔
71
        history.push(
2✔
72
          isNFT(asset) ? locations.nft(asset.contractAddress, asset.tokenId) : locations.item(asset.contractAddress, asset.itemId)
×
73
        )
74
      } else {
75
        if (isNFT(asset)) {
2✔
76
          const { bidService } = VendorFactory.build(asset.vendor)
1✔
77

78
          if (!bidService) {
1!
79
            throw new Error("Couldn't find a valid bid service for vendor")
×
80
          }
81
          const txHash = (yield call([bidService, 'place'], wallet, asset, price, expiresAt, fingerprint)) as Awaited<
1✔
82
            ReturnType<typeof bidService.place>
83
          >
84
          yield put(placeBidSuccess(asset, price, expiresAt, asset.chainId, wallet.address, fingerprint, txHash))
1✔
85
        } else {
86
          throw new Error('Only NFTs are supported for bidding')
1✔
87
        }
88
      }
89
    } catch (error) {
90
      yield put(
5✔
91
        placeBidFailure(asset, price, expiresAt, isErrorWithMessage(error) ? error.message : t('global.unknown_error'), fingerprint)
5!
92
      )
93
    }
94
  }
95

96
  function* handleAcceptBidRequest(action: AcceptBidRequestAction) {
97
    const { bid } = action.payload
8✔
98
    let txHash = ''
8✔
99
    try {
8✔
100
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
8✔
101
      if ('tradeId' in bid) {
8✔
102
        if (isBidsOffchainEnabled) {
2!
103
          const trade: Trade = yield call([marketplaceAPI, 'fetchTrade'], bid.tradeId)
2✔
104
          const tradeToAccept = tradeUtils.getTradeToAccept(trade)
1✔
105
          const offchainMarketplaceContract: ContractData = yield call(getDCLContract, ContractName.OffChainMarketplace, trade.chainId)
1✔
106
          txHash = yield call(
1✔
107
            sendTransaction as (contract: ContractData, contractMethodName: string, ...contractArguments: any[]) => Promise<string>,
108
            offchainMarketplaceContract,
109
            'function accept(Trade[] calldata _trades) external;',
110
            [tradeToAccept]
111
          )
112
        } else {
NEW
113
          throw new Error('not able to accept offchain bids')
×
114
        }
115
      } else {
116
        const contract = (yield select(getContract, {
6✔
117
          address: bid.contractAddress
118
        })) as ReturnType<typeof getContract>
119
        if (!contract || !contract.vendor) {
6✔
120
          throw new Error(
1✔
121
            contract
1!
122
              ? `Couldn't find a valid vendor for contract ${contract?.address}`
123
              : `Couldn't find a valid vendor for contract ${bid.contractAddress}`
124
          )
125
        }
126
        const vendor = (yield call([VendorFactory, 'build'], contract.vendor)) as ReturnType<typeof VendorFactory.build>
5✔
127
        if (!vendor.bidService) {
4!
NEW
128
          throw new Error("Couldn't find a valid bid service for vendor")
×
129
        }
130

131
        const wallet = (yield select(getWallet)) as ReturnType<typeof getWallet>
4✔
132
        txHash = (yield call([vendor.bidService, 'accept'], wallet, bid)) as Awaited<ReturnType<typeof vendor.bidService.accept>>
4✔
133
      }
134

135
      yield put(acceptBidtransactionSubmitted(bid, txHash))
4✔
136
      const nft = (yield select(getCurrentNFT)) as ReturnType<typeof getCurrentNFT>
4✔
137
      if (nft?.openRentalId) {
4✔
138
        yield call(waitForTx, txHash)
2✔
139
        const rental = (yield select(getRentalById, nft.openRentalId)) as ReturnType<typeof getRentalById>
1✔
140
        if (isRentalListingOpen(rental)) {
1✔
141
          yield call(waitUntilRentalChangesStatus, nft, RentalStatus.CANCELLED)
1✔
142
        }
143
      }
144

145
      yield put(acceptBidSuccess(bid))
3✔
146
    } catch (error) {
147
      yield put(acceptBidFailure(bid, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
5!
148
    }
149
  }
150

151
  function* handleCancelBidRequest(action: CancelBidRequestAction) {
152
    const { bid } = action.payload
×
153
    try {
×
154
      const contract = (yield select(getContract, {
×
155
        address: bid.contractAddress
156
      })) as ReturnType<typeof getContract>
157

158
      if (!contract || !contract.vendor) {
×
159
        throw new Error(`Couldn't find a valid vendor for contract ${contract?.address}`)
×
160
      }
161
      const { bidService } = VendorFactory.build(contract.vendor)
×
162
      if (!bidService) {
×
163
        throw new Error("Couldn't find a valid bid service for vendor")
×
164
      }
165

166
      const wallet = (yield select(getWallet)) as ReturnType<typeof getWallet>
×
167
      const txHash = (yield call([bidService, 'cancel'], wallet, bid)) as Awaited<ReturnType<typeof bidService.cancel>>
×
168

169
      yield put(cancelBidSuccess(bid, txHash))
×
170
    } catch (error) {
171
      yield put(cancelBidFailure(bid, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
×
172
    }
173
  }
174

175
  function* handleFetchBidsByAddressRequest(action: FetchBidsByAddressRequestAction) {
176
    const { address } = action.payload
×
177
    try {
×
178
      let sellerBids: Bid[] = []
×
179
      let bidderBids: Bid[] = []
×
180

NEW
181
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
×
NEW
182
      if (isBidsOffchainEnabled) {
×
NEW
183
        const bids = (yield all([
×
184
          call([marketplaceAPI, 'fetchBids'], { seller: address }),
185
          call([marketplaceAPI, 'fetchBids'], { bidder: address })
186
        ])) as [Awaited<ReturnType<typeof marketplaceAPI.fetchBids>>, Awaited<ReturnType<typeof marketplaceAPI.fetchBids>>]
NEW
187
        sellerBids = bids[0].results
×
NEW
188
        bidderBids = bids[1].results
×
189
      } else {
NEW
190
        for (const vendorName of Object.values(VendorName)) {
×
NEW
191
          const { bidService } = VendorFactory.build(vendorName)
×
NEW
192
          if (bidService === undefined) {
×
NEW
193
            continue
×
194
          }
195

NEW
196
          const bids = (yield all([call([bidService, 'fetchBySeller'], address), call([bidService, 'fetchByBidder'], address)])) as [
×
197
            Awaited<ReturnType<typeof bidService.fetchBySeller>>,
198
            Awaited<ReturnType<typeof bidService.fetchByBidder>>
199
          ]
NEW
200
          sellerBids = sellerBids.concat(bids[0])
×
NEW
201
          bidderBids = bidderBids.concat(bids[1])
×
202
        }
203
      }
UNCOV
204
      yield put(fetchBidsByAddressSuccess(address, sellerBids, bidderBids))
×
205
    } catch (error) {
206
      yield put(fetchBidsByAddressFailure(address, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
×
207
    }
208
  }
209

210
  function* handleFetchBidsByNFTRequest(action: FetchBidsByNFTRequestAction) {
211
    const { nft } = action.payload
×
212
    try {
×
213
      const { bidService } = VendorFactory.build(nft.vendor)
×
214
      if (!bidService) {
×
215
        throw new Error("Couldn't find a valid bid service for vendor")
×
216
      }
217

218
      const bids = (yield call([bidService, 'fetchByNFT'], nft)) as Awaited<ReturnType<typeof bidService.fetchByNFT>>
×
219

220
      yield put(fetchBidsByNFTSuccess(nft, bids))
×
221
    } catch (error) {
222
      yield put(fetchBidsByNFTFailure(nft, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
×
223
    }
224
  }
225
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc