1 | import { TokenInfo } from './types';
|
2 |
|
3 | export type TokenInfoChangeKey = Exclude<
|
4 | keyof TokenInfo,
|
5 | 'address' | 'chainId'
|
6 | >;
|
7 | export type TokenInfoChanges = Array<TokenInfoChangeKey>;
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | function compareTokenInfoProperty(a: unknown, b: unknown): boolean {
|
16 | if (a === b) return true;
|
17 | if (typeof a !== typeof b) return false;
|
18 | if (Array.isArray(a) && Array.isArray(b)) {
|
19 | return a.every((el, i) => b[i] === el);
|
20 | }
|
21 | return false;
|
22 | }
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | export interface TokenListDiff {
|
28 | |
29 |
|
30 |
|
31 | readonly added: TokenInfo[];
|
32 | |
33 |
|
34 |
|
35 | readonly removed: TokenInfo[];
|
36 | |
37 |
|
38 |
|
39 | readonly changed: {
|
40 | [chainId: number]: {
|
41 | [address: string]: TokenInfoChanges;
|
42 | };
|
43 | };
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | export function diffTokenLists(
|
52 | base: TokenInfo[],
|
53 | update: TokenInfo[]
|
54 | ): TokenListDiff {
|
55 | const indexedBase = base.reduce<{
|
56 | [chainId: number]: { [address: string]: TokenInfo };
|
57 | }>((memo, tokenInfo) => {
|
58 | if (!memo[tokenInfo.chainId]) memo[tokenInfo.chainId] = {};
|
59 | memo[tokenInfo.chainId][tokenInfo.address] = tokenInfo;
|
60 | return memo;
|
61 | }, {});
|
62 |
|
63 | const newListUpdates = update.reduce<{
|
64 | added: TokenInfo[];
|
65 | changed: {
|
66 | [chainId: number]: {
|
67 | [address: string]: TokenInfoChanges;
|
68 | };
|
69 | };
|
70 | index: {
|
71 | [chainId: number]: {
|
72 | [address: string]: true;
|
73 | };
|
74 | };
|
75 | }>(
|
76 | (memo, tokenInfo) => {
|
77 | const baseToken = indexedBase[tokenInfo.chainId]?.[tokenInfo.address];
|
78 | if (!baseToken) {
|
79 | memo.added.push(tokenInfo);
|
80 | } else {
|
81 | const changes: TokenInfoChanges = Object.keys(tokenInfo)
|
82 | .filter(
|
83 | (s): s is TokenInfoChangeKey => s !== 'address' && s !== 'chainId'
|
84 | )
|
85 | .filter(s => {
|
86 | return !compareTokenInfoProperty(tokenInfo[s], baseToken[s]);
|
87 | });
|
88 | if (changes.length > 0) {
|
89 | if (!memo.changed[tokenInfo.chainId]) {
|
90 | memo.changed[tokenInfo.chainId] = {};
|
91 | }
|
92 | memo.changed[tokenInfo.chainId][tokenInfo.address] = changes;
|
93 | }
|
94 | }
|
95 |
|
96 | if (!memo.index[tokenInfo.chainId]) {
|
97 | memo.index[tokenInfo.chainId] = {
|
98 | [tokenInfo.address]: true,
|
99 | };
|
100 | } else {
|
101 | memo.index[tokenInfo.chainId][tokenInfo.address] = true;
|
102 | }
|
103 |
|
104 | return memo;
|
105 | },
|
106 | { added: [], changed: {}, index: {} }
|
107 | );
|
108 |
|
109 | const removed = base.reduce<TokenInfo[]>((list, curr) => {
|
110 | if (
|
111 | !newListUpdates.index[curr.chainId] ||
|
112 | !newListUpdates.index[curr.chainId][curr.address]
|
113 | ) {
|
114 | list.push(curr);
|
115 | }
|
116 | return list;
|
117 | }, []);
|
118 |
|
119 | return {
|
120 | added: newListUpdates.added,
|
121 | changed: newListUpdates.changed,
|
122 | removed,
|
123 | };
|
124 | }
|
125 |
|
\ | No newline at end of file |