@paraswap/dex-lib 4.1.3 → 4.1.4-bal-v3-hooks.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/abi/balancer-v3/directionalFeeHook.json +684 -0
- package/build/abi/balancer-v3/stableSurgeHook.json +976 -0
- package/build/dex/balancer-v3/balancer-test-helpers.d.ts +5 -0
- package/build/dex/balancer-v3/balancer-test-helpers.js +122 -0
- package/build/dex/balancer-v3/balancer-test-helpers.js.map +1 -0
- package/build/dex/balancer-v3/balancer-v3-pool.d.ts +4 -1
- package/build/dex/balancer-v3/balancer-v3-pool.js +20 -13
- package/build/dex/balancer-v3/balancer-v3-pool.js.map +1 -1
- package/build/dex/balancer-v3/balancer-v3.d.ts +4 -0
- package/build/dex/balancer-v3/balancer-v3.js +29 -3
- package/build/dex/balancer-v3/balancer-v3.js.map +1 -1
- package/build/dex/balancer-v3/config.js +39 -0
- package/build/dex/balancer-v3/config.js.map +1 -1
- package/build/dex/balancer-v3/getOnChainState.d.ts +1 -2
- package/build/dex/balancer-v3/getOnChainState.js +0 -2
- package/build/dex/balancer-v3/getOnChainState.js.map +1 -1
- package/build/dex/balancer-v3/getPoolsApi.d.ts +2 -1
- package/build/dex/balancer-v3/getPoolsApi.js +17 -10
- package/build/dex/balancer-v3/getPoolsApi.js.map +1 -1
- package/build/dex/balancer-v3/getTopPoolsApi.d.ts +5 -1
- package/build/dex/balancer-v3/getTopPoolsApi.js +6 -3
- package/build/dex/balancer-v3/getTopPoolsApi.js.map +1 -1
- package/build/dex/balancer-v3/hooks/balancer-hook-event-subscriber.d.ts +54 -0
- package/build/dex/balancer-v3/hooks/balancer-hook-event-subscriber.js +153 -0
- package/build/dex/balancer-v3/hooks/balancer-hook-event-subscriber.js.map +1 -0
- package/build/dex/balancer-v3/hooks/directionalFeeHook.d.ts +26 -0
- package/build/dex/balancer-v3/hooks/directionalFeeHook.js +41 -0
- package/build/dex/balancer-v3/hooks/directionalFeeHook.js.map +1 -0
- package/build/dex/balancer-v3/hooks/loadHooksConfig.d.ts +2 -0
- package/build/dex/balancer-v3/hooks/loadHooksConfig.js +11 -0
- package/build/dex/balancer-v3/hooks/loadHooksConfig.js.map +1 -0
- package/build/dex/balancer-v3/hooks/stableSurgeHook.d.ts +39 -0
- package/build/dex/balancer-v3/hooks/stableSurgeHook.js +99 -0
- package/build/dex/balancer-v3/hooks/stableSurgeHook.js.map +1 -0
- package/build/dex/balancer-v3/types.d.ts +4 -1
- package/package.json +2 -2
- package/scripts/run-dex-integration-v3.ts +4 -4
- package/src/abi/balancer-v3/directionalFeeHook.json +684 -0
- package/src/abi/balancer-v3/stableSurgeHook.json +976 -0
- package/src/dex/balancer-v3/balancer-hooks-events.test.ts +99 -0
- package/src/dex/balancer-v3/balancer-stableSurge.test.ts +110 -0
- package/src/dex/balancer-v3/balancer-test-helpers.ts +199 -0
- package/src/dex/balancer-v3/balancer-v3-e2e.test.ts +20 -0
- package/src/dex/balancer-v3/balancer-v3-integration.test.ts +2 -197
- package/src/dex/balancer-v3/balancer-v3-pool.ts +37 -13
- package/src/dex/balancer-v3/balancer-v3.ts +42 -1
- package/src/dex/balancer-v3/config.ts +39 -0
- package/src/dex/balancer-v3/getOnChainState.ts +1 -4
- package/src/dex/balancer-v3/getPoolsApi.ts +42 -23
- package/src/dex/balancer-v3/getTopPoolsApi.ts +13 -2
- package/src/dex/balancer-v3/hooks/balancer-hook-event-subscriber.ts +226 -0
- package/src/dex/balancer-v3/hooks/directionalFeeHook.ts +51 -0
- package/src/dex/balancer-v3/hooks/loadHooksConfig.ts +12 -0
- package/src/dex/balancer-v3/hooks/stableSurgeHook.ts +163 -0
- package/src/dex/balancer-v3/types.ts +4 -2
- package/tests/constants-e2e.ts +6 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EventFragment,
|
|
3
|
+
Fragment,
|
|
4
|
+
FunctionFragment,
|
|
5
|
+
Interface,
|
|
6
|
+
} from '@ethersproject/abi';
|
|
7
|
+
import _ from 'lodash';
|
|
8
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
9
|
+
import { Log, Logger } from '../../../types';
|
|
10
|
+
import { catchParseLogError } from '../../../utils';
|
|
11
|
+
import { StatefulEventSubscriber } from '../../../stateful-event-subscriber';
|
|
12
|
+
import { IDexHelper } from '../../../dex-helper/idex-helper';
|
|
13
|
+
import { loadHooksConfig } from './loadHooksConfig';
|
|
14
|
+
import {
|
|
15
|
+
DirectionalFee,
|
|
16
|
+
DirectionalFeeConfig,
|
|
17
|
+
DirectionalFeeHookState,
|
|
18
|
+
getDirectionalFeeHookState,
|
|
19
|
+
} from './directionalFeeHook';
|
|
20
|
+
import exitFeeHookAbi from '../../../abi/balancer-v3/directionalFeeHook.json';
|
|
21
|
+
import {
|
|
22
|
+
getStableSurgeHookState,
|
|
23
|
+
maxSurgeFeePercentageChangedEvent,
|
|
24
|
+
StableSurge,
|
|
25
|
+
StableSurgeConfig,
|
|
26
|
+
StableSurgeHookState,
|
|
27
|
+
thresholdSurgePercentageChangedEvent,
|
|
28
|
+
} from './stableSurgeHook';
|
|
29
|
+
import stableSurgeHookAbi from '../../../abi/balancer-v3/stableSurgeHook.json';
|
|
30
|
+
|
|
31
|
+
// Add each supported hook state here
|
|
32
|
+
export type HookState = DirectionalFeeHookState | StableSurgeHookState;
|
|
33
|
+
|
|
34
|
+
export type HookStateMap = {
|
|
35
|
+
[address: string]: HookState;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type HookConfig = DirectionalFeeConfig | StableSurgeConfig;
|
|
39
|
+
|
|
40
|
+
export type HooksConfigMap = {
|
|
41
|
+
[hookAddress: string]: HookConfig;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* This will handle events/state for each supported hook contract. State is used during swap calculations.
|
|
46
|
+
* Each hook should have its own implementation file for state typing, fetching state and handling events.
|
|
47
|
+
*/
|
|
48
|
+
export class BalancerEventHook extends StatefulEventSubscriber<HookStateMap> {
|
|
49
|
+
handlers: {
|
|
50
|
+
[event: string]: (
|
|
51
|
+
event: any,
|
|
52
|
+
state: DeepReadonly<HookStateMap>,
|
|
53
|
+
log: Readonly<Log>,
|
|
54
|
+
) => DeepReadonly<HookStateMap> | null;
|
|
55
|
+
} = {};
|
|
56
|
+
|
|
57
|
+
interfaces: Interface[];
|
|
58
|
+
logDecoder: (log: Log) => any;
|
|
59
|
+
|
|
60
|
+
addressesSubscribed: string[];
|
|
61
|
+
|
|
62
|
+
hooksConfigMap: HooksConfigMap;
|
|
63
|
+
|
|
64
|
+
constructor(
|
|
65
|
+
readonly parentName: string,
|
|
66
|
+
protected network: number,
|
|
67
|
+
protected dexHelper: IDexHelper,
|
|
68
|
+
logger: Logger,
|
|
69
|
+
) {
|
|
70
|
+
super(parentName, 'Balancer_Hooks', dexHelper, logger);
|
|
71
|
+
|
|
72
|
+
// Load supported hooks from config file
|
|
73
|
+
this.hooksConfigMap = loadHooksConfig(network);
|
|
74
|
+
|
|
75
|
+
// Add each hook ABI here
|
|
76
|
+
this.interfaces = [
|
|
77
|
+
new Interface(exitFeeHookAbi),
|
|
78
|
+
new Interface(stableSurgeHookAbi),
|
|
79
|
+
];
|
|
80
|
+
this.logDecoder = (log: Log) =>
|
|
81
|
+
this.combineInterfaces(this.interfaces).parseLog(log);
|
|
82
|
+
|
|
83
|
+
// Subscribe to all hooks
|
|
84
|
+
this.addressesSubscribed = Object.keys(this.hooksConfigMap);
|
|
85
|
+
|
|
86
|
+
// StableSurgeHook events
|
|
87
|
+
this.handlers['ThresholdSurgePercentageChanged'] =
|
|
88
|
+
thresholdSurgePercentageChangedEvent.bind(this);
|
|
89
|
+
this.handlers['MaxSurgeFeePercentageChanged'] =
|
|
90
|
+
maxSurgeFeePercentageChangedEvent.bind(this);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The function is called every time any of the subscribed
|
|
95
|
+
* addresses release log. The function accepts the current
|
|
96
|
+
* state, updates the state according to the log, and returns
|
|
97
|
+
* the updated state.
|
|
98
|
+
* @param state - Current state of event subscriber
|
|
99
|
+
* @param log - Log released by one of the subscribed addresses
|
|
100
|
+
* @returns Updates state of the event subscriber after the log
|
|
101
|
+
*/
|
|
102
|
+
protected processLog(
|
|
103
|
+
state: DeepReadonly<HookStateMap>,
|
|
104
|
+
log: Readonly<Log>,
|
|
105
|
+
): DeepReadonly<HookStateMap> | null {
|
|
106
|
+
try {
|
|
107
|
+
const event = this.logDecoder(log);
|
|
108
|
+
if (event.name in this.handlers) {
|
|
109
|
+
return this.handlers[event.name](event, state, log);
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
catchParseLogError(e, this.logger);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The function generates state using on-chain calls. This
|
|
120
|
+
* function is called to regenerate state if the event based
|
|
121
|
+
* system fails to fetch events and the local state is no
|
|
122
|
+
* more correct.
|
|
123
|
+
* @param blockNumber - Blocknumber for which the state should
|
|
124
|
+
* should be generated
|
|
125
|
+
* @returns state of the event subscriber at blocknumber
|
|
126
|
+
*/
|
|
127
|
+
async generateState(
|
|
128
|
+
blockNumber: number,
|
|
129
|
+
): Promise<DeepReadonly<HookStateMap>> {
|
|
130
|
+
// Only hooks that are supported will be called
|
|
131
|
+
const hookState: HookStateMap = {};
|
|
132
|
+
|
|
133
|
+
await Promise.all(
|
|
134
|
+
Object.keys(this.hooksConfigMap).map(async hookAddress => {
|
|
135
|
+
const hookConfig = this.hooksConfigMap[hookAddress];
|
|
136
|
+
if (hookConfig.type === DirectionalFee.type) {
|
|
137
|
+
hookState[hookAddress] = await getDirectionalFeeHookState();
|
|
138
|
+
} else if (hookConfig.type === StableSurge.type) {
|
|
139
|
+
hookState[hookAddress] = await getStableSurgeHookState(
|
|
140
|
+
this.interfaces[1],
|
|
141
|
+
hookAddress,
|
|
142
|
+
hookConfig.factory,
|
|
143
|
+
this.dexHelper,
|
|
144
|
+
blockNumber,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// for (const hookAddress in this.hooksConfigMap) {
|
|
151
|
+
// const hookConfig = this.hooksConfigMap[hookAddress];
|
|
152
|
+
// if (hookConfig.type === DirectionalFee.type) {
|
|
153
|
+
// hookState[hookAddress] = await getDirectionalFeeHookState();
|
|
154
|
+
// } else if (hookConfig.type === StableSurge.type) {
|
|
155
|
+
// hookState[hookAddress] = await getStableSurgeHookState(
|
|
156
|
+
// this.interfaces[1],
|
|
157
|
+
// hookAddress,
|
|
158
|
+
// hookConfig.factory,
|
|
159
|
+
// this.dexHelper,
|
|
160
|
+
// blockNumber,
|
|
161
|
+
// );
|
|
162
|
+
// }
|
|
163
|
+
// }
|
|
164
|
+
|
|
165
|
+
return hookState;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async updateHookState() {
|
|
169
|
+
// Not all hooks need updated, only ones with settings/state that can't be handled by events
|
|
170
|
+
const blockNumber = await this.dexHelper.provider.getBlockNumber();
|
|
171
|
+
const currentState =
|
|
172
|
+
(_.cloneDeep(this.getStaleState()) as HookStateMap) || {};
|
|
173
|
+
|
|
174
|
+
await Promise.all(
|
|
175
|
+
Object.keys(this.hooksConfigMap).map(async hookAddress => {
|
|
176
|
+
const hookConfig = this.hooksConfigMap[hookAddress];
|
|
177
|
+
// StableSurge can have new registered pools with associated hook state
|
|
178
|
+
if (hookConfig.type === StableSurge.type) {
|
|
179
|
+
currentState[hookAddress] = await getStableSurgeHookState(
|
|
180
|
+
this.interfaces[1],
|
|
181
|
+
hookAddress,
|
|
182
|
+
hookConfig.factory,
|
|
183
|
+
this.dexHelper,
|
|
184
|
+
blockNumber,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
this.setState(currentState, blockNumber);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
combineInterfaces = (interfaces: Interface[]): Interface => {
|
|
194
|
+
// Use a Map to store unique fragments, keyed by their string representation
|
|
195
|
+
const uniqueFragments = new Map<string, Fragment>();
|
|
196
|
+
|
|
197
|
+
interfaces.forEach((interfaceInstance: Interface) => {
|
|
198
|
+
interfaceInstance.fragments.forEach((fragment: Fragment) => {
|
|
199
|
+
let key: string;
|
|
200
|
+
|
|
201
|
+
if (fragment instanceof FunctionFragment) {
|
|
202
|
+
// For functions, use the signature as the key
|
|
203
|
+
// This includes name and parameter types
|
|
204
|
+
key = fragment.format();
|
|
205
|
+
} else if (fragment instanceof EventFragment) {
|
|
206
|
+
// For events, use the signature as the key
|
|
207
|
+
key = fragment.format();
|
|
208
|
+
} else {
|
|
209
|
+
// For other fragment types (like errors), use their string representation
|
|
210
|
+
key = fragment.toString();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Only add if we haven't seen this signature before
|
|
214
|
+
if (!uniqueFragments.has(key)) {
|
|
215
|
+
uniqueFragments.set(key, fragment);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Convert the Map values back to an array
|
|
221
|
+
const dedupedFragments = Array.from(uniqueFragments.values());
|
|
222
|
+
|
|
223
|
+
// Create a new interface with the deduped fragments
|
|
224
|
+
return new Interface(dedupedFragments);
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Each supported hook should implement its own version of this file making it easy to retrieve required onchain state.
|
|
3
|
+
*/
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
import { Log } from '../../../types';
|
|
6
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
7
|
+
import { HookStateMap } from './balancer-hook-event-subscriber';
|
|
8
|
+
|
|
9
|
+
export const DirectionalFee = {
|
|
10
|
+
type: 'DirectionalFee' as const,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type DirectionalFeeConfig = {
|
|
14
|
+
type: typeof DirectionalFee.type;
|
|
15
|
+
address: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type DirectionalFeeHookState = {
|
|
19
|
+
test: string; // Note - this is just a dummy example as this specific test hook doesn't have any required state but is used to prove concept
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Retrieve any initial hook state required.
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
export async function getDirectionalFeeHookState(): Promise<DirectionalFeeHookState> {
|
|
27
|
+
// Note - for other hooks this function would retrieve any initial state using onchain calls
|
|
28
|
+
return { test: 'initial' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Handles the event emitted when a new hook is registered.
|
|
33
|
+
* @param event
|
|
34
|
+
* @param state
|
|
35
|
+
* @param log
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
export function exitFeeHookExampleRegisteredEvent(
|
|
39
|
+
event: any,
|
|
40
|
+
state: DeepReadonly<HookStateMap>,
|
|
41
|
+
log: Readonly<Log>,
|
|
42
|
+
): DeepReadonly<HookStateMap> | null {
|
|
43
|
+
// Note - This is a non-important event but demos the use case. Other hooks would implement this for any state changes required for swap maths.
|
|
44
|
+
const hookAddress = event.args.hooksContract.toLowerCase();
|
|
45
|
+
if (!state[hookAddress]) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const newState = _.cloneDeep(state) as HookStateMap;
|
|
49
|
+
newState[hookAddress] = { test: event.args.pool };
|
|
50
|
+
return newState;
|
|
51
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BalancerV3Config } from './../config';
|
|
2
|
+
import { HooksConfigMap } from './balancer-hook-event-subscriber';
|
|
3
|
+
|
|
4
|
+
export function loadHooksConfig(network: number): HooksConfigMap {
|
|
5
|
+
const hooks = BalancerV3Config.BalancerV3[network].hooks;
|
|
6
|
+
|
|
7
|
+
if (!hooks) return {};
|
|
8
|
+
|
|
9
|
+
return Object.fromEntries(
|
|
10
|
+
hooks.map(hook => [hook.address.toLowerCase(), hook]),
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { Log } from '../../../types';
|
|
3
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
4
|
+
import { HookStateMap } from './balancer-hook-event-subscriber';
|
|
5
|
+
import { Interface } from '@ethersproject/abi';
|
|
6
|
+
import { Contract } from 'ethers';
|
|
7
|
+
import { IDexHelper } from '../../../dex-helper';
|
|
8
|
+
import { callData, decodeThrowError } from '../getOnChainState';
|
|
9
|
+
|
|
10
|
+
export const StableSurge = {
|
|
11
|
+
type: 'StableSurge' as const,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type StableSurgeConfig = {
|
|
15
|
+
type: typeof StableSurge.type;
|
|
16
|
+
factory: string;
|
|
17
|
+
address: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type StableSurgePoolSetting = {
|
|
21
|
+
surgeThresholdPercentage: bigint;
|
|
22
|
+
maxSurgeFeePercentage: bigint;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type StableSurgeHookState = Record<string, StableSurgePoolSetting>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Retrieve any initial hook state required.
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
31
|
+
export async function getStableSurgeHookState(
|
|
32
|
+
hookInterface: Interface,
|
|
33
|
+
hookAddress: string,
|
|
34
|
+
factoryAddress: string,
|
|
35
|
+
dexHelper: IDexHelper,
|
|
36
|
+
blockNumber: number,
|
|
37
|
+
): Promise<StableSurgeHookState> {
|
|
38
|
+
// Find all pools with hook
|
|
39
|
+
const resolverContract = new Contract(
|
|
40
|
+
factoryAddress,
|
|
41
|
+
['function getPools() external view returns (address[])'],
|
|
42
|
+
dexHelper.provider,
|
|
43
|
+
);
|
|
44
|
+
const pools = (await resolverContract.callStatic.getPools({
|
|
45
|
+
blockTag: blockNumber,
|
|
46
|
+
})) as string[];
|
|
47
|
+
const surgeThresholdCallData: callData[] = pools.flatMap(pool => {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
target: hookAddress,
|
|
51
|
+
callData: hookInterface.encodeFunctionData(
|
|
52
|
+
'getSurgeThresholdPercentage',
|
|
53
|
+
[pool],
|
|
54
|
+
),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
target: hookAddress,
|
|
58
|
+
callData: hookInterface.encodeFunctionData('getMaxSurgeFeePercentage', [
|
|
59
|
+
pool,
|
|
60
|
+
]),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 500 is an arbitrary number chosen based on the blockGasLimit
|
|
66
|
+
const slicedMultiCallData = _.chunk(surgeThresholdCallData, 500);
|
|
67
|
+
|
|
68
|
+
const multicallDataResult = (
|
|
69
|
+
await Promise.all(
|
|
70
|
+
slicedMultiCallData.map(async _multiCallData =>
|
|
71
|
+
dexHelper.multiContract.methods
|
|
72
|
+
.tryAggregate(false, _multiCallData)
|
|
73
|
+
.call({}, blockNumber),
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
).flat();
|
|
77
|
+
|
|
78
|
+
return mapPoolsToDecodedData(hookInterface, pools, multicallDataResult);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function mapPoolsToDecodedData(
|
|
82
|
+
hookInterface: Interface,
|
|
83
|
+
pools: string[],
|
|
84
|
+
data: any[],
|
|
85
|
+
): StableSurgeHookState {
|
|
86
|
+
return pools.reduce((acc, pool, index) => {
|
|
87
|
+
const dataIndex = index * 2;
|
|
88
|
+
|
|
89
|
+
// Ensure we have enough data
|
|
90
|
+
if (dataIndex + 1 >= data.length) {
|
|
91
|
+
throw new Error(`Insufficient data for pool ${pool}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Decode the corresponding data pairs
|
|
95
|
+
const decodedX = decodeThrowError(
|
|
96
|
+
hookInterface,
|
|
97
|
+
'getSurgeThresholdPercentage',
|
|
98
|
+
data[dataIndex],
|
|
99
|
+
'n/a',
|
|
100
|
+
);
|
|
101
|
+
const decodedY = decodeThrowError(
|
|
102
|
+
hookInterface,
|
|
103
|
+
'getMaxSurgeFeePercentage',
|
|
104
|
+
data[dataIndex + 1],
|
|
105
|
+
'n/a',
|
|
106
|
+
);
|
|
107
|
+
// Add to accumulator
|
|
108
|
+
acc[pool.toLowerCase()] = {
|
|
109
|
+
surgeThresholdPercentage: decodedX[0].toBigInt(),
|
|
110
|
+
maxSurgeFeePercentage: decodedY[0].toBigInt(),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return acc;
|
|
114
|
+
}, {} as StableSurgeHookState);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handles the event emitted when threshold surge percentage changed.
|
|
119
|
+
* @param event
|
|
120
|
+
* @param state
|
|
121
|
+
* @param log
|
|
122
|
+
* @returns
|
|
123
|
+
*/
|
|
124
|
+
export function thresholdSurgePercentageChangedEvent(
|
|
125
|
+
event: any,
|
|
126
|
+
state: DeepReadonly<HookStateMap>,
|
|
127
|
+
log: Readonly<Log>,
|
|
128
|
+
): DeepReadonly<HookStateMap> | null {
|
|
129
|
+
// ThresholdSurgePercentageChanged(pool, newSurgeThresholdPercentage)
|
|
130
|
+
const hookAddress = log.address.toLowerCase();
|
|
131
|
+
if (!state[hookAddress]) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const newState = _.cloneDeep(state) as HookStateMap;
|
|
135
|
+
(newState[hookAddress] as StableSurgeHookState)[
|
|
136
|
+
event.args.pool.toLowerCase()
|
|
137
|
+
].surgeThresholdPercentage = BigInt(event.args.newSurgeThresholdPercentage);
|
|
138
|
+
return newState;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Handles the event emitted when max surge fee percentage changed.
|
|
143
|
+
* @param event
|
|
144
|
+
* @param state
|
|
145
|
+
* @param log
|
|
146
|
+
* @returns
|
|
147
|
+
*/
|
|
148
|
+
export function maxSurgeFeePercentageChangedEvent(
|
|
149
|
+
event: any,
|
|
150
|
+
state: DeepReadonly<HookStateMap>,
|
|
151
|
+
log: Readonly<Log>,
|
|
152
|
+
): DeepReadonly<HookStateMap> | null {
|
|
153
|
+
// MaxSurgeFeePercentageChanged(pool, newMaxSurgeFeePercentage)
|
|
154
|
+
const hookAddress = log.address.toLowerCase();
|
|
155
|
+
if (!state[hookAddress]) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
const newState = _.cloneDeep(state) as HookStateMap;
|
|
159
|
+
(newState[hookAddress] as StableSurgeHookState)[
|
|
160
|
+
event.args.pool.toLowerCase()
|
|
161
|
+
].maxSurgeFeePercentage = BigInt(event.args.newMaxSurgeFeePercentage);
|
|
162
|
+
return newState;
|
|
163
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BufferState } from '@balancer-labs/balancer-maths';
|
|
2
2
|
import { Address } from '../../types';
|
|
3
|
+
import { HookConfig } from './hooks/balancer-hook-event-subscriber';
|
|
3
4
|
|
|
4
5
|
// Immutable data types available on all pools (Available from API)
|
|
5
6
|
export type CommonImmutablePoolState = {
|
|
@@ -12,7 +13,9 @@ export type CommonImmutablePoolState = {
|
|
|
12
13
|
weights: bigint[];
|
|
13
14
|
// TODO re-introduce this once added to API
|
|
14
15
|
// scalingFactors: bigint[];
|
|
16
|
+
hookAddress: string | undefined;
|
|
15
17
|
hookType: string | undefined;
|
|
18
|
+
supportsUnbalancedLiquidity: boolean;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
// Mutable data types available on all pools (Available via onchain calls/events)
|
|
@@ -26,8 +29,6 @@ export interface CommonMutableState {
|
|
|
26
29
|
isPoolPaused: boolean;
|
|
27
30
|
// TODO remove this once API provides it
|
|
28
31
|
scalingFactors: bigint[];
|
|
29
|
-
// TODO remove this once API provides it
|
|
30
|
-
hasHook: boolean;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
type CommonPoolState = CommonImmutablePoolState & CommonMutableState;
|
|
@@ -76,6 +77,7 @@ export type DexParams = {
|
|
|
76
77
|
// https://github.com/balancer/balancer-v3-monorepo/blob/main/pkg/interfaces/contracts/vault/IRouter.sol
|
|
77
78
|
balancerRouterAddress: string;
|
|
78
79
|
balancerBatchRouterAddress: string;
|
|
80
|
+
hooks?: HookConfig[];
|
|
79
81
|
};
|
|
80
82
|
|
|
81
83
|
export type TokenInfo = {
|
package/tests/constants-e2e.ts
CHANGED
|
@@ -1694,6 +1694,10 @@ export const Tokens: {
|
|
|
1694
1694
|
address: '0xC768c589647798a6EE01A91FdE98EF2ed046DBD6',
|
|
1695
1695
|
decimals: 6,
|
|
1696
1696
|
},
|
|
1697
|
+
waBasGHO: {
|
|
1698
|
+
address: '0x88b1Cd4b430D95b406E382C3cDBaE54697a0286E',
|
|
1699
|
+
decimals: 18,
|
|
1700
|
+
},
|
|
1697
1701
|
aaveUSDC: {
|
|
1698
1702
|
address: '0x4e65fe4dba92790696d040ac24aa414708f5c0ab',
|
|
1699
1703
|
decimals: 6,
|
|
@@ -2145,7 +2149,7 @@ export const Holders: {
|
|
|
2145
2149
|
DOG: '0xbe3ab8a87730684ef1e476064c2e43c3e982f8e8',
|
|
2146
2150
|
stataUSDC: '0x88Cac91ADDE2208039A227B373C2A692C0700547',
|
|
2147
2151
|
aaveUSDC: '0x5DE3c5BE52D7aDbdC3aEFe2eA061A2ECE0C7d766',
|
|
2148
|
-
waBasUSDC: '
|
|
2152
|
+
waBasUSDC: '0x450C01300B83bE379113256038b4Dd3b45d23B5e',
|
|
2149
2153
|
USDM: '0x426c4966fC76Bf782A663203c023578B744e4C5E',
|
|
2150
2154
|
crvUSD: '0xBbAbDB1385deA5285113581A7024d6DC04131101',
|
|
2151
2155
|
cbETH: '0x50e011dD1e2b4906F1534623cD134B30422bb11E',
|
|
@@ -2156,6 +2160,7 @@ export const Holders: {
|
|
|
2156
2160
|
waBaswstETH: '0x854B004700885A61107B458f11eCC169A019b764',
|
|
2157
2161
|
sUSDS: '0xC9716E991396AA933Fa7A481003a5953bcBabC17',
|
|
2158
2162
|
smUSDC: '0x88457F47c9BEC8379eB90d8dC3592F79084e555d',
|
|
2163
|
+
waBasGHO: '0x450C01300B83bE379113256038b4Dd3b45d23B5e',
|
|
2159
2164
|
},
|
|
2160
2165
|
[Network.SEPOLIA]: {
|
|
2161
2166
|
bal: '0xDb4ff41B4C1222c2b1869A67Be115070688989a2',
|