Repository: prebid/prebid-server
Pull request #3454 (2024-02-06 00:47:01)

Targeting: Add alwaysincludedeals flag
Addresses this issue #2214

The goal is to add a new flag on the request at `ext.prebid.targeting.alwaysincludedeals`, that if true: PBS-core would generate targeting key-value pairs for all bids with a dealid. This is independent of which other ext.prebid.targeting flags are present.


Patch:
diff --git a/.github/workflows/adapter-code-coverage.yml b/.github/workflows/adapter-code-coverage.yml
index b5fbd557..4d5751c2 100644
--- a/.github/workflows/adapter-code-coverage.yml
+++ b/.github/workflows/adapter-code-coverage.yml
@@ -10,12 +10,12 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Install Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version: 1.20.5
 
       - name: Checkout pull request branch
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
           ref: ${{github.event.pull_request.head.ref}}
@@ -23,7 +23,7 @@ jobs:
 
       - name: Get adapter directories
         id: get_directories
-        uses: actions/github-script@v6
+        uses: actions/github-script@v7
         with:
           result-encoding: string
           script: |
@@ -68,7 +68,7 @@ jobs:
           rm -f -r ./*
 
       - name: Checkout coverage-preview branch
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
           ref: coverage-preview
@@ -94,7 +94,7 @@ jobs:
 
       - name: Add coverage summary to pull request
         if: steps.run_coverage.outputs.coverage_dir != '' && steps.commit_coverage.outputs.remote_coverage_preview_dir != ''
-        uses: actions/github-script@v6
+        uses: actions/github-script@v7
         with:
           script: |
             const utils = require('./.github/workflows/helpers/pull-request-utils.js')
diff --git a/.github/workflows/helpers/pull-request-utils.js b/.github/workflows/helpers/pull-request-utils.js
index 941ae5b6..73c80396 100644
--- a/.github/workflows/helpers/pull-request-utils.js
+++ b/.github/workflows/helpers/pull-request-utils.js
@@ -1,7 +1,9 @@
 const synchronizeEvent = "synchronize",
   openedEvent = "opened",
   completedStatus = "completed",
-  resultSize = 100
+  resultSize = 100,
+  adminPermission = "admin",
+  writePermission = "write"
 
 class diffHelper {
   constructor(input) {
@@ -407,8 +409,31 @@ class coverageHelper {
   }
 }
 
+class userHelper {
+  constructor(input) {
+    this.owner = input.context.repo.owner
+    this.repo = input.context.repo.repo
+    this.github = input.github
+    this.user = input.user
+  }
+
+  /*
+    Checks if the user has write permissions for the repository
+    @returns {boolean} - returns true if the user has write permissions, otherwise false
+  */
+  async hasWritePermissions() {
+    const { data } = await this.github.rest.repos.getCollaboratorPermissionLevel({
+      owner: this.owner,
+      repo: this.repo,
+      username: this.user,
+    })
+    return data.permission === writePermission || data.permission === adminPermission
+  }
+}
+
 module.exports = {
   diffHelper: (input) => new diffHelper(input),
   semgrepHelper: (input) => new semgrepHelper(input),
   coverageHelper: (input) => new coverageHelper(input),
+  userHelper: (input) => new userHelper(input),
 }
diff --git a/.github/workflows/issue_prioritization.yml b/.github/workflows/issue_prioritization.yml
index 3843507b..ec58073d 100644
--- a/.github/workflows/issue_prioritization.yml
+++ b/.github/workflows/issue_prioritization.yml
@@ -10,7 +10,7 @@ jobs:
     steps:
       - name: Generate token
         id: generate_token
-        uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0
+        uses: tibdex/github-app-token@v2.1.0
         with:
           app_id: ${{ secrets.PBS_PROJECT_APP_ID }}
           private_key: ${{ secrets.PBS_PROJECT_APP_PEM }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f1d2b10c..d3559732 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,13 +25,25 @@ jobs:
     permissions:
       contents: read
     steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          repository: ${{ github.repository }}
+          ref: master
       - name: Check user permission
-        uses: actions-cool/check-user-permission@v2.2.0
+        uses: actions/github-script@v7
         id: check
         with:
-          require: 'write'
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          result-encoding: string
+          script: |
+            const utils = require('./.github/workflows/helpers/pull-request-utils.js')
+            const helper = utils.userHelper({github, context, user: '${{ github.actor }}'})
+            const hasPermission = await helper.hasWritePermissions()
+            return hasPermission
     outputs:
-      hasWritePermission: ${{ steps.check.outputs.require-result }}
+      hasWritePermission: ${{ steps.check.outputs.result }}
 
   build-master:
     name: Build master
@@ -40,7 +52,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
-        uses: actions/checkout@master
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
           repository: ${{ github.repository }}
@@ -52,13 +64,12 @@ jobs:
   publish-tag:
     name: Publish tag
     needs: build-master
-    if: contains(needs.check-permission.outputs.hasWritePermission, 'true')
     permissions:
       contents: write
     runs-on: ubuntu-latest
     steps:
       - name: Checkout Prebid Server
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
       - name: Create & publish tag
@@ -111,7 +122,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout Prebid Server
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
       - name: Build image
diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml
index baa10b93..09a0fd56 100644
--- a/.github/workflows/security.yml
+++ b/.github/workflows/security.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
       - name: Checkout Code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           # Resolves to empty string for push events and falls back to HEAD.
           ref: ${{ github.event.pull_request.head.sha }}
@@ -29,6 +29,6 @@ jobs:
           severity: 'CRITICAL,HIGH'
           
       - name: Upload Results To GitHub Security Tab
-        uses: github/codeql-action/upload-sarif@v2
+        uses: github/codeql-action/upload-sarif@v3
         with:
           sarif_file: 'trivy-results.sarif'
\ No newline at end of file
diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml
index 565bb9b8..b641d566 100644
--- a/.github/workflows/semgrep.yml
+++ b/.github/workflows/semgrep.yml
@@ -9,7 +9,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
           ref: ${{github.event.pull_request.head.ref}}
@@ -17,7 +17,7 @@ jobs:
 
       - name: Calculate diff
         id: calculate_diff
-        uses: actions/github-script@v6
+        uses: actions/github-script@v7
         with:
           result-encoding: string
           script: |
@@ -52,7 +52,7 @@ jobs:
       - name: Add pull request comment
         id: add_pull_request_comment
         if: contains(steps.should_run_semgrep.outputs.hasChanges, 'true')
-        uses: actions/github-script@v6.4.1
+        uses: actions/github-script@v7
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           result-encoding: string
diff --git a/.github/workflows/validate-merge.yml b/.github/workflows/validate-merge.yml
index 07f1baca..c44b4002 100644
--- a/.github/workflows/validate-merge.yml
+++ b/.github/workflows/validate-merge.yml
@@ -10,12 +10,12 @@ jobs:
     
     steps:
     - name: Install Go
-      uses: actions/setup-go@v4
+      uses: actions/setup-go@v5
       with:
         go-version: 1.20.5
 
     - name: Checkout Merged Branch
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Validate
       run: |
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 9047e1f4..a3914c3c 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -16,12 +16,12 @@ jobs:
     
     steps:
     - name: Install Go
-      uses: actions/setup-go@v4
+      uses: actions/setup-go@v5
       with:
         go-version: ${{ matrix.go-version }}
 
     - name: Checkout Code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
       with:
         # Resolves to empty string for push events and falls back to HEAD.
         ref: ${{ github.event.pull_request.head.sha }}
diff --git a/adapters/adprime/adprime.go b/adapters/adprime/adprime.go
index fe5f640e..b53ab4aa 100644
--- a/adapters/adprime/adprime.go
+++ b/adapters/adprime/adprime.go
@@ -148,7 +148,8 @@ func (a *AdprimeAdapter) MakeBids(internalRequest *openrtb2.BidRequest, external
 
 	for _, sb := range bidResp.SeatBid {
 		for i := range sb.Bid {
-			bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
+			bidType, err := getBidMediaType(&sb.Bid[i])
+
 			if err != nil {
 				errs = append(errs, err)
 			} else {
@@ -163,22 +164,15 @@ func (a *AdprimeAdapter) MakeBids(internalRequest *openrtb2.BidRequest, external
 	return bidResponse, errs
 }
 
-func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
-	for _, imp := range imps {
-		if imp.ID == impID {
-			if imp.Banner != nil {
-				return openrtb_ext.BidTypeBanner, nil
-			}
-			if imp.Video != nil {
-				return openrtb_ext.BidTypeVideo, nil
-			}
-			if imp.Native != nil {
-				return openrtb_ext.BidTypeNative, nil
-			}
-		}
-	}
-
-	return "", &errortypes.BadInput{
-		Message: fmt.Sprintf("Failed to find impression \"%s\"", impID),
+func getBidMediaType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("Unable to fetch mediaType in multi-format: %s", bid.ImpID)
 	}
 }
diff --git a/adapters/adview/adview.go b/adapters/adview/adview.go
index 88a236dc..d880cc49 100644
--- a/adapters/adview/adview.go
+++ b/adapters/adview/adview.go
@@ -19,6 +19,10 @@ type adapter struct {
 	endpoint *template.Template
 }
 
+type adviewBidExt struct {
+	BidType int `json:"formattype,omitempty"`
+}
+
 // Builder builds a new instance of the adview adapter for the given bidder with the given config.
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint)
@@ -33,63 +37,78 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co
 }
 
 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
-	var bidderExt adapters.ExtImpBidder
-	imp := &request.Imp[0]
-	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
-		return nil, []error{&errortypes.BadInput{
-			Message: fmt.Sprintf("invalid imp.ext, %s", err.Error()),
-		}}
-	}
-	//use adview
-	var advImpExt openrtb_ext.ExtImpAdView
-	if err := json.Unmarshal(bidderExt.Bidder, &advImpExt); err != nil {
-		return nil, []error{&errortypes.BadInput{
-			Message: fmt.Sprintf("invalid bidderExt.Bidder, %s", err.Error()),
-		}}
-	}
 
-	imp.TagID = advImpExt.MasterTagID //tagid means posid
-	//for adview bid request
-	if imp.Banner != nil {
-		if len(imp.Banner.Format) != 0 {
-			bannerCopy := *imp.Banner
-			bannerCopy.H = &imp.Banner.Format[0].H
-			bannerCopy.W = &imp.Banner.Format[0].W
-			imp.Banner = &bannerCopy
+	var requests []*adapters.RequestData
+	var errors []error
+
+	//must copy the original request.
+	requestCopy := *request
+	for _, imp := range request.Imp {
+		var bidderExt adapters.ExtImpBidder
+		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+			errors = append(errors, &errortypes.BadInput{
+				Message: fmt.Sprintf("invalid imp.ext, %s", err.Error()),
+			})
+			continue
+		}
+		//use adview
+		var advImpExt openrtb_ext.ExtImpAdView
+		if err := json.Unmarshal(bidderExt.Bidder, &advImpExt); err != nil {
+			errors = append(errors, &errortypes.BadInput{
+				Message: fmt.Sprintf("invalid bidderExt.Bidder, %s", err.Error()),
+			})
+			continue
 		}
-	}
 
-	// Check if imp comes with bid floor amount defined in a foreign currency
-	if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {
-		// Convert to US dollars
-		convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
-		if err != nil {
-			return nil, []error{err}
+		imp.TagID = advImpExt.MasterTagID //tagid means posid
+		//for adview bid request
+		if imp.Banner != nil {
+			if len(imp.Banner.Format) != 0 {
+				bannerCopy := *imp.Banner
+				bannerCopy.H = &imp.Banner.Format[0].H
+				bannerCopy.W = &imp.Banner.Format[0].W
+				imp.Banner = &bannerCopy
+			}
 		}
-		// Update after conversion. All imp elements inside request.Imp are shallow copies
-		// therefore, their non-pointer values are not shared memory and are safe to modify.
-		imp.BidFloorCur = "USD"
-		imp.BidFloor = convertedValue
-	}
 
-	// Set the CUR of bid to USD after converting all floors
-	request.Cur = []string{"USD"}
+		// Check if imp comes with bid floor amount defined in a foreign currency
+		if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {
+			// Convert to US dollars
+			convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
+			if err != nil {
+				errors = append(errors, err)
+				continue
+			}
+			// Update after conversion. All imp elements inside request.Imp are shallow copies
+			// therefore, their non-pointer values are not shared memory and are safe to modify.
+			imp.BidFloorCur = "USD"
+			imp.BidFloor = convertedValue
+		}
 
-	url, err := a.buildEndpointURL(&advImpExt)
-	if err != nil {
-		return nil, []error{err}
-	}
+		// Set the CUR of bid to USD after converting all floors
+		requestCopy.Cur = []string{"USD"}
+		requestCopy.Imp = []openrtb2.Imp{imp}
 
-	reqJSON, err := json.Marshal(request)
-	if err != nil {
-		return nil, []error{err}
-	}
+		url, err := a.buildEndpointURL(&advImpExt)
+		if err != nil {
+			errors = append(errors, err)
+			continue
+		}
+
+		reqJSON, err := json.Marshal(requestCopy) //request
+		if err != nil {
+			errors = append(errors, err)
+			continue
+		}
 
-	return []*adapters.RequestData{{
-		Method: http.MethodPost,
-		Body:   reqJSON,
-		Uri:    url,
-	}}, nil
+		requestData := &adapters.RequestData{
+			Method: http.MethodPost,
+			Uri:    url,
+			Body:   reqJSON,
+		}
+		requests = append(requests, requestData)
+	}
+	return requests, errors
 }
 
 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
@@ -117,12 +136,13 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R
 	}
 
 	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
