Skip to content

Commit aad9973

Browse files
committed
implemented pagination
1 parent 353712d commit aad9973

File tree

4 files changed

+437
-47
lines changed

4 files changed

+437
-47
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@bancor/carbon-sdk",
33
"type": "module",
44
"source": "src/index.ts",
5-
"version": "0.0.117-DEV",
5+
"version": "0.0.118-DEV",
66
"description": "The SDK is a READ-ONLY tool, intended to facilitate working with Carbon contracts. It's a convenient wrapper around our matching algorithm, allowing programs and users get a ready to use transaction data that will allow them to manage strategies and fulfill trades",
77
"main": "dist/index.cjs",
88
"module": "dist/index.js",

src/contracts-api/Reader.ts

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { BigNumber } from '../utils/numerics';
22
import { StrategyStructOutput } from '../abis/types/CarbonController';
33
import { Contracts } from './Contracts';
4-
import { isETHAddress, MultiCall, multicall } from './utils';
4+
import {
5+
isETHAddress,
6+
MultiCall,
7+
MulticallService,
8+
DefaultMulticallService,
9+
} from './utils';
510
import { Logger } from '../common/logger';
611
import {
712
EncodedStrategy,
@@ -51,13 +56,19 @@ function toStrategy(res: StrategyStructOutput): EncodedStrategy {
5156
*/
5257
export default class Reader implements Fetcher {
5358
private _contracts: Contracts;
59+
private _multicallService: MulticallService;
5460

55-
public constructor(contracts: Contracts) {
61+
public constructor(
62+
contracts: Contracts,
63+
multicallService?: MulticallService
64+
) {
5665
this._contracts = contracts;
66+
this._multicallService =
67+
multicallService ?? new DefaultMulticallService(contracts.multicall);
5768
}
5869

5970
private _multicall(calls: MultiCall[], blockHeight?: number) {
60-
return multicall(calls, this._contracts.multicall, blockHeight);
71+
return this._multicallService.execute(calls, blockHeight);
6172
}
6273

6374
public async strategy(id: BigNumber): Promise<EncodedStrategy> {
@@ -90,39 +101,88 @@ export default class Reader implements Fetcher {
90101
token0: string,
91102
token1: string
92103
): Promise<EncodedStrategy[]> {
93-
const res = await this._contracts.carbonController.strategiesByPair(
94-
token0,
95-
token1,
96-
0,
97-
0
98-
) ?? [];
99-
return res.map((r) => toStrategy(r));
104+
const allStrategies: EncodedStrategy[] = [];
105+
let startIndex = 0;
106+
const chunkSize = 1000;
107+
108+
while (true) {
109+
const res =
110+
(await this._contracts.carbonController.strategiesByPair(
111+
token0,
112+
token1,
113+
startIndex,
114+
startIndex + chunkSize
115+
)) ?? [];
116+
117+
allStrategies.push(...res.map((r) => toStrategy(r)));
118+
119+
if (res.length < chunkSize) break;
120+
121+
startIndex += chunkSize;
122+
}
123+
124+
return allStrategies;
100125
}
101126

102-
// TODO: add a method to get all strategies by a list of pairs. Returns a collection of pairs and their strategies. It will use multicall to call strategiesByPair method from the contracts.
103127
public async strategiesByPairs(pairs: TokenPair[]): Promise<
104128
{
105129
pair: TokenPair;
106130
strategies: EncodedStrategy[];
107131
}[]
108132
> {
109-
const results = await this._multicall(
133+
const chunkSize = 1000;
134+
const results: { pair: TokenPair; strategies: EncodedStrategy[] }[] = [];
135+
const pairsNeedingMore: { pair: TokenPair; index: number }[] = [];
136+
137+
// First, get the first chunk for all pairs using multicall
138+
const firstChunkResults = await this._multicall(
110139
pairs.map((pair) => ({
111140
contractAddress: this._contracts.carbonController.address,
112141
interface: this._contracts.carbonController.interface,
113142
methodName: 'strategiesByPair',
114-
methodParameters: [pair[0], pair[1], 0, 0],
143+
methodParameters: [pair[0], pair[1], 0, chunkSize],
115144
}))
116145
);
117-
if (!results || results.length === 0) return [];
118-
console.debug('results', results);
119-
return results.map((result, i) => {
120-
const strategiesResult = result[0] as StrategyStructOutput[] ?? [];
121-
return {
122-
pair: pairs[i],
146+
147+
if (!firstChunkResults || firstChunkResults.length === 0) return [];
148+
149+
// Process first chunk results and identify pairs needing more
150+
firstChunkResults.forEach((result, i) => {
151+
const strategiesResult = (result[0] ?? []) as StrategyStructOutput[];
152+
const currentPair = pairs[i];
153+
154+
results.push({
155+
pair: currentPair,
123156
strategies: strategiesResult.map((r) => toStrategy(r)),
124-
};
157+
});
158+
159+
// If we got a full chunk, we need to fetch more
160+
if (strategiesResult.length === chunkSize) {
161+
pairsNeedingMore.push({ pair: currentPair, index: i });
162+
}
125163
});
164+
165+
// Fetch remaining strategies for pairs that need it
166+
for (const { pair, index } of pairsNeedingMore) {
167+
let startIndex = chunkSize;
168+
169+
while (true) {
170+
const res =
171+
(await this._contracts.carbonController.strategiesByPair(
172+
pair[0],
173+
pair[1],
174+
startIndex,
175+
startIndex + chunkSize
176+
)) ?? [];
177+
178+
results[index].strategies.push(...res.map((r) => toStrategy(r)));
179+
180+
if (res.length < chunkSize) break;
181+
startIndex += chunkSize;
182+
}
183+
}
184+
185+
return results;
126186
}
127187

128188
public async tokensByOwner(owner: string) {
@@ -164,7 +224,7 @@ export default class Reader implements Fetcher {
164224
);
165225
if (!results || results.length === 0) return [];
166226
return results.map((res, i) => {
167-
return [pairs[i][0], pairs[i][1], res[0]];
227+
return [pairs[i][0], pairs[i][1], Number(res[0])];
168228
});
169229
}
170230

src/contracts-api/utils.ts

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,57 @@ export interface MultiCall {
1414
methodParameters: any[];
1515
}
1616

17-
export const multicall = async (
18-
calls: MultiCall[],
19-
multicallContract: Multicall,
20-
blockHeight?: number
21-
) => {
22-
try {
23-
const encoded = calls.map((call) => ({
24-
target: call.contractAddress.toLocaleLowerCase(),
25-
callData: call.interface.encodeFunctionData(
26-
call.methodName,
27-
call.methodParameters
28-
),
29-
}));
30-
const encodedRes = await multicallContract.tryAggregate(false, encoded, {
31-
blockTag: blockHeight,
32-
});
17+
export interface MulticallService {
18+
execute(calls: MultiCall[], blockHeight?: number): Promise<unknown[][]>;
19+
}
3320

34-
return encodedRes.map((call, i) => {
35-
if (!call.success) return [];
21+
export class DefaultMulticallService implements MulticallService {
22+
constructor(private readonly multicallContract: Multicall) {}
3623

37-
return calls[i].interface.decodeFunctionResult(
38-
calls[i].methodName,
39-
call.returnData
24+
async execute(
25+
calls: MultiCall[],
26+
blockHeight?: number
27+
): Promise<unknown[][]> {
28+
try {
29+
const encoded = calls.map((call) => ({
30+
target: call.contractAddress.toLocaleLowerCase(),
31+
callData: call.interface.encodeFunctionData(
32+
call.methodName,
33+
call.methodParameters
34+
),
35+
}));
36+
const encodedRes = await this.multicallContract.tryAggregate(
37+
false,
38+
encoded,
39+
{
40+
blockTag: blockHeight,
41+
}
4042
);
41-
});
42-
} catch {
43-
/* empty */
43+
44+
return encodedRes.map((call, i) => {
45+
if (!call.success) return [];
46+
const result = calls[i].interface.decodeFunctionResult(
47+
calls[i].methodName,
48+
call.returnData
49+
);
50+
return Array.isArray(result) ? result : [result];
51+
});
52+
} catch (error: unknown) {
53+
const errorMessage =
54+
error instanceof Error ? error.message : 'Unknown error';
55+
throw new Error(`Multicall execution failed: ${errorMessage}`);
56+
}
4457
}
58+
}
59+
60+
// For backward compatibility
61+
export const multicall = async (
62+
calls: MultiCall[],
63+
multicallContract: Multicall,
64+
blockHeight?: number
65+
): Promise<unknown[][]> => {
66+
const service = new DefaultMulticallService(multicallContract);
67+
return service.execute(calls, blockHeight);
4568
};
4669

4770
export const isETHAddress = (address: string) => {

0 commit comments

Comments
 (0)