Repository: prebid/prebid-server
Pull request #3058 (2023-08-27 12:11:35)

Taboola Bidder Adapter:  Change Endpoint 
 changes in the server endpoint, using query params for publisher id and exchange params instead of path params 


Patch:
diff --git a/account/account.go b/account/account.go
index 052d0199..156ad5e6 100644
--- a/account/account.go
+++ b/account/account.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-
 	"github.com/buger/jsonparser"
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/config"
@@ -12,6 +11,7 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/prebid/prebid-server/util/iputil"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 )
 
@@ -103,6 +103,14 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r
 		return nil, errs
 	}
 
+	if ipV6Err := account.Privacy.IPv6Config.Validate(nil); len(ipV6Err) > 0 {
+		account.Privacy.IPv6Config.AnonKeepBits = iputil.IPv6DefaultMaskingBitSize
+	}
+
+	if ipV4Err := account.Privacy.IPv4Config.Validate(nil); len(ipV4Err) > 0 {
+		account.Privacy.IPv4Config.AnonKeepBits = iputil.IPv4DefaultMaskingBitSize
+	}
+
 	// set the value of events.enabled field based on deprecated events_enabled field and ensure backward compatibility
 	deprecateEventsEnabledField(account)
 
diff --git a/adapters/axonix/axonix.go b/adapters/axonix/axonix.go
index 7413cec3..2289050b 100644
--- a/adapters/axonix/axonix.go
+++ b/adapters/axonix/axonix.go
@@ -5,6 +5,8 @@ import (
 	"fmt"
 	"net/http"
 	"net/url"
+	"strconv"
+	"strings"
 	"text/template"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -105,6 +107,7 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R
 	for _, seatBid := range response.SeatBid {
 		for _, bid := range seatBid.Bid {
 			bid := bid
+			resolveMacros(&bid)
 			b := &adapters.TypedBid{
 				Bid:     &bid,
 				BidType: getMediaType(bid.ImpID, request.Imp),
@@ -129,3 +132,12 @@ func getMediaType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
 	}
 	return openrtb_ext.BidTypeBanner
 }
+
+func resolveMacros(bid *openrtb2.Bid) {
+	if bid == nil {
+		return
+	}
+	price := strconv.FormatFloat(bid.Price, 'f', -1, 64)
+	bid.NURL = strings.Replace(bid.NURL, "${AUCTION_PRICE}", price, -1)
+	bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1)
+}
diff --git a/adapters/bematterfull/bematterfull.go b/adapters/bematterfull/bematterfull.go
new file mode 100644
index 00000000..bf63e7c8
--- /dev/null
+++ b/adapters/bematterfull/bematterfull.go
@@ -0,0 +1,161 @@
+package bematterfull
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type bidType struct {
+	Type string `json:"type"`
+}
+
+type bidExt struct {
+	Prebid bidType `json:"prebid"`
+}
+
+type adapter struct {
+	endpoint *template.Template
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+	tmpl, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint URL template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: tmpl,
+	}
+
+	return bidder, nil
+}
+
+func (a *adapter) buildEndpointFromRequest(imp *openrtb2.Imp) (string, error) {
+	var impExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
+		return "", &errortypes.BadInput{
+			Message: fmt.Sprintf("Failed to deserialize bidder impression extension: %v", err),
+		}
+	}
+
+	var bematterfullExt openrtb_ext.ExtBematterfull
+	if err := json.Unmarshal(impExt.Bidder, &bematterfullExt); err != nil {
+		return "", &errortypes.BadInput{
+			Message: fmt.Sprintf("Failed to deserialize Bematterfull extension: %v", err),
+		}
+	}
+
+	endpointParams := macros.EndpointTemplateParams{
+		Host:     bematterfullExt.Env,
+		SourceId: bematterfullExt.Pid,
+	}
+
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+	var requests []*adapters.RequestData
+	var errs []error
+
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+
+	requestCopy := *request
+	for _, imp := range request.Imp {
+		requestCopy.Imp = []openrtb2.Imp{imp}
+
+		endpoint, err := a.buildEndpointFromRequest(&imp)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		requestJSON, err := json.Marshal(requestCopy)
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+
+		request := &adapters.RequestData{
+			Method:  http.MethodPost,
+			Body:    requestJSON,
+			Uri:     endpoint,
+			Headers: headers,
+		}
+
+		requests = append(requests, request)
+	}
+
+	return requests, errs
+}
+
+func (a *adapter) MakeBids(openRTBRequest *openrtb2.BidRequest, requestToBidder *adapters.RequestData, bidderRawResponse *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+	if adapters.IsResponseStatusCodeNoContent(bidderRawResponse) {
+		return nil, nil
+	}
+
+	if bidderRawResponse.StatusCode == http.StatusServiceUnavailable {
+		return nil, []error{&errortypes.BadInput{
+			Message: "Bidder Bematterfull is unavailable. Please contact the bidder support.",
+		}}
+	}
+
+	if err := adapters.CheckResponseStatusCodeForErrors(bidderRawResponse); err != nil {
+		return nil, []error{err}
+	}
+
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(bidderRawResponse.Body, &bidResp); err != nil {
+		return nil, []error{err}
+	}
+
+	if len(bidResp.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Array SeatBid cannot be empty",
+		}}
+	}
+
+	return prepareBidResponse(bidResp.SeatBid)
+}
+
+func prepareBidResponse(seats []openrtb2.SeatBid) (*adapters.BidderResponse, []error) {
+	errs := []error{}
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(seats))
+
+	for _, seatBid := range seats {
+		for bidId, bid := range seatBid.Bid {
+			var bidExt bidExt
+			if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
+				errs = append(errs, &errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Failed to parse Bid[%d].Ext: %s", bidId, err.Error()),
+				})
+				continue
+			}
+
+			bidType, err := openrtb_ext.ParseBidType(bidExt.Prebid.Type)
+			if err != nil {
+				errs = append(errs, &errortypes.BadServerResponse{
+					Message: fmt.Sprintf("Bid[%d].Ext.Prebid.Type expects one of the following values: 'banner', 'native', 'video', 'audio', got '%s'", bidId, bidExt.Prebid.Type),
+				})
+				continue
+			}
+
+			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+				Bid:     &seatBid.Bid[bidId],
+				BidType: bidType,
+			})
+		}
+	}
+
+	return bidResponse, errs
+}
diff --git a/adapters/datablocks/datablocks.go b/adapters/datablocks/datablocks.go
index 8929c848..a39721a4 100644
--- a/adapters/datablocks/datablocks.go
+++ b/adapters/datablocks/datablocks.go
@@ -27,7 +27,7 @@ func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *
 		"Accept":       {"application/json"},
 	}
 
-	// Pull the host and source ID info from the bidder params.
+	// Pull the source ID info from the bidder params.
 	reqImps, err := splitImpressions(request.Imp)
 
 	if err != nil {
@@ -45,7 +45,7 @@ func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *
 			continue
 		}
 
-		urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)}
+		urlParams := macros.EndpointTemplateParams{SourceId: strconv.Itoa(reqExt.SourceId)}
 		url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)
 
 		if err != nil {
@@ -137,7 +137,7 @@ func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
 	var datablocksExt openrtb_ext.ExtImpDatablocks
 	if err := json.Unmarshal(bidderExt.Bidder, &datablocksExt); err != nil {
 		return nil, &errortypes.BadInput{
-			Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()),
+			Message: fmt.Sprintf("Cannot Resolve sourceId: %s", err.Error()),
 		}
 	}
 
@@ -147,12 +147,6 @@ func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
 		}
 	}
 
-	if len(datablocksExt.Host) < 1 {
-		return nil, &errortypes.BadInput{
-			Message: "Invalid/Missing Host",
-		}
-	}
-
 	return &datablocksExt, nil
 }
 
diff --git a/config/account.go b/config/account.go
index 02040211..038a8daa 100644
--- a/config/account.go
+++ b/config/account.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/iputil"
 )
 
 // ChannelType enumerates the values of integrations Prebid Server can configure for an account
@@ -40,7 +41,7 @@ type Account struct {
 	Validations             Validations                                 `mapstructure:"validations" json:"validations"`
 	DefaultBidLimit         int                                         `mapstructure:"default_bid_limit" json:"default_bid_limit"`
 	BidAdjustments          *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"`
-	Privacy                 *AccountPrivacy                             `mapstructure:"privacy" json:"privacy"`
+	Privacy                 AccountPrivacy                              `mapstructure:"privacy" json:"privacy"`
 }
 
 // CookieSync represents the account-level defaults for the cookie sync endpoint.
@@ -295,5 +296,31 @@ func (a *AccountChannel) IsSet() bool {
 }
 
 type AccountPrivacy struct {
-	AllowActivities AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
+	AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
+	IPv6Config      IPv6             `mapstructure:"ipv6" json:"ipv6"`
+	IPv4Config      IPv4             `mapstructure:"ipv4" json:"ipv4"`
+}
+
+type IPv6 struct {
+	AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
+}
+
+type IPv4 struct {
+	AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
+}
+
+func (ip *IPv6) Validate(errs []error) []error {
+	if ip.AnonKeepBits > iputil.IPv6BitSize || ip.AnonKeepBits < 0 {
+		err := fmt.Errorf("bits cannot exceed %d in ipv6 address, or be less than 0", iputil.IPv6BitSize)
+		errs = append(errs, err)
+	}
+	return errs
+}
+
+func (ip *IPv4) Validate(errs []error) []error {
+	if ip.AnonKeepBits > iputil.IPv4BitSize || ip.AnonKeepBits < 0 {
+		err := fmt.Errorf("bits cannot exceed %d in ipv4 address, or be less than 0", iputil.IPv4BitSize)
+		errs = append(errs, err)
+	}
+	return errs
 }
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index df9e9d5a..96d6fd15 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -241,17 +241,11 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 	for fileName, data := range bidderConfigs {
 		bidderName := strings.Split(fileName, ".")
 		if len(bidderName) == 2 && bidderName[1] == "yaml" {
-			normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
-			if !bidderNameExists {
-				return nil, fmt.Errorf("error parsing config for bidder %s: unknown bidder", fileName)
-			}
 			info := BidderInfo{}
 			if err := yaml.Unmarshal(data, &info); err != nil {
 				return nil, fmt.Errorf("error parsing config for bidder %s: %v", fileName, err)
 			}
 
-			bidderInfos[string(normalizedBidderName)] = info
-
 			//need to maintain nullable fields from BidderInfo struct into bidderInfoNullableFields
 			//to handle the default values in aliases yaml
 			if len(info.AliasOf) > 0 {
@@ -260,7 +254,25 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 					return nil, fmt.Errorf("error parsing config for aliased bidder %s: %v", fileName, err)
 				}
 
+				//required for CoreBidderNames function to also return aliasBiddernames
+				if err := openrtb_ext.SetAliasBidderName(bidderName[0], openrtb_ext.BidderName(info.AliasOf)); err != nil {
+					return nil, err
+				}
+
+				normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
+				if !bidderNameExists {
+					return nil, fmt.Errorf("error parsing config for an alias %s: unknown bidder", fileName)
+				}
+
 				aliasNillableFieldsByBidder[string(normalizedBidderName)] = aliasFields
+				bidderInfos[string(normalizedBidderName)] = info
+			} else {
+				normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
+				if !bidderNameExists {
+					return nil, fmt.Errorf("error parsing config for bidder %s: unknown bidder", fileName)
+				}
+
+				bidderInfos[string(normalizedBidderName)] = info
 			}
 		}
 	}
