diff --git a/indexer/services/comlink/__tests__/controllers/api/v4/affiliates-controller.test.ts b/indexer/services/comlink/__tests__/controllers/api/v4/affiliates-controller.test.ts
index 6ea0639617..09c0a3ba70 100644
--- a/indexer/services/comlink/__tests__/controllers/api/v4/affiliates-controller.test.ts
+++ b/indexer/services/comlink/__tests__/controllers/api/v4/affiliates-controller.test.ts
@@ -1,8 +1,8 @@
-import { RequestMethod } from '../../../../src/types';
+import { AffiliateSnapshotRequest, RequestMethod } from '../../../../src/types';
import request from 'supertest';
import { sendRequest } from '../../../helpers/helpers';
-describe('test-controller#V4', () => {
+describe('affiliates-controller#V4', () => {
describe('GET /referral_code', () => {
it('should return referral code for a valid address string', async () => {
const address = 'some_address';
@@ -17,4 +17,65 @@ describe('test-controller#V4', () => {
});
});
});
+
+ describe('GET /address', () => {
+ it('should return address for a valid referral code string', async () => {
+ const referralCode = 'TempCode123';
+ const response: request.Response = await sendRequest({
+ type: RequestMethod.GET,
+ path: `/v4/affiliates/address?referralCode=${referralCode}`,
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({
+ address: 'some_address',
+ });
+ });
+ });
+
+ describe('GET /snapshot', () => {
+ it('should return snapshots when all params specified', async () => {
+ const req: AffiliateSnapshotRequest = {
+ limit: 10,
+ offset: 10,
+ sortByReferredFees: true,
+ };
+ const response: request.Response = await sendRequest({
+ type: RequestMethod.GET,
+ path: `/v4/affiliates/snapshot?limit=${req.limit}&offset=${req.offset}&sortByReferredFees=${req.sortByReferredFees}`,
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body.affiliateList).toHaveLength(10);
+ expect(response.body.currentOffset).toBe(10);
+ expect(response.body.total).toBe(10);
+ });
+
+ it('should return snapshots when optional params not specified', async () => {
+ const response: request.Response = await sendRequest({
+ type: RequestMethod.GET,
+ path: '/v4/affiliates/snapshot',
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body.affiliateList).toHaveLength(1000);
+ expect(response.body.currentOffset).toBe(0);
+ expect(response.body.total).toBe(1000);
+ });
+ });
+
+ describe('GET /total_volume', () => {
+ it('should return total_volume for a valid address', async () => {
+ const address = 'some_address';
+ const response: request.Response = await sendRequest({
+ type: RequestMethod.GET,
+ path: `/v4/affiliates/total_volume?address=${address}`,
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({
+ totalVolume: 111.1,
+ });
+ });
+ });
});
diff --git a/indexer/services/comlink/public/api-documentation.md b/indexer/services/comlink/public/api-documentation.md
index 0d7eee846b..0387e35656 100644
--- a/indexer/services/comlink/public/api-documentation.md
+++ b/indexer/services/comlink/public/api-documentation.md
@@ -30,7 +30,9 @@ headers = {
# baseURL = 'https://indexer.dydx.trade/v4'
baseURL = 'https://dydx-testnet.imperator.co/v4'
-r = requests.get(f'{baseURL}/addresses/{address}', headers = headers)
+r = requests.get(f'{baseURL}/affiliates/address', params={
+ 'referralCode': 'string'
+}, headers = headers)
print(r.json())
@@ -46,7 +48,7 @@ const headers = {
// const baseURL = 'https://indexer.dydx.trade/v4';
const baseURL = 'https://dydx-testnet.imperator.co/v4';
-fetch(`${baseURL}/addresses/{address}`,
+fetch(`${baseURL}/affiliates/address?referralCode=string`,
{
method: 'GET',
@@ -60,13 +62,13 @@ fetch(`${baseURL}/addresses/{address}`,
```
-`GET /addresses/{address}`
+`GET /affiliates/address`
### Parameters
|Name|In|Type|Required|Description|
|---|---|---|---|---|
-|address|path|string|true|none|
+|referralCode|query|string|true|none|
> Example responses
@@ -74,72 +76,7 @@ fetch(`${baseURL}/addresses/{address}`,
```json
{
- "subaccounts": [
- {
- "address": "string",
- "subaccountNumber": 0,
- "equity": "string",
- "freeCollateral": "string",
- "openPerpetualPositions": {
- "property1": {
- "market": "string",
- "status": "OPEN",
- "side": "LONG",
- "size": "string",
- "maxSize": "string",
- "entryPrice": "string",
- "realizedPnl": "string",
- "createdAt": "string",
- "createdAtHeight": "string",
- "sumOpen": "string",
- "sumClose": "string",
- "netFunding": "string",
- "unrealizedPnl": "string",
- "closedAt": null,
- "exitPrice": "string",
- "subaccountNumber": 0
- },
- "property2": {
- "market": "string",
- "status": "OPEN",
- "side": "LONG",
- "size": "string",
- "maxSize": "string",
- "entryPrice": "string",
- "realizedPnl": "string",
- "createdAt": "string",
- "createdAtHeight": "string",
- "sumOpen": "string",
- "sumClose": "string",
- "netFunding": "string",
- "unrealizedPnl": "string",
- "closedAt": null,
- "exitPrice": "string",
- "subaccountNumber": 0
- }
- },
- "assetPositions": {
- "property1": {
- "symbol": "string",
- "side": "LONG",
- "size": "string",
- "assetId": "string",
- "subaccountNumber": 0
- },
- "property2": {
- "symbol": "string",
- "side": "LONG",
- "size": "string",
- "assetId": "string",
- "subaccountNumber": 0
- }
- },
- "marginEnabled": true,
- "updatedAtHeight": "string",
- "latestProcessedBlockHeight": "string"
- }
- ],
- "totalTradingRewards": "string"
+ "address": "string"
}
```
@@ -147,7 +84,7 @@ fetch(`${baseURL}/addresses/{address}`,
|Status|Meaning|Description|Schema|
|---|---|---|---|
-|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Ok|[AddressResponse](#schemaaddressresponse)|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Ok|[AffiliateAddressResponse](#schemaaffiliateaddressresponse)|
+## GetSnapshot
+
+
+
+> Code samples
+
+```python
+import requests
+headers = {
+ 'Accept': 'application/json'
+}
+
+# For the deployment by DYDX token holders, use
+# baseURL = 'https://indexer.dydx.trade/v4'
+baseURL = 'https://dydx-testnet.imperator.co/v4'
+
+r = requests.get(f'{baseURL}/affiliates/snapshot', headers = headers)
+
+print(r.json())
+
+```
+
+```javascript
+
+const headers = {
+ 'Accept':'application/json'
+};
+
+// For the deployment by DYDX token holders, use
+// const baseURL = 'https://indexer.dydx.trade/v4';
+const baseURL = 'https://dydx-testnet.imperator.co/v4';
+
+fetch(`${baseURL}/affiliates/snapshot`,
+{
+ method: 'GET',
+
+ headers: headers
+})
+.then(function(res) {
+ return res.json();
+}).then(function(body) {
+ console.log(body);
+});
+
+```
+
+`GET /affiliates/snapshot`
+
+### Parameters
+
+|Name|In|Type|Required|Description|
+|---|---|---|---|---|
+|offset|query|number(double)|false|none|
+|limit|query|number(double)|false|none|
+|sortByReferredFees|query|boolean|false|none|
+
+> Example responses
+
+> 200 Response
+
+```json
+{
+ "affiliateList": [
+ {
+ "affiliateAddress": "string",
+ "affiliateEarnings": 0.1,
+ "affiliateReferralCode": "string",
+ "affiliateReferredTrades": 0.1,
+ "affiliateTotalReferredFees": 0.1,
+ "affiliateReferredUsers": 0.1,
+ "affiliateReferredNetProtocolEarnings": 0.1
+ }
+ ],
+ "total": 0.1,
+ "currentOffset": 0.1
+}
+```
+
+### Responses
+
+|Status|Meaning|Description|Schema|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Ok|[AffiliateSnapshotResponse](#schemaaffiliatesnapshotresponse)|
+
+
+
+## GetTotalVolume
+
+
+
+> Code samples
+
+```python
+import requests
+headers = {
+ 'Accept': 'application/json'
+}
+
+# For the deployment by DYDX token holders, use
+# baseURL = 'https://indexer.dydx.trade/v4'
+baseURL = 'https://dydx-testnet.imperator.co/v4'
+
+r = requests.get(f'{baseURL}/affiliates/total_volume', params={
+ 'address': 'string'
+}, headers = headers)
+
+print(r.json())
+
+```
+
+```javascript
+
+const headers = {
+ 'Accept':'application/json'
+};
+
+// For the deployment by DYDX token holders, use
+// const baseURL = 'https://indexer.dydx.trade/v4';
+const baseURL = 'https://dydx-testnet.imperator.co/v4';
+
+fetch(`${baseURL}/affiliates/total_volume?address=string`,
+{
+ method: 'GET',
+
+ headers: headers
+})
+.then(function(res) {
+ return res.json();
+}).then(function(body) {
+ console.log(body);
+});
+
+```
+
+`GET /affiliates/total_volume`
+
+### Parameters
+
+|Name|In|Type|Required|Description|
+|---|---|---|---|---|
+|address|query|string|true|none|
+
+> Example responses
+
+> 200 Response
+
+```json
+{
+ "totalVolume": 0.1
+}
+```
+
+### Responses
+
+|Status|Meaning|Description|Schema|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Ok|[AffiliateTotalVolumeResponse](#schemaaffiliatetotalvolumeresponse)|
+
+
+
## GetAssetPositions
@@ -3838,6 +3939,112 @@ This operation does not require authentication
|---|---|---|---|---|
|referralCode|string¦null|true|none|none|
+## AffiliateAddressResponse
+
+
+
+
+
+
+```json
+{
+ "address": "string"
+}
+
+```
+
+### Properties
+
+|Name|Type|Required|Restrictions|Description|
+|---|---|---|---|---|
+|address|string¦null|true|none|none|
+
+## AffiliateSnapshotResponseObject
+
+
+
+
+
+
+```json
+{
+ "affiliateAddress": "string",
+ "affiliateEarnings": 0.1,
+ "affiliateReferralCode": "string",
+ "affiliateReferredTrades": 0.1,
+ "affiliateTotalReferredFees": 0.1,
+ "affiliateReferredUsers": 0.1,
+ "affiliateReferredNetProtocolEarnings": 0.1
+}
+
+```
+
+### Properties
+
+|Name|Type|Required|Restrictions|Description|
+|---|---|---|---|---|
+|affiliateAddress|string|true|none|none|
+|affiliateEarnings|number(double)|true|none|none|
+|affiliateReferralCode|string|true|none|none|
+|affiliateReferredTrades|number(double)|true|none|none|
+|affiliateTotalReferredFees|number(double)|true|none|none|
+|affiliateReferredUsers|number(double)|true|none|none|
+|affiliateReferredNetProtocolEarnings|number(double)|true|none|none|
+
+## AffiliateSnapshotResponse
+
+
+
+
+
+
+```json
+{
+ "affiliateList": [
+ {
+ "affiliateAddress": "string",
+ "affiliateEarnings": 0.1,
+ "affiliateReferralCode": "string",
+ "affiliateReferredTrades": 0.1,
+ "affiliateTotalReferredFees": 0.1,
+ "affiliateReferredUsers": 0.1,
+ "affiliateReferredNetProtocolEarnings": 0.1
+ }
+ ],
+ "total": 0.1,
+ "currentOffset": 0.1
+}
+
+```
+
+### Properties
+
+|Name|Type|Required|Restrictions|Description|
+|---|---|---|---|---|
+|affiliateList|[[AffiliateSnapshotResponseObject](#schemaaffiliatesnapshotresponseobject)]|true|none|none|
+|total|number(double)|true|none|none|
+|currentOffset|number(double)|true|none|none|
+
+## AffiliateTotalVolumeResponse
+
+
+
+
+
+
+```json
+{
+ "totalVolume": 0.1
+}
+
+```
+
+### Properties
+
+|Name|Type|Required|Restrictions|Description|
+|---|---|---|---|---|
+|totalVolume|number(double)¦null|true|none|none|
+
## AssetPositionResponse
diff --git a/indexer/services/comlink/public/swagger.json b/indexer/services/comlink/public/swagger.json
index 9f534db311..27e6608b94 100644
--- a/indexer/services/comlink/public/swagger.json
+++ b/indexer/services/comlink/public/swagger.json
@@ -253,6 +253,99 @@
"type": "object",
"additionalProperties": false
},
+ "AffiliateAddressResponse": {
+ "properties": {
+ "address": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "required": [
+ "address"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "AffiliateSnapshotResponseObject": {
+ "properties": {
+ "affiliateAddress": {
+ "type": "string"
+ },
+ "affiliateEarnings": {
+ "type": "number",
+ "format": "double"
+ },
+ "affiliateReferralCode": {
+ "type": "string"
+ },
+ "affiliateReferredTrades": {
+ "type": "number",
+ "format": "double"
+ },
+ "affiliateTotalReferredFees": {
+ "type": "number",
+ "format": "double"
+ },
+ "affiliateReferredUsers": {
+ "type": "number",
+ "format": "double"
+ },
+ "affiliateReferredNetProtocolEarnings": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "required": [
+ "affiliateAddress",
+ "affiliateEarnings",
+ "affiliateReferralCode",
+ "affiliateReferredTrades",
+ "affiliateTotalReferredFees",
+ "affiliateReferredUsers",
+ "affiliateReferredNetProtocolEarnings"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "AffiliateSnapshotResponse": {
+ "properties": {
+ "affiliateList": {
+ "items": {
+ "$ref": "#/components/schemas/AffiliateSnapshotResponseObject"
+ },
+ "type": "array"
+ },
+ "total": {
+ "type": "number",
+ "format": "double"
+ },
+ "currentOffset": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "required": [
+ "affiliateList",
+ "total",
+ "currentOffset"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "AffiliateTotalVolumeResponse": {
+ "properties": {
+ "totalVolume": {
+ "type": "number",
+ "format": "double",
+ "nullable": true
+ }
+ },
+ "required": [
+ "totalVolume"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
"AssetPositionResponse": {
"properties": {
"positions": {
@@ -1561,6 +1654,108 @@
]
}
},
+ "/affiliates/address": {
+ "get": {
+ "operationId": "GetAddress",
+ "responses": {
+ "200": {
+ "description": "Ok",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AffiliateAddressResponse"
+ }
+ }
+ }
+ }
+ },
+ "security": [],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "referralCode",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ]
+ }
+ },
+ "/affiliates/snapshot": {
+ "get": {
+ "operationId": "GetSnapshot",
+ "responses": {
+ "200": {
+ "description": "Ok",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AffiliateSnapshotResponse"
+ }
+ }
+ }
+ }
+ },
+ "security": [],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "offset",
+ "required": false,
+ "schema": {
+ "format": "double",
+ "type": "number"
+ }
+ },
+ {
+ "in": "query",
+ "name": "limit",
+ "required": false,
+ "schema": {
+ "format": "double",
+ "type": "number"
+ }
+ },
+ {
+ "in": "query",
+ "name": "sortByReferredFees",
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ]
+ }
+ },
+ "/affiliates/total_volume": {
+ "get": {
+ "operationId": "GetTotalVolume",
+ "responses": {
+ "200": {
+ "description": "Ok",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AffiliateTotalVolumeResponse"
+ }
+ }
+ }
+ }
+ },
+ "security": [],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "address",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ]
+ }
+ },
"/assetPositions": {
"get": {
"operationId": "GetAssetPositions",
diff --git a/indexer/services/comlink/src/controllers/api/v4/affiliates-controller.ts b/indexer/services/comlink/src/controllers/api/v4/affiliates-controller.ts
index 7498758e71..f4ac198ba1 100644
--- a/indexer/services/comlink/src/controllers/api/v4/affiliates-controller.ts
+++ b/indexer/services/comlink/src/controllers/api/v4/affiliates-controller.ts
@@ -9,25 +9,96 @@ import { getReqRateLimiter } from '../../../caches/rate-limiters';
import config from '../../../config';
import { handleControllerError } from '../../../lib/helpers';
import { rateLimiterMiddleware } from '../../../lib/rate-limit';
+import { handleValidationErrors } from '../../../request-helpers/error-handler';
import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats';
-import { AffiliateReferralCodeRequest, AffiliateReferralCodeResponse } from '../../../types';
+import {
+ AffiliateAddressRequest,
+ AffiliateReferralCodeRequest,
+ AffiliateReferralCodeResponse,
+ AffiliateAddressResponse,
+ AffiliateSnapshotResponse,
+ AffiliateSnapshotResponseObject,
+ AffiliateSnapshotRequest,
+ AffiliateTotalVolumeResponse,
+ AffiliateTotalVolumeRequest,
+} from '../../../types';
const router: express.Router = express.Router();
const controllerName: string = 'affiliates-controller';
+// TODO(OTE-731): replace api stubs with real logic
@Route('affiliates')
class AffiliatesController extends Controller {
@Get('/referral_code')
async getReferralCode(
@Query() address: string, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise {
- // TODO: OTE-731 replace apit stubs with real logic
// simulate a delay
await new Promise((resolve) => setTimeout(resolve, 100));
return {
referralCode: 'TempCode123',
};
}
+
+ @Get('/address')
+ async getAddress(
+ @Query() referralCode: string, // eslint-disable-line @typescript-eslint/no-unused-vars
+ ): Promise {
+ // simulate a delay
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ return {
+ address: 'some_address',
+ };
+ }
+
+ @Get('/snapshot')
+ async getSnapshot(
+ @Query() offset?: number,
+ @Query() limit?: number,
+ @Query() sortByReferredFees?: boolean,
+ ): Promise {
+ const finalOffset = offset ?? 0;
+ const finalLimit = limit ?? 1000;
+ // eslint-disable-next-line
+ const finalSortByReferredFees = sortByReferredFees ?? false;
+
+ // simulate a delay
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ const snapshot: AffiliateSnapshotResponseObject = {
+ affiliateAddress: 'some_address',
+ affiliateEarnings: 100,
+ affiliateReferralCode: 'TempCode123',
+ affiliateReferredTrades: 1000,
+ affiliateTotalReferredFees: 100,
+ affiliateReferredUsers: 10,
+ affiliateReferredNetProtocolEarnings: 1000,
+ };
+
+ const affiliateSnapshots: AffiliateSnapshotResponseObject[] = [];
+ for (let i = 0; i < finalLimit; i++) {
+ affiliateSnapshots.push(snapshot);
+ }
+
+ const response: AffiliateSnapshotResponse = {
+ affiliateList: affiliateSnapshots,
+ total: finalLimit,
+ currentOffset: finalOffset,
+ };
+
+ return response;
+ }
+
+ @Get('/total_volume')
+ public async getTotalVolume(
+ @Query() address: string, // eslint-disable-line @typescript-eslint/no-unused-vars
+ ): Promise {
+ // simulate a delay
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ return {
+ totalVolume: 111.1,
+ };
+ }
}
router.get(
@@ -40,6 +111,7 @@ router.get(
errorMessage: 'address must be a valid string',
},
}),
+ handleValidationErrors,
ExportResponseCodeStats({ controllerName }),
async (req: express.Request, res: express.Response) => {
const start: number = Date.now();
@@ -68,4 +140,142 @@ router.get(
},
);
+router.get(
+ '/address',
+ rateLimiterMiddleware(getReqRateLimiter),
+ ...checkSchema({
+ referralCode: {
+ in: ['query'],
+ isString: true,
+ errorMessage: 'referralCode must be a valid string',
+ },
+ }),
+ handleValidationErrors,
+ ExportResponseCodeStats({ controllerName }),
+ async (req: express.Request, res: express.Response) => {
+ const start: number = Date.now();
+ const {
+ referralCode,
+ }: AffiliateAddressRequest = matchedData(req) as AffiliateAddressRequest;
+
+ try {
+ const controller: AffiliatesController = new AffiliatesController();
+ const response: AffiliateAddressResponse = await controller.getAddress(referralCode);
+ return res.send(response);
+ } catch (error) {
+ return handleControllerError(
+ 'AffiliatesController GET /address',
+ 'Affiliates address error',
+ error,
+ req,
+ res,
+ );
+ } finally {
+ stats.timing(
+ `${config.SERVICE_NAME}.${controllerName}.get_address.timing`,
+ Date.now() - start,
+ );
+ }
+ },
+);
+
+router.get(
+ '/snapshot',
+ rateLimiterMiddleware(getReqRateLimiter),
+ ...checkSchema({
+ offset: {
+ in: ['query'],
+ isInt: true,
+ toInt: true,
+ optional: true,
+ errorMessage: 'offset must be a valid integer',
+ },
+ limit: {
+ in: ['query'],
+ isInt: true,
+ toInt: true,
+ optional: true,
+ errorMessage: 'limit must be a valid integer',
+ },
+ sortByReferredFees: {
+ in: ['query'],
+ isBoolean: true,
+ toBoolean: true,
+ optional: true,
+ errorMessage: 'sortByReferredFees must be a boolean',
+ },
+ }),
+ handleValidationErrors,
+ ExportResponseCodeStats({ controllerName }),
+ async (req: express.Request, res: express.Response) => {
+ const start: number = Date.now();
+ const {
+ offset,
+ limit,
+ sortByReferredFees,
+ }: AffiliateSnapshotRequest = matchedData(req) as AffiliateSnapshotRequest;
+
+ try {
+ const controller: AffiliatesController = new AffiliatesController();
+ const response: AffiliateSnapshotResponse = await controller.getSnapshot(
+ offset,
+ limit,
+ sortByReferredFees,
+ );
+ return res.send(response);
+ } catch (error) {
+ return handleControllerError(
+ 'AffiliatesController GET /snapshot',
+ 'Affiliates snapshot error',
+ error,
+ req,
+ res,
+ );
+ } finally {
+ stats.timing(
+ `${config.SERVICE_NAME}.${controllerName}.get_snapshot.timing`,
+ Date.now() - start,
+ );
+ }
+ },
+);
+
+router.get(
+ '/total_volume',
+ rateLimiterMiddleware(getReqRateLimiter),
+ ...checkSchema({
+ address: {
+ in: ['query'],
+ isString: true,
+ errorMessage: 'address must be a valid string',
+ },
+ }),
+ ExportResponseCodeStats({ controllerName }),
+ async (req: express.Request, res: express.Response) => {
+ const start: number = Date.now();
+ const {
+ address,
+ }: AffiliateTotalVolumeRequest = matchedData(req) as AffiliateTotalVolumeRequest;
+
+ try {
+ const controller: AffiliatesController = new AffiliatesController();
+ const response: AffiliateTotalVolumeResponse = await controller.getTotalVolume(address);
+ return res.send(response);
+ } catch (error) {
+ return handleControllerError(
+ 'AffiliateTotalVolumeResponse GET /total_volume',
+ 'Affiliate total volume error',
+ error,
+ req,
+ res,
+ );
+ } finally {
+ stats.timing(
+ `${config.SERVICE_NAME}.${controllerName}.get_total_volume.timing`,
+ Date.now() - start,
+ );
+ }
+ },
+);
+
export default router;
diff --git a/indexer/services/comlink/src/types.ts b/indexer/services/comlink/src/types.ts
index 1715cddf8c..f1845c5bc7 100644
--- a/indexer/services/comlink/src/types.ts
+++ b/indexer/services/comlink/src/types.ts
@@ -669,7 +669,21 @@ export interface MegavaultPositionResponse {
/* ------- Affiliates Types ------- */
export interface AffiliateReferralCodeRequest{
- address: string
+ address: string,
+}
+
+export interface AffiliateAddressRequest{
+ referralCode: string,
+}
+
+export interface AffiliateSnapshotRequest{
+ limit?: number,
+ offset?: number,
+ sortByReferredFees?: boolean,
+}
+
+export interface AffiliateTotalVolumeRequest{
+ address: string,
}
export interface AffiliateReferralCodeResponse {
@@ -683,7 +697,7 @@ export interface AffiliateAddressResponse {
export interface AffiliateSnapshotResponse {
affiliateList: AffiliateSnapshotResponseObject[],
total: number,
- currentOffset: number
+ currentOffset: number,
}
export interface AffiliateSnapshotResponseObject {
@@ -693,5 +707,9 @@ export interface AffiliateSnapshotResponseObject {
affiliateReferredTrades: number,
affiliateTotalReferredFees: number,
affiliateReferredUsers: number,
- affiliateReferredNetProtocolEarnings: number
+ affiliateReferredNetProtocolEarnings: number,
+}
+
+export interface AffiliateTotalVolumeResponse {
+ totalVolume: number | null,
}