-	bidResponse.Currency = "USD" //we just support USD for resp
+	//we just support USD for resp
+	bidResponse.Currency = "USD"
 
 	var errors []error
 	for _, seatBid := range response.SeatBid {
 		for i, bid := range seatBid.Bid {
-			bidType, err := getMediaTypeForImp(bid.ImpID, request.Imp)
+			bidType, err := getMediaTypeForBid(bid)
 			if err != nil {
 				errors = append(errors, err)
 				continue
@@ -143,17 +163,15 @@ func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpAdView) (string, er
 	return macros.ResolveMacros(a.endpoint, endpointParams)
 }
 
-func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
-	mediaType := openrtb_ext.BidTypeBanner
-	for _, imp := range imps {
-		if imp.ID == impID {
-			if imp.Video != nil {
-				mediaType = openrtb_ext.BidTypeVideo
-			} else if imp.Native != nil {
-				mediaType = openrtb_ext.BidTypeNative
-			}
-			return mediaType, nil
-		}
+func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
+	switch bid.MType {
+	case openrtb2.MarkupBanner:
+		return openrtb_ext.BidTypeBanner, nil
+	case openrtb2.MarkupVideo:
+		return openrtb_ext.BidTypeVideo, nil
+	case openrtb2.MarkupNative:
+		return openrtb_ext.BidTypeNative, nil
+	default:
+		return "", fmt.Errorf("Unable to fetch mediaType in impID: %s, mType: %d", bid.ImpID, bid.MType)
 	}
-	return mediaType, nil
 }
diff --git a/adapters/bidder.go b/adapters/bidder.go
index 2bbcb657..5f486771 100644
--- a/adapters/bidder.go
+++ b/adapters/bidder.go
@@ -45,7 +45,7 @@ type Bidder interface {
 type TimeoutBidder interface {
 	Bidder
 
-	// MakeTimeoutNotice functions much the same as MakeRequests, except it is fed the bidder request that timed out,
+	// MakeTimeoutNotification functions much the same as MakeRequests, except it is fed the bidder request that timed out,
 	// and expects that only one notification "request" will be generated. A use case for multiple timeout notifications
 	// has not been anticipated.
 	//
diff --git a/adapters/improvedigital/improvedigital.go b/adapters/improvedigital/improvedigital.go
index c00acfa7..3fd514be 100644
--- a/adapters/improvedigital/improvedigital.go
+++ b/adapters/improvedigital/improvedigital.go
@@ -287,13 +287,17 @@ func (a *ImprovedigitalAdapter) getAdditionalConsentProvidersUserExt(request ope
 	}
 	// End validating additional consent
 
-	// Check if string contain ~, then substring after ~ to end of string
-	consentStr := string(cpMapValue)
-	var tildaPosition int
-	if tildaPosition = strings.Index(consentStr, "~"); tildaPosition == -1 {
+	// Trim enclosing quotes after casting json.RawMessage to string
+	consentStr := strings.Trim((string)(cpMapValue), "\"")
+	// Split by ~ and take only the second string (if exists) as the consented providers spec
+	var consentStrParts = strings.Split(consentStr, "~")
+	if len(consentStrParts) < 2 {
+		return nil, nil
+	}
+	cpStr = strings.TrimSpace(consentStrParts[1])
+	if len(cpStr) == 0 {
 		return nil, nil
 	}
-	cpStr = consentStr[tildaPosition+1 : len(consentStr)-1]
 
 	// Prepare consent providers string
 	cpStr = fmt.Sprintf("[%s]", strings.Replace(cpStr, ".", ",", -1))
diff --git a/adapters/yieldlab/types.go b/adapters/yieldlab/types.go
index 90612700..9d3bc6a2 100644
--- a/adapters/yieldlab/types.go
+++ b/adapters/yieldlab/types.go
@@ -1,18 +1,60 @@
 package yieldlab
 
 import (
+	"github.com/prebid/prebid-server/v2/openrtb_ext"
 	"strconv"
 	"time"
 )
 
 type bidResponse struct {
-	ID         uint64 `json:"id"`
-	Price      uint   `json:"price"`
-	Advertiser string `json:"advertiser"`
-	Adsize     string `json:"adsize"`
-	Pid        uint64 `json:"pid"`
-	Did        uint64 `json:"did"`
-	Pvid       string `json:"pvid"`
+	ID         uint64       `json:"id"`
+	Price      uint         `json:"price"`
+	Advertiser string       `json:"advertiser"`
+	Adsize     string       `json:"adsize"`
+	Pid        uint64       `json:"pid"`
+	Did        uint64       `json:"did"`
+	Pvid       string       `json:"pvid"`
+	DSA        *dsaResponse `json:"dsa,omitempty"`
+}
+
+// dsaResponse defines Digital Service Act (DSA) parameters from Yieldlab yieldprobe response.
+type dsaResponse struct {
+	Behalf       string            `json:"behalf,omitempty"`
+	Paid         string            `json:"paid,omitempty"`
+	Adrender     *int              `json:"adrender,omitempty"`
+	Transparency []dsaTransparency `json:"transparency,omitempty"`
+}
+
+// openRTBExtRegsWithDSA defines the contract for bidrequest.regs.ext with the missing DSA property.
+//
+// The openrtb_ext.ExtRegs needs to be extended on yieldlab adapter level until DSA has been implemented
+// by the prebid server team (https://github.com/prebid/prebid-server/issues/3424).
+type openRTBExtRegsWithDSA struct {
+	openrtb_ext.ExtRegs
+	DSA *dsaRequest `json:"dsa,omitempty"`
+}
+
+// responseExtWithDSA defines seatbid.bid.ext with the DSA object.
+type responseExtWithDSA struct {
+	DSA dsaResponse `json:"dsa"`
+}
+
+// dsaRequest defines Digital Service Act (DSA) parameter
+// as specified by the OpenRTB 2.X DSA Transparency community extension.
+//
+// Should rather come from openrtb_ext package but will be defined here until DSA has been
+// implemented by the prebid server team (https://github.com/prebid/prebid-server/issues/3424).
+type dsaRequest struct {
+	Required     *int              `json:"dsarequired"`
+	PubRender    *int              `json:"pubrender"`
+	DataToPub    *int              `json:"datatopub"`
+	Transparency []dsaTransparency `json:"transparency"`
+}
+
+// dsaTransparency Digital Service Act (DSA) transparency object
+type dsaTransparency struct {
+	Domain string `json:"domain,omitempty"`
+	Params []int  `json:"dsaparams,omitempty"`
 }
 
 type cacheBuster func() string
diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go
index 744a45ae..ed7c283e 100644
--- a/adapters/yieldlab/yieldlab.go
+++ b/adapters/yieldlab/yieldlab.go
@@ -96,11 +96,94 @@ func (a *YieldlabAdapter) makeEndpointURL(req *openrtb2.BidRequest, params *open
 		}
 	}
 
+	dsa, err := getDSA(req)
+	if err != nil {
+		return "", err
+	}
+	if dsa != nil {
+		if dsa.Required != nil {
+			q.Set("dsarequired", strconv.Itoa(*dsa.Required))
+		}
+		if dsa.PubRender != nil {
+			q.Set("dsapubrender", strconv.Itoa(*dsa.PubRender))
+		}
+		if dsa.DataToPub != nil {
+			q.Set("dsadatatopub", strconv.Itoa(*dsa.DataToPub))
+		}
+		if len(dsa.Transparency) != 0 {
+			transparencyParam := makeDSATransparencyURLParam(dsa.Transparency)
+			if len(transparencyParam) != 0 {
+				q.Set("dsatransparency", transparencyParam)
+			}
+		}
+	}
+
 	uri.RawQuery = q.Encode()
 
 	return uri.String(), nil
 }
 
+// getDSA extracts the Digital Service Act (DSA) properties from the request.
+func getDSA(req *openrtb2.BidRequest) (*dsaRequest, error) {
+	if req.Regs == nil || req.Regs.Ext == nil {
+		return nil, nil
+	}
+
+	var extRegs openRTBExtRegsWithDSA
+	err := json.Unmarshal(req.Regs.Ext, &extRegs)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse Regs.Ext object from Yieldlab response: %v", err)
+	}
+
+	return extRegs.DSA, nil
+}
+
+// makeDSATransparencyURLParam creates the transparency url parameter
+// as specified by the OpenRTB 2.X DSA Transparency community extension.
+//
+// Example result: platform1domain.com~1~~SSP2domain.com~1_2
+func makeDSATransparencyURLParam(transparencyObjects []dsaTransparency) string {
+	valueSeparator, itemSeparator, objectSeparator := "_", "~", "~~"
+
+	var b strings.Builder
+
+	concatParams := func(params []int) {
+		b.WriteString(strconv.Itoa(params[0]))
+		for _, param := range params[1:] {
+			b.WriteString(valueSeparator)
+			b.WriteString(strconv.Itoa(param))
+		}
+	}
+
+	concatTransparency := func(object dsaTransparency) {
+		if len(object.Domain) == 0 {
+			return
+		}
+
+		b.WriteString(object.Domain)
+		if len(object.Params) != 0 {
+			b.WriteString(itemSeparator)
+			concatParams(object.Params)
+		}
+	}
+
+	concatTransparencies := func(objects []dsaTransparency) {
+		if len(objects) == 0 {
+			return
+		}
+
+		concatTransparency(objects[0])
+		for _, obj := range objects[1:] {
+			b.WriteString(objectSeparator)
+			concatTransparency(obj)
+		}
+	}
+
+	concatTransparencies(transparencyObjects)
+
+	return b.String()
+}
+
 func (a *YieldlabAdapter) makeFormats(req *openrtb2.BidRequest) (bool, string) {
 	var formats []string
 	const sizesSeparator, adslotSizesSeparator = "|", ","
@@ -253,6 +336,7 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 		}
 	}
 
+	var bidErrors []error
 	for _, bid := range bids {
 		width, height, err := splitSize(bid.Adsize)
 		if err != nil {
@@ -269,7 +353,13 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 		if imp, exists := adslotToImpMap[strconv.FormatUint(bid.ID, 10)]; !exists {
 			continue
 		} else {
-			var bidType openrtb_ext.BidType
+			extJson, err := makeResponseExt(bid)
+			if err != nil {
+				bidErrors = append(bidErrors, err)
+				// skip as bids with missing ext.dsa will be discarded anyway
+				continue
+			}
+
 			responseBid := &openrtb2.Bid{
 				ID:     strconv.FormatUint(bid.ID, 10),
 				Price:  float64(bid.Price) / 100,
@@ -278,8 +368,10 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 				DealID: strconv.FormatUint(bid.Pid, 10),
 				W:      int64(width),
 				H:      int64(height),
+				Ext:    extJson,
 			}
 
+			var bidType openrtb_ext.BidType
 			if imp.Video != nil {
 				bidType = openrtb_ext.BidTypeVideo
 				responseBid.NURL = a.makeAdSourceURL(internalRequest, req, bid)
@@ -299,7 +391,18 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa
 		}
 	}
 
-	return bidderResponse, nil
+	return bidderResponse, bidErrors
+}
+
+func makeResponseExt(bid *bidResponse) (json.RawMessage, error) {
+	if bid.DSA != nil {
+		extJson, err := json.Marshal(responseExtWithDSA{*bid.DSA})
+		if err != nil {
+			return nil, fmt.Errorf("failed to make JSON for seatbid.bid.ext for adslotID %v. This is most likely a programming issue", bid.ID)
+		}
+		return extJson, nil
+	}
+	return nil, nil
 }
 
 func (a *YieldlabAdapter) findBidReq(adslotID uint64, params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab {
diff --git a/exchange/auction.go b/exchange/auction.go
index c02e9141..94f0121d 100644
--- a/exchange/auction.go
+++ b/exchange/auction.go
@@ -108,7 +108,7 @@ func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout in
 
 func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, numImps int, preferDeals bool) *auction {
 	winningBids := make(map[string]*entities.PbsOrtbBid, numImps)
-	winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid, numImps)
+	allBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid, numImps)
 
 	for bidderName, seatBid := range seatBids {
 		if seatBid != nil {
@@ -118,10 +118,10 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, nu
 					winningBids[bid.Bid.ImpID] = bid
 				}
 
-				if bidMap, ok := winningBidsByBidder[bid.Bid.ImpID]; ok {
+				if bidMap, ok := allBidsByBidder[bid.Bid.ImpID]; ok {
 					bidMap[bidderName] = append(bidMap[bidderName], bid)
 				} else {
-					winningBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						bidderName: {bid},
 					}
 				}
@@ -130,8 +130,8 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, nu
 	}
 
 	return &auction{
-		winningBids:         winningBids,
-		winningBidsByBidder: winningBidsByBidder,
+		winningBids:     winningBids,
+		allBidsByBidder: allBidsByBidder,
 	}
 }
 
@@ -151,7 +151,7 @@ func isNewWinningBid(bid, wbid *openrtb2.Bid, preferDeals bool) bool {
 func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, preferDeals bool, accountDefaultBidLimit int) {
 	bidsSnipped := false
 	// sort bids for multibid targeting
-	for _, topBidsPerBidder := range a.winningBidsByBidder {
+	for _, topBidsPerBidder := range a.allBidsByBidder {
 		for bidder, topBids := range topBidsPerBidder {
 			sort.Slice(topBids, func(i, j int) bool {
 				return isNewWinningBid(topBids[i].Bid, topBids[j].Bid, preferDeals)
@@ -187,7 +187,7 @@ func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderNa
 
 func (a *auction) setRoundedPrices(targetingData targetData) {
 	roundedPrices := make(map[*entities.PbsOrtbBid]string, 5*len(a.winningBids))
-	for _, topBidsPerImp := range a.winningBidsByBidder {
+	for _, topBidsPerImp := range a.allBidsByBidder {
 		for _, topBidsPerBidder := range topBidsPerImp {
 			for _, topBid := range topBidsPerBidder {
 				roundedPrices[topBid] = GetPriceBucket(*topBid.Bid, targetingData)
@@ -225,7 +225,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
 	for _, imp := range bidRequest.Imp {
 		expByImp[imp.ID] = imp.Exp
 	}
-	for impID, topBidsPerImp := range a.winningBidsByBidder {
+	for impID, topBidsPerImp := range a.allBidsByBidder {
 		for bidderName, topBidsPerBidder := range topBidsPerImp {
 			for _, topBid := range topBidsPerBidder {
 				isOverallWinner := a.winningBids[impID] == topBid
@@ -394,8 +394,8 @@ func defTTL(bidType openrtb_ext.BidType, defaultTTLs *config.DefaultTTLs) (ttl i
 type auction struct {
 	// winningBids is a map from imp.id to the highest overall CPM bid in that imp.
 	winningBids map[string]*entities.PbsOrtbBid
-	// winningBidsByBidder stores the highest bid on each imp by each bidder.
-	winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
+	// allBidsByBidder is map from ImpID to another map that maps bidderName to all bids from that bidder.
+	allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
 	// roundedPrices stores the price strings rounded for each bid according to the price granularity.
 	roundedPrices map[*entities.PbsOrtbBid]string
 	// cacheIds stores the UUIDs from Prebid Cache for fetching the full bid JSON.
diff --git a/exchange/bidder.go b/exchange/bidder.go
index 96f8ac9b..8b54f984 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -523,8 +523,6 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques
 }
 
 func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.RequestData, logger util.LogMsg, bidderRequestStartTime time.Time, tmaxAdjustments *TmaxAdjustmentsPreprocessed) *httpCallInfo {
-	var requestBody []byte
-
 	requestBody, err := getRequestBody(req, bidder.config.EndpointCompression)
 	if err != nil {
 		return &httpCallInfo{
@@ -532,7 +530,7 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re
 			err:     err,
 		}
 	}
-	httpReq, err := http.NewRequest(req.Method, req.Uri, bytes.NewBuffer(requestBody))
+	httpReq, err := http.NewRequest(req.Method, req.Uri, requestBody)
 	if err != nil {
 		return &httpCallInfo{
 			request: req,
@@ -746,18 +744,16 @@ func hasShorterDurationThanTmax(ctx bidderTmaxContext, tmaxAdjustments TmaxAdjus
 	return false
 }
 
-func getRequestBody(req *adapters.RequestData, endpointCompression string) ([]byte, error) {
-	var requestBody []byte
-
+func getRequestBody(req *adapters.RequestData, endpointCompression string) (*bytes.Buffer, error) {
 	switch strings.ToUpper(endpointCompression) {
 	case Gzip:
 		// Compress to GZIP
-		var b bytes.Buffer
+		b := bytes.NewBuffer(make([]byte, 0, len(req.Body)))
 
 		w := gzipWriterPool.Get().(*gzip.Writer)
 		defer gzipWriterPool.Put(w)
 
-		w.Reset(&b)
+		w.Reset(b)
 		_, err := w.Write(req.Body)
 		if err != nil {
 			return nil, err
@@ -766,14 +762,13 @@ func getRequestBody(req *adapters.RequestData, endpointCompression string) ([]by
 		if err != nil {
 			return nil, err
 		}
-		requestBody = b.Bytes()
 
 		// Set Header
 		req.Headers.Set("Content-Encoding", "gzip")
 
-		return requestBody, nil
+		return b, nil
 	default:
-		return req.Body, nil
+		return bytes.NewBuffer(req.Body), nil
 	}
 }
 
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 7c604886..003ce559 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -574,7 +574,7 @@ func applyDealSupport(bidRequest *openrtb2.BidRequest, auc *auction, bidCategory
 	errs := []error{}
 	impDealMap := getDealTiers(bidRequest)
 
-	for impID, topBidsPerImp := range auc.winningBidsByBidder {
+	for impID, topBidsPerImp := range auc.allBidsByBidder {
 		impDeal := impDealMap[impID]
 		for bidder, topBidsPerBidder := range topBidsPerImp {
 			bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(bidder.String())
diff --git a/exchange/targeting.go b/exchange/targeting.go
index 0047ba8b..d5ce7152 100644
--- a/exchange/targeting.go
+++ b/exchange/targeting.go
@@ -26,6 +26,7 @@ type targetData struct {
 	includeCacheVast          bool
 	includeFormat             bool
 	preferDeals               bool
+	alwaysIncludeDeals        bool
 	// cacheHost and cachePath exist to supply cache host and path as targeting parameters
 	cacheHost string
 	cachePath string
@@ -38,7 +39,7 @@ type targetData struct {
 // it's ok if those stay in the auction. For now, this method implements a very naive cache strategy.
 // In the future, we should implement a more clever retry & backoff strategy to balance the success rate & performance.
 func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMapping map[string]string, truncateTargetAttr *int, multiBidMap map[string]openrtb_ext.ExtMultiBid) {
-	for impId, topBidsPerImp := range auc.winningBidsByBidder {
+	for impId, topBidsPerImp := range auc.allBidsByBidder {
 		overallWinner := auc.winningBids[impId]
 		for originalBidderName, topBidsPerBidder := range topBidsPerImp {
 			targetingBidderCode := originalBidderName
@@ -61,40 +62,42 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi
 
 				isOverallWinner := overallWinner == topBid
 
+				bidHasDeal := len(topBid.Bid.DealID) > 0
+
 				targets := make(map[string]string, 10)
 				if cpm, ok := auc.roundedPrices[topBid]; ok {
-					targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
-				targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr)
+				targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				if hbSize := makeHbSize(topBid.Bid); hbSize != "" {
-					targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if cacheID, ok := auc.cacheIds[topBid.Bid]; ok {
-					targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if vastID, ok := auc.vastCacheIds[topBid.Bid]; ok {
-					targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if targData.includeFormat {
-					targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 
 				if targData.cacheHost != "" {
-					targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if targData.cachePath != "" {
-					targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 
-				if deal := topBid.Bid.DealID; len(deal) > 0 {
-					targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, deal, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+				if bidHasDeal {
+					targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, topBid.Bid.DealID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 
 				if isApp {
-					targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				if len(categoryMapping) > 0 {
-					targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr)
+					targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
 				}
 				topBid.BidTargets = targets
 			}
@@ -102,7 +105,7 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi
 	}
 }
 
-func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int) {
+func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int, bidHasDeal bool) {
 	var maxLength int
 	if truncateTargetAttr != nil {
 		maxLength = *truncateTargetAttr
@@ -112,7 +115,7 @@ func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.Targ
 	} else {
 		maxLength = MaxKeyLength
 	}
-	if targData.includeBidderKeys {
+	if targData.includeBidderKeys || (targData.alwaysIncludeDeals && bidHasDeal) {
 		keys[key.BidderKey(bidderName, maxLength)] = value
 	}
 	if targData.includeWinners && overallWinner {
diff --git a/exchange/utils.go b/exchange/utils.go
index 87358068..676c015a 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -879,6 +879,7 @@ func getExtTargetData(requestExtPrebid *openrtb_ext.ExtRequestPrebid, cacheInstr
 			priceGranularity:          *requestExtPrebid.Targeting.PriceGranularity,
 			mediaTypePriceGranularity: requestExtPrebid.Targeting.MediaTypePriceGranularity,
 			preferDeals:               requestExtPrebid.Targeting.PreferDeals,
+			alwaysIncludeDeals:        requestExtPrebid.Targeting.AlwaysIncludeDeals,
 		}
 	}
 
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 90a95a1b..f5b403d6 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -199,6 +199,7 @@ type ExtRequestTargeting struct {
 	DurationRangeSec          []int                     `json:"durationrangesec,omitempty"`
 	PreferDeals               bool                      `json:"preferdeals,omitempty"`
 	AppendBidderNames         bool                      `json:"appendbiddernames,omitempty"`
+	AlwaysIncludeDeals        bool                      `json:"alwaysincludedeals,omitempty"`
 }
 
 type ExtIncludeBrandCategory struct {
diff --git a/static/bidder-info/kargo.yaml b/static/bidder-info/kargo.yaml
index e1022032..1a7a77eb 100644
--- a/static/bidder-info/kargo.yaml
+++ b/static/bidder-info/kargo.yaml
@@ -11,7 +11,7 @@ capabilities:
       - native
 userSync:
   redirect:
-    url: "https://crb.kargo.com/api/v1/dsync/PrebidServer?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}"
+    url: "https://crb.kargo.com/api/v1/dsync/PrebidServer?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&r={{.RedirectURL}}"
     userMacro: "$UID"
 endpointCompression: "GZIP"
 openrtb:
diff --git a/static/bidder-info/medianet.yaml b/static/bidder-info/medianet.yaml
index ea47de2b..ad741e8f 100644
--- a/static/bidder-info/medianet.yaml
+++ b/static/bidder-info/medianet.yaml
@@ -3,6 +3,7 @@ extra_info: "https://medianet.golang.pbs.com"
 maintainer:
   email: "prebid-support@media.net"
 gvlVendorID: 142
+endpointCompression: gzip
 modifyingVastXmlAllowed: true
 capabilities:
   app:
@@ -17,5 +18,5 @@ capabilities:
       - native
 userSync:
   redirect:
-    url: https://hbx.media.net/cksync.php?cs=1&type=pbs&ovsid=setstatuscode&bidder=medianet&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}
+    url: https://hbx.media.net/cksync.php?cs=1&type=pbs&ovsid=setstatuscode&bidder=medianet&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}
     userMacro: "<vsid>"
\ No newline at end of file
diff --git a/static/bidder-info/yieldmo.yaml b/static/bidder-info/yieldmo.yaml
index 9356a541..02ab8721 100644
--- a/static/bidder-info/yieldmo.yaml
+++ b/static/bidder-info/yieldmo.yaml
@@ -13,5 +13,5 @@ capabilities:
     - video
 userSync:
   redirect:
-    url: "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri={{.RedirectURL}}"
+    url: "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirectUri={{.RedirectURL}}"
     userMacro: "$UID"
\ No newline at end of file


Test patch:
diff --git a/adapters/adprime/adprimetest/exemplary/simple-banner.json b/adapters/adprime/adprimetest/exemplary/simple-banner.json
index e96abe5c..88ed1770 100644
--- a/adapters/adprime/adprimetest/exemplary/simple-banner.json
+++ b/adapters/adprime/adprimetest/exemplary/simple-banner.json
@@ -91,6 +91,7 @@
                                     "dealid": "test_dealid",
                                     "w": 300,
                                     "h": 250,
+                                    "mtype": 1,
                                     "ext": {
                                         "prebid": {
                                             "type": "banner"
@@ -120,6 +121,7 @@
                         "dealid": "test_dealid",
                         "w": 300,
                         "h": 250,
+                        "mtype": 1,
                         "ext": {
                             "prebid": {
                                 "type": "banner"
diff --git a/adapters/adprime/adprimetest/exemplary/simple-native.json b/adapters/adprime/adprimetest/exemplary/simple-native.json
index 0ea8a0a1..4ec94ede 100644
--- a/adapters/adprime/adprimetest/exemplary/simple-native.json
+++ b/adapters/adprime/adprimetest/exemplary/simple-native.json
@@ -75,6 +75,7 @@
                                     "dealid": "test_dealid",
                                     "w": 300,
                                     "h": 250,
+                                    "mtype": 4,
                                     "ext": {
                                         "prebid": {
                                             "type": "native"
@@ -104,6 +105,7 @@
                         "dealid": "test_dealid",
                         "w": 300,
                         "h": 250,
+                        "mtype": 4,
                         "ext": {
                             "prebid": {
                                 "type": "native"
diff --git a/adapters/adprime/adprimetest/exemplary/simple-video.json b/adapters/adprime/adprimetest/exemplary/simple-video.json
index 164e3040..722cfd29 100644
--- a/adapters/adprime/adprimetest/exemplary/simple-video.json
+++ b/adapters/adprime/adprimetest/exemplary/simple-video.json
@@ -87,6 +87,7 @@
                                     "cid": "test_cid",
                                     "crid": "test_crid",
                                     "dealid": "test_dealid",
+                                    "mtype": 2,
                                     "ext": {
                                         "prebid": {
                                             "type": "video"
@@ -115,6 +116,7 @@
                         "cid": "test_cid",
                         "crid": "test_crid",
                         "dealid": "test_dealid",
+                        "mtype": 2,
                         "ext": {
                             "prebid": {
                                 "type": "video"
diff --git a/adapters/adprime/adprimetest/exemplary/simple-web-banner.json b/adapters/adprime/adprimetest/exemplary/simple-web-banner.json
index 74797b19..db518934 100644
--- a/adapters/adprime/adprimetest/exemplary/simple-web-banner.json
+++ b/adapters/adprime/adprimetest/exemplary/simple-web-banner.json
@@ -91,6 +91,7 @@
                                     "dealid": "test_dealid",
                                     "w": 468,
                                     "h": 60,
+                                    "mtype": 1,
                                     "ext": {
                                         "prebid": {
                                             "type": "banner"
@@ -120,6 +121,7 @@
                         "dealid": "test_dealid",
                         "w": 468,
                         "h": 60,
+                        "mtype": 1,
                         "ext": {
                             "prebid": {
                                 "type": "banner"
diff --git a/adapters/adprime/adprimetest/exemplary/withAudiences.json b/adapters/adprime/adprimetest/exemplary/withAudiences.json
index ed8e1c00..40a03046 100644
--- a/adapters/adprime/adprimetest/exemplary/withAudiences.json
+++ b/adapters/adprime/adprimetest/exemplary/withAudiences.json
@@ -95,6 +95,7 @@
                                     "dealid": "test_dealid",
                                     "w": 300,
                                     "h": 250,
+                                    "mtype": 1,
                                     "ext": {
                                         "prebid": {
                                             "type": "banner"
@@ -124,6 +125,7 @@
                         "dealid": "test_dealid",
                         "w": 300,
                         "h": 250,
+                        "mtype": 1,
                         "ext": {
                             "prebid": {
                                 "type": "banner"
diff --git a/adapters/adprime/adprimetest/exemplary/withKeywords.json b/adapters/adprime/adprimetest/exemplary/withKeywords.json
index 3e1528fe..b7018097 100644
--- a/adapters/adprime/adprimetest/exemplary/withKeywords.json
+++ b/adapters/adprime/adprimetest/exemplary/withKeywords.json
@@ -93,6 +93,7 @@
                                     "dealid": "test_dealid",
                                     "w": 300,
                                     "h": 250,
+                                    "mtype": 1,
                                     "ext": {
                                         "prebid": {
                                             "type": "banner"
@@ -122,6 +123,7 @@
                         "dealid": "test_dealid",
                         "w": 300,
                         "h": 250,
+                        "mtype": 1,
                         "ext": {
                             "prebid": {
                                 "type": "banner"
diff --git a/adapters/adprime/adprimetest/supplemental/bad_media_type.json b/adapters/adprime/adprimetest/supplemental/bad_media_type.json
index d5883370..3dce9019 100644
--- a/adapters/adprime/adprimetest/supplemental/bad_media_type.json
+++ b/adapters/adprime/adprimetest/supplemental/bad_media_type.json
@@ -85,7 +85,7 @@
     "expectedBidResponses": [{"currency":"USD","bids":[]}],
     "expectedMakeBidsErrors": [
         {
-            "value": "Failed to find impression \"test-imp-id\"",
+            "value": "Unable to fetch mediaType in multi-format: test-imp-id",
             "comparison": "literal"
         }
     ]
diff --git a/adapters/adview/adviewtest/exemplary/banner-app-format.json b/adapters/adview/adviewtest/exemplary/banner-app-format.json
index 0ab49515..f35f9db7 100644
--- a/adapters/adview/adviewtest/exemplary/banner-app-format.json
+++ b/adapters/adview/adviewtest/exemplary/banner-app-format.json
@@ -115,7 +115,11 @@
                   ],
                   "crid": "20",
                   "w": 320,
-                  "h": 50
+                  "h": 50,
+                  "mtype": 1,
+                  "ext": {
+                    "formattype": 0
+                  }
                 }
               ],
               "type": "banner",
@@ -148,7 +152,11 @@
             ],
             "crid": "20",
             "w": 320,
-            "h": 50
+            "h": 50,
+            "mtype": 1,
+            "ext": {
+              "formattype": 0
+            }
           },
           "type": "banner"
         }
diff --git a/adapters/adview/adviewtest/exemplary/banner-app-resp-no-formattype.json b/adapters/adview/adviewtest/exemplary/banner-app-resp-no-formattype.json
new file mode 100644
index 00000000..e3b92913
--- /dev/null
+++ b/adapters/adview/adviewtest/exemplary/banner-app-resp-no-formattype.json
@@ -0,0 +1,150 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "ifa":"00000000-0000-0000-0000-000000000000",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "banner": {
+          "w": 320,
+          "h": 50
+        },
+        "ext": {
+          "bidder": {
+            "placementId": "posid00001",
+            "accountId": "accountid01"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "https://bid.adview.com/agent/thirdAdxService/accountid01",
+        "body": {
+          "id": "some-request-id",
+          "cur": ["USD"],
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "ifa":"00000000-0000-0000-0000-000000000000",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "posid00001",
+              "ext": {
+                 "bidder": {
+                   "placementId": "posid00001",
+                   "accountId": "accountid01"
+                 }
+              }
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50,
+                  "mtype": 1
+                }
+              ],
+              "type": "banner",
+              "seat": "adview"
+            }
+          ],
+          "cur": "USD",
+          "ext": {
+            "responsetimemillis": {
+              "adview": 154
+            },
+            "tmaxrequest": 1000
+          }
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 320,
+            "h": 50,
+            "mtype": 1
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/adview/adviewtest/exemplary/banner-app.json b/adapters/adview/adviewtest/exemplary/banner-app.json
index 6aad1e8d..2c740504 100644
--- a/adapters/adview/adviewtest/exemplary/banner-app.json
+++ b/adapters/adview/adviewtest/exemplary/banner-app.json
@@ -105,7 +105,11 @@
                   ],
                   "crid": "20",
                   "w": 320,
-                  "h": 50
+                  "h": 50,
+                  "mtype": 1,
+                  "ext": {
+                    "formattype": 0
+                  }
                 }
               ],
               "type": "banner",
@@ -138,7 +142,11 @@
             ],
             "crid": "20",
             "w": 320,
-            "h": 50
+            "h": 50,
+            "mtype": 1,
+            "ext": {
+              "formattype": 0
+            }
           },
           "type": "banner"
         }
diff --git a/adapters/adview/adviewtest/exemplary/native-app.json b/adapters/adview/adviewtest/exemplary/native-app.json
index 804494e5..aea0f58d 100644
--- a/adapters/adview/adviewtest/exemplary/native-app.json
+++ b/adapters/adview/adviewtest/exemplary/native-app.json
@@ -103,7 +103,11 @@
                   "adomain": [
                     "awesome.com"
                   ],
-                  "crid": "20"
+                  "crid": "20",
+                  "mtype": 4,
+                  "ext": {
+                    "formattype": 2
+                  }
                 }
               ],
               "type": "native",
@@ -132,9 +136,13 @@
             "price": 3.5,
             "adm": "awesome-markup",
             "crid": "20",
+            "mtype": 4,
             "adomain": [
               "awesome.com"
-            ]
+            ],
+            "ext": {
+              "formattype": 2
+            }
           },
           "type": "native"
         }
diff --git a/adapters/adview/adviewtest/exemplary/video-app.json b/adapters/adview/adviewtest/exemplary/video-app.json
index 57c9b855..8697fc0f 100644
--- a/adapters/adview/adviewtest/exemplary/video-app.json
+++ b/adapters/adview/adviewtest/exemplary/video-app.json
@@ -115,7 +115,11 @@
                   ],
                   "crid": "20",
                   "w": 1280,
-                  "h": 720
+                  "h": 720,
+                  "mtype": 2,
+                  "ext": {
+                    "formattype": 1
+                  }
                 }
               ],
               "seat": "adview"
@@ -147,7 +151,11 @@
               "awesome.com"
             ],
             "w": 1280,
-            "h": 720
+            "h": 720,
+            "mtype": 2,
+            "ext": {
+              "formattype": 1
+            }
           },
           "type": "video"
         }
diff --git a/adapters/improvedigital/improvedigitaltest/supplemental/addtl-consent-multi-tilda.json b/adapters/improvedigital/improvedigitaltest/supplemental/addtl-consent-multi-tilda.json
new file mode 100644
index 00000000..24c4b813
--- /dev/null
+++ b/adapters/improvedigital/improvedigitaltest/supplemental/addtl-consent-multi-tilda.json
@@ -0,0 +1,94 @@
+{
+  "mockBidRequest": {
+    "id": "addtl-consent-request-id",
+    "site": {
+      "page": "https://good.site/url"
+    },
+    "imp": [{
+      "id": "test-imp-id",
+      "banner": {
+        "format": [{
+          "w": 300,
+          "h": 250
+        }]
+      },
+      "ext": {
+        "bidder": {
+          "placementId": 13245
+        }
+      }
+    }],
+    "user": {
+      "ext":{"consent":"ABC","ConsentedProvidersSettings":{"consented_providers":"1~10.20.90~2"}}
+    }
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://localhost/pbs",
+      "body": {
+        "id": "addtl-consent-request-id",
+        "site": {
+          "page": "https://good.site/url"
+        },
+        "imp": [{
+          "id": "test-imp-id",
+          "banner": {
+            "format": [{
+              "w": 300,
+              "h": 250
+            }]
+          },
+          "ext": {
+            "bidder": {
+              "placementId": 13245
+            }
+          }
+        }],
+        "user": {
+          "ext": {"consent": "ABC","ConsentedProvidersSettings":{"consented_providers":"1~10.20.90~2"},"consented_providers_settings": {"consented_providers": [10,20,90]}
+          }
+        }
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": {
+        "id": "addtl-consent-request-id",
+        "seatbid": [{
+          "seat": "improvedigital",
+          "bid": [{
+            "id": "randomid",
+            "impid": "test-imp-id",
+            "price": 0.500000,
+            "adid": "12345678",
+            "adm": "some-test-ad",
+            "cid": "987",
+            "crid": "12345678",
+            "h": 250,
+            "w": 300
+          }]
+        }],
+        "cur": "USD"
+      }
+    }
+  }],
+
+  "expectedBidResponses": [{
+    "currency": "USD",
+    "bids": [{
+      "bid": {
+        "id": "randomid",
+        "impid": "test-imp-id",
+        "price": 0.5,
+        "adm": "some-test-ad",
+        "adid": "12345678",
+        "cid": "987",
+        "crid": "12345678",
+        "w": 300,
+        "h": 250
+      },
+      "type": "banner"
+    }]
+  }]
+}
diff --git a/adapters/yieldlab/yieldlab_test.go b/adapters/yieldlab/yieldlab_test.go
index 7704c886..49dcc31e 100644
--- a/adapters/yieldlab/yieldlab_test.go
+++ b/adapters/yieldlab/yieldlab_test.go
@@ -257,6 +257,98 @@ func Test_makeSupplyChain(t *testing.T) {
 	}
 }
 
+func Test_makeDSATransparencyUrlParam(t *testing.T) {
+	tests := []struct {
+		name           string
+		transparencies []dsaTransparency
+		expected       string
+	}{
+		{
+			name:           "No transparency objects",
+			transparencies: []dsaTransparency{},
+			expected:       "",
+		},
+		{
+			name:           "Nil transparency",
+			transparencies: nil,
+			expected:       "",
+		},
+		{
+			name: "Params without a domain",
+			transparencies: []dsaTransparency{
+				{
+					Params: []int{1, 2},
+				},
+			},
+			expected: "",
+		},
+		{
+			name: "Params without a params",
+			transparencies: []dsaTransparency{
+				{
+					Domain: "domain.com",
+				},
+			},
+			expected: "domain.com",
+		},
+		{
+			name: "One object; No Params",
+			transparencies: []dsaTransparency{
+				{
+					Domain: "domain.com",
+					Params: []int{},
+				},
+			},
+			expected: "domain.com",
+		},
+		{
+			name: "One object; One Param",
+			transparencies: []dsaTransparency{
+				{
+					Domain: "domain.com",
+					Params: []int{1},
+				},
+			},
+			expected: "domain.com~1",
+		},
+		{
+			name: "Three domain objects",
+			transparencies: []dsaTransparency{
+				{
+					Domain: "domain1.com",
+					Params: []int{1, 2},
+				},
+				{
+					Domain: "domain2.com",
+					Params: []int{3, 4},
+				},
+				{
+					Domain: "domain3.com",
+					Params: []int{5, 6},
+				},
+			},
+			expected: "domain1.com~1_2~~domain2.com~3_4~~domain3.com~5_6",
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			actual := makeDSATransparencyURLParam(test.transparencies)
+			assert.Equal(t, test.expected, actual)
+		})
+	}
+}
+
+func Test_getDSA_invalidRequestExt(t *testing.T) {
+	req := &openrtb2.BidRequest{
+		Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"DSA":"wrongValueType"}`)},
+	}
+
+	dsa, err := getDSA(req)
+
+	assert.NotNil(t, err)
+	assert.Nil(t, dsa)
+}
+
 func TestYieldlabAdapter_makeEndpointURL_invalidEndpoint(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderYieldlab, config.Adapter{
 		Endpoint: "test$:/something§"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
diff --git a/adapters/yieldlab/yieldlabtest/exemplary/dsa.json b/adapters/yieldlab/yieldlabtest/exemplary/dsa.json
new file mode 100644
index 00000000..0c815835
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/exemplary/dsa.json
@@ -0,0 +1,169 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-with-DSA",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 1,
+        "dsa": {
+          "dsarequired": 3,
+          "pubrender": 0,
+          "datatopub": 2,
+          "transparency": [
+            {
+              "domain": "platform1domain.com",
+              "dsaparams": [
+                1
+              ]
+            },
+            {
+              "domain": "SSP2domain.com",
+              "dsaparams": [
+                1,
+                2
+              ]
+            }
+          ]
+        }
+      }
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    },
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c",
+      "ext": {
+        "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&dsadatatopub=2&dsapubrender=0&dsarequired=3&dsatransparency=platform1domain.com~1~~SSP2domain.com~1_2&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728x90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5",
+            "dsa": {
+              "required": 3,
+              "behalf": "on behalf of yieldlab",
+              "paid": "by yieldlab",
+              "transparency": [
+                {
+                  "domain": "yieldlab.de",
+                  "dsaparams": [
+                    1,
+                    2
+                  ]
+                }
+              ],
+              "adrender": 0
+            }
+          }
+        ]
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/12345/123456789/728x90?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&gdpr=1&id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "12345123433",
+            "dealid": "1234",
+            "ext": {
+              "dsa": {
+                "adrender": 0,
+                "transparency": [
+                  {
+                    "domain": "yieldlab.de",
+                    "dsaparams": [
+                      1,
+                      2
+                    ]
+                  }
+                ],
+                "behalf": "on behalf of yieldlab",
+                "paid": "by yieldlab"
+              }
+            },
+            "id": "12345",
+            "impid": "test-imp-id",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty.json b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty.json
new file mode 100644
index 00000000..594e5bb4
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty.json
@@ -0,0 +1,127 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-with-empty-DSA",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 1,
+        "dsa": {
+        }
+      }
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    },
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c",
+      "ext": {
+        "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728x90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5",
+            "dsa": {
+            }
+          }
+        ]
+
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/12345/123456789/728x90?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&gdpr=1&id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "12345123433",
+            "dealid": "1234",
+            "ext": {
+              "dsa": {
+              }
+            },
+            "id": "12345",
+            "impid": "test-imp-id",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty_transparency.json b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty_transparency.json
new file mode 100644
index 00000000..c966a716
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty_transparency.json
@@ -0,0 +1,139 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-with-empty-DSA-transparency",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 1,
+        "dsa": {
+          "transparency": [
+            {
+            }
+          ]
+        }
+      }
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    },
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c",
+      "ext": {
+        "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Accept": [
+            "application/json"
+          ],
+          "Cookie": [
+            "id=34a53e82-0dc3-4815-8b7e-b725ede0361c"
+          ],
+          "Referer": [
+            "http://localhost:9090/gdpr.html"
+          ],
+          "User-Agent": [
+            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+          ],
+          "X-Forwarded-For": [
+            "169.254.13.37"
+          ]
+        },
+        "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728x90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads"
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": [
+          {
+            "id": 12345,
+            "price": 201,
+            "advertiser": "yieldlab",
+            "adsize": "728x90",
+            "pid": 1234,
+            "did": 5678,
+            "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5",
+            "dsa": {
+              "transparency": [
+                {
+                }
+              ]
+            }
+          }
+        ]
+
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "EUR",
+      "bids": [
+        {
+          "bid": {
+            "adm": "<script src=\"https://ad.yieldlab.net/d/12345/123456789/728x90?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&gdpr=1&id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing\"></script>",
+            "crid": "12345123433",
+            "dealid": "1234",
+            "ext": {
+              "dsa": {
+                "transparency": [
+                  {
+                  }
+                ]
+              }
+            },
+            "id": "12345",
+            "impid": "test-imp-id",
+            "price": 2.01,
+            "w": 728,
+            "h": 90
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/yieldlab/yieldlabtest/supplemental/invalid_reg_ext.json b/adapters/yieldlab/yieldlabtest/supplemental/invalid_reg_ext.json
new file mode 100644
index 00000000..1723b8fb
--- /dev/null
+++ b/adapters/yieldlab/yieldlabtest/supplemental/invalid_reg_ext.json
@@ -0,0 +1,67 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-with-wrong-DSA-type",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 728,
+              "h": 90
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "adslotId": "12345",
+            "supplyId": "123456789",
+            "targeting": {
+              "key1": "value1",
+              "key2": "value2"
+            },
+            "extId": "abc"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ifa": "hello-ads",
+      "devicetype": 4,
+      "connectiontype": 6,
+      "geo": {
+        "lat": 51.499488,
+        "lon": -0.128953
+      },
+      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
+      "ip": "169.254.13.37",
+      "h": 1098,
+      "w": 814
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 1,
+        "DSA": ""
+      }
+    },
+    "site": {
+      "id": "fake-site-id",
+      "publisher": {
+        "id": "1"
+      },
+      "page": "http://localhost:9090/gdpr.html"
+    },
+    "user": {
+      "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c",
+      "ext": {
+        "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02"
+      }
+    }
+  },
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "failed to parse Regs.Ext object from Yieldlab response: json: cannot unmarshal string into Go struct field openRTBExtRegsWithDSA.dsa of type yieldlab.dsaRequest",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json b/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json
index ba243500..40943900 100644
--- a/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json
+++ b/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json
@@ -21,6 +21,15 @@
     ],
     "site": {
       "id": "fake-site-id"
+    },
+    "regs": {
+      "coppa": 1,
+      "ext": {
+        "gdpr": 1,
+        "us_privacy": "uspConsentString",
+        "gpp": "gppString",
+        "gpp_sid": [6]
+      }
     }
   },
   "httpCalls": [
@@ -47,6 +56,15 @@
           ],
           "site": {
             "id": "fake-site-id"
+          },
+          "regs": {
+            "coppa": 1,
+            "ext": {
+              "gdpr": 1,
+              "us_privacy": "uspConsentString",
+              "gpp": "gppString",
+              "gpp_sid": [6]
+            }
           }
         }
       },
diff --git a/exchange/auction_test.go b/exchange/auction_test.go
index 40b1aba9..17224dcd 100644
--- a/exchange/auction_test.go
+++ b/exchange/auction_test.go
@@ -191,7 +191,7 @@ func loadCacheSpec(filename string) (*cacheSpec, error) {
 func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 	var bid *entities.PbsOrtbBid
 	winningBidsByImp := make(map[string]*entities.PbsOrtbBid)
-	winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid)
+	allBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid)
 	roundedPrices := make(map[*entities.PbsOrtbBid]string)
 	bidCategory := make(map[string]string)
 
@@ -210,15 +210,15 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 		}
 
 		// Map this bid if it's the highest we've seen from this bidder so far
-		if bidMap, ok := winningBidsByBidder[bid.Bid.ImpID]; ok {
+		if bidMap, ok := allBidsByBidder[bid.Bid.ImpID]; ok {
 			bidMap[pbsBid.Bidder] = append(bidMap[pbsBid.Bidder], bid)
 		} else {
-			winningBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				pbsBid.Bidder: {bid},
 			}
 		}
 
-		for _, topBidsPerBidder := range winningBidsByBidder {
+		for _, topBidsPerBidder := range allBidsByBidder {
 			for _, topBids := range topBidsPerBidder {
 				sort.Slice(topBids, func(i, j int) bool {
 					return isNewWinningBid(topBids[i].Bid, topBids[j].Bid, true)
@@ -263,9 +263,9 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
 	}
 
 	testAuction := &auction{
-		winningBids:         winningBidsByImp,
-		winningBidsByBidder: winningBidsByBidder,
-		roundedPrices:       roundedPrices,
+		winningBids:     winningBidsByImp,
+		allBidsByBidder: allBidsByBidder,
+		roundedPrices:   roundedPrices,
 	}
 	evTracking := &eventTracking{
 		accountID:          "TEST_ACC_ID",
@@ -405,7 +405,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p230,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p123},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p230},
@@ -433,7 +433,7 @@ func TestNewAuction(t *testing.T) {
 					"imp1": &bid1p230,
 					"imp2": &bid2p144,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p230},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p077},
@@ -462,7 +462,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p123,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p123},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -486,7 +486,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p088d,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p123},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -510,7 +510,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p166d,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -537,7 +537,7 @@ func TestNewAuction(t *testing.T) {
 				winningBids: map[string]*entities.PbsOrtbBid{
 					"imp1": &bid1p166d,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d},
 						"rubicon":  []*entities.PbsOrtbBid{&bid1p088d},
@@ -563,7 +563,7 @@ func TestNewAuction(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -645,11 +645,11 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 	}
 
 	type fields struct {
-		winningBids         map[string]*entities.PbsOrtbBid
-		winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
-		roundedPrices       map[*entities.PbsOrtbBid]string
-		cacheIds            map[*openrtb2.Bid]string
-		vastCacheIds        map[*openrtb2.Bid]string
+		winningBids     map[string]*entities.PbsOrtbBid
+		allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
+		roundedPrices   map[*entities.PbsOrtbBid]string
+		cacheIds        map[*openrtb2.Bid]string
+		vastCacheIds    map[*openrtb2.Bid]string
 	}
 	type args struct {
 		adapterBids            map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
@@ -657,8 +657,8 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 		accountDefaultBidLimit int
 	}
 	type want struct {
-		winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
-		adapterBids         map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
+		allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid
+		adapterBids     map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid
 	}
 	tests := []struct {
 		description string
@@ -673,7 +673,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -697,7 +697,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 				preferDeals:            true,
 			},
 			want: want{
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077, &bid1p001},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -724,7 +724,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -748,7 +748,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 				preferDeals:            true,
 			},
 			want: want{
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077, &bid1p001},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -775,7 +775,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 					"imp1": &bid1p166d,
 					"imp2": &bid2p166,
 				},
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -799,7 +799,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 				preferDeals:            true,
 			},
 			want: want{
-				winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 					"imp1": {
 						"appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077},
 						"pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123},
@@ -823,14 +823,14 @@ func TestValidateAndUpdateMultiBid(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			a := &auction{
-				winningBids:         tt.fields.winningBids,
-				winningBidsByBidder: tt.fields.winningBidsByBidder,
-				roundedPrices:       tt.fields.roundedPrices,
-				cacheIds:            tt.fields.cacheIds,
-				vastCacheIds:        tt.fields.vastCacheIds,
+				winningBids:     tt.fields.winningBids,
+				allBidsByBidder: tt.fields.allBidsByBidder,
+				roundedPrices:   tt.fields.roundedPrices,
+				cacheIds:        tt.fields.cacheIds,
+				vastCacheIds:    tt.fields.vastCacheIds,
 			}
 			a.validateAndUpdateMultiBid(tt.args.adapterBids, tt.args.preferDeals, tt.args.accountDefaultBidLimit)
-			assert.Equal(t, tt.want.winningBidsByBidder, tt.fields.winningBidsByBidder, tt.description)
+			assert.Equal(t, tt.want.allBidsByBidder, tt.fields.allBidsByBidder, tt.description)
 			assert.Equal(t, tt.want.adapterBids, tt.args.adapterBids, tt.description)
 		})
 	}
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index a1662ab7..9f092b7d 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -3377,11 +3377,11 @@ func TestGetRequestBody(t *testing.T) {
 			if test.endpointCompression == "GZIP" {
 				assert.Equal(t, "gzip", req.Headers.Get("Content-Encoding"))
 
-				decompressedReqBody, err := decompressGzip(requestBody)
+				decompressedReqBody, err := decompressGzip(requestBody.Bytes())
 				assert.NoError(t, err)
 				assert.Equal(t, test.givenReqBody, decompressedReqBody)
 			} else {
-				assert.Equal(t, test.givenReqBody, requestBody)
+				assert.Equal(t, test.givenReqBody, requestBody.Bytes())
 			}
 		})
 	}
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index ee2e0f6e..6cd48198 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -3542,7 +3542,7 @@ func TestApplyDealSupport(t *testing.T) {
 		}
 
 		auc := &auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"imp_id1": {
 					test.in.bidderName: {&bid},
 				},
@@ -3551,8 +3551,8 @@ func TestApplyDealSupport(t *testing.T) {
 
 		dealErrs := applyDealSupport(bidRequest, auc, bidCategory, nil)
 
-		assert.Equal(t, test.expected.hbPbCatDur, bidCategory[auc.winningBidsByBidder["imp_id1"][test.in.bidderName][0].Bid.ID], test.description)
-		assert.Equal(t, test.expected.dealTierSatisfied, auc.winningBidsByBidder["imp_id1"][test.in.bidderName][0].DealTierSatisfied, "expected.dealTierSatisfied=%v when %v", test.expected.dealTierSatisfied, test.description)
+		assert.Equal(t, test.expected.hbPbCatDur, bidCategory[auc.allBidsByBidder["imp_id1"][test.in.bidderName][0].Bid.ID], test.description)
+		assert.Equal(t, test.expected.dealTierSatisfied, auc.allBidsByBidder["imp_id1"][test.in.bidderName][0].DealTierSatisfied, "expected.dealTierSatisfied=%v when %v", test.expected.dealTierSatisfied, test.description)
 		if len(test.expected.dealErr) > 0 {
 			assert.Containsf(t, dealErrs, errors.New(test.expected.dealErr), "Expected error message not found in deal errors")
 		}
@@ -3593,7 +3593,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					},
 				},
 				auc: &auction{
-					winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
 								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
@@ -3639,7 +3639,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					},
 				},
 				auc: &auction{
-					winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
 								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
@@ -3690,7 +3690,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 					},
 				},
 				auc: &auction{
-					winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+					allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 						"imp_id1": {
 							openrtb_ext.BidderName("appnexus"): {
 								&entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", ""},
@@ -3729,7 +3729,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) {
 			errs := applyDealSupport(tt.args.bidRequest, tt.args.auc, tt.args.bidCategory, tt.args.multiBid)
 			assert.Equal(t, tt.want.errs, errs)
 
-			for impID, topBidsPerImp := range tt.args.auc.winningBidsByBidder {
+			for impID, topBidsPerImp := range tt.args.auc.allBidsByBidder {
 				for bidder, topBidsPerBidder := range topBidsPerImp {
 					for i, topBid := range topBidsPerBidder {
 						assert.Equal(t, tt.want.expectedHbPbCatDur[impID][bidder.String()][i], tt.args.bidCategory[topBid.Bid.ID], tt.name)
diff --git a/exchange/exchangetest/targeting-always-include-deals.json b/exchange/exchangetest/targeting-always-include-deals.json
new file mode 100644
index 00000000..1ffb8088
--- /dev/null
+++ b/exchange/exchangetest/targeting-always-include-deals.json
@@ -0,0 +1,296 @@
+{
+  "incomingRequest": {
+    "ortbRequest": {
+      "id": "some-request-id",
+      "site": {
+        "page": "test.somepage.com"
+      },
+      "imp": [
+        {
+          "id": "my-imp-id",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 1
+                },
+                "audienceNetwork": {
+                  "placementId": "some-placement"
+                }
+              }
+            }
+          }
+        },
+        {
+          "id": "imp-id-2",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ]
+          },
+          "ext": {
+            "prebid": {
+              "bidder": {
+                "appnexus": {
+                  "placementId": 2
+                },
+                "audienceNetwork": {
+                  "placementId": "some-other-placement"
+                }
+              }
+            }
+          }
+        }
+      ],
+      "ext": {
+        "prebid": {
+          "targeting": {
+            "pricegranularity": {
+              "precision": 2,
+              "ranges": [
+                {
+                  "min": 0,
+                  "max": 20,
+                  "increment": 0.1
+                }
+              ]
+            },
+            "includewinners": true,
+            "includebidderkeys": false,
+            "alwaysincludedeals": true
+          }
+        }
+      }
+    }
+  },
+  "outgoingRequests": {
+    "appnexus": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "winning-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.71,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-1",
+                  "dealid": "deal-1"
+                },
+                "bidType": "video"
+              },
+              {
+                "ortbBid": {
+                  "id": "losing-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.21,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-2",
+                  "dealid": "deal-2"
+                },
+                "bidType": "video"
+              },
+              {
+                "ortbBid": {
+                  "id": "other-bid",
+                  "impid": "imp-id-2",
+                  "price": 0.61,
+                  "w": 300,
+                  "h": 500,
+                  "crid": "creative-3",
+                  "dealid": "deal-3"
+                },
+                "bidType": "video"
+              }
+            ],
+            "seat": "appnexus"
+          }
+        ]
+      }
+    },
+    "audienceNetwork": {
+      "mockResponse": {
+        "pbsSeatBids": [
+          {
+            "pbsBids": [
+              {
+                "ortbBid": {
+                  "id": "contending-bid",
+                  "impid": "my-imp-id",
+                  "price": 0.51,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-4",
+                  "dealid": "deal-4"
+                },
+                "bidType": "video"
+              },
+              {
+                "ortbBid": {
+                  "id": "losing-bid-aN",
+                  "impid": "imp-id-2",
+                  "price": 0.40,
+                  "w": 200,
+                  "h": 250,
+                  "crid": "creative-5"
+                },
+                "bidType": "video"
+              }
+            ],
+            "seat": "audienceNetwork"
+          }
+        ]
+      }
+    }
+  },
+  "response": {
+    "bids": {
+      "id": "some-request-id",
+      "seatbid": [
+        {
+          "seat": "audienceNetwork",
+          "bid": [
+            {
+              "id": "contending-bid",
+              "impid": "my-imp-id",
+              "price": 0.51,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-4",
+              "dealid": "deal-4",
+              "ext": {
+                "origbidcpm": 0.51,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "audienceNetwork"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder_audienceNe": "audienceNetwork",
+                    "hb_cache_host_audien": "www.pbcserver.com",
+                    "hb_cache_path_audien": "/pbcache/endpoint",
+                    "hb_deal_audienceNetw": "deal-4",
+                    "hb_pb_audienceNetwor": "0.50",
+                    "hb_size_audienceNetw": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid-aN",
+              "impid": "imp-id-2",
+              "price": 0.40,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-5",
+              "ext": {
+                "origbidcpm": 0.40,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "audienceNetwork"
+                  },
+                  "type": "video"
+                }
+              }
+            }
+          ]
+        },
+        {
+          "seat": "appnexus",
+          "bid": [
+            {
+              "id": "winning-bid",
+              "impid": "my-imp-id",
+              "price": 0.71,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-1",
+              "dealid": "deal-1",
+              "ext": {
+                "origbidcpm": 0.71,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_pb": "0.70",
+                    "hb_pb_appnexus": "0.70",
+                    "hb_deal":"deal-1",
+                    "hb_deal_appnexus":"deal-1",
+                    "hb_size": "200x250",
+                    "hb_size_appnexus": "200x250"
+                  }
+                }
+              }
+            },
+            {
+              "id": "losing-bid",
+              "impid": "my-imp-id",
+              "price": 0.21,
+              "w": 200,
+              "h": 250,
+              "crid": "creative-2",
+              "dealid": "deal-2",
+              "ext": {
+                "origbidcpm": 0.21,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video"
+                }
+              }
+            },
+            {
+              "id": "other-bid",
+              "impid": "imp-id-2",
+              "price": 0.61,
+              "w": 300,
+              "h": 500,
+              "crid": "creative-3",
+              "dealid": "deal-3",
+              "ext": {
+                "origbidcpm": 0.61,
+                "prebid": {
+                  "meta": {
+                    "adaptercode": "appnexus"
+                  },
+                  "type": "video",
+                  "targeting": {
+                    "hb_bidder": "appnexus",
+                    "hb_bidder_appnexus": "appnexus",
+                    "hb_cache_host": "www.pbcserver.com",
+                    "hb_cache_host_appnex": "www.pbcserver.com",
+                    "hb_cache_path": "/pbcache/endpoint",
+                    "hb_cache_path_appnex": "/pbcache/endpoint",
+                    "hb_deal":"deal-3",
+                    "hb_deal_appnexus":"deal-3",
+                    "hb_pb": "0.60",
+                    "hb_pb_appnexus": "0.60",
+                    "hb_size": "300x500",
+                    "hb_size_appnexus": "300x500"
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index c124178e..e6cc429e 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -319,6 +319,11 @@ var bid2p166 *openrtb2.Bid = &openrtb2.Bid{
 	Price: 1.66,
 }
 
+var bid175 *openrtb2.Bid = &openrtb2.Bid{
+	Price:  1.75,
+	DealID: "mydeal2",
+}
+
 var (
 	truncateTargetAttrValue10       int = 10
 	truncateTargetAttrValue5        int = 5
@@ -339,7 +344,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -374,7 +379,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -409,6 +414,54 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 		},
 		TruncateTargetAttr: nil,
 	},
+	{
+		Description: "Targeting with alwaysIncludeDeals",
+		TargetData: targetData{
+			priceGranularity:   lookupPriceGranularity("med"),
+			alwaysIncludeDeals: true,
+		},
+		Auction: auction{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+				"ImpId-1": {
+					openrtb_ext.BidderAppnexus: {{
+						Bid:     bid111,
+						BidType: openrtb_ext.BidTypeBanner,
+					}},
+					openrtb_ext.BidderRubicon: {{
+						Bid:     bid175,
+						BidType: openrtb_ext.BidTypeBanner,
+					}},
+					openrtb_ext.BidderPubmatic: {{
+						Bid:     bid123,
+						BidType: openrtb_ext.BidTypeBanner,
+					}},
+				},
+			},
+		},
+		ExpectedPbsBids: map[string]map[openrtb_ext.BidderName][]ExpectedPbsBid{
+			"ImpId-1": {
+				openrtb_ext.BidderAppnexus: []ExpectedPbsBid{
+					{
+						BidTargets: map[string]string{
+							"hb_bidder_appnexus": "appnexus",
+							"hb_pb_appnexus":     "1.10",
+							"hb_deal_appnexus":   "mydeal",
+						},
+					},
+				},
+				openrtb_ext.BidderRubicon: []ExpectedPbsBid{
+					{
+						BidTargets: map[string]string{
+							"hb_bidder_rubicon": "rubicon",
+							"hb_pb_rubicon":     "1.70",
+							"hb_deal_rubicon":   "mydeal2",
+						},
+					},
+				},
+			},
+		},
+		TruncateTargetAttr: nil,
+	},
 	{
 		Description: "Full basic targeting with hd_format",
 		TargetData: targetData{
@@ -418,7 +471,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeFormat:     true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -467,7 +520,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			cachePath:         "cache",
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -520,7 +573,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -550,7 +603,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -592,7 +645,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -634,7 +687,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeBidderKeys: true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -676,7 +729,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -711,7 +764,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -746,7 +799,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeWinners:   true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {{
 						Bid:     bid123,
@@ -783,7 +836,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{
 			includeFormat:     true,
 		},
 		Auction: auction{
-			winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
+			allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{
 				"ImpId-1": {
 					openrtb_ext.BidderAppnexus: {
 						{
@@ -918,7 +971,7 @@ func TestSetTargeting(t *testing.T) {
 		auc.setRoundedPrices(test.TargetData)
 		winningBids := make(map[string]*entities.PbsOrtbBid)
 		// Set winning bids from the auction data
-		for imp, bidsByBidder := range auc.winningBidsByBidder {
+		for imp, bidsByBidder := range auc.allBidsByBidder {
 			for _, bids := range bidsByBidder {
 				for _, bid := range bids {
 					if winningBid, ok := winningBids[imp]; ok {
@@ -939,12 +992,12 @@ func TestSetTargeting(t *testing.T) {
 				for i, expected := range expectedTargets {
 					assert.Equal(t,
 						expected.BidTargets,
-						auc.winningBidsByBidder[imp][bidder][i].BidTargets,
+						auc.allBidsByBidder[imp][bidder][i].BidTargets,
 						"Test: %s\nTargeting failed for bidder %s on imp %s.",
 						test.Description,
 						string(bidder),
 						imp)
-					assert.Equal(t, expected.TargetBidderCode, auc.winningBidsByBidder[imp][bidder][i].TargetBidderCode)
+					assert.Equal(t, expected.TargetBidderCode, auc.allBidsByBidder[imp][bidder][i].TargetBidderCode)
 				}
 			}
 		}