@@ -276,8 +288,7 @@ func processBidderAliases(aliasNillableFieldsByBidder map[string]aliasNillableFi
 		if err := validateAliases(aliasBidderInfo, bidderInfos, bidderName); err != nil {
 			return nil, err
 		}
-		//required for CoreBidderNames function to also return aliasBiddernames
-		openrtb_ext.SetAliasBidderName(openrtb_ext.BidderName(string(bidderName)))
+
 		parentBidderInfo := bidderInfos[aliasBidderInfo.AliasOf]
 		if aliasBidderInfo.AppSecret == "" {
 			aliasBidderInfo.AppSecret = parentBidderInfo.AppSecret
diff --git a/config/config.go b/config/config.go
index 2b539c57..e564c34a 100644
--- a/config/config.go
+++ b/config/config.go
@@ -149,12 +149,11 @@ func (cfg *Configuration) validate(v *viper.Viper) []error {
 		errs = append(errs, errors.New("account_defaults.Events.VASTEvents has no effect as the feature is under development."))
 	}
 
-	if cfg.AccountDefaults.Privacy != nil {
-		glog.Warning("account_defaults.Privacy has no effect as the feature is under development.")
-	}
-
 	errs = cfg.Experiment.validate(errs)
 	errs = cfg.BidderInfos.validate(errs)
+	errs = cfg.AccountDefaults.Privacy.IPv6Config.Validate(errs)
+	errs = cfg.AccountDefaults.Privacy.IPv4Config.Validate(errs)
+
 	return errs
 }
 
@@ -1019,6 +1018,8 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("account_defaults.price_floors.max_rules", 100)
 	v.SetDefault("account_defaults.price_floors.max_schema_dims", 3)
 	v.SetDefault("account_defaults.events_enabled", false)
+	v.SetDefault("account_defaults.privacy.ipv6.anon_keep_bits", 56)
+	v.SetDefault("account_defaults.privacy.ipv4.anon_keep_bits", 24)
 
 	v.SetDefault("compression.response.enable_gzip", false)
 	v.SetDefault("compression.request.enable_gzip", false)
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 5caa543c..50cb4491 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -149,7 +149,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 		}
 	}
 
-	activityControl, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activityControl, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		if errortypes.ContainsFatalError([]error{activitiesErr}) {
 			activityControl = privacy.ActivityControl{}
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index fb4b1928..142f7320 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -229,7 +229,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		errL = append(errL, activitiesErr)
 		writeError(errL, w, &labels)
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 6ce2c323..6fe5aab9 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/privacy"
 	"io"
 	"net/http"
 	"net/url"
@@ -15,6 +14,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/buger/jsonparser"
 	"github.com/gofrs/uuid"
 	"github.com/golang/glog"
@@ -193,7 +194,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		errL = append(errL, activitiesErr)
 		if errortypes.ContainsFatalError(errL) {
@@ -1966,8 +1967,12 @@ func setAuctionTypeImplicitly(r *openrtb_ext.RequestWrapper) {
 }
 
 func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
+	if r.Site == nil {
+		r.Site = &openrtb2.Site{}
+	}
+
 	referrerCandidate := httpReq.Referer()
-	if referrerCandidate == "" && r.Site != nil && r.Site.Page != "" {
+	if referrerCandidate == "" && r.Site.Page != "" {
 		referrerCandidate = r.Site.Page // If http referer is disabled and thus has empty value - use site.page instead
 	}
 
@@ -1981,17 +1986,13 @@ func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
 		}
 	}
 
-	if r.Site != nil {
-		if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
-			siteExt.SetAmp(&notAmp)
-		}
+	if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
+		siteExt.SetAmp(&notAmp)
 	}
+
 }
 
 func setSitePageIfEmpty(site *openrtb2.Site, sitePage string) {
-	if site == nil {
-		site = &openrtb2.Site{}
-	}
 	if site.Page == "" {
 		site.Page = sitePage
 	}
diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
index d21ca4a9..8ec2273e 100644
--- a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
+++ b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
@@ -19,5 +19,5 @@
     ]
   },
   "expectedReturnCode": 400,
