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

decentraland / marketplace / 10032821866

22 Jul 2024 12:58AM UTC coverage: 66.714% (+0.1%) from 66.605%
10032821866

push

github

web-flow
feat: show correctly bids on nfts (#2270)

2654 of 5162 branches covered (51.41%)

Branch coverage included in aggregate %.

24 of 33 new or added lines in 8 files covered. (72.73%)

1 existing line in 1 file now uncovered.

7762 of 10451 relevant lines covered (74.27%)

77.9 hits per line

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

73.21
/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 { isErrorWithMessage } from '../../lib/error'
32✔
8
import { isNFT } from '../asset/utils'
32✔
9
import { getContract } from '../contract/selectors'
32✔
10
import { getIsBidsOffChainEnabled } from '../features/selectors'
32✔
11
import { getCurrentNFT } from '../nft/selectors'
32✔
12
import { getRentalById } from '../rental/selectors'
32✔
13
import { isRentalListingOpen, waitUntilRentalChangesStatus } from '../rental/utils'
32✔
14
import { locations } from '../routing/locations'
32✔
15
import { getBidPlacedSuccessToast } from '../toast/toasts'
32✔
16
import { BidService } from '../vendor/decentraland'
17
import { TradeService } from '../vendor/decentraland/TradeService'
18
import { VendorName } from '../vendor/types'
32✔
19
import { VendorFactory } from '../vendor/VendorFactory'
32✔
20
import { getWallet } from '../wallet/selectors'
32✔
21
import {
22
  PLACE_BID_REQUEST,
23
  PlaceBidRequestAction,
24
  placeBidFailure,
25
  placeBidSuccess,
26
  FETCH_BIDS_BY_ADDRESS_REQUEST,
27
  fetchBidsByAddressSuccess,
28
  fetchBidsByAddressFailure,
29
  FetchBidsByAddressRequestAction,
30
  ACCEPT_BID_REQUEST,
31
  CANCEL_BID_REQUEST,
32
  AcceptBidRequestAction,
33
  acceptBidSuccess,
34
  acceptBidFailure,
35
  CancelBidRequestAction,
36
  cancelBidSuccess,
37
  cancelBidFailure,
38
  FETCH_BIDS_BY_ASSET_REQUEST,
39
  FetchBidsByAssetRequestAction,
40
  fetchBidsByAssetSuccess,
41
  fetchBidsByAssetFailure,
42
  acceptBidtransactionSubmitted
43
} from './actions'
32✔
44
import * as bidUtils from './utils'
32✔
45

46
export function* bidSaga(bidService: BidService, tradeService: TradeService) {
274✔
47
  yield takeEvery(PLACE_BID_REQUEST, handlePlaceBidRequest)
274✔
48
  yield takeEvery(ACCEPT_BID_REQUEST, handleAcceptBidRequest)
274✔
49
  yield takeEvery(CANCEL_BID_REQUEST, handleCancelBidRequest)
274✔
50
  yield takeEvery(FETCH_BIDS_BY_ADDRESS_REQUEST, handleFetchBidsByAddressRequest)
274✔
51
  yield takeEvery(FETCH_BIDS_BY_ASSET_REQUEST, handleFetchBidsByAssetRequest)
274✔
52

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

61
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
6✔
62

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

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

94
  function* handleAcceptBidRequest(action: AcceptBidRequestAction) {
95
    const { bid } = action.payload
9✔
96
    let txHash = ''
9✔
97
    try {
9✔
98
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
9✔
99
      if (bidUtils.isBidTrade(bid)) {
9✔
100
        if (!isBidsOffchainEnabled) {
3!
101
          throw new Error('not able to accept offchain bids')
×
102
        }
103

104
        const trade: Trade = yield call([tradeService, 'fetchTrade'], bid.tradeId)
3✔
105
        txHash = yield call([tradeService, 'accept'], trade)
1✔
106
      } else {
107
        const contract = (yield select(getContract, {
6✔
108
          address: bid.contractAddress
109
        })) as ReturnType<typeof getContract>
110
        if (!contract || !contract.vendor) {
6✔
111
          throw new Error(
1✔
112
            contract
1!
113
              ? `Couldn't find a valid vendor for contract ${contract?.address}`
114
              : `Couldn't find a valid vendor for contract ${bid.contractAddress}`
115
          )
116
        }
117
        const vendor = (yield call([VendorFactory, 'build'], contract.vendor)) as ReturnType<typeof VendorFactory.build>
5✔
118
        if (!vendor.bidService) {
4!
119
          throw new Error("Couldn't find a valid bid service for vendor")
×
120
        }
121

122
        const wallet = (yield select(getWallet)) as ReturnType<typeof getWallet>
4✔
123
        txHash = (yield call([vendor.bidService, 'accept'], wallet, bid)) as Awaited<ReturnType<typeof vendor.bidService.accept>>
4✔
124
      }
125

126
      yield put(acceptBidtransactionSubmitted(bid, txHash))
4✔
127
      const nft = (yield select(getCurrentNFT)) as ReturnType<typeof getCurrentNFT>
4✔
128
      if (nft?.openRentalId) {
4✔
129
        yield call(waitForTx, txHash)
2✔
130
        const rental = (yield select(getRentalById, nft.openRentalId)) as ReturnType<typeof getRentalById>
1✔
131
        if (isRentalListingOpen(rental)) {
1✔
132
          yield call(waitUntilRentalChangesStatus, nft, RentalStatus.CANCELLED)
1✔
133
        }
134
      }
135

136
      yield put(acceptBidSuccess(bid))
3✔
137
    } catch (error) {
138
      yield put(acceptBidFailure(bid, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
6!
139
    }
140
  }
141

142
  function* handleCancelBidRequest(action: CancelBidRequestAction) {
143
    const { bid } = action.payload
5✔
144
    let txHash = ''
5✔
145
    try {
5✔
146
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
5✔
147
      if (bidUtils.isBidTrade(bid)) {
5✔
148
        if (!isBidsOffchainEnabled) {
1!
149
          throw new Error('not able to cancel offchain bids')
×
150
        }
151

152
        const trade: Trade = yield call([tradeService, 'fetchTrade'], bid.tradeId)
1✔
153
        txHash = yield call([tradeService, 'cancel'], trade)
1✔
154
      } else {
155
        const contract = (yield select(getContract, {
4✔
156
          address: bid.contractAddress
157
        })) as ReturnType<typeof getContract>
158

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

167
        const wallet = (yield select(getWallet)) as ReturnType<typeof getWallet>
2✔
168
        txHash = (yield call([bidService, 'cancel'], wallet, bid)) as Awaited<ReturnType<typeof bidService.cancel>>
2✔
169
      }
170

171
      yield put(cancelBidSuccess(bid, txHash))
2✔
172
    } catch (error) {
173
      yield put(cancelBidFailure(bid, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
3!
174
    }
175
  }
176

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

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

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

212
  function* handleFetchBidsByAssetRequest(action: FetchBidsByAssetRequestAction) {
213
    const { asset } = action.payload
4✔
214
    let bids: Bid[] = []
4✔
215
    try {
4✔
216
      const isBidsOffchainEnabled: boolean = yield select(getIsBidsOffChainEnabled)
4✔
217
      if (isBidsOffchainEnabled) {
4✔
218
        const response: Awaited<ReturnType<typeof bidService.fetchBids>> = yield call([bidService, 'fetchBids'], {
2✔
219
          contractAddress: asset.contractAddress,
220
          ...(isNFT(asset) ? { tokenId: asset.tokenId } : { itemId: asset.itemId })
2!
221
        })
222
        bids = response.results
1✔
223
      } else if (isNFT(asset)) {
2✔
224
        const { bidService } = VendorFactory.build(asset.vendor)
2✔
225
        if (!bidService) {
2!
NEW
226
          throw new Error("Couldn't find a valid bid service for vendor")
×
227
        }
228

229
        bids = (yield call([bidService, 'fetchByNFT'], asset)) as Awaited<ReturnType<typeof bidService.fetchByNFT>>
2✔
230
      }
231

232
      yield put(fetchBidsByAssetSuccess(asset, bids))
2✔
233
    } catch (error) {
234
      yield put(fetchBidsByAssetFailure(asset, isErrorWithMessage(error) ? error.message : t('global.unknown_error')))
2!
235
    }
236
  }
237
}
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