diff --git a/modules/lassoBidAdapter.js b/modules/lassoBidAdapter.js
new file mode 100644
index 00000000000..b2f3ccf9a7c
--- /dev/null
+++ b/modules/lassoBidAdapter.js
@@ -0,0 +1,133 @@
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER } from '../src/mediaTypes.js';
+import { getStorageManager } from '../src/storageManager.js';
+import { ajax } from '../src/ajax.js';
+import { config } from '../src/config.js';
+
+const BIDDER_CODE = 'lasso';
+const ENDPOINT_URL = 'https://trc.lhmos.com/prebid';
+const GET_IUD_URL = 'https://secure.adnxs.com/getuid?';
+const COOKIE_NAME = 'aim-xr';
+const storage = getStorageManager({bidderCode: BIDDER_CODE});
+
+export const spec = {
+ code: BIDDER_CODE,
+ isBidRequestValid: function(bid) {
+ return !!(bid.params && bid.params.adUnitId);
+ },
+
+ buildRequests: function(validBidRequests, bidderRequest) {
+ if (validBidRequests.length === 0) {
+ return [];
+ }
+
+ let aimXR = '';
+ if (storage.cookiesAreEnabled) {
+ aimXR = storage.getCookie(COOKIE_NAME, undefined);
+ }
+
+ return validBidRequests.map(bidRequest => {
+ let sizes = []
+ if (bidRequest.mediaTypes && bidRequest.mediaTypes[BANNER] && bidRequest.mediaTypes[BANNER].sizes) {
+ sizes = bidRequest.mediaTypes[BANNER].sizes;
+ }
+
+ const payload = {
+ auctionStart: bidderRequest.auctionStart,
+ url: encodeURIComponent(window.location.href),
+ bidderRequestId: bidRequest.bidderRequestId,
+ adUnitCode: bidRequest.adUnitCode,
+ auctionId: bidRequest.auctionId,
+ bidId: bidRequest.bidId,
+ transactionId: bidRequest.transactionId,
+ device: JSON.stringify(getDeviceData()),
+ sizes,
+ aimXR,
+ uid: '$UID',
+ params: JSON.stringify(bidRequest.params),
+ crumbs: JSON.stringify(bidRequest.crumbs),
+ prebidVersion: '$prebid.version$',
+ version: 1,
+ coppa: config.getConfig('coppa') == true ? 1 : 0,
+ ccpa: bidderRequest.uspConsent || undefined
+ }
+
+ return {
+ method: 'GET',
+ url: getBidRequestUrl(aimXR),
+ data: payload,
+ options: {
+ withCredentials: false
+ },
+ };
+ });
+ },
+
+ interpretResponse: function(serverResponse) {
+ const response = serverResponse && serverResponse.body;
+ const bidResponses = [];
+
+ if (!response) {
+ return bidResponses;
+ }
+
+ const bidResponse = {
+ requestId: response.bidid,
+ cpm: response.bid.price,
+ currency: response.cur,
+ width: response.bid.w,
+ height: response.bid.h,
+ creativeId: response.bid.crid,
+ netRevenue: response.netRevenue,
+ ttl: response.ttl,
+ ad: response.bid.ad,
+ mediaType: response.bid.mediaType,
+ meta: {
+ secondaryCatIds: response.bid.cat,
+ advertiserDomains: response.bid.advertiserDomains,
+ advertiserName: response.meta.advertiserName,
+ mediaType: response.bid.mediaType
+ }
+ };
+ bidResponses.push(bidResponse);
+ return bidResponses;
+ },
+
+ onTimeout: function(timeoutData) {
+ if (timeoutData === null) {
+ return;
+ }
+ ajax(ENDPOINT_URL + '/timeout', null, JSON.stringify(timeoutData), {
+ method: 'POST',
+ withCredentials: false
+ });
+ },
+
+ onBidWon: function(bid) {
+ ajax(ENDPOINT_URL + '/won', null, JSON.stringify(bid), {
+ method: 'POST',
+ withCredentials: false
+ });
+ },
+
+ supportedMediaTypes: [BANNER]
+}
+
+function getBidRequestUrl(aimXR) {
+ if (!aimXR) {
+ return GET_IUD_URL + ENDPOINT_URL + '/request';
+ }
+ return ENDPOINT_URL + '/request'
+}
+
+function getDeviceData() {
+ const win = window.top;
+ return {
+ ua: navigator.userAgent,
+ width: win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth,
+ height: win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight,
+ browserLanguage: navigator.language,
+ }
+}
+
+registerBidder(spec);
diff --git a/modules/lassoBidAdapter.md b/modules/lassoBidAdapter.md
new file mode 100644
index 00000000000..43927fe890c
--- /dev/null
+++ b/modules/lassoBidAdapter.md
@@ -0,0 +1,29 @@
+# Overview
+
+**Module Name**: Lasso Bidder Adapter
+**Module Type**: Bidder Adapter
+**Maintainer**: headerbidding@lassomarketing.io
+
+# Description
+
+Connects to Lasso demand source to fetch bids.
+Only banner format supported.
+
+# Test Parameters
+
+```
+var adUnits = [{
+ code: 'banner-ad-unit',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]],
+ }
+ },
+ bids: [{
+ bidder: 'lasso',
+ params: {
+ adUnitId: '0'
+ }
+ }]
+}];
+```
diff --git a/test/spec/modules/lassoBidAdapter_spec.js b/test/spec/modules/lassoBidAdapter_spec.js
new file mode 100644
index 00000000000..5825927a931
--- /dev/null
+++ b/test/spec/modules/lassoBidAdapter_spec.js
@@ -0,0 +1,177 @@
+import { expect } from 'chai';
+import { spec } from 'modules/lassoBidAdapter.js';
+import { server } from '../../mocks/xhr';
+
+const ENDPOINT_URL = 'https://trc.lhmos.com/prebid';
+
+const bid = {
+ bidder: 'lasso',
+ params: {
+ adUnitId: 123456
+ },
+ auctionStart: Date.now(),
+ adUnitCode: 'adunit-code',
+ auctionId: 'cfa6f46d-4584-46e1-9c00-54769abb51e3',
+ bidderRequestId: 'a123b456c789d',
+ bidId: '123a456b789',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
+ sizes: [[300, 250]],
+ src: 'client',
+ transactionId: '26740296-0111-4b7a-80df-7196823026f4'
+};
+
+const bidderRequest = {
+ auctionId: 'cfa6f46d-4584-46e1-9c00-54769abb51e3',
+ auctionStart: Date.now(),
+ start: Date.now(),
+ biddeCode: 'lasso',
+ bidderRequestId: 'a123b456c789d',
+ bids: [bid],
+ timeout: 10000
+};
+
+describe('lassoBidAdapter', function () {
+ describe('All needed functions are available', function() {
+ it(`isBidRequestValid is present and type function`, function () {
+ expect(spec.isBidRequestValid).to.exist.and.to.be.a('function')
+ });
+
+ it(`buildRequests is present and type function`, function () {
+ expect(spec.buildRequests).to.exist.and.to.be.a('function')
+ });
+
+ it(`interpretResponse is present and type function`, function () {
+ expect(spec.interpretResponse).to.exist.and.to.be.a('function')
+ });
+
+ it(`onTimeout is present and type function`, function () {
+ expect(spec.onTimeout).to.exist.and.to.be.a('function')
+ });
+
+ it(`onBidWon is present and type function`, function () {
+ expect(spec.onBidWon).to.exist.and.to.be.a('function')
+ });
+ });
+
+ describe('isBidRequestValid', function () {
+ it('should return true when required params found', function () {
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
+ });
+ it('should return true when there are extra params', function () {
+ const bid = Object.assign({}, bid, {
+ params: {
+ adUnitId: 123456,
+ zone: 1,
+ publisher: 'test'
+ }
+ })
+ expect(spec.isBidRequestValid(bid)).to.equal(true);
+ });
+ it('should return false when there are no params', function () {
+ const invalidBid = { ...bid };
+ delete invalidBid.params;
+ expect(spec.isBidRequestValid(invalidBid)).to.equal(false);
+ });
+ });
+
+ describe('buildRequests', function () {
+ const validBidRequests = spec.buildRequests([bid], bidderRequest);
+ expect(validBidRequests).to.be.an('array').that.is.not.empty;
+
+ const bidRequest = validBidRequests[0];
+
+ it('Returns valid bidRequest', function () {
+ expect(bidRequest).to.exist;
+ expect(bidRequest.method).to.exist;
+ expect(bidRequest.url).to.exist;
+ expect(bidRequest.data).to.exist;
+ });
+
+ it('Returns GET method', function() {
+ expect(bidRequest.method).to.exist;
+ expect(bidRequest.method).to.equal('GET');
+ });
+ });
+
+ describe('interpretResponse', function () {
+ let serverResponse = {
+ body: {
+ bidid: '123456789',
+ id: '33302780340222111',
+ bid: {
+ price: 1,
+ w: 728,
+ h: 90,
+ crid: 123456,
+ ad: '',
+ mediaType: 'banner'
+ },
+ meta: {
+ cat: ['1', '2', '3', '4'],
+ advertiserDomains: ['lassomarketing.io'],
+ advertiserName: 'Lasso'
+ },
+ cur: 'USD',
+ netRevenue: false,
+ ttl: 300,
+ }
+ };
+
+ it('should get the correct bid response', function () {
+ let expectedResponse = {
+ requestId: '123456789',
+ cpm: 1,
+ currency: 'USD',
+ width: 728,
+ height: 90,
+ creativeId: 123456,
+ netRevenue: false,
+ ttl: 300,
+ ad: '',
+ mediaType: 'banner',
+ meta: {
+ secondaryCatIds: ['1', '2', '3', '4'],
+ advertiserDomains: ['lassomarketing.io'],
+ advertiserName: 'Lasso',
+ mediaType: 'banner'
+ }
+ };
+ let result = spec.interpretResponse(serverResponse);
+ expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse));
+ });
+ });
+
+ describe('onTimeout', () => {
+ it('should send timeout', () => {
+ const timeoutData = {
+ bidder: 'lasso',
+ auctionId: 'cfa6f46d-4584-46e1-9c00-54769abb51e3',
+ dUnitCode: 'adunit-code',
+ bidId: '123a456b789',
+ params: {
+ adUnitId: 123456,
+ },
+ timeout: 3000
+ };
+ spec.onTimeout(timeoutData);
+
+ expect(server.requests[0].method).to.equal('POST');
+ expect(server.requests[0].url).to.equal(ENDPOINT_URL + '/timeout');
+ expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(timeoutData);
+ });
+ });
+
+ describe('onBidWon', () => {
+ it('should send bid won request', () => {
+ spec.onBidWon(bid);
+
+ expect(server.requests[0].method).to.equal('POST');
+ expect(server.requests[0].url).to.equal(ENDPOINT_URL + '/won');
+ expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(bid);
+ });
+ });
+});