Skip to content
This repository was archived by the owner on Aug 12, 2023. It is now read-only.

Commit 2b0051c

Browse files
authored
Introduce app definitions framework (#430)
* Introduce app definitions framework * Ensure edge case is caught
1 parent e45cd83 commit 2b0051c

8 files changed

+507
-0
lines changed

package-lock.json

+43
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@0x/web3-wrapper": "6.0.7",
4545
"@bugsnag/js": "6.4.2",
4646
"@elastic/elasticsearch": "7.5.0",
47+
"@hapi/joi": "17.1.1",
4748
"aws-elasticsearch-connector": "8.2.0",
4849
"aws-sdk": "2.585.0",
4950
"axios": "0.19.0",

src/apps/index.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const _ = require('lodash');
2+
const { getAppDefinitions } = require('../definitions/apps');
3+
4+
const prettifyUndefined = value => (value === undefined ? '(none)' : value);
5+
6+
const getErrorForDuplicate = (type, metadata) => {
7+
const { affiliateAddress, feeRecipientAddress, takerAddress } = metadata;
8+
9+
return new Error(
10+
`Multiple ${type} apps match metadata:` +
11+
'\r\n\r\n' +
12+
`affiliateAddress: ${prettifyUndefined(affiliateAddress)}\r\n` +
13+
`feeRecipientAddress: ${prettifyUndefined(feeRecipientAddress)}\r\n` +
14+
`takerAddress: ${prettifyUndefined(takerAddress)}`,
15+
);
16+
};
17+
18+
const resolveApps = metadata => {
19+
const { affiliateAddress, feeRecipientAddress, takerAddress } = metadata;
20+
21+
const appDefinitions = getAppDefinitions();
22+
const mappings = _.flatMap(appDefinitions, d => d.mappings);
23+
const matches = mappings.filter(
24+
mapping =>
25+
(mapping.affiliateAddress === affiliateAddress ||
26+
mapping.affiliateAddress === undefined) &&
27+
(mapping.feeRecipientAddress === feeRecipientAddress ||
28+
mapping.feeRecipientAddress === undefined) &&
29+
(mapping.takerAddress === takerAddress ||
30+
mapping.takerAddress === undefined),
31+
);
32+
33+
if (matches.filter(m => m.type === 'relayer').length > 1) {
34+
throw getErrorForDuplicate('relayer', metadata);
35+
}
36+
37+
if (matches.filter(m => m.type === 'consumer').length > 1) {
38+
throw getErrorForDuplicate('consumer', metadata);
39+
}
40+
41+
return matches.map(match => {
42+
const appDefinition = appDefinitions.find(d => d.mappings.includes(match));
43+
44+
return {
45+
id: appDefinition.id,
46+
type: match.type,
47+
};
48+
});
49+
};
50+
51+
module.exports = { resolveApps };

src/apps/index.test.js

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
const { getAppDefinitions } = require('../definitions/apps');
2+
const { resolveApps } = require('.');
3+
4+
jest.mock('../definitions/apps');
5+
6+
describe('apps', () => {
7+
describe('resolveAppAttributions', () => {
8+
beforeAll(() => {
9+
getAppDefinitions.mockReturnValue([
10+
{
11+
id: '8fc6beb5-3019-45f7-a55a-9a4c6b4b6513',
12+
name: '1inch.exchange',
13+
mappings: [
14+
{
15+
type: 'relayer',
16+
feeRecipientAddress: '0x4d37f28d2db99e8d35a6c725a5f1749a085850a3',
17+
},
18+
{
19+
type: 'relayer',
20+
feeRecipientAddress: '0x68a17b587caf4f9329f0e372e3a78d23a46de6b5',
21+
},
22+
{
23+
type: 'consumer',
24+
takerAddress: '0x11111254369792b2ca5d084ab5eea397ca8fa48b',
25+
},
26+
],
27+
},
28+
{
29+
id: 'f3db0044-858a-4a0a-bcea-0b6ac8610c70',
30+
name: 'Odee',
31+
mappings: [
32+
{
33+
type: 'relayer',
34+
feeRecipientAddress: '0x382310cbb159b64c2e7c5675d110202701a436dd',
35+
},
36+
],
37+
},
38+
{
39+
id: '5067df8b-f9cd-4a34-aee1-38d607100145',
40+
name: 'Matcha',
41+
mappings: [
42+
{
43+
type: 'relayer',
44+
feeRecipientAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
45+
},
46+
{
47+
type: 'consumer',
48+
affiliateAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
49+
},
50+
],
51+
},
52+
{
53+
id: '4cdcde07-8ca7-4ad0-9d80-3f6d16400f14',
54+
name: 'Paradex',
55+
mappings: [
56+
{
57+
type: 'relayer',
58+
takerAddress: '0x4969358e80cdc3d74477d7447bffa3b2e2acbe92',
59+
},
60+
],
61+
},
62+
{
63+
id: 'daea0778-bec0-42fc-abe1-775519818af0',
64+
name: 'Dodgy Relayer',
65+
mappings: [
66+
{
67+
type: 'relayer',
68+
takerAddress: '0xd2045edc40199019e221d71c0913343f7908d0d5',
69+
},
70+
],
71+
},
72+
]);
73+
});
74+
75+
it('should resolve consumer which matches on taker address', () => {
76+
const apps = resolveApps({
77+
feeRecipientAddress: '0x55662e225a3376759c24331a9aed764f8f0c9fbb',
78+
takerAddress: '0x11111254369792b2ca5d084ab5eea397ca8fa48b',
79+
});
80+
81+
expect(apps).toEqual([
82+
{ id: '8fc6beb5-3019-45f7-a55a-9a4c6b4b6513', type: 'consumer' },
83+
]);
84+
});
85+
86+
it('should resolve relayer which matches on fee recipient address', () => {
87+
const apps = resolveApps({
88+
feeRecipientAddress: '0x382310cbb159b64c2e7c5675d110202701a436dd',
89+
takerAddress: '0x693c188e40f760ecf00d2946ef45260b84fbc43e',
90+
});
91+
92+
expect(apps).toEqual([
93+
{ id: 'f3db0044-858a-4a0a-bcea-0b6ac8610c70', type: 'relayer' },
94+
]);
95+
});
96+
97+
it('should resolve relayer which matches on taker address', () => {
98+
const apps = resolveApps({
99+
feeRecipientAddress: '0x55662e225a3376759c24331a9aed764f8f0c9fbb',
100+
takerAddress: '0x4969358e80cdc3d74477d7447bffa3b2e2acbe92',
101+
});
102+
103+
expect(apps).toEqual([
104+
{ id: '4cdcde07-8ca7-4ad0-9d80-3f6d16400f14', type: 'relayer' },
105+
]);
106+
});
107+
108+
it('should resolve consumer which matches on affiliate address', () => {
109+
const apps = resolveApps({
110+
affiliateAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
111+
feeRecipientAddress: '0x55662e225a3376759c24331a9aed764f8f0c9fbb',
112+
});
113+
114+
expect(apps).toEqual([
115+
{ id: '5067df8b-f9cd-4a34-aee1-38d607100145', type: 'consumer' },
116+
]);
117+
});
118+
119+
it('should resolve mixed relayer-consumer combination', () => {
120+
const apps = resolveApps({
121+
affiliateAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
122+
feeRecipientAddress: '0x382310cbb159b64c2e7c5675d110202701a436dd',
123+
});
124+
125+
expect(apps).toEqual([
126+
{ id: 'f3db0044-858a-4a0a-bcea-0b6ac8610c70', type: 'relayer' },
127+
{ id: '5067df8b-f9cd-4a34-aee1-38d607100145', type: 'consumer' },
128+
]);
129+
});
130+
131+
it('should resolve matching relayer-consumer combination', () => {
132+
const apps = resolveApps({
133+
affiliateAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
134+
feeRecipientAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
135+
});
136+
137+
expect(apps).toEqual([
138+
{ id: '5067df8b-f9cd-4a34-aee1-38d607100145', type: 'relayer' },
139+
{ id: '5067df8b-f9cd-4a34-aee1-38d607100145', type: 'consumer' },
140+
]);
141+
});
142+
143+
it('should throw an error when multiple matching relayers found', () => {
144+
expect(() =>
145+
resolveApps({
146+
feeRecipientAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
147+
takerAddress: '0xd2045edc40199019e221d71c0913343f7908d0d5',
148+
}),
149+
).toThrow(
150+
new Error(
151+
'Multiple relayer apps match metadata:' +
152+
'\r\n\r\n' +
153+
'affiliateAddress: (none)\r\n' +
154+
'feeRecipientAddress: 0x86003b044f70dac0abc80ac8957305b6370893ed\r\n' +
155+
'takerAddress: 0xd2045edc40199019e221d71c0913343f7908d0d5',
156+
),
157+
);
158+
});
159+
160+
it('should throw an error when multiple matching consumers found', () => {
161+
expect(() =>
162+
resolveApps({
163+
affiliateAddress: '0x86003b044f70dac0abc80ac8957305b6370893ed',
164+
takerAddress: '0x11111254369792b2ca5d084ab5eea397ca8fa48b',
165+
}),
166+
).toThrow(
167+
new Error(
168+
'Multiple consumer apps match metadata:' +
169+
'\r\n\r\n' +
170+
'affiliateAddress: 0x86003b044f70dac0abc80ac8957305b6370893ed\r\n' +
171+
'feeRecipientAddress: (none)\r\n' +
172+
'takerAddress: 0x11111254369792b2ca5d084ab5eea397ca8fa48b',
173+
),
174+
);
175+
});
176+
});
177+
});
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"id": "8fc6beb5-3019-45f7-a55a-9a4c6b4b6513",
3+
"name": "1inch Exchange",
4+
"logoUrl": "https://resources.0xtracker.com/logos/1inch-exchange.png",
5+
"urlSlug": "1inch-exchange",
6+
"websiteUrl": "https://1inch.exchange",
7+
"mappings": [
8+
{
9+
"type": "relayer",
10+
"feeRecipientAddress": "0x4d37f28d2db99e8d35a6c725a5f1749a085850a3"
11+
},
12+
{
13+
"type": "relayer",
14+
"feeRecipientAddress": "0x68a17b587caf4f9329f0e372e3a78d23a46de6b5"
15+
},
16+
{
17+
"type": "consumer",
18+
"takerAddress": "0x9b57672d596b513d284976d565f3acb326041db0"
19+
},
20+
{
21+
"type": "consumer",
22+
"takerAddress": "0x0000000053d411becdb4a82d8603edc6d8b8b3bc"
23+
},
24+
{
25+
"type": "consumer",
26+
"takerAddress": "0x111111254b08ceeee8ad6ca827de9952d2a46781"
27+
},
28+
{
29+
"type": "consumer",
30+
"takerAddress": "0x111112549cfedf7822eb11fbd8fd485d8a10f93f"
31+
},
32+
{
33+
"type": "consumer",
34+
"takerAddress": "0x5be5b30a720093c9d1b1e6cf8441280a48a495f8"
35+
},
36+
{
37+
"type": "consumer",
38+
"takerAddress": "0x979ff11dcbf3ac66a4d15a7b3a5b306ccbbef4e9"
39+
},
40+
{
41+
"type": "consumer",
42+
"takerAddress": "0x5c5b40855fc364b797c1f539f7eb39cdc94a8a91"
43+
},
44+
{
45+
"type": "consumer",
46+
"takerAddress": "0x11111254369792b2ca5d084ab5eea397ca8fa48b"
47+
},
48+
{
49+
"type": "consumer",
50+
"takerAddress": "0xe4c577bdec9ce0f6c54f2f82aed5b1913b71ae2f"
51+
},
52+
{
53+
"type": "consumer",
54+
"takerAddress": "0x0000000f8ef4be2b7aed6724e893c1b674b9682d"
55+
},
56+
{
57+
"type": "consumer",
58+
"takerAddress": "0x0000000006adbd7c01bc0738cdbfc3932600ad63"
59+
},
60+
{
61+
"type": "consumer",
62+
"takerAddress": "0x54f424ef9c1583e89e87116d88918cd37c38c264"
63+
},
64+
{
65+
"type": "consumer",
66+
"takerAddress": "0x9328c82a7c96b171689972fd0292cf6ccca4c6bb"
67+
}
68+
],
69+
"categories": [
70+
"dex-aggregator",
71+
"exchange"
72+
]
73+
}

0 commit comments

Comments
 (0)