-  "expectedErrorMessage": "Invalid request: request.site or request.app must be defined, but not both.\n"
+  "expectedErrorMessage": "Invalid request: request.site should include at least one of request.site.id or request.site.page.\n"
 }
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 8fc2f844..5f848446 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -303,7 +303,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+	activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 	if activitiesErr != nil {
 		errL = append(errL, activitiesErr)
 		if errortypes.ContainsFatalError(errL) {
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 561989e0..30a1cce2 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -104,7 +104,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			return
 		}
 
-		activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
+		activities, activitiesErr := privacy.NewActivityControl(&account.Privacy)
 		if activitiesErr != nil {
 			if errortypes.ContainsFatalError([]error{activitiesErr}) {
 				activities = privacy.ActivityControl{}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index fb1dd69a..84b1f1af 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -44,6 +44,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/axonix"
 	"github.com/prebid/prebid-server/adapters/beachfront"
 	"github.com/prebid/prebid-server/adapters/beintoo"
+	"github.com/prebid/prebid-server/adapters/bematterfull"
 	"github.com/prebid/prebid-server/adapters/between"
 	"github.com/prebid/prebid-server/adapters/beyondmedia"
 	"github.com/prebid/prebid-server/adapters/bidmachine"
@@ -237,6 +238,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderAxonix:            axonix.Builder,
 		openrtb_ext.BidderBeachfront:        beachfront.Builder,
 		openrtb_ext.BidderBeintoo:           beintoo.Builder,
+		openrtb_ext.BidderBematterfull:      bematterfull.Builder,
 		openrtb_ext.BidderBetween:           between.Builder,
 		openrtb_ext.BidderBeyondMedia:       beyondmedia.Builder,
 		openrtb_ext.BidderBidmachine:        bidmachine.Builder,
diff --git a/exchange/utils.go b/exchange/utils.go
index baedcf78..267d928c 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -214,7 +214,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 
 		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTids, scopedName)
 
-		privacyEnforcement.Apply(bidderRequest.BidRequest)
+		privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy)
 		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
 
 		// GPP downgrade: always downgrade unless we can confirm GPP is supported
diff --git a/floors/enforce.go b/floors/enforce.go
index 1f50dd2b..9318c9d2 100644
--- a/floors/enforce.go
+++ b/floors/enforce.go
@@ -137,7 +137,7 @@ func enforceFloorToBids(bidRequestWrapper *openrtb_ext.RequestWrapper, seatBids
 				}
 
 				bidPrice := rate * bid.Bid.Price
-				if reqImp.BidFloor > bidPrice {
+				if (bidPrice + floorPrecision) < reqImp.BidFloor {
 					rejectedBid := &entities.PbsOrtbSeatBid{
 						Currency: seatBid.Currency,
 						Seat:     seatBid.Seat,
diff --git a/floors/floors.go b/floors/floors.go
index f9e13ec0..52591915 100644
--- a/floors/floors.go
+++ b/floors/floors.go
@@ -16,15 +16,16 @@ type Price struct {
 }
 
 const (
-	defaultCurrency  string = "USD"
-	defaultDelimiter string = "|"
-	catchAll         string = "*"
-	skipRateMin      int    = 0
-	skipRateMax      int    = 100
-	modelWeightMax   int    = 100
-	modelWeightMin   int    = 1
-	enforceRateMin   int    = 0
-	enforceRateMax   int    = 100
+	defaultCurrency  string  = "USD"
+	defaultDelimiter string  = "|"
+	catchAll         string  = "*"
+	skipRateMin      int     = 0
+	skipRateMax      int     = 100
+	modelWeightMax   int     = 100
+	modelWeightMin   int     = 1
+	enforceRateMin   int     = 0
+	enforceRateMax   int     = 100
+	floorPrecision   float64 = 0.01
 )
 
 // EnrichWithPriceFloors checks for floors enabled in account and request and selects floors data from dynamic fetched if present
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 7dc620d4..85cfed5c 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -66,6 +66,7 @@ type ExtBidPrebidMeta struct {
 	PrimaryCategoryID    string          `json:"primaryCatId,omitempty"`
 	RendererName         string          `json:"rendererName,omitempty"`
 	RendererVersion      string          `json:"rendererVersion,omitempty"`
+	RendererData         json.RawMessage `json:"rendererData,omitempty"`
 	SecondaryCategoryIDs []string        `json:"secondaryCatIds,omitempty"`
 }
 
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index bed2f056..c60058eb 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -15,7 +15,7 @@ import (
 // BidderName refers to a core bidder id or an alias id.
 type BidderName string
 
-var aliasBidderNames []BidderName
+var aliasBidderToParent map[BidderName]BidderName = map[BidderName]BidderName{}
 
 var coreBidderNames []BidderName = []BidderName{
 	Bidder33Across,
@@ -62,6 +62,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderAxonix,
 	BidderBeachfront,
 	BidderBeintoo,
+	BidderBematterfull,
 	BidderBetween,
 	BidderBeyondMedia,
 	BidderBidmachine,
@@ -223,17 +224,18 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderZetaGlobalSsp,
 }
 
-func SetAliasBidderName(name BidderName) {
-	aliasBidderNames = append(aliasBidderNames, name)
-	coreBidderNames = append(coreBidderNames, name)
+func GetAliasBidderToParent() map[BidderName]BidderName {
+	return aliasBidderToParent
 }
 
-func GetAliasBidderNamesSet() map[BidderName]struct{} {
-	set := map[BidderName]struct{}{}
-	for _, name := range aliasBidderNames {
-		set[name] = struct{}{}
+func SetAliasBidderName(aliasBidderName string, parentBidderName BidderName) error {
+	if IsBidderNameReserved(aliasBidderName) {
+		return fmt.Errorf("alias %s is a reserved bidder name and cannot be used", aliasBidderName)
 	}
-	return set
+	aliasBidder := BidderName(aliasBidderName)
+	coreBidderNames = append(coreBidderNames, aliasBidder)
+	aliasBidderToParent[aliasBidder] = parentBidderName
+	return nil
 }
 
 func (name BidderName) MarshalJSON() ([]byte, error) {
@@ -351,6 +353,7 @@ const (
 	BidderAxonix            BidderName = "axonix"
 	BidderBeachfront        BidderName = "beachfront"
 	BidderBeintoo           BidderName = "beintoo"
+	BidderBematterfull      BidderName = "bematterfull"
 	BidderBetween           BidderName = "between"
 	BidderBeyondMedia       BidderName = "beyondmedia"
 	BidderBidmachine        BidderName = "bidmachine"
@@ -553,11 +556,11 @@ var bidderNameLookup = func() map[string]BidderName {
 		lookup[bidderNameLower] = name
 	}
 	return lookup
-}()
+}
 
 func NormalizeBidderName(name string) (BidderName, bool) {
 	nameLower := strings.ToLower(name)
-	bidderName, exists := bidderNameLookup[nameLower]
+	bidderName, exists := bidderNameLookup()[nameLower]
 	return bidderName, exists
 }
 
@@ -570,10 +573,42 @@ type BidderParamValidator interface {
 	Schema(name BidderName) string
 }
 
+type bidderParamsFileSystem interface {
+	readDir(name string) ([]os.DirEntry, error)
+	readFile(name string) ([]byte, error)
+	newReferenceLoader(source string) gojsonschema.JSONLoader
+	newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error)
+	abs(path string) (string, error)
+}
+
+type standardBidderParamsFileSystem struct{}
+
+func (standardBidderParamsFileSystem) readDir(name string) ([]os.DirEntry, error) {
+	return os.ReadDir(name)
+}
+
+func (standardBidderParamsFileSystem) readFile(name string) ([]byte, error) {
+	return os.ReadFile(name)
+}
+
+func (standardBidderParamsFileSystem) newReferenceLoader(source string) gojsonschema.JSONLoader {
+	return gojsonschema.NewReferenceLoader(source)
+}
+
+func (standardBidderParamsFileSystem) newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error) {
+	return gojsonschema.NewSchema(l)
+}
+
+func (standardBidderParamsFileSystem) abs(path string) (string, error) {
+	return filepath.Abs(path)
+}
+
+var paramsValidator bidderParamsFileSystem = standardBidderParamsFileSystem{}
+
 // NewBidderParamsValidator makes a BidderParamValidator, assuming all the necessary files exist in the filesystem.
 // This will error if, for example, a Bidder gets added but no JSON schema is written for them.
 func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, error) {
-	fileInfos, err := os.ReadDir(schemaDirectory)
+	fileInfos, err := paramsValidator.readDir(schemaDirectory)
 	if err != nil {
 		return nil, fmt.Errorf("Failed to read JSON schemas from directory %s. %v", schemaDirectory, err)
 	}
@@ -587,17 +622,17 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		if _, ok := bidderMap[bidderName]; !ok {
 			return nil, fmt.Errorf("File %s/%s does not match a valid BidderName.", schemaDirectory, fileInfo.Name())
 		}
-		toOpen, err := filepath.Abs(filepath.Join(schemaDirectory, fileInfo.Name()))
+		toOpen, err := paramsValidator.abs(filepath.Join(schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to get an absolute representation of the path: %s, %v", toOpen, err)
 		}
-		schemaLoader := gojsonschema.NewReferenceLoader("file:///" + filepath.ToSlash(toOpen))
-		loadedSchema, err := gojsonschema.NewSchema(schemaLoader)
+		schemaLoader := paramsValidator.newReferenceLoader("file:///" + filepath.ToSlash(toOpen))
+		loadedSchema, err := paramsValidator.newSchema(schemaLoader)
 		if err != nil {
 			return nil, fmt.Errorf("Failed to load json schema at %s: %v", toOpen, err)
 		}
 
-		fileBytes, err := os.ReadFile(fmt.Sprintf("%s/%s", schemaDirectory, fileInfo.Name()))
+		fileBytes, err := paramsValidator.readFile(fmt.Sprintf("%s/%s", schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to read file %s/%s: %v", schemaDirectory, fileInfo.Name(), err)
 		}
@@ -606,6 +641,15 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		schemaContents[BidderName(bidderName)] = string(fileBytes)
 	}
 
+	//set alias bidder params schema to its parent
+	for alias, parent := range aliasBidderToParent {
+		parentSchema := schemas[parent]
+		schemas[alias] = parentSchema
+
+		parentSchemaContents := schemaContents[parent]
+		schemaContents[alias] = parentSchemaContents
+	}
+
 	return &bidderParamValidator{
 		schemaContents: schemaContents,
 		parsedSchemas:  schemas,
diff --git a/openrtb_ext/imp_bematterfull.go b/openrtb_ext/imp_bematterfull.go
new file mode 100644
index 00000000..bc3ca451
--- /dev/null
+++ b/openrtb_ext/imp_bematterfull.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtBematterfull struct {
+	Env string `json:"env"`
+	Pid string `json:"pid"`
+}
diff --git a/openrtb_ext/imp_datablocks.go b/openrtb_ext/imp_datablocks.go
index c8659d3f..dc3bae3f 100644
--- a/openrtb_ext/imp_datablocks.go
+++ b/openrtb_ext/imp_datablocks.go
@@ -2,6 +2,5 @@ package openrtb_ext
 
 // ExtImpDatablocks defines the contract for bidrequest.imp[i].ext.prebid.bidder.datablocks
 type ExtImpDatablocks struct {
-	SourceId int    `json:"sourceId"`
-	Host     string `json:"host"`
+	SourceId int `json:"sourceId"`
 }
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 6f39f0be..f5418ba4 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -352,8 +352,9 @@ type ExtRequestSdk struct {
 }
 
 type ExtRequestSdkRenderer struct {
-	Name    string `json:"name,omitempty"`
-	Version string `json:"version,omitempty"`
+	Name    string          `json:"name,omitempty"`
+	Version string          `json:"version,omitempty"`
+	Data    json.RawMessage `json:"data,omitempty"`
 }
 
 type ExtMultiBid struct {
diff --git a/privacy/activitycontrol.go b/privacy/activitycontrol.go
index fee5c650..5d68ce71 100644
--- a/privacy/activitycontrol.go
+++ b/privacy/activitycontrol.go
@@ -22,7 +22,7 @@ func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, er
 	ac := ActivityControl{}
 	var err error
 
-	if privacyConf == nil {
+	if privacyConf == nil || privacyConf.AllowActivities == nil {
 		return ac, nil
 	}
 
diff --git a/privacy/enforcement.go b/privacy/enforcement.go
index 1a66cfab..8074d96a 100644
--- a/privacy/enforcement.go
+++ b/privacy/enforcement.go
@@ -1,6 +1,9 @@
 package privacy
 
-import "github.com/prebid/openrtb/v19/openrtb2"
+import (
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/config"
+)
 
 // Enforcement represents the privacy policies to enforce for an OpenRTB bid request.
 type Enforcement struct {
@@ -27,8 +30,8 @@ func (e Enforcement) AnyActivities() bool {
 }
 
 // Apply cleans personally identifiable information from an OpenRTB bid request.
-func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest) {
-	e.apply(bidRequest, NewScrubber())
+func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest, privacy config.AccountPrivacy) {
+	e.apply(bidRequest, NewScrubber(privacy.IPv6Config, privacy.IPv4Config))
 }
 
 func (e Enforcement) apply(bidRequest *openrtb2.BidRequest, scrubber Scrubber) {
@@ -55,21 +58,16 @@ func (e Enforcement) getDeviceIDScrubStrategy() ScrubStrategyDeviceID {
 
 func (e Enforcement) getIPv4ScrubStrategy() ScrubStrategyIPV4 {
 	if e.COPPA || e.GDPRGeo || e.CCPA || e.LMT {
-		return ScrubStrategyIPV4Lowest8
+		return ScrubStrategyIPV4Subnet
 	}
 
 	return ScrubStrategyIPV4None
 }
 
 func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 {
-	if e.COPPA {
-		return ScrubStrategyIPV6Lowest32
+	if e.GDPRGeo || e.CCPA || e.LMT || e.COPPA {
+		return ScrubStrategyIPV6Subnet
 	}
-
-	if e.GDPRGeo || e.CCPA || e.LMT {
-		return ScrubStrategyIPV6Lowest16
-	}
-
 	return ScrubStrategyIPV6None
 }
 
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index f2fb790d..4cc90c82 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -2,8 +2,10 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
-	"strings"
+	"net"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 )
@@ -15,8 +17,8 @@ const (
 	// ScrubStrategyIPV4None does not remove any part of an IPV4 address.
 	ScrubStrategyIPV4None ScrubStrategyIPV4 = iota
 
-	// ScrubStrategyIPV4Lowest8 zeroes out the last 8 bits of an IPV4 address.
-	ScrubStrategyIPV4Lowest8
+	// ScrubStrategyIPV4Subnet zeroes out the last 8 bits of an IPV4 address.
+	ScrubStrategyIPV4Subnet
 )
 
 // ScrubStrategyIPV6 defines the approach to scrub PII from an IPV6 address.
@@ -26,11 +28,8 @@ const (
 	// ScrubStrategyIPV6None does not remove any part of an IPV6 address.
 	ScrubStrategyIPV6None ScrubStrategyIPV6 = iota
 
-	// ScrubStrategyIPV6Lowest16 zeroes out the last 16 bits of an IPV6 address.
-	ScrubStrategyIPV6Lowest16
-
-	// ScrubStrategyIPV6Lowest32 zeroes out the last 32 bits of an IPV6 address.
-	ScrubStrategyIPV6Lowest32
+	// ScrubStrategyIPV6Subnet zeroes out the last 16 bits of an IPV6 sub net address.
+	ScrubStrategyIPV6Subnet
 )
 
 // ScrubStrategyGeo defines the approach to scrub PII from geographical data.
@@ -76,14 +75,20 @@ type Scrubber interface {
 	ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User
 }
 
-type scrubber struct{}
+type scrubber struct {
+	ipV6 config.IPv6
+	ipV4 config.IPv4
+}
 
 // NewScrubber returns an OpenRTB scrubber.
-func NewScrubber() Scrubber {
-	return scrubber{}
+func NewScrubber(ipV6 config.IPv6, ipV4 config.IPv4) Scrubber {
+	return scrubber{
+		ipV6: ipV6,
+		ipV4: ipV4,
+	}
 }
 
-func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
+func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
 	var userExtParsed map[string]json.RawMessage
 	userExtModified := false
 
@@ -169,8 +174,8 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc
 			if deviceCopy.Geo != nil {
 				deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo)
 			}
-			deviceCopy.IP = scrubIPV4Lowest8(deviceCopy.IP)
-			deviceCopy.IPv6 = scrubIPV6Lowest32Bits(deviceCopy.IPv6)
+			deviceCopy.IP = scrubIP(deviceCopy.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize)
+			deviceCopy.IPv6 = scrubIP(deviceCopy.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize)
 		}
 	}
 
@@ -179,7 +184,7 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc
 	return bidRequest
 }
 
-func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
+func (s scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
 	if device == nil {
 		return nil
 	}
@@ -198,15 +203,13 @@ func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, i
 	}
 
 	switch ipv4 {
-	case ScrubStrategyIPV4Lowest8:
-		deviceCopy.IP = scrubIPV4Lowest8(device.IP)
+	case ScrubStrategyIPV4Subnet:
+		deviceCopy.IP = scrubIP(device.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize)
 	}
 
 	switch ipv6 {
-	case ScrubStrategyIPV6Lowest16:
-		deviceCopy.IPv6 = scrubIPV6Lowest16Bits(device.IPv6)
-	case ScrubStrategyIPV6Lowest32:
-		deviceCopy.IPv6 = scrubIPV6Lowest32Bits(device.IPv6)
+	case ScrubStrategyIPV6Subnet:
+		deviceCopy.IPv6 = scrubIP(device.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize)
 	}
 
 	switch geo {
@@ -244,44 +247,13 @@ func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo S
 	return &userCopy
 }
 
-func scrubIPV4Lowest8(ip string) string {
-	i := strings.LastIndex(ip, ".")
-	if i == -1 {
-		return ""
-	}
-
-	return ip[0:i] + ".0"
-}
-
-func scrubIPV6Lowest16Bits(ip string) string {
-	ip = removeLowestIPV6Segment(ip)
-
-	if ip != "" {
-		ip += ":0"
-	}
-
-	return ip
-}
-
-func scrubIPV6Lowest32Bits(ip string) string {
-	ip = removeLowestIPV6Segment(ip)
-	ip = removeLowestIPV6Segment(ip)
-
-	if ip != "" {
-		ip += ":0:0"
-	}
-
-	return ip
-}
-
-func removeLowestIPV6Segment(ip string) string {
-	i := strings.LastIndex(ip, ":")
-
-	if i == -1 {
+func scrubIP(ip string, ones, bits int) string {
+	if ip == "" {
 		return ""
 	}
-
-	return ip[0:i]
+	ipMask := net.CIDRMask(ones, bits)
+	ipMasked := net.ParseIP(ip).Mask(ipMask)
+	return ipMasked.String()
 }
 
 func scrubGeoFull(geo *openrtb2.Geo) *openrtb2.Geo {
diff --git a/router/router.go b/router/router.go
index fcf674b4..f7270445 100644
--- a/router/router.go
+++ b/router/router.go
@@ -55,6 +55,10 @@ import (
 // This function stores the file contents in memory, and should not be used on large directories.
 // If the root directory, or any of the files in it, cannot be read, then the program will exit.
 func NewJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.BidderParamValidator, aliases map[string]string) httprouter.Handle {
+	return newJsonDirectoryServer(schemaDirectory, validator, aliases, openrtb_ext.GetAliasBidderToParent())
+}
+
+func newJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.BidderParamValidator, aliases map[string]string, yamlAliases map[openrtb_ext.BidderName]openrtb_ext.BidderName) httprouter.Handle {
 	// Slurp the files into memory first, since they're small and it minimizes request latency.
 	files, err := os.ReadDir(schemaDirectory)
 	if err != nil {
@@ -73,6 +77,11 @@ func NewJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.Bidder
 		data[bidder] = json.RawMessage(validator.Schema(bidderName))
 	}
 
+	// Add in any aliases
+	for aliasName, parentBidder := range yamlAliases {
+		data[string(aliasName)] = json.RawMessage(validator.Schema(parentBidder))
+	}
+
 	// Add in any default aliases
 	for aliasName, bidderName := range aliases {
 		bidderData, ok := data[bidderName]
diff --git a/static/bidder-info/bematterfull.yaml b/static/bidder-info/bematterfull.yaml
new file mode 100644
index 00000000..f4b48ee7
--- /dev/null
+++ b/static/bidder-info/bematterfull.yaml
@@ -0,0 +1,19 @@
+endpoint: "http://prebid-srv.mtflll-system.live/?pid={{.SourceId}}&host={{.Host}}"
+maintainer:
+  email: "adops@bematterfull.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
+userSync:
+  # mtflll-system.live supports user syncing, but requires configuration by the host. contact this
+  # bidder directly at the email address in this file to ask about enabling user sync.
+  supports:
+    - redirect
\ No newline at end of file
diff --git a/static/bidder-info/datablocks.yaml b/static/bidder-info/datablocks.yaml
index 418b0cab..1f8d6e8d 100644
--- a/static/bidder-info/datablocks.yaml
+++ b/static/bidder-info/datablocks.yaml
@@ -1,4 +1,4 @@
-endpoint: "http://{{.Host}}/openrtb2?sid={{.SourceId}}"
+endpoint: "http://pbserver.dblks.net/openrtb2?sid={{.SourceId}}"
 maintainer:
   email: "prebid@datablocks.net"
 capabilities:
diff --git a/static/bidder-info/huaweiads.yaml b/static/bidder-info/huaweiads.yaml
index 8fe35b4e..fc36ff95 100644
--- a/static/bidder-info/huaweiads.yaml
+++ b/static/bidder-info/huaweiads.yaml
@@ -1,7 +1,7 @@
 endpoint: "https://acd.op.hicloud.com/ppsadx/getResult"
 disabled: true
 maintainer:
-  email: hwads@huawei.com
+  email: prebid@huawei.com
 gvlVendorID: 856
 modifyingVastXmlAllowed: true
 capabilities:
diff --git a/static/bidder-info/sovrn.yaml b/static/bidder-info/sovrn.yaml
index 06dadcf9..62d74152 100644
--- a/static/bidder-info/sovrn.yaml
+++ b/static/bidder-info/sovrn.yaml
@@ -17,6 +17,5 @@ userSync:
     url: "https://ap.lijit.com/pixel?redir={{.RedirectURL}}"
     userMacro: "$UID"
   iframe:
-    url: "https://ap.lijit.com/beacon/prebid-server/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}"
+    url: "https://ap.lijit.com/beacon/prebid-server/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&url={{.RedirectURL}}"
     userMacro: "$UID"
-    
\ No newline at end of file
diff --git a/static/bidder-info/taboola.yaml b/static/bidder-info/taboola.yaml
index 348165a9..436f7469 100644
--- a/static/bidder-info/taboola.yaml
+++ b/static/bidder-info/taboola.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://{{.MediaType}}.bidder.taboola.com/OpenRTB/PS/auction/{{.GvlID}}/{{.PublisherID}}"
+endpoint: "https://{{.MediaType}}.bidder.taboola.com/OpenRTB/PS/auction?exchange={{.GvlID}}&publisher={{.PublisherID}}"
 maintainer:
   email:  ps-team@taboola.com
 gvlVendorID: 42
diff --git a/static/bidder-params/bematterfull.json b/static/bidder-params/bematterfull.json
new file mode 100644
index 00000000..db3a5050
--- /dev/null
+++ b/static/bidder-params/bematterfull.json
@@ -0,0 +1,22 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Bematterfull Adapter Params",
+  "description": "A schema which validates params accepted by the bematterfull adapter",
+  "type": "object",
+  "properties": {
+    "env": {
+      "type": "string",
+      "description": "bematterfull environment",
+      "minLength": 1
+    },
+    "pid": {
+      "type": "string",
+      "description": "Uniq placement ID",
+      "minLength": 1
+    }
+  },
+  "required": [
+    "env",
+    "pid"
+  ]
+}
diff --git a/static/bidder-params/datablocks.json b/static/bidder-params/datablocks.json
index 2abb9867..59b7f947 100644
--- a/static/bidder-params/datablocks.json
+++ b/static/bidder-params/datablocks.json
@@ -9,11 +9,7 @@
         "type": "integer",
         "minimum": 1,
         "description": "Website Source Id"
-      },
-      "host": {
-        "type": "string",
-        "description": "Network Host to request from"
       }
   },
-  "required": ["host", "sourceId"]
+  "required": ["sourceId"]
 }
diff --git a/util/iputil/parse.go b/util/iputil/parse.go
index bcb00760..c226ece8 100644
--- a/util/iputil/parse.go
+++ b/util/iputil/parse.go
@@ -15,6 +15,14 @@ const (
 	IPv6       IPVersion = 6
 )
 
+const (
+	IPv4BitSize = 32
+	IPv6BitSize = 128
+
+	IPv4DefaultMaskingBitSize = 24
+	IPv6DefaultMaskingBitSize = 56
+)
+
 // ParseIP parses v as an ip address returning the result and version, or nil and unknown if invalid.
 func ParseIP(v string) (net.IP, IPVersion) {
 	if ip := net.ParseIP(v); ip != nil {


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index 4bac0894..d2694abc 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -12,17 +12,19 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
 
 var mockAccountData = map[string]json.RawMessage{
-	"valid_acct":                json.RawMessage(`{"disabled":false}`),
-	"disabled_acct":             json.RawMessage(`{"disabled":true}`),
-	"malformed_acct":            json.RawMessage(`{"disabled":"invalid type"}`),
-	"gdpr_channel_enabled_acct": json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`),
-	"ccpa_channel_enabled_acct": json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`),
+	"valid_acct":                                   json.RawMessage(`{"disabled":false}`),
+	"invalid_acct_ipv6_ipv4":                       json.RawMessage(`{"disabled":false, "privacy": {"ipv6": {"anon_keep_bits": -32}, "ipv4": {"anon_keep_bits": -16}}}`),
+	"disabled_acct":                                json.RawMessage(`{"disabled":true}`),
+	"malformed_acct":                               json.RawMessage(`{"disabled":"invalid type"}`),
+	"gdpr_channel_enabled_acct":                    json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`),
+	"ccpa_channel_enabled_acct":                    json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`),
 	"gdpr_channel_enabled_deprecated_purpose_acct": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}, "channel_enabled":{"amp":true}}}`),
 	"gdpr_deprecated_purpose1":                     json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`),
 	"gdpr_deprecated_purpose2":                     json.RawMessage(`{"disabled":false,"gdpr":{"purpose2":{"enforce_purpose":"full"}}}`),
@@ -54,6 +56,8 @@ func TestGetAccount(t *testing.T) {
 		required bool
 		// account_defaults.disabled
 		disabled bool
+		// checkDefaultIP indicates IPv6 and IPv6 should be set to default values
+		checkDefaultIP bool
 		// expected error, or nil if account should be found
 		err error
 	}{
@@ -78,6 +82,8 @@ func TestGetAccount(t *testing.T) {
 		{accountID: "valid_acct", required: false, disabled: true, err: nil},
 		{accountID: "valid_acct", required: true, disabled: true, err: nil},
 
+		{accountID: "invalid_acct_ipv6_ipv4", required: true, disabled: false, err: nil, checkDefaultIP: true},
+
 		// pubID given and matches a host account explicitly disabled (Disabled: true on account json)
 		{accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}},
 		{accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}},
@@ -129,6 +135,10 @@ func TestGetAccount(t *testing.T) {
 				assert.Nil(t, account, "return account must be nil on error")
 				assert.IsType(t, test.err, errors[0], "error is of unexpected type")
 			}
+			if test.checkDefaultIP {
+				assert.Equal(t, account.Privacy.IPv6Config.AnonKeepBits, iputil.IPv6DefaultMaskingBitSize, "ipv6 should be set to default value")
+				assert.Equal(t, account.Privacy.IPv4Config.AnonKeepBits, iputil.IPv4DefaultMaskingBitSize, "ipv4 should be set to default value")
+			}
 		})
 	}
 }
diff --git a/adapters/axonix/axonixtest/exemplary/simple-banner.json b/adapters/axonix/axonixtest/exemplary/simple-banner.json
index 40441a46..d1199b80 100644
--- a/adapters/axonix/axonixtest/exemplary/simple-banner.json
+++ b/adapters/axonix/axonixtest/exemplary/simple-banner.json
@@ -67,7 +67,8 @@
                 "impid": "test-imp-id",
                 "price": 0.500000,
                 "adid": "29681110",
-                "adm": "some-test-ad",
+                "adm": "some-test-ad imp_${AUCTION_PRICE} creativeview_${AUCTION_PRICE}",
+                "nurl": "nurl.link/win?p=${AUCTION_PRICE}",
                 "adomain": ["yahoo.com"],
                 "cid": "958",
                 "crid": "29681110",
@@ -90,7 +91,8 @@
           "id": "7706636740145184841",
           "impid": "test-imp-id",
           "price": 0.5,
-          "adm": "some-test-ad",
+          "adm": "some-test-ad imp_0.5 creativeview_0.5",
+          "nurl": "nurl.link/win?p=0.5",
           "adid": "29681110",
           "adomain": ["yahoo.com"],
           "cid": "958",
diff --git a/adapters/bematterfull/bematterfulltest/exemplary/banner.json b/adapters/bematterfull/bematterfulltest/exemplary/banner.json
new file mode 100644
index 00000000..d90cc677
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/exemplary/banner.json
@@ -0,0 +1,279 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "1",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      },
+      {
+        "id": "2",
+        "secure": 1,
+        "bidfloor": 0.2,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250,
+          "pos": 4
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": ["IAB12"],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=533faf0754cd43ceab591077781b909b&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "1",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250,
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": ["IAB12"],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": ["test.com"],
+                  "cat": ["IAB1"],
+                  "cid": "cid",
+                  "crid": "crid1",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    },
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=533faf0754cd43ceab591077781b909b&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "2",
+              "secure": 1,
+              "bidfloor": 0.2,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250,
+                "pos": 4
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": ["IAB12"],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 2.4,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test3</span>",
+                  "adomain": ["test.com"],
+                  "cat": ["IAB1"],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 1.2,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test2</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid1",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    },
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "2",
+            "price": 2.4,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test3</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/exemplary/native.json b/adapters/bematterfull/bematterfulltest/exemplary/native.json
new file mode 100644
index 00000000..7f81dce6
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/exemplary/native.json
@@ -0,0 +1,162 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "native": {
+          "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+          "ver": "1.1"
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=533faf0754cd43ceab591077781b909b&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "native": {
+                "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+                "ver": "1.1"
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "{}",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "ext": {
+                    "prebid": {
+                      "type": "native"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "id",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "{}",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "ext": {
+              "prebid": {
+                "type": "native"
+              }
+            }
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/exemplary/video.json b/adapters/bematterfull/bematterfulltest/exemplary/video.json
new file mode 100644
index 00000000..eb6a02a4
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/exemplary/video.json
@@ -0,0 +1,202 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "video": {
+          "mimes": [
+            "video/mp4",
+            "video/ogg",
+            "video/webm"
+          ],
+          "minduration": 3,
+          "maxduration": 3000,
+          "protocols": [
+            2,
+            3,
+            5,
+            6,
+            7,
+            8
+          ],
+          "w": 480,
+          "h": 320,
+          "linearity": 1,
+          "playbackmethod": [
+            2
+          ],
+          "pos": 0
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "533faf0754cd43ceab591077781b909b"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=533faf0754cd43ceab591077781b909b&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "video": {
+                "mimes": [
+                  "video/mp4",
+                  "video/ogg",
+                  "video/webm"
+                ],
+                "minduration": 3,
+                "maxduration": 3000,
+                "protocols": [
+                  2,
+                  3,
+                  5,
+                  6,
+                  7,
+                  8
+                ],
+                "w": 480,
+                "h": 320,
+                "linearity": 1,
+                "playbackmethod": [
+                  2
+                ],
+                "pos": 0
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "533faf0754cd43ceab591077781b909b"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "id",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<VAST>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "id",
+            "impid": "id",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<VAST>",
+            "adomain": [
+              "test.com"
+            ],
+            "cat": [
+              "IAB1"
+            ],
+            "cid": "cid",
+            "crid": "crid",
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/bad-response.json b/adapters/bematterfull/bematterfulltest/supplemental/bad-response.json
new file mode 100644
index 00000000..bf459fce
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/bad-response.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "533faf0754cd43ceab591077781b902"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=533faf0754cd43ceab591077781b902&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "533faf0754cd43ceab591077781b902"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": ""
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/empty-mediatype.json b/adapters/bematterfull/bematterfulltest/supplemental/empty-mediatype.json
new file mode 100644
index 00000000..b408c751
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/empty-mediatype.json
@@ -0,0 +1,188 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "533faf0754cd43ceab591077781b902"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=533faf0754cd43ceab591077781b902&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "533faf0754cd43ceab591077781b902"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test1</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }, {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "some": "value"
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bid[1].Ext.Prebid.Type expects one of the following values: 'banner', 'native', 'video', 'audio', got ''",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency":"USD",
+      "bids":[
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test1</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/empty-seatbid-0-bid.json b/adapters/bematterfull/bematterfulltest/supplemental/empty-seatbid-0-bid.json
new file mode 100644
index 00000000..94c79058
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/empty-seatbid-0-bid.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [],
+  "expectedBidResponses": [{"currency":"USD","bids":[]}]
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/empty-seatbid.json b/adapters/bematterfull/bematterfulltest/supplemental/empty-seatbid.json
new file mode 100644
index 00000000..3a7fee5a
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/empty-seatbid.json
@@ -0,0 +1,111 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "a1580f2f-be6d-11eb-a150-d094662c1c35",
+          "bidid": "359da97d0384d8a14767029c18fd840d",
+          "seatbid": [],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Array SeatBid cannot be empty",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/invalid-ext-bidder-object.json b/adapters/bematterfull/bematterfulltest/supplemental/invalid-ext-bidder-object.json
new file mode 100644
index 00000000..67778fd4
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/invalid-ext-bidder-object.json
@@ -0,0 +1,49 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": []
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to deserialize Bematterfull extension: json: cannot unmarshal array into Go value of type openrtb_ext.ExtBematterfull",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/invalid-ext-object.json b/adapters/bematterfull/bematterfulltest/supplemental/invalid-ext-object.json
new file mode 100644
index 00000000..aa215eb3
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/invalid-ext-object.json
@@ -0,0 +1,47 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": ""
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [],
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "Failed to deserialize bidder impression extension: json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/invalid-mediatype.json b/adapters/bematterfull/bematterfulltest/supplemental/invalid-mediatype.json
new file mode 100644
index 00000000..138dd7bb
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/invalid-mediatype.json
@@ -0,0 +1,190 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "id",
+          "bidid": "id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "id",
+                  "impid": "1",
+                  "price": 0.1,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test1</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "banner"
+                    }
+                  }
+                }, {
+                  "id": "id",
+                  "impid": "2",
+                  "price": 1.2,
+                  "nurl": "http://test.com/nurl",
+                  "burl": "http://test.com/burl",
+                  "adm": "<span>Test2</span>",
+                  "adomain": [
+                    "test.com"
+                  ],
+                  "cat": [
+                    "IAB1"
+                  ],
+                  "cid": "cid",
+                  "crid": "crid",
+                  "w": 300,
+                  "h": 250,
+                  "ext": {
+                    "prebid": {
+                      "type": "wrong"
+                    }
+                  }
+                }
+              ],
+              "seat": "seat"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bid[1].Ext.Prebid.Type expects one of the following values: 'banner', 'native', 'video', 'audio', got 'wrong'",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "currency":"USD",
+      "bids":[
+        {
+          "bid": {
+            "id": "id",
+            "impid": "1",
+            "price": 0.1,
+            "nurl": "http://test.com/nurl",
+            "burl": "http://test.com/burl",
+            "adm": "<span>Test1</span>",
+            "adomain": ["test.com"],
+            "cat": ["IAB1"],
+            "cid": "cid",
+            "crid": "crid",
+            "w": 300,
+            "h": 250,
+            "ext": {
+              "prebid": {
+                "type": "banner"
+              }
+            }
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/status-204.json b/adapters/bematterfull/bematterfulltest/supplemental/status-204.json
new file mode 100644
index 00000000..e5cf2f40
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/status-204.json
@@ -0,0 +1,100 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 204,
+        "body": {}
+      }
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/status-400.json b/adapters/bematterfull/bematterfulltest/supplemental/status-400.json
new file mode 100644
index 00000000..0a74f74f
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/status-400.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 400,
+        "body": "The Key has a different ad format"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/status-503.json b/adapters/bematterfull/bematterfulltest/supplemental/status-503.json
new file mode 100644
index 00000000..71c8936e
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/status-503.json
@@ -0,0 +1,105 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 503
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bidder Bematterfull is unavailable. Please contact the bidder support.",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bematterfull/bematterfulltest/supplemental/unexpected-status.json b/adapters/bematterfull/bematterfulltest/supplemental/unexpected-status.json
new file mode 100644
index 00000000..9f4c43ab
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest/supplemental/unexpected-status.json
@@ -0,0 +1,106 @@
+{
+  "mockBidRequest": {
+    "id": "id",
+    "imp": [
+      {
+        "id": "id",
+        "secure": 1,
+        "bidfloor": 0.01,
+        "bidfloorcur": "USD",
+        "banner": {
+          "w": 300,
+          "h": 250
+        },
+        "ext": {
+          "bidder": {
+            "env": "mtflll-stage",
+            "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+          }
+        }
+      }
+    ],
+    "device": {
+      "ua": "UA",
+      "ip": "123.3.4.123"
+    },
+    "regs": {
+      "ext": {
+        "gdpr": 0
+      }
+    },
+    "user": {
+      "id": "userid"
+    },
+    "site": {
+      "id": "id",
+      "domain": "test,com",
+      "cat": [
+        "IAB12"
+      ],
+      "publisher": {
+        "id": "pubid"
+      }
+    }
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "uri": "http://prebid-srv.mtflll-system.live/?pid=dc230510f7eb516f0eb9a10e5913d3b5&host=mtflll-stage",
+        "body": {
+          "id": "id",
+          "imp": [
+            {
+              "id": "id",
+              "secure": 1,
+              "bidfloor": 0.01,
+              "bidfloorcur": "USD",
+              "banner": {
+                "w": 300,
+                "h": 250
+              },
+              "ext": {
+                "bidder": {
+                  "env": "mtflll-stage",
+                  "pid": "dc230510f7eb516f0eb9a10e5913d3b5"
+                }
+              }
+            }
+          ],
+          "device": {
+            "ua": "UA",
+            "ip": "123.3.4.123"
+          },
+          "regs": {
+            "ext": {
+              "gdpr": 0
+            }
+          },
+          "user": {
+            "id": "userid"
+          },
+          "site": {
+            "id": "id",
+            "domain": "test,com",
+            "cat": [
+              "IAB12"
+            ],
+            "publisher": {
+              "id": "pubid"
+            }
+          }
+        }
+      },
+      "mockResponse": {
+        "status": 403,
+        "body": "Access is denied"
+      }
+    }
+  ],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: 403. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ],
+  "expectedBidResponses": []
+}
diff --git a/adapters/bematterfull/bematterfulltest_test.go b/adapters/bematterfull/bematterfulltest_test.go
new file mode 100644
index 00000000..f3d1f412
--- /dev/null
+++ b/adapters/bematterfull/bematterfulltest_test.go
@@ -0,0 +1,27 @@
+package bematterfull
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(
+		openrtb_ext.BidderSmartHub,
+		config.Adapter{
+			Endpoint: "http://prebid-srv.mtflll-system.live/?pid={{.SourceId}}&host={{.Host}}",
+		},
+		config.Server{
+			ExternalUrl: "http://hosturl.com",
+			GvlID:       1,
+			DataCenter:  "2",
+		},
+	)
+
+	assert.NoError(t, buildErr)
+	adapterstest.RunJSONBidderTest(t, "bematterfulltest", bidder)
+}
diff --git a/adapters/bematterfull/params_test.go b/adapters/bematterfull/params_test.go
new file mode 100644
index 00000000..5983dd3e
--- /dev/null
+++ b/adapters/bematterfull/params_test.go
@@ -0,0 +1,53 @@
+package bematterfull
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{"env":"mtflll-stage", "pid":"123456"}`,
+}
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderBematterfull, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected bematterfull params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`[]`,
+	`{}`,
+	`{"some": "param"}`,
+	`{"env":"mtflll-stage"}`,
+	`{"pid":"1234"}`,
+	`{"othervalue":"Lorem ipsum"}`,
+	`{"env":"mtflll-stage", pid:""}`,
+	`{"env":"", pid:"1234"}`,
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderBematterfull, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/datablocks/datablocks_test.go b/adapters/datablocks/datablocks_test.go
index 9a5fb9a2..3d4d746d 100644
--- a/adapters/datablocks/datablocks_test.go
+++ b/adapters/datablocks/datablocks_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderDatablocks, config.Adapter{
-		Endpoint: "http://{{.Host}}/openrtb2?sid={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint: "http://pbserver.dblks.net/openrtb2?sid={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/datablocks/datablockstest/exemplary/multi-request.json b/adapters/datablocks/datablockstest/exemplary/multi-request.json
index 286e058a..eafd8092 100644
--- a/adapters/datablocks/datablockstest/exemplary/multi-request.json
+++ b/adapters/datablocks/datablockstest/exemplary/multi-request.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -34,7 +34,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -54,7 +54,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -73,7 +73,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
@@ -91,7 +91,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/native.json b/adapters/datablocks/datablockstest/exemplary/native.json
index 9487079c..c4cf189e 100644
--- a/adapters/datablocks/datablockstest/exemplary/native.json
+++ b/adapters/datablocks/datablockstest/exemplary/native.json
@@ -14,7 +14,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -38,7 +38,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -54,7 +54,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/simple-banner.json b/adapters/datablocks/datablockstest/exemplary/simple-banner.json
index 289549ab..5c01a29a 100644
--- a/adapters/datablocks/datablockstest/exemplary/simple-banner.json
+++ b/adapters/datablocks/datablockstest/exemplary/simple-banner.json
@@ -21,7 +21,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -41,7 +41,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -64,7 +64,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/simple-video.json b/adapters/datablocks/datablockstest/exemplary/simple-video.json
index 6f807310..fcb2defa 100644
--- a/adapters/datablocks/datablockstest/exemplary/simple-video.json
+++ b/adapters/datablocks/datablockstest/exemplary/simple-video.json
@@ -18,7 +18,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -39,7 +39,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -59,7 +59,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-host.json b/adapters/datablocks/datablockstest/supplemental/bad-host.json
deleted file mode 100644
index cee5efbe..00000000
--- a/adapters/datablocks/datablockstest/supplemental/bad-host.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "bad-host-test",
-    "imp": [
-      {
-        "id": "bad-host-test-imp",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": 
-        {
-          "bidder":
-          {
-              "host": "",
-              "sourceId": 123
-          }
-        }
-      }
-    ]
-  },
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Invalid/Missing Host",
-      "comparison": "literal"
-    } 
-  ]
-}
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-response-body.json b/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
index 7c4801e1..e60c6aab 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-server-response.json b/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
index da2924f6..cdcc0c37 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json b/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
index 08f2fddf..3182e71d 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
@@ -16,7 +16,7 @@
         {
           "bidder":
           {
-              "host": "search.nutella.datablocks.net",
+              "host": "pbserver.dblks.net",
               "sourceId": 0
           }
         }
diff --git a/adapters/datablocks/datablockstest/supplemental/missing-extparam.json b/adapters/datablocks/datablockstest/supplemental/missing-extparam.json
deleted file mode 100644
index d272cd53..00000000
--- a/adapters/datablocks/datablockstest/supplemental/missing-extparam.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "missing-extbid-test",
-    "imp": [
-      {
-        "id": "missing-extbid-test",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "sourceId":54326
-        }
-      }
-    ]
-  },
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Cannot Resolve host or sourceId: unexpected end of JSON input",
-      "comparison": "literal"
-    } 
-  ]
-
-  
-}
diff --git a/adapters/datablocks/datablockstest/supplemental/no-content-response.json b/adapters/datablocks/datablockstest/supplemental/no-content-response.json
index 7fc1b75c..0e53f472 100644
--- a/adapters/datablocks/datablockstest/supplemental/no-content-response.json
+++ b/adapters/datablocks/datablockstest/supplemental/no-content-response.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/config/account_test.go b/config/account_test.go
index 20c60532..31d9796a 100644
--- a/config/account_test.go
+++ b/config/account_test.go
@@ -910,3 +910,49 @@ func TestAccountPriceFloorsValidate(t *testing.T) {
 		})
 	}
 }
+
+func TestIPMaskingValidate(t *testing.T) {
+	tests := []struct {
+		name    string
+		privacy AccountPrivacy
+		want    []error
+	}{
+		{
+			name: "valid",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: 1},
+				IPv6Config: IPv6{AnonKeepBits: 0},
+			},
+		},
+		{
+			name: "invalid",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: -100},
+				IPv6Config: IPv6{AnonKeepBits: -200},
+			},
+			want: []error{
+				errors.New("bits cannot exceed 32 in ipv4 address, or be less than 0"),
+				errors.New("bits cannot exceed 128 in ipv6 address, or be less than 0"),
+			},
+		},
+		{
+			name: "mixed",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: 10},
+				IPv6Config: IPv6{AnonKeepBits: -10},
+			},
+			want: []error{
+				errors.New("bits cannot exceed 128 in ipv6 address, or be less than 0"),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var errs []error
+			errs = tt.privacy.IPv4Config.Validate(errs)
+			errs = tt.privacy.IPv6Config.Validate(errs)
+			assert.ElementsMatch(t, errs, tt.want)
+		})
+	}
+}
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index e505d2f8..77625c0c 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -152,6 +152,14 @@ func TestProcessBidderInfo(t *testing.T) {
 			expectedBidderInfos: nil,
 			expectError:         "error parsing config for bidder bidderA.yaml",
 		},
+		{
+			description: "Invalid alias name",
+			bidderInfos: map[string][]byte{
+				"all.yaml": []byte(testSimpleAliasYAML),
+			},
+			expectedBidderInfos: nil,
+			expectError:         "alias all is a reserved bidder name and cannot be used",
+		},
 		{
 			description: "Valid aliases",
 			bidderInfos: map[string][]byte{
@@ -442,7 +450,6 @@ func TestProcessAliasBidderInfo(t *testing.T) {
 			assert.Equal(t, test.expectedErr, err)
 		} else {
 			assert.Equal(t, test.expectedBidderInfos, bidderInfos)
-			assert.Contains(t, openrtb_ext.CoreBidderNames(), openrtb_ext.BidderName("bidderB"))
 		}
 	}
 }
diff --git a/config/config_test.go b/config/config_test.go
index a35891ba..057ed06e 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -206,6 +206,9 @@ func TestDefaults(t *testing.T) {
 	cmpUnsignedInts(t, "tmax_adjustments.bidder_network_latency_buffer_ms", 0, cfg.TmaxAdjustments.BidderNetworkLatencyBuffer)
 	cmpUnsignedInts(t, "tmax_adjustments.pbs_response_preparation_duration_ms", 0, cfg.TmaxAdjustments.PBSResponsePreparationDuration)
 
+	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 56, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
+	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 24, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
+
 	//Assert purpose VendorExceptionMap hash tables were built correctly
 	expectedTCF2 := TCF2{
 		Enabled: true,
@@ -477,6 +480,11 @@ account_defaults:
         use_dynamic_data: true
         max_rules: 120
         max_schema_dims: 5
+    privacy:
+        ipv6:
+            anon_keep_bits: 50
+        ipv4:
+            anon_keep_bits: 20
 tmax_adjustments:
   enabled: true
   bidder_response_duration_min_ms: 700
@@ -583,6 +591,9 @@ func TestFullConfig(t *testing.T) {
 	cmpBools(t, "account_defaults.events_enabled", *cfg.AccountDefaults.EventsEnabled, true)
 	cmpNils(t, "account_defaults.events.enabled", cfg.AccountDefaults.Events.Enabled)
 
+	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
+	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
+
 	// Assert compression related defaults
 	cmpBools(t, "enable_gzip", false, cfg.EnableGzip)
 	cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP)
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 22ecc1fa..45c27f0e 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -2108,7 +2108,7 @@ func (p *fakePermissions) AuctionActivitiesAllowed(ctx context.Context, bidderCo
 
 func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
 	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+		AllowActivities: &config.AllowActivities{
 			SyncUser: config.Activity{
 				Default: ptrutil.ToPtr(true),
 				Rules: []config.ActivityRule{
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index c42209e0..84e5cf4b 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -965,19 +965,22 @@ func TestImplicitDNTEndToEnd(t *testing.T) {
 func TestReferer(t *testing.T) {
 	testCases := []struct {
 		description             string
-		givenSitePage           string
-		givenSiteDomain         string
-		givenPublisherDomain    string
 		givenReferer            string
 		expectedDomain          string
 		expectedPage            string
 		expectedPublisherDomain string
+		bidReq                  *openrtb_ext.RequestWrapper
 	}{
 		{
 			description:             "site.page/domain are unchanged when site.page/domain and http referer are not set",
 			expectedDomain:          "",
 			expectedPage:            "",
 			expectedPublisherDomain: "",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.page/domain are derived from referer when neither is set and http referer is set",
@@ -985,39 +988,73 @@ func TestReferer(t *testing.T) {
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "somepage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.domain is derived from site.page when site.page is set and http referer is not set",
-			givenSitePage:           "https://test.somepage.com",
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "somepage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.domain is derived from http referer when site.page and http referer are set",
-			givenSitePage:           "https://test.somepage.com",
 			givenReferer:            "http://test.com",
 			expectedDomain:          "test.com",
 			expectedPublisherDomain: "test.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.page/domain are unchanged when site.page/domain and http referer are set",
-			givenSitePage:           "https://test.somepage.com",
-			givenSiteDomain:         "some.domain.com",
 			givenReferer:            "http://test.com",
 			expectedDomain:          "some.domain.com",
 			expectedPublisherDomain: "test.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Domain:    "some.domain.com",
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "Publisher domain shouldn't be overrwriten if already set",
-			givenSitePage:           "https://test.somepage.com",
-			givenSiteDomain:         "",
-			givenPublisherDomain:    "differentpage.com",
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "differentpage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Domain: "",
+					Page:   "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{
+						Domain: "differentpage.com",
+					},
+				},
+			}},
+		},
+		{
+			description:             "request.site is nil",
+			givenReferer:            "http://test.com",
+			expectedDomain:          "test.com",
+			expectedPublisherDomain: "test.com",
+			expectedPage:            "http://test.com",
+			bidReq:                  &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}},
 		},
 	}
 
@@ -1025,20 +1062,12 @@ func TestReferer(t *testing.T) {
 		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json")))
 		httpReq.Header.Set("Referer", test.givenReferer)
 
-		bidReq := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
-			Site: &openrtb2.Site{
-				Domain:    test.givenSiteDomain,
-				Page:      test.givenSitePage,
-				Publisher: &openrtb2.Publisher{Domain: test.givenPublisherDomain},
-			},
-		}}
-
-		setSiteImplicitly(httpReq, bidReq)
+		setSiteImplicitly(httpReq, test.bidReq)
 
-		assert.NotNil(t, bidReq.Site, test.description)
-		assert.Equal(t, test.expectedDomain, bidReq.Site.Domain, test.description)
-		assert.Equal(t, test.expectedPage, bidReq.Site.Page, test.description)
-		assert.Equal(t, test.expectedPublisherDomain, bidReq.Site.Publisher.Domain, test.description)
+		assert.NotNil(t, test.bidReq.Site, test.description)
+		assert.Equal(t, test.expectedDomain, test.bidReq.Site.Domain, test.description)
+		assert.Equal(t, test.expectedPage, test.bidReq.Site.Page, test.description)
+		assert.Equal(t, test.expectedPublisherDomain, test.bidReq.Site.Publisher.Domain, test.description)
 	}
 }
 
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index a32afd24..3c2528e2 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -1299,8 +1299,8 @@ func mockDepsInvalidPrivacy(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		&mockAccountFetcher{data: mockVideoAccountData},
 		&config.Configuration{MaxRequestSize: maxSize,
 			AccountDefaults: config.Account{
-				Privacy: &config.AccountPrivacy{
-					AllowActivities: config.AllowActivities{
+				Privacy: config.AccountPrivacy{
+					AllowActivities: &config.AllowActivities{
 						TransmitPreciseGeo: config.Activity{Rules: []config.ActivityRule{
 							{Condition: config.ActivityCondition{ComponentName: []string{"bidderA.BidderB.bidderC"}}},
 						}},
diff --git a/exchange/exchangetest/request-ext-prebid-filtering.json b/exchange/exchangetest/request-ext-prebid-filtering.json
index 4355f517..8e85d8ed 100644
--- a/exchange/exchangetest/request-ext-prebid-filtering.json
+++ b/exchange/exchangetest/request-ext-prebid-filtering.json
@@ -65,7 +65,10 @@
                         "renderers": [
                             {
                                 "name": "test-name",
-                                "version": "test-version"
+                                "version": "test-version",
+                                "data" : {
+                                    "complex": "data"
+                                }
                             }
                         ]
                     }
@@ -121,7 +124,10 @@
                                 "renderers": [
                                     {
                                         "name": "test-name",
-                                        "version": "test-version"
+                                        "version": "test-version",
+                                        "data": {
+                                            "complex": "data"
+                                        }
                                     }
                                 ]
                             }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 63ca54c2..1ab86fcd 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -4272,7 +4272,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	testCases := []struct {
 		name                 string
 		req                  *openrtb2.BidRequest
-		privacyConfig        *config.AccountPrivacy
+		privacyConfig        config.AccountPrivacy
 		componentName        string
 		allow                bool
 		expectedReqNumber    int
@@ -4364,7 +4364,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		activities, err := privacy.NewActivityControl(test.privacyConfig)
+		activities, err := privacy.NewActivityControl(&test.privacyConfig)
 		assert.NoError(t, err, "")
 		auctionReq := AuctionRequest{
 			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
@@ -4410,33 +4410,33 @@ func buildDefaultActivityConfig(componentName string, allow bool) config.Activit
 	}
 }
 
-func getFetchBidsActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getFetchBidsActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			FetchBids: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitUFPDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitUFPDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitUserFPD: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitTIDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitTIDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitTids: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
diff --git a/floors/enforce_test.go b/floors/enforce_test.go
index bb5d4434..725ac22b 100644
--- a/floors/enforce_test.go
+++ b/floors/enforce_test.go
@@ -196,6 +196,71 @@ func TestEnforceFloorToBids(t *testing.T) {
 			},
 			expErrs: []error{},
 		},
+		{
+			name: "Bids with price less than bidfloor with floorsPrecision",
+			args: args{
+				bidRequestWrapper: func() *openrtb_ext.RequestWrapper {
+					bw := openrtb_ext.RequestWrapper{
+						BidRequest: &openrtb2.BidRequest{
+							ID: "some-request-id",
+							Imp: []openrtb2.Imp{
+								{ID: "some-impression-id-1", BidFloor: 1, BidFloorCur: "USD"},
+								{ID: "some-impression-id-2", BidFloor: 2, BidFloorCur: "USD"},
+							},
+						},
+					}
+					bw.RebuildRequest()
+					return &bw
+				}(),
+				seatBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+					"pubmatic": {
+						Bids: []*entities.PbsOrtbBid{
+							{Bid: &openrtb2.Bid{ID: "some-bid-1", Price: 0.998, ImpID: "some-impression-id-1"}},
+							{Bid: &openrtb2.Bid{ID: "some-bid-2", Price: 1.5, DealID: "deal_Id", ImpID: "some-impression-id-2"}},
+						},
+						Seat:     "pubmatic",
+						Currency: "USD",
+					},
+					"appnexus": {
+						Bids: []*entities.PbsOrtbBid{
+							{Bid: &openrtb2.Bid{ID: "some-bid-11", Price: 0.8, ImpID: "some-impression-id-1"}},
+							{Bid: &openrtb2.Bid{ID: "some-bid-12", Price: 2.2, ImpID: "some-impression-id-2"}},
+						},
+						Seat:     "appnexus",
+						Currency: "USD",
+					},
+				},
+				conversions:       currency.Conversions(convert{}),
+				enforceDealFloors: false,
+			},
+			expEligibleBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+				"pubmatic": {
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-1", Price: 0.998, ImpID: "some-impression-id-1"}},
+						{Bid: &openrtb2.Bid{ID: "some-bid-2", Price: 1.5, DealID: "deal_Id", ImpID: "some-impression-id-2"}},
+					},
+					Seat:     "pubmatic",
+					Currency: "USD",
+				},
+				"appnexus": {
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-12", Price: 2.2, ImpID: "some-impression-id-2"}},
+					},
+					Seat:     "appnexus",
+					Currency: "USD",
+				},
+			},
+			expRejectedBids: []*entities.PbsOrtbSeatBid{
+				{
+					Seat:     "appnexus",
+					Currency: "USD",
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-11", Price: 0.8, ImpID: "some-impression-id-1"}},
+					},
+				},
+			},
+			expErrs: []error{},
+		},
 		{
 			name: "Bids with different currency with enforceDealFloor true",
 			args: args{
@@ -1154,7 +1219,6 @@ func TestUpdateEnforcePBS(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			got := updateEnforcePBS(tt.args.enforceFloors, tt.args.reqExt)
 			assert.Equal(t, tt.want, got, tt.name)
-
 		})
 	}
 }
diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go
index eb60f452..2176c28e 100644
--- a/openrtb_ext/bidders_test.go
+++ b/openrtb_ext/bidders_test.go
@@ -2,7 +2,10 @@ package openrtb_ext
 
 import (
 	"encoding/json"
+	"errors"
+	"os"
 	"testing"
+	"testing/fstest"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/xeipuuv/gojsonschema"
@@ -126,8 +129,154 @@ func TestIsBidderNameReserved(t *testing.T) {
 	}
 }
 
-func TestCoreBidderNames(t *testing.T) {
-	bidderA := "BidderA"
-	SetAliasBidderName(BidderName(bidderA))
-	assert.Contains(t, CoreBidderNames(), BidderName(bidderA))
+func TestSetAliasBidderName(t *testing.T) {
+	parentBidder := BidderName("pBidder")
+	existingCoreBidderNames := coreBidderNames
+
+	testCases := []struct {
+		aliasBidderName string
+		err             error
+	}{
+		{"aBidder", nil},
+		{"all", errors.New("alias all is a reserved bidder name and cannot be used")},
+	}
+
+	for _, test := range testCases {
+		err := SetAliasBidderName(test.aliasBidderName, parentBidder)
+		if err != nil {
+			assert.Equal(t, test.err, err)
+		} else {
+			assert.Contains(t, CoreBidderNames(), BidderName(test.aliasBidderName))
+			assert.Contains(t, aliasBidderToParent, BidderName(test.aliasBidderName))
+		}
+	}
+
+	//reset package variables to not interfere with other test cases. Example - TestBidderParamSchemas
+	coreBidderNames = existingCoreBidderNames
+	aliasBidderToParent = map[BidderName]BidderName{}
+}
+
+type mockParamsHelper struct {
+	fs              fstest.MapFS
+	absFilePath     string
+	absPathErr      error
+	schemaLoaderErr error
+	readFileErr     error
+}
+
+func (m *mockParamsHelper) readDir(name string) ([]os.DirEntry, error) {
+	return m.fs.ReadDir(name)
+}
+
+func (m *mockParamsHelper) readFile(name string) ([]byte, error) {
+	if m.readFileErr != nil {
+		return nil, m.readFileErr
+	}
+	return m.fs.ReadFile(name)
+}
+
+func (m *mockParamsHelper) newReferenceLoader(source string) gojsonschema.JSONLoader {
+	return nil
+}
+
+func (m *mockParamsHelper) newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error) {
+	return nil, m.schemaLoaderErr
+}
+
+func (m *mockParamsHelper) abs(path string) (string, error) {
+	return m.absFilePath, m.absPathErr
+}
+
+func TestNewBidderParamsValidator(t *testing.T) {
+	testCases := []struct {
+		description     string
+		paramsValidator mockParamsHelper
+		dir             string
+		expectedErr     error
+	}{
+		{
+			description: "Valid case",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+			},
+			dir: "test",
+		},
+		{
+			description:     "failed to read directory",
+			paramsValidator: mockParamsHelper{},
+			dir:             "t",
+			expectedErr:     errors.New("Failed to read JSON schemas from directory t. open t: file does not exist"),
+		},
+		{
+			description: "file name does not match the bidder name",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/anyBidder.json": {
+						Data: []byte("{}"),
+					},
+				},
+			},
+			dir:         "test",
+			expectedErr: errors.New("File test/anyBidder.json does not match a valid BidderName."),
+		},
+		{
+			description: "abs file path error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				absFilePath: "test/app.json",
+				absPathErr:  errors.New("any abs error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to get an absolute representation of the path: test/app.json, any abs error"),
+		},
+		{
+			description: "schema loader error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				schemaLoaderErr: errors.New("any schema loader error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to load json schema at : any schema loader error"),
+		},
+		{
+			description: "read file error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				readFileErr: errors.New("any read file error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to read file test/appnexus.json: any read file error"),
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			aliasBidderToParent = map[BidderName]BidderName{"rubicon": "appnexus"}
+			paramsValidator = &test.paramsValidator
+			bidderValidator, err := NewBidderParamsValidator(test.dir)
+			if test.expectedErr == nil {
+				assert.NoError(t, err)
+				assert.Contains(t, bidderValidator.Schema("appnexus"), "{}")
+				assert.Contains(t, bidderValidator.Schema("rubicon"), "{}")
+			} else {
+				assert.Equal(t, err, test.expectedErr)
+			}
+		})
+	}
 }
diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go
index c9528536..33148d1e 100644
--- a/openrtb_ext/bidders_validate_test.go
+++ b/openrtb_ext/bidders_validate_test.go
@@ -24,9 +24,6 @@ var validator BidderParamValidator
 // returns valid JSON for all known CoreBidderNames.
 func TestBidderParamSchemas(t *testing.T) {
 	for _, bidderName := range CoreBidderNames() {
-		if _, ok := GetAliasBidderNamesSet()[bidderName]; ok {
-			continue
-		}
 		schema := validator.Schema(bidderName)
 		if schema == "" {
 			t.Errorf("No schema exists for bidder %s. Does static/bidder-params/%s.json exist?", bidderName, bidderName)
diff --git a/privacy/activitycontrol_test.go b/privacy/activitycontrol_test.go
index 7927014a..db5d588d 100644
--- a/privacy/activitycontrol_test.go
+++ b/privacy/activitycontrol_test.go
@@ -12,20 +12,20 @@ import (
 func TestNewActivityControl(t *testing.T) {
 	testCases := []struct {
 		name            string
-		privacyConf     *config.AccountPrivacy
+		privacyConf     config.AccountPrivacy
 		activityControl ActivityControl
 		err             error
 	}{
 		{
-			name:            "privacy_config_is_nil",
-			privacyConf:     nil,
+			name:            "privacy_config_is_empty",
+			privacyConf:     config.AccountPrivacy{},
 			activityControl: ActivityControl{plans: nil},
 			err:             nil,
 		},
 		{
 			name: "privacy_config_is_specified_and_correct",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					SyncUser:                 getDefaultActivityConfig(),
 					FetchBids:                getDefaultActivityConfig(),
 					EnrichUserFPD:            getDefaultActivityConfig(),
@@ -50,8 +50,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_SyncUser_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					SyncUser: getIncorrectActivityConfig(),
 				},
 			},
@@ -60,8 +60,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_FetchBids_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					FetchBids: getIncorrectActivityConfig(),
 				},
 			},
@@ -70,8 +70,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_EnrichUserFPD_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					EnrichUserFPD: getIncorrectActivityConfig(),
 				},
 			},
@@ -80,8 +80,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_ReportAnalytics_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					ReportAnalytics: getIncorrectActivityConfig(),
 				},
 			},
@@ -90,8 +90,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitUserFPD_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitUserFPD: getIncorrectActivityConfig(),
 				},
 			},
@@ -100,8 +100,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitPreciseGeo_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitPreciseGeo: getIncorrectActivityConfig(),
 				},
 			},
@@ -110,8 +110,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitUniqueRequestIds_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitUniqueRequestIds: getIncorrectActivityConfig(),
 				},
 			},
@@ -120,8 +120,8 @@ func TestNewActivityControl(t *testing.T) {
 		},
 		{
 			name: "privacy_config_is_specified_and_TransmitTids_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
 					TransmitTids: getIncorrectActivityConfig(),
 				},
 			},
@@ -132,7 +132,7 @@ func TestNewActivityControl(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualAC, actualErr := NewActivityControl(test.privacyConf)
+			actualAC, actualErr := NewActivityControl(&test.privacyConf)
 			if test.err == nil {
 				assert.Equal(t, test.activityControl, actualAC)
 				assert.NoError(t, actualErr)
diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go
index be1ab0dc..a97779eb 100644
--- a/privacy/enforcement_test.go
+++ b/privacy/enforcement_test.go
@@ -76,8 +76,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     true,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -92,8 +92,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -108,8 +108,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -124,8 +124,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -156,8 +156,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDNone,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserNone,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -172,8 +172,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     true,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -188,8 +188,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -329,7 +329,7 @@ func TestApplyToggle(t *testing.T) {
 				m.On("ScrubUser", req.User, ScrubStrategyUserIDAndDemographic, ScrubStrategyGeoFull).Return(replacedUser).Once()
 			}
 			if test.expectedScrubDeviceExecuted {
-				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Lowest8, ScrubStrategyIPV6Lowest32, ScrubStrategyGeoFull).Return(replacedDevice).Once()
+				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Subnet, ScrubStrategyIPV6Subnet, ScrubStrategyGeoFull).Return(replacedDevice).Once()
 			}
 
 			test.enforcement.apply(req, m)
diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go
index 0691b2c7..59e593fc 100644
--- a/privacy/scrubber_test.go
+++ b/privacy/scrubber_test.go
@@ -2,6 +2,7 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/config"
 	"testing"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -38,12 +39,12 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "",
 				IFA:      "",
 				IP:       "1.2.3.0",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+				IPv6:     "2001:1db8:2233:4400::",
 				Geo:      &openrtb2.Geo{},
 			},
 			id:   ScrubStrategyDeviceIDAll,
-			ipv4: ScrubStrategyIPV4Lowest8,
-			ipv6: ScrubStrategyIPV6Lowest32,
+			ipv4: ScrubStrategyIPV4Subnet,
+			ipv6: ScrubStrategyIPV6Subnet,
 			geo:  ScrubStrategyGeoFull,
 		},
 		{
@@ -57,7 +58,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "",
 				IFA:      "",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDAll,
@@ -76,16 +77,16 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.0",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDNone,
-			ipv4: ScrubStrategyIPV4Lowest8,
+			ipv4: ScrubStrategyIPV4Subnet,
 			ipv6: ScrubStrategyIPV6None,
 			geo:  ScrubStrategyGeoNone,
 		},
 		{
-			description: "Isolated - IPv6 - Lowest 16",
+			description: "Isolated - IPv6",
 			expected: &openrtb2.Device{
 				DIDMD5:   "anyDIDMD5",
 				DIDSHA1:  "anyDIDSHA1",
@@ -95,31 +96,12 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:0",
+				IPv6:     "2001:1db8:2233:4400::",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDNone,
 			ipv4: ScrubStrategyIPV4None,
-			ipv6: ScrubStrategyIPV6Lowest16,
-			geo:  ScrubStrategyGeoNone,
-		},
-		{
-			description: "Isolated - IPv6 - Lowest 32",
-			expected: &openrtb2.Device{
-				DIDMD5:   "anyDIDMD5",
-				DIDSHA1:  "anyDIDSHA1",
-				DPIDMD5:  "anyDPIDMD5",
-				DPIDSHA1: "anyDPIDSHA1",
-				MACSHA1:  "anyMACSHA1",
-				MACMD5:   "anyMACMD5",
-				IFA:      "anyIFA",
-				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
-				Geo:      device.Geo,
-			},
-			id:   ScrubStrategyDeviceIDNone,
-			ipv4: ScrubStrategyIPV4None,
-			ipv6: ScrubStrategyIPV6Lowest32,
+			ipv6: ScrubStrategyIPV6Subnet,
 			geo:  ScrubStrategyGeoNone,
 		},
 		{
@@ -133,7 +115,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo: &openrtb2.Geo{
 					Lat:   123.46,
 					Lon:   678.89,
@@ -158,7 +140,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      &openrtb2.Geo{},
 			},
 			id:   ScrubStrategyDeviceIDNone,
@@ -167,15 +149,16 @@ func TestScrubDevice(t *testing.T) {
 			geo:  ScrubStrategyGeoFull,
 		},
 	}
-
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
-		result := NewScrubber().ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo)
+		result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubDeviceNil(t *testing.T) {
-	result := NewScrubber().ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone)
+	testIPMasking := getTestIPMasking()
+	result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
@@ -292,14 +275,16 @@ func TestScrubUser(t *testing.T) {
 		},
 	}
 
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
-		result := NewScrubber().ScrubUser(user, test.scrubUser, test.scrubGeo)
+		result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(user, test.scrubUser, test.scrubGeo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubUserNil(t *testing.T) {
-	result := NewScrubber().ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
+	testIPMasking := getTestIPMasking()
+	result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
@@ -336,7 +321,7 @@ func TestScrubRequest(t *testing.T) {
 				},
 				Device: &openrtb2.Device{
 					IP:   "1.2.3.4",
-					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329",
 					Geo:  device.Geo,
 				},
 			},
@@ -354,7 +339,7 @@ func TestScrubRequest(t *testing.T) {
 				},
 				Device: &openrtb2.Device{
 					IP:   "1.2.3.4",
-					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329",
 					Geo:  device.Geo,
 				},
 			},
@@ -431,7 +416,7 @@ func TestScrubRequest(t *testing.T) {
 					MACSHA1:  "anyMACSHA1",
 					MACMD5:   "anyMACMD5",
 					IP:       "1.2.3.0",
-					IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+					IPv6:     "2001:1db8:2233:4400::",
 					Geo: &openrtb2.Geo{
 						Lat: 123.46, Lon: 678.89,
 						Metro: "some metro",
@@ -443,6 +428,7 @@ func TestScrubRequest(t *testing.T) {
 		},
 	}
 
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
 		t.Run(test.description, func(t *testing.T) {
 			bidRequest := &openrtb2.BidRequest{
@@ -462,127 +448,110 @@ func TestScrubRequest(t *testing.T) {
 			}
 			bidRequest.User.EIDs = []openrtb2.EID{{Source: "test"}}
 
-			result := NewScrubber().ScrubRequest(bidRequest, test.enforcement)
+			result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubRequest(bidRequest, test.enforcement)
 			assert.Equal(t, test.expected, result, test.description)
 		})
 	}
 }
 
-func TestScrubIPV4(t *testing.T) {
+func TestScrubIP(t *testing.T) {
 	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
+		IP        string
+		cleanedIP string
+		bits      int
+		maskBits  int
 	}{
 		{
-			IP:          "0.0.0.0",
-			cleanedIP:   "0.0.0.0",
-			description: "Shouldn't do anything for a 0.0.0.0 IP address",
+			IP:        "0:0:0:0:0:0:0:0",
+			cleanedIP: "::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "192.127.111.134",
-			cleanedIP:   "192.127.111.0",
-			description: "Should remove the lowest 8 bits",
+			IP:        "",
+			cleanedIP: "",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "192.127.111.0",
-			cleanedIP:   "192.127.111.0",
-			description: "Shouldn't change anything if the lowest 8 bits are already 0",
+			IP:        "1111:2222:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:2222:3333:4400::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "1111:2222:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:2222::",
+			bits:      128,
+			maskBits:  34,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
-		},
-	}
-
-	for _, test := range testCases {
-		result := scrubIPV4Lowest8(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
-	}
-}
-
-func TestScrubIPV6Lowest16Bits(t *testing.T) {
-	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
-	}{
-		{
-			IP:          "0:0:0:0",
-			cleanedIP:   "0:0:0:0",
-			description: "Shouldn't do anything for a 0:0:0:0 IP address",
+			IP:        "1111:0:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:0:3333:4400::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:8329",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0042:0",
-			description: "Should remove lowest 16 bits",
+			IP:        "1111::6666:7777:8888",
+			cleanedIP: "1111::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:0",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0042:0",
-			description: "Shouldn't do anything if the lowest 16 bits are already 0",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:8329",
+			cleanedIP: "2001:1db8::ff00:0:0",
+			bits:      128,
+			maskBits:  96,
 		},
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0:0",
+			cleanedIP: "2001:1db8::ff00:0:0",
+			bits:      128,
+			maskBits:  96,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:8329",
+			cleanedIP: "2001:1db8::ff00:42:0",
+			bits:      128,
+			maskBits:  112,
 		},
-	}
-
-	for _, test := range testCases {
-		result := scrubIPV6Lowest16Bits(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
-	}
-}
-
-func TestScrubIPV6Lowest32Bits(t *testing.T) {
-	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
-	}{
 		{
-			IP:          "0:0:0:0",
-			cleanedIP:   "0:0:0:0",
-			description: "Shouldn't do anything for a 0:0:0:0 IP address",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:0",
+			cleanedIP: "2001:1db8::ff00:42:0",
+			bits:      128,
+			maskBits:  112,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:8329",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0:0",
-			description: "Should remove lowest 32 bits",
+			IP:        "127.0.0.1",
+			cleanedIP: "127.0.0.0",
+			bits:      32,
+			maskBits:  24,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0:0",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0:0",
-			description: "Shouldn't do anything if the lowest 32 bits are already 0",
+			IP:        "0.0.0.0",
+			cleanedIP: "0.0.0.0",
+			bits:      32,
+			maskBits:  24,
 		},
-
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "192.127.111.134",
+			cleanedIP: "192.127.111.0",
+			bits:      32,
+			maskBits:  24,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "192.127.111.0",
+			cleanedIP: "192.127.111.0",
+			bits:      32,
+			maskBits:  24,
 		},
 	}
-
 	for _, test := range testCases {
-		result := scrubIPV6Lowest32Bits(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
+		t.Run(test.IP, func(t *testing.T) {
+			// bits: ipv6 - 128, ipv4 - 32
+			result := scrubIP(test.IP, test.maskBits, test.bits)
+			assert.Equal(t, test.cleanedIP, result)
+		})
 	}
 }
 
@@ -739,7 +708,7 @@ func getTestDevice() *openrtb2.Device {
 		MACMD5:   "anyMACMD5",
 		IFA:      "anyIFA",
 		IP:       "1.2.3.4",
-		IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+		IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 		Geo: &openrtb2.Geo{
 			Lat:   123.456,
 			Lon:   678.89,
@@ -748,5 +717,15 @@ func getTestDevice() *openrtb2.Device {
 			ZIP:   "some zip",
 		},
 	}
+}
 
+func getTestIPMasking() config.AccountPrivacy {
+	return config.AccountPrivacy{
+		IPv6Config: config.IPv6{
+			54,
+		},
+		IPv4Config: config.IPv4{
+			24,
+		},
+	}
 }
diff --git a/router/router_test.go b/router/router_test.go
index 2b4ff7fd..41c2a724 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -37,8 +37,9 @@ func ensureHasKey(t *testing.T, data map[string]json.RawMessage, key string) {
 }
 
 func TestNewJsonDirectoryServer(t *testing.T) {
-	alias := map[string]string{"aliastest": "appnexus"}
-	handler := NewJsonDirectoryServer("../static/bidder-params", &testValidator{}, alias)
+	defaultAlias := map[string]string{"aliastest": "appnexus"}
+	yamlAlias := map[openrtb_ext.BidderName]openrtb_ext.BidderName{openrtb_ext.BidderName("alias"): openrtb_ext.BidderName("parentAlias")}
+	handler := newJsonDirectoryServer("../static/bidder-params", &testValidator{}, defaultAlias, yamlAlias)
 	recorder := httptest.NewRecorder()
 	request, _ := http.NewRequest("GET", "/whatever", nil)
 	handler(recorder, request, nil)
@@ -59,6 +60,7 @@ func TestNewJsonDirectoryServer(t *testing.T) {
 	}
 
 	ensureHasKey(t, data, "aliastest")
+	ensureHasKey(t, data, "alias")
 }
 
 func TestCheckSupportedUserSyncEndpoints(t *testing.T) {