@defisaver/automation-sdk 3.3.18 → 3.3.19-sub-fetching-dev

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.
Files changed (55) hide show
  1. package/cjs/automation/private/StrategiesAutomation.d.ts +5 -1
  2. package/cjs/automation/private/StrategiesAutomation.js +19 -2
  3. package/cjs/automation/private/StrategiesAutomation.test.js +55 -0
  4. package/cjs/constants/index.js +35 -0
  5. package/cjs/services/apiSubscriptionsService.d.ts +7 -0
  6. package/cjs/services/apiSubscriptionsService.js +33 -0
  7. package/cjs/services/apiSubscriptionsService.test.d.ts +1 -0
  8. package/cjs/services/apiSubscriptionsService.test.js +69 -0
  9. package/cjs/services/ethereumService.test.js +4 -7
  10. package/cjs/services/strategiesService.js +40 -9
  11. package/cjs/services/strategiesService.test.js +0 -1
  12. package/cjs/services/strategySubService.d.ts +4 -0
  13. package/cjs/services/strategySubService.js +24 -0
  14. package/cjs/services/strategySubService.test.js +295 -0
  15. package/cjs/services/subDataService.d.ts +38 -0
  16. package/cjs/services/subDataService.js +76 -2
  17. package/cjs/services/subDataService.test.js +494 -4
  18. package/cjs/types/enums.d.ts +15 -2
  19. package/cjs/types/enums.js +15 -1
  20. package/cjs/types/index.d.ts +29 -3
  21. package/esm/automation/private/StrategiesAutomation.d.ts +5 -1
  22. package/esm/automation/private/StrategiesAutomation.js +20 -3
  23. package/esm/automation/private/StrategiesAutomation.test.js +56 -1
  24. package/esm/constants/index.js +35 -0
  25. package/esm/services/apiSubscriptionsService.d.ts +7 -0
  26. package/esm/services/apiSubscriptionsService.js +29 -0
  27. package/esm/services/apiSubscriptionsService.test.d.ts +1 -0
  28. package/esm/services/apiSubscriptionsService.test.js +67 -0
  29. package/esm/services/ethereumService.test.js +4 -7
  30. package/esm/services/strategiesService.js +40 -9
  31. package/esm/services/strategiesService.test.js +0 -1
  32. package/esm/services/strategySubService.d.ts +4 -0
  33. package/esm/services/strategySubService.js +24 -0
  34. package/esm/services/strategySubService.test.js +295 -0
  35. package/esm/services/subDataService.d.ts +38 -0
  36. package/esm/services/subDataService.js +74 -0
  37. package/esm/services/subDataService.test.js +494 -4
  38. package/esm/types/enums.d.ts +15 -2
  39. package/esm/types/enums.js +14 -0
  40. package/esm/types/index.d.ts +29 -3
  41. package/package.json +1 -1
  42. package/src/automation/private/StrategiesAutomation.test.ts +70 -1
  43. package/src/automation/private/StrategiesAutomation.ts +25 -1
  44. package/src/constants/index.ts +35 -0
  45. package/src/services/apiSubscriptionsService.test.ts +75 -0
  46. package/src/services/apiSubscriptionsService.ts +33 -0
  47. package/src/services/ethereumService.test.ts +5 -8
  48. package/src/services/strategiesService.test.ts +0 -1
  49. package/src/services/strategiesService.ts +43 -10
  50. package/src/services/strategySubService.test.ts +382 -0
  51. package/src/services/strategySubService.ts +87 -0
  52. package/src/services/subDataService.test.ts +608 -4
  53. package/src/services/subDataService.ts +107 -0
  54. package/src/types/enums.ts +14 -0
  55. package/src/types/index.ts +31 -2
@@ -1,8 +1,8 @@
1
1
  import type Web3 from 'web3';
2
2
  import type { AbiItem } from 'web3-utils';
3
3
  import type { BaseContract, BlockType } from './contracts/generated/types';
4
- import type { Subscribe, StrategyModel } from './contracts/generated/SubStorage';
5
- import type { ChainId, Strategies, Bundles, ProtocolIdentifiers, RatioState, CloseToAssetType } from './enums';
4
+ import type { StrategyModel } from './contracts/generated/SubStorage';
5
+ import type { ChainId, Strategies, Bundles, ProtocolIdentifiers, RatioState, CloseToAssetType, SubscriptionStatus } from './enums';
6
6
  export type PlaceholderType = any;
7
7
  export type EthereumAddress = string;
8
8
  export type BlockNumber = BlockType;
@@ -53,6 +53,23 @@ interface _SubscriptionOptions {
53
53
  unexpiredOnly: boolean;
54
54
  }
55
55
  export type SubscriptionOptions = Partial<_SubscriptionOptions>;
56
+ /** Subscription record as returned by the automation API (GET /v1/subscriptions) */
57
+ export interface ApiSubscriptionRecord {
58
+ id: number;
59
+ wallet: EthereumAddress;
60
+ wallet_type: string;
61
+ is_enabled: boolean;
62
+ invalid: boolean;
63
+ status: SubscriptionStatus;
64
+ is_bundle: boolean;
65
+ strategy_or_bundle_id: number;
66
+ strategy_ids: number[];
67
+ sub_data_hash: string;
68
+ trigger_data: string[];
69
+ sub_data: string[];
70
+ block_number: number;
71
+ additional_triggers: string[] | null;
72
+ }
56
73
  export declare namespace Interfaces {
57
74
  interface ProtocolBase {
58
75
  id: ProtocolIdentifiers.StrategiesAutomation | ProtocolIdentifiers.LegacyAutomation;
@@ -238,6 +255,8 @@ export declare namespace Position {
238
255
  subId: number;
239
256
  subIds?: number[];
240
257
  isEnabled?: boolean;
258
+ /** Set only for subscriptions parsed from the automation API */
259
+ status?: SubscriptionStatus;
241
260
  subHash: string;
242
261
  blockNumber: BlockNumber;
243
262
  protocol: Interfaces.Protocol;
@@ -291,10 +310,17 @@ export interface BundlesInfo {
291
310
  [ChainId.Base]: BaseBundleInfo;
292
311
  }
293
312
  export type StrategyOrBundleIds = typeof Strategies.MainnetIds[keyof typeof Strategies.MainnetIds] | typeof Strategies.OptimismIds[keyof typeof Strategies.OptimismIds] | typeof Strategies.ArbitrumIds[keyof typeof Strategies.ArbitrumIds] | typeof Strategies.BaseIds[keyof typeof Strategies.BaseIds] | typeof Bundles.MainnetIds[keyof typeof Bundles.MainnetIds] | typeof Bundles.OptimismIds[keyof typeof Bundles.OptimismIds] | typeof Bundles.ArbitrumIds[keyof typeof Bundles.ArbitrumIds] | typeof Bundles.BaseIds[keyof typeof Bundles.BaseIds];
313
+ /** Fields of the Subscribe event that parsing relies on */
314
+ export interface SubscriptionEventData {
315
+ subId: string;
316
+ proxy: EthereumAddress;
317
+ subHash: string;
318
+ subStruct: StrategyModel.StrategySubStructOutputStruct;
319
+ }
294
320
  export interface ParseData {
295
321
  chainId: ChainId;
296
322
  blockNumber: BlockNumber;
297
- subscriptionEventData: Subscribe['returnValues'];
323
+ subscriptionEventData: SubscriptionEventData;
298
324
  strategiesSubsData: StrategyModel.StoredSubDataStructOutputStruct;
299
325
  }
300
326
  type MapToProtocolVersion<T> = {
@@ -1,6 +1,6 @@
1
1
  import type Web3 from 'web3';
2
2
  import type { PastEventOptions } from 'web3-eth-contract';
3
- import type { Position, Interfaces, EthereumAddress, SubscriptionOptions, Contract, ParseData, BlockNumber } from '../../types';
3
+ import type { Position, Interfaces, EthereumAddress, SubscriptionOptions, Contract, ParseData, BlockNumber, ApiSubscriptionRecord } from '../../types';
4
4
  import type { StrategyModel, Subscribe, SubStorage, UpdateData } from '../../types/contracts/generated/SubStorage';
5
5
  import type { ChainId } from '../../types/enums';
6
6
  import Automation from './Automation';
@@ -30,6 +30,10 @@ export default class StrategiesAutomation extends Automation {
30
30
  protected mergeSubs(_subscriptions: (Position.Automated | null)[]): Position.Automated[];
31
31
  protected _getSubscriptions(addresses?: EthereumAddress[], options?: SubscriptionOptions): Promise<(Position.Automated | null)[]>;
32
32
  getSubscriptions(options?: SubscriptionOptions): Promise<(Position.Automated | null)[]>;
33
+ /**
34
+ * @description Parses subscriptions returned by the automation API (GET /v1/subscriptions)
35
+ */
36
+ parseSubscriptionsFromApi(records: ApiSubscriptionRecord[], options?: SubscriptionOptions): (Position.Automated | null)[];
33
37
  getSubscriptionsFor(addresses: EthereumAddress[], options?: SubscriptionOptions): Promise<(Position.Automated | null)[]>;
34
38
  }
35
39
  export {};
@@ -9,11 +9,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import Dec from 'decimal.js';
11
11
  import PromisePool from 'es6-promise-pool';
12
- import { Strategies, ProtocolIdentifiers } from '../../types/enums';
12
+ import { Strategies, ProtocolIdentifiers, SubscriptionStatus } from '../../types/enums';
13
13
  import { addToObjectIf, isDefined, isUndefined } from '../../services/utils';
14
14
  import { getAbiItem, makeSubStorageContract } from '../../services/contractService';
15
15
  import { getEventsFromContract, multicall } from '../../services/ethereumService';
16
16
  import { parseStrategiesAutomatedPosition } from '../../services/strategiesService';
17
+ import { parseDataFromApiSubscription } from '../../services/apiSubscriptionsService';
17
18
  import Automation from './Automation';
18
19
  export default class StrategiesAutomation extends Automation {
19
20
  constructor(args) {
@@ -121,9 +122,11 @@ export default class StrategiesAutomation extends Automation {
121
122
  if (mergePairIndex !== -1) {
122
123
  const mergePair = mergeExtension[mergePairIndex];
123
124
  mergeExtension.splice(mergePairIndex, 1);
124
- subscriptions.push(Object.assign(Object.assign(Object.assign({}, mergePair), current), {
125
+ subscriptions.push(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, mergePair), current), {
125
126
  // @ts-ignore
126
- blockNumber: Dec.max(mergePair.blockNumber, current.blockNumber).toNumber(), subIds: [current.subId, mergePair.subId], isEnabled: mergePair.isEnabled || current.isEnabled, specific: Object.assign(Object.assign({}, mergePair.specific), current.specific) }));
127
+ blockNumber: Dec.max(mergePair.blockNumber, current.blockNumber).toNumber(), subIds: [current.subId, mergePair.subId], isEnabled: mergePair.isEnabled || current.isEnabled }), addToObjectIf(isDefined(current.status), {
128
+ status: mergePair.status === SubscriptionStatus.Active ? SubscriptionStatus.Active : current.status,
129
+ })), { specific: Object.assign(Object.assign({}, mergePair.specific), current.specific) }));
127
130
  }
128
131
  else {
129
132
  subscriptions.push(Object.assign(Object.assign({}, current), { subIds: [current.subId] }));
@@ -184,6 +187,20 @@ export default class StrategiesAutomation extends Automation {
184
187
  return this._getSubscriptions(undefined, options);
185
188
  });
186
189
  }
190
+ /**
191
+ * @description Parses subscriptions returned by the automation API (GET /v1/subscriptions)
192
+ */
193
+ parseSubscriptionsFromApi(records, options) {
194
+ const filtered = (options === null || options === void 0 ? void 0 : options.enabledOnly) ? records.filter((record) => record.is_enabled) : records;
195
+ let subscriptions = filtered.map((record) => {
196
+ const position = this.getParsedSubscriptions(parseDataFromApiSubscription(record, this.chainId));
197
+ return position && Object.assign(Object.assign({}, position), { status: record.status });
198
+ });
199
+ if (options === null || options === void 0 ? void 0 : options.mergeSubs) {
200
+ subscriptions = this.mergeSubs(subscriptions);
201
+ }
202
+ return (options === null || options === void 0 ? void 0 : options.unexpiredOnly) ? this.removeExpiredSubscriptions(subscriptions) : subscriptions;
203
+ }
187
204
  getSubscriptionsFor(addresses, options) {
188
205
  return __awaiter(this, void 0, void 0, function* () {
189
206
  return this._getSubscriptions(addresses, options);
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import Web3 from 'web3';
11
11
  import { expect } from 'chai';
12
- import { ChainId } from '../../types/enums';
12
+ import { ChainId, SubscriptionStatus } from '../../types/enums';
13
13
  import '../../configuration';
14
14
  import StrategiesAutomation from './StrategiesAutomation';
15
15
  require('dotenv').config({ path: '.env' });
@@ -688,4 +688,59 @@ describe('Feature: StrategiesAutomation.ts', () => {
688
688
  expect(loneBoost === null || loneBoost === void 0 ? void 0 : loneBoost.subId).to.equal(1);
689
689
  });
690
690
  }));
691
+ describe('When testing StrategiesAutomation.parseSubscriptionsFromApi', () => {
692
+ const strategiesAutomation = new StrategiesAutomation({
693
+ chainId: ChainId.Ethereum,
694
+ provider: Web3_1,
695
+ providerFork: null,
696
+ });
697
+ const owner = '0x9cB7E19861665366011899d74E75d4F2A419aEeD';
698
+ const subData = [
699
+ '0x0000000000000000000000000000000000000000000000001bc16d674ec80000',
700
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
701
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
702
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
703
+ ];
704
+ // Aave V3 leverage management: repay (bundle 8, ratio under) and boost (bundle 9, ratio over) for the same owner and market
705
+ const repay = {
706
+ id: 379,
707
+ wallet: owner,
708
+ wallet_type: 'safe',
709
+ is_enabled: true,
710
+ invalid: false,
711
+ status: SubscriptionStatus.Active,
712
+ is_bundle: true,
713
+ strategy_or_bundle_id: 8,
714
+ strategy_ids: [34, 35],
715
+ sub_data_hash: '0xafa4d200be62f171b57b1ae0f4e8348d1ac3f6d0812ad6da74a2adae8037dde1',
716
+ trigger_data: ['0000000000000000000000009cb7e19861665366011899d74e75d4f2a419aeed0000000000000000000000002f39d218133afab8f2b819b1066c7e434ad94e9e00000000000000000000000000000000000000000000000019ac8532c27900000000000000000000000000000000000000000000000000000000000000000001'],
717
+ sub_data: subData,
718
+ block_number: 18015756,
719
+ additional_triggers: null,
720
+ };
721
+ const boost = Object.assign(Object.assign({}, repay), { id: 380, is_enabled: false, status: SubscriptionStatus.Disabled, strategy_or_bundle_id: 9, sub_data_hash: '0x1111111111111111111111111111111111111111111111111111111111111111', trigger_data: ['0000000000000000000000009cb7e19861665366011899d74e75d4f2a419aeed0000000000000000000000002f39d218133afab8f2b819b1066c7e434ad94e9e0000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000000000'] });
722
+ it('Given API records should return parsed positions carrying the API status', () => {
723
+ const positions = strategiesAutomation.parseSubscriptionsFromApi([repay, boost]);
724
+ expect(positions).to.have.length(2);
725
+ expect(positions.map((p) => p === null || p === void 0 ? void 0 : p.subId)).to.eql([379, 380]);
726
+ expect(positions.map((p) => p === null || p === void 0 ? void 0 : p.status)).to.eql([SubscriptionStatus.Active, SubscriptionStatus.Disabled]);
727
+ expect(positions.map((p) => p === null || p === void 0 ? void 0 : p.isEnabled)).to.eql([true, false]);
728
+ });
729
+ it('Given mergeSubs option should merge the repay and boost pair and keep the active status', () => {
730
+ var _a, _b, _c;
731
+ const positions = strategiesAutomation.parseSubscriptionsFromApi([repay, boost], { mergeSubs: true });
732
+ expect(positions).to.have.length(1);
733
+ expect((_a = positions[0]) === null || _a === void 0 ? void 0 : _a.subIds).to.eql([379, 380]);
734
+ expect((_b = positions[0]) === null || _b === void 0 ? void 0 : _b.isEnabled).to.equal(true);
735
+ expect((_c = positions[0]) === null || _c === void 0 ? void 0 : _c.status).to.equal(SubscriptionStatus.Active);
736
+ });
737
+ it('Given enabledOnly option should drop disabled records before parsing', () => {
738
+ const positions = strategiesAutomation.parseSubscriptionsFromApi([repay, boost], { enabledOnly: true });
739
+ expect(positions.map((p) => p === null || p === void 0 ? void 0 : p.subId)).to.eql([379]);
740
+ });
741
+ it('Given a record of an unknown strategy should return null for it', () => {
742
+ const positions = strategiesAutomation.parseSubscriptionsFromApi([Object.assign(Object.assign({}, repay), { strategy_or_bundle_id: 9999 })]);
743
+ expect(positions).to.eql([null]);
744
+ });
745
+ });
691
746
  });
@@ -116,6 +116,11 @@ export const MAINNET_STRATEGIES_INFO = {
116
116
  strategyId: Strategies.Identifiers.CollateralSwitch,
117
117
  protocol: PROTOCOLS.Spark,
118
118
  },
119
+ [Strategies.MainnetIds.SPARK_GENERIC_FL_COLLATERAL_SWITCH]: {
120
+ strategyOrBundleId: Strategies.MainnetIds.SPARK_GENERIC_FL_COLLATERAL_SWITCH,
121
+ strategyId: Strategies.Identifiers.EoaCollateralSwitch,
122
+ protocol: PROTOCOLS.Spark,
123
+ },
119
124
  };
120
125
  export const OPTIMISM_STRATEGIES_INFO = {
121
126
  [Strategies.OptimismIds.EXCHANGE_DCA]: {
@@ -543,6 +548,31 @@ export const MAINNET_BUNDLES_INFO = {
543
548
  strategyId: Strategies.Identifiers.EoaCloseOnPrice,
544
549
  protocol: PROTOCOLS.AaveV4,
545
550
  },
551
+ [Bundles.MainnetIds.SPARK_EOA_REPAY]: {
552
+ strategyOrBundleId: Bundles.MainnetIds.SPARK_EOA_REPAY,
553
+ strategyId: Strategies.Identifiers.EoaRepay,
554
+ protocol: PROTOCOLS.Spark,
555
+ },
556
+ [Bundles.MainnetIds.SPARK_EOA_BOOST]: {
557
+ strategyOrBundleId: Bundles.MainnetIds.SPARK_EOA_BOOST,
558
+ strategyId: Strategies.Identifiers.EoaBoost,
559
+ protocol: PROTOCOLS.Spark,
560
+ },
561
+ [Bundles.MainnetIds.SPARK_EOA_CLOSE]: {
562
+ strategyOrBundleId: Bundles.MainnetIds.SPARK_EOA_CLOSE,
563
+ strategyId: Strategies.Identifiers.EoaCloseOnPrice,
564
+ protocol: PROTOCOLS.Spark,
565
+ },
566
+ [Bundles.MainnetIds.SPARK_EOA_REPAY_ON_PRICE]: {
567
+ strategyOrBundleId: Bundles.MainnetIds.SPARK_EOA_REPAY_ON_PRICE,
568
+ strategyId: Strategies.Identifiers.EoaRepayOnPrice,
569
+ protocol: PROTOCOLS.Spark,
570
+ },
571
+ [Bundles.MainnetIds.SPARK_EOA_BOOST_ON_PRICE]: {
572
+ strategyOrBundleId: Bundles.MainnetIds.SPARK_EOA_BOOST_ON_PRICE,
573
+ strategyId: Strategies.Identifiers.EoaBoostOnPrice,
574
+ protocol: PROTOCOLS.Spark,
575
+ },
546
576
  [Bundles.MainnetIds.MAKER_SW_LIQUIDATION_PROTECTION]: {
547
577
  strategyOrBundleId: Bundles.MainnetIds.MAKER_SW_LIQUIDATION_PROTECTION,
548
578
  strategyId: Strategies.Identifiers.LiquidationProtection,
@@ -618,6 +648,11 @@ export const MAINNET_BUNDLES_INFO = {
618
648
  strategyId: Strategies.Identifiers.EoaCloseOnPrice,
619
649
  protocol: PROTOCOLS.MorphoBlue,
620
650
  },
651
+ [Bundles.MainnetIds.SPARK_EOA_LIQUIDATION_PROTECTION]: {
652
+ strategyOrBundleId: Bundles.MainnetIds.SPARK_EOA_LIQUIDATION_PROTECTION,
653
+ strategyId: Strategies.Identifiers.EoaLiquidationProtection,
654
+ protocol: PROTOCOLS.Spark,
655
+ },
621
656
  };
622
657
  export const OPTIMISM_BUNDLES_INFO = {
623
658
  [Bundles.OptimismIds.AAVE_V3_REPAY]: {
@@ -0,0 +1,7 @@
1
+ import type { ApiSubscriptionRecord, ParseData } from '../types';
2
+ import type { ChainId } from '../types/enums';
3
+ /**
4
+ * @description Maps a subscription record returned by the automation API to the data
5
+ * the parser expects from Subscribe events and SubStorage (trigger data is stored without 0x prefix)
6
+ */
7
+ export declare function parseDataFromApiSubscription(record: ApiSubscriptionRecord, chainId: ChainId): ParseData;
@@ -0,0 +1,29 @@
1
+ function addHexPrefix(value) {
2
+ return value.startsWith('0x') ? value : `0x${value}`;
3
+ }
4
+ /**
5
+ * @description Maps a subscription record returned by the automation API to the data
6
+ * the parser expects from Subscribe events and SubStorage (trigger data is stored without 0x prefix)
7
+ */
8
+ export function parseDataFromApiSubscription(record, chainId) {
9
+ return {
10
+ chainId,
11
+ blockNumber: record.block_number,
12
+ subscriptionEventData: {
13
+ subId: record.id.toString(),
14
+ proxy: record.wallet,
15
+ subHash: record.sub_data_hash,
16
+ subStruct: {
17
+ strategyOrBundleId: record.strategy_or_bundle_id.toString(),
18
+ isBundle: record.is_bundle,
19
+ triggerData: record.trigger_data.map(addHexPrefix),
20
+ subData: record.sub_data.map(addHexPrefix),
21
+ },
22
+ },
23
+ strategiesSubsData: {
24
+ userProxy: record.wallet,
25
+ isEnabled: record.is_enabled,
26
+ strategySubHash: record.sub_data_hash,
27
+ },
28
+ };
29
+ }
@@ -0,0 +1 @@
1
+ import '../configuration';
@@ -0,0 +1,67 @@
1
+ import { expect } from 'chai';
2
+ import { ChainId, SubscriptionStatus } from '../types/enums';
3
+ import '../configuration';
4
+ import { parseDataFromApiSubscription } from './apiSubscriptionsService';
5
+ import { parseStrategiesAutomatedPosition } from './strategiesService';
6
+ describe('Feature: apiSubscriptionsService.ts', () => {
7
+ describe('When testing apiSubscriptionsService.parseDataFromApiSubscription', () => {
8
+ // same subscription as in strategiesService.test.ts, in the shape the automation API returns it
9
+ const record = {
10
+ id: 379,
11
+ wallet: '0x9cB7E19861665366011899d74E75d4F2A419aEeD',
12
+ wallet_type: 'safe',
13
+ is_enabled: true,
14
+ invalid: false,
15
+ status: SubscriptionStatus.Active,
16
+ is_bundle: true,
17
+ strategy_or_bundle_id: 8,
18
+ strategy_ids: [34, 35],
19
+ sub_data_hash: '0xafa4d200be62f171b57b1ae0f4e8348d1ac3f6d0812ad6da74a2adae8037dde1',
20
+ trigger_data: [
21
+ '0000000000000000000000009cb7e19861665366011899d74e75d4f2a419aeed0000000000000000000000002f39d218133afab8f2b819b1066c7e434ad94e9e00000000000000000000000000000000000000000000000019ac8532c27900000000000000000000000000000000000000000000000000000000000000000001',
22
+ ],
23
+ sub_data: [
24
+ '0x0000000000000000000000000000000000000000000000001bc16d674ec80000',
25
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
26
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
27
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
28
+ ],
29
+ block_number: 18015756,
30
+ additional_triggers: null,
31
+ };
32
+ const fromEvents = {
33
+ chainId: ChainId.Ethereum,
34
+ blockNumber: 18015756,
35
+ subscriptionEventData: {
36
+ subId: '379',
37
+ proxy: '0x9cB7E19861665366011899d74E75d4F2A419aEeD',
38
+ subHash: '0xafa4d200be62f171b57b1ae0f4e8348d1ac3f6d0812ad6da74a2adae8037dde1',
39
+ subStruct: {
40
+ strategyOrBundleId: '8',
41
+ isBundle: true,
42
+ triggerData: [
43
+ '0x0000000000000000000000009cb7e19861665366011899d74e75d4f2a419aeed0000000000000000000000002f39d218133afab8f2b819b1066c7e434ad94e9e00000000000000000000000000000000000000000000000019ac8532c27900000000000000000000000000000000000000000000000000000000000000000001',
44
+ ],
45
+ subData: record.sub_data,
46
+ },
47
+ },
48
+ strategiesSubsData: {
49
+ userProxy: '0x9cB7E19861665366011899d74E75d4F2A419aEeD',
50
+ isEnabled: true,
51
+ strategySubHash: '0xafa4d200be62f171b57b1ae0f4e8348d1ac3f6d0812ad6da74a2adae8037dde1',
52
+ },
53
+ };
54
+ it('Given an API record should return the same parse data as Subscribe event and SubStorage would', () => {
55
+ expect(parseDataFromApiSubscription(record, ChainId.Ethereum)).to.eql(fromEvents);
56
+ });
57
+ it('Given an API record the parsed position should match the one parsed from events', () => {
58
+ const fromApi = parseStrategiesAutomatedPosition(parseDataFromApiSubscription(record, ChainId.Ethereum));
59
+ expect(fromApi).to.not.equal(null);
60
+ expect(fromApi).to.eql(parseStrategiesAutomatedPosition(fromEvents));
61
+ });
62
+ it('Given a disabled record should keep it disabled', () => {
63
+ const parseData = parseDataFromApiSubscription(Object.assign(Object.assign({}, record), { is_enabled: false }), ChainId.Ethereum);
64
+ expect(parseData.strategiesSubsData.isEnabled).to.equal(false);
65
+ });
66
+ });
67
+ });
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import Web3 from 'web3';
11
11
  import { expect } from 'chai';
12
+ import { omit } from 'lodash';
12
13
  import { getAssetInfo } from '@defisaver/tokens';
13
14
  import { ChainId } from '../types/enums';
14
15
  import '../configuration';
@@ -96,7 +97,6 @@ describe('Feature: ethereumService.ts', () => {
96
97
  'address': '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0',
97
98
  'blockHash': '0xb92cab2569456dbfbdb853d2c67d72c9a7580543dbcb55d483a77322b40755a4',
98
99
  'blockNumber': 15166163,
99
- 'blockTimestamp': '0x62d53ad8',
100
100
  'event': 'Transfer',
101
101
  'id': 'log_e2258e3a',
102
102
  'logIndex': 385,
@@ -123,7 +123,7 @@ describe('Feature: ethereumService.ts', () => {
123
123
  },
124
124
  ],
125
125
  [
126
- makeErc20Contract(Web3_1, getAssetInfo('LUSD').address, ChainId.Ethereum),
126
+ Object.assign(Object.assign({}, makeErc20Contract(Web3_1, getAssetInfo('LUSD').address, ChainId.Ethereum)), { createdBlock: 15166163 }),
127
127
  null,
128
128
  'Transfer',
129
129
  {
@@ -142,7 +142,6 @@ describe('Feature: ethereumService.ts', () => {
142
142
  'address': '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
143
143
  'blockHash': '0xacb0213af63b4c17c436f084a96d1ac385641a59a9a4cf014ae3337cbe545aa7',
144
144
  'blockNumber': 5353002,
145
- 'blockTimestamp': '0x624c1a5b',
146
145
  'event': 'Transfer',
147
146
  'id': 'log_f49645b8',
148
147
  'logIndex': 1,
@@ -188,7 +187,6 @@ describe('Feature: ethereumService.ts', () => {
188
187
  'address': '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
189
188
  'blockHash': '0xacb0213af63b4c17c436f084a96d1ac385641a59a9a4cf014ae3337cbe545aa7',
190
189
  'blockNumber': 5353002,
191
- 'blockTimestamp': '0x624c1a5b',
192
190
  'event': 'Transfer',
193
191
  'id': 'log_f49645b8',
194
192
  'logIndex': 1,
@@ -231,9 +229,8 @@ describe('Feature: ethereumService.ts', () => {
231
229
  ];
232
230
  examples.forEach(([expected, actual]) => {
233
231
  it(`Given ${actual} should return expected value: ${JSON.stringify(expected)}`, () => __awaiter(void 0, void 0, void 0, function* () {
234
- const data = yield getEventsFromContract(...actual);
235
- console.log(data);
236
- expect(yield getEventsFromContract(...actual)).to.eql(expected);
232
+ const events = yield getEventsFromContract(...actual);
233
+ expect(events.map((event) => omit(event, 'blockTimestamp'))).to.eql(expected);
237
234
  }));
238
235
  });
239
236
  });
@@ -330,11 +330,17 @@ function parseAaveV3CollateralSwitch(position, parseData) {
330
330
  function parseSparkCollateralSwitch(position, parseData) {
331
331
  const _position = cloneDeep(position);
332
332
  const { subStruct } = parseData.subscriptionEventData;
333
+ const isEOA = _position.strategy.strategyId.includes('eoa');
333
334
  const triggerData = triggerService.sparkQuotePriceTrigger.decode(subStruct.triggerData);
334
- const subData = subDataService.sparkCollateralSwitchSubData.decode(subStruct.subData);
335
+ const subData = isEOA
336
+ ? subDataService.sparkGenericFLCollateralSwitchSubData.decode(subStruct.subData)
337
+ : subDataService.sparkCollateralSwitchSubData.decode(subStruct.subData);
335
338
  _position.strategyData.decoded.triggerData = triggerData;
336
339
  _position.strategyData.decoded.subData = subData;
337
340
  _position.positionId = getPositionId(_position.chainId, _position.protocol.id, _position.owner, subData.marketAddr);
341
+ _position.strategy.strategyId = isEOA
342
+ ? Strategies.Identifiers.EoaCollateralSwitch
343
+ : Strategies.Identifiers.CollateralSwitch;
338
344
  return _position;
339
345
  }
340
346
  function parseAaveV4LeverageManagement(position, parseData) {
@@ -746,19 +752,22 @@ function parseSparkLeverageManagement(position, parseData) {
746
752
  const _position = cloneDeep(position);
747
753
  const { subStruct, subId, subHash } = parseData.subscriptionEventData;
748
754
  const { isEnabled } = parseData.strategiesSubsData;
755
+ const isEOA = _position.strategy.strategyId.includes('eoa');
749
756
  const triggerData = triggerService.sparkRatioTrigger.decode(subStruct.triggerData);
750
- const subData = subDataService.legacySparkLeverageManagementSubData.decode(subStruct.subData);
757
+ const subData = isEOA
758
+ ? subDataService.sparkGenericLeverageManagementSubData.decode(subStruct.subData)
759
+ : subDataService.legacySparkLeverageManagementSubData.decode(subStruct.subData);
751
760
  _position.strategyData.decoded.triggerData = triggerData;
752
761
  _position.strategyData.decoded.subData = subData;
753
762
  _position.positionId = getPositionId(_position.chainId, _position.protocol.id, _position.owner, triggerData.market);
754
- const isRepay = _position.strategy.strategyId === Strategies.Identifiers.Repay;
763
+ const isRepay = [Strategies.Identifiers.Repay, Strategies.Identifiers.EoaRepay].includes(_position.strategy.strategyId);
755
764
  if (isRepay) {
756
765
  _position.specific = {
757
766
  triggerRepayRatio: triggerData.ratio,
758
767
  targetRepayRatio: subData.targetRatio,
759
768
  repayEnabled: isEnabled,
760
769
  subId1: Number(subId),
761
- mergeWithId: Strategies.Identifiers.Boost,
770
+ mergeWithId: isEOA ? Strategies.Identifiers.EoaBoost : Strategies.Identifiers.Boost,
762
771
  subHashRepay: subHash,
763
772
  };
764
773
  }
@@ -768,19 +777,24 @@ function parseSparkLeverageManagement(position, parseData) {
768
777
  targetBoostRatio: subData.targetRatio,
769
778
  boostEnabled: isEnabled,
770
779
  subId2: Number(subId),
771
- mergeId: Strategies.Identifiers.Boost,
780
+ mergeId: isEOA ? Strategies.Identifiers.EoaBoost : Strategies.Identifiers.Boost,
772
781
  subHashBoost: subHash,
773
782
  };
774
783
  }
775
- _position.strategy.strategyId = Strategies.IdOverrides.LeverageManagement;
784
+ _position.strategy.strategyId = isEOA
785
+ ? Strategies.IdOverrides.EoaLeverageManagement
786
+ : Strategies.IdOverrides.LeverageManagement;
776
787
  return _position;
777
788
  }
778
789
  function parseSparkLiquidationProtection(position, parseData) {
779
790
  const _position = cloneDeep(position);
780
791
  const { subStruct, subId, subHash } = parseData.subscriptionEventData;
781
792
  const { isEnabled } = parseData.strategiesSubsData;
793
+ const isEOA = _position.strategy.strategyId.includes('eoa');
782
794
  const triggerData = triggerService.sparkRatioTrigger.decode(subStruct.triggerData);
783
- const subData = subDataService.sparkLiquidationProtectionSubData.decode(subStruct.subData);
795
+ const subData = isEOA
796
+ ? subDataService.sparkGenericLiquidationProtectionSubData.decode(subStruct.subData)
797
+ : subDataService.sparkLiquidationProtectionSubData.decode(subStruct.subData);
784
798
  _position.strategyData.decoded.triggerData = triggerData;
785
799
  _position.strategyData.decoded.subData = subData;
786
800
  _position.positionId = getPositionId(_position.chainId, _position.protocol.id, _position.owner, triggerData.market);
@@ -791,14 +805,19 @@ function parseSparkLiquidationProtection(position, parseData) {
791
805
  subId1: Number(subId),
792
806
  subHashRepay: subHash,
793
807
  };
794
- _position.strategy.strategyId = Strategies.IdOverrides.LiquidationProtection;
808
+ _position.strategy.strategyId = isEOA
809
+ ? Strategies.IdOverrides.EoaLiquidationProtection
810
+ : Strategies.IdOverrides.LiquidationProtection;
795
811
  return _position;
796
812
  }
797
813
  function parseSparkLeverageManagementOnPrice(position, parseData) {
798
814
  const _position = cloneDeep(position);
799
815
  const { subStruct } = parseData.subscriptionEventData;
816
+ const isEOA = _position.strategy.strategyId.includes('eoa');
800
817
  const triggerData = triggerService.sparkQuotePriceTrigger.decode(subStruct.triggerData);
801
- const subData = subDataService.sparkLeverageManagementOnPriceSubData.decode(subStruct.subData);
818
+ const subData = isEOA
819
+ ? subDataService.sparkLeverageManagementOnPriceGenericSubData.decode(subStruct.subData)
820
+ : subDataService.sparkLeverageManagementOnPriceSubData.decode(subStruct.subData);
802
821
  _position.strategyData.decoded.triggerData = triggerData;
803
822
  _position.strategyData.decoded.subData = subData;
804
823
  _position.positionId = getPositionId(_position.chainId, _position.protocol.id, _position.owner, subData.marketAddr);
@@ -815,6 +834,9 @@ function parseSparkLeverageManagementOnPrice(position, parseData) {
815
834
  price: triggerData.price,
816
835
  ratioState: triggerData.ratioState,
817
836
  };
837
+ _position.strategy.strategyId = isEOA
838
+ ? Strategies.IdOverrides.EoaLeverageManagementOnPrice
839
+ : Strategies.IdOverrides.LeverageManagementOnPrice;
818
840
  return _position;
819
841
  }
820
842
  function parseSparkCloseOnPrice(position, parseData) {
@@ -822,9 +844,11 @@ function parseSparkCloseOnPrice(position, parseData) {
822
844
  const { subStruct } = parseData.subscriptionEventData;
823
845
  const triggerData = triggerService.sparkQuotePriceRangeTrigger.decode(subStruct.triggerData);
824
846
  const subData = subDataService.sparkCloseGenericSubData.decode(subStruct.subData);
847
+ const isEOA = _position.strategy.strategyId.includes('eoa');
825
848
  _position.strategyData.decoded.triggerData = triggerData;
826
849
  _position.strategyData.decoded.subData = subData;
827
850
  _position.positionId = getPositionId(_position.chainId, _position.protocol.id, _position.owner, subData.marketAddr);
851
+ _position.strategy.strategyId = isEOA ? Strategies.Identifiers.EoaCloseOnPrice : Strategies.Identifiers.CloseOnPrice;
828
852
  const { takeProfitType, stopLossType } = getStopLossAndTakeProfitTypeByCloseStrategyType(+subData.closeType);
829
853
  _position.specific = {
830
854
  collAsset: subData.collAsset,
@@ -1247,8 +1271,15 @@ const parsingMethodsMapping = {
1247
1271
  [Strategies.Identifiers.RepayOnPrice]: parseSparkLeverageManagementOnPrice,
1248
1272
  [Strategies.Identifiers.BoostOnPrice]: parseSparkLeverageManagementOnPrice,
1249
1273
  [Strategies.Identifiers.CloseOnPrice]: parseSparkCloseOnPrice,
1274
+ [Strategies.Identifiers.EoaRepay]: parseSparkLeverageManagement,
1275
+ [Strategies.Identifiers.EoaBoost]: parseSparkLeverageManagement,
1276
+ [Strategies.Identifiers.EoaRepayOnPrice]: parseSparkLeverageManagementOnPrice,
1277
+ [Strategies.Identifiers.EoaBoostOnPrice]: parseSparkLeverageManagementOnPrice,
1278
+ [Strategies.Identifiers.EoaCloseOnPrice]: parseSparkCloseOnPrice,
1250
1279
  [Strategies.Identifiers.CollateralSwitch]: parseSparkCollateralSwitch,
1280
+ [Strategies.Identifiers.EoaCollateralSwitch]: parseSparkCollateralSwitch,
1251
1281
  [Strategies.Identifiers.LiquidationProtection]: parseSparkLiquidationProtection,
1282
+ [Strategies.Identifiers.EoaLiquidationProtection]: parseSparkLiquidationProtection,
1252
1283
  },
1253
1284
  [ProtocolIdentifiers.StrategiesAutomation.CrvUSD]: {
1254
1285
  [Strategies.Identifiers.Repay]: parseCrvUSDLeverageManagement,
@@ -81,7 +81,6 @@ describe('Feature: strategiesService.ts', () => {
81
81
  subId: '379',
82
82
  proxy: '0x9cb7e19861665366011899d74e75d4f2a419aeed',
83
83
  subHash: '0xafa4d200be62f171b57b1ae0f4e8348d1ac3f6d0812ad6da74a2adae8037dde1',
84
- // @ts-ignore
85
84
  subStruct: {
86
85
  strategyOrBundleId: '8',
87
86
  isBundle: true,
@@ -93,7 +93,11 @@ export declare const sparkEncode: {
93
93
  closeOnPriceGeneric(strategyOrBundleId: number, collAsset: EthereumAddress, collAssetId: number, debtAsset: EthereumAddress, debtAssetId: number, marketAddr: EthereumAddress, user: EthereumAddress, stopLossPrice?: number, stopLossType?: CloseToAssetType, takeProfitPrice?: number, takeProfitType?: CloseToAssetType): (number | boolean | string[])[];
94
94
  leverageManagement(strategyOrBundleId: number, market: EthereumAddress, user: EthereumAddress, ratioState: RatioState, targetRatio: number, triggerRatio: number): (number | boolean | string[])[];
95
95
  liquidationProtection(strategyOrBundleId: number, market: EthereumAddress, user: EthereumAddress, ratioState: RatioState, targetRatio: number, triggerRatio: number): (number | boolean | string[])[];
96
+ leverageManagementGeneric(strategyOrBundleId: number, market: EthereumAddress, user: EthereumAddress, ratioState: RatioState, targetRatio: number, triggerRatio: number): (number | boolean | string[])[];
97
+ liquidationProtectionGeneric(strategyOrBundleId: number, market: EthereumAddress, user: EthereumAddress, ratioState: RatioState, targetRatio: number, triggerRatio: number): (number | boolean | string[])[];
98
+ leverageManagementOnPriceGeneric(strategyOrBundleId: number, price: number, ratioState: RatioState, collAsset: EthereumAddress, collAssetId: number, debtAsset: EthereumAddress, debtAssetId: number, marketAddr: EthereumAddress, targetRatio: number, user: EthereumAddress): (number | boolean | string[])[];
96
99
  collateralSwitch(strategyOrBundleId: number, fromAsset: EthereumAddress, fromAssetId: number, toAsset: EthereumAddress, toAssetId: number, marketAddr: EthereumAddress, amountToSwitch: string, baseTokenAddress: EthereumAddress, quoteTokenAddress: EthereumAddress, price: number, state: RatioState): (number | boolean | string[])[];
100
+ collateralSwitchGeneric(strategyOrBundleId: number, fromAsset: EthereumAddress, fromAssetId: number, toAsset: EthereumAddress, toAssetId: number, marketAddr: EthereumAddress, amountToSwitch: string, user: EthereumAddress, baseTokenAddress: EthereumAddress, quoteTokenAddress: EthereumAddress, price: number, state: RatioState): (number | boolean | string[])[];
97
101
  };
98
102
  export declare const crvUSDEncode: {
99
103
  leverageManagement(owner: EthereumAddress, controllerAddr: EthereumAddress, ratioState: RatioState, targetRatio: number, triggerRatio: number, collTokenAddr: EthereumAddress, crvUSDAddr: EthereumAddress): (boolean | string[] | Bundles.MainnetIds)[];
@@ -250,12 +250,36 @@ export const sparkEncode = {
250
250
  const triggerData = triggerService.sparkRatioTrigger.encode(user, market, triggerRatio, ratioState);
251
251
  return [strategyOrBundleId, isBundle, triggerData, subData];
252
252
  },
253
+ leverageManagementGeneric(strategyOrBundleId, market, user, ratioState, targetRatio, triggerRatio) {
254
+ const isBundle = true;
255
+ const subData = subDataService.sparkGenericLeverageManagementSubData.encode(targetRatio, ratioState, market, user);
256
+ const triggerData = triggerService.sparkRatioTrigger.encode(user, market, triggerRatio, ratioState);
257
+ return [strategyOrBundleId, isBundle, triggerData, subData];
258
+ },
259
+ liquidationProtectionGeneric(strategyOrBundleId, market, user, ratioState, targetRatio, triggerRatio) {
260
+ const isBundle = true;
261
+ const subData = subDataService.sparkGenericLiquidationProtectionSubData.encode(targetRatio, ratioState, market, user);
262
+ const triggerData = triggerService.sparkRatioTrigger.encode(user, market, triggerRatio, ratioState);
263
+ return [strategyOrBundleId, isBundle, triggerData, subData];
264
+ },
265
+ leverageManagementOnPriceGeneric(strategyOrBundleId, price, ratioState, collAsset, collAssetId, debtAsset, debtAssetId, marketAddr, targetRatio, user) {
266
+ const isBundle = true;
267
+ const subData = subDataService.sparkLeverageManagementOnPriceGenericSubData.encode(collAsset, collAssetId, debtAsset, debtAssetId, marketAddr, targetRatio, user);
268
+ const triggerData = triggerService.sparkQuotePriceTrigger.encode(collAsset, debtAsset, price, ratioState);
269
+ return [strategyOrBundleId, isBundle, triggerData, subData];
270
+ },
253
271
  collateralSwitch(strategyOrBundleId, fromAsset, fromAssetId, toAsset, toAssetId, marketAddr, amountToSwitch, baseTokenAddress, quoteTokenAddress, price, state) {
254
272
  const isBundle = false;
255
273
  const subDataEncoded = subDataService.sparkCollateralSwitchSubData.encode(fromAsset, fromAssetId, toAsset, toAssetId, marketAddr, amountToSwitch);
256
274
  const triggerDataEncoded = triggerService.sparkQuotePriceTrigger.encode(baseTokenAddress, quoteTokenAddress, price, state);
257
275
  return [strategyOrBundleId, isBundle, triggerDataEncoded, subDataEncoded];
258
276
  },
277
+ collateralSwitchGeneric(strategyOrBundleId, fromAsset, fromAssetId, toAsset, toAssetId, marketAddr, amountToSwitch, user, baseTokenAddress, quoteTokenAddress, price, state) {
278
+ const isBundle = false;
279
+ const subDataEncoded = subDataService.sparkGenericFLCollateralSwitchSubData.encode(fromAsset, fromAssetId, toAsset, toAssetId, marketAddr, amountToSwitch, user);
280
+ const triggerDataEncoded = triggerService.sparkQuotePriceTrigger.encode(baseTokenAddress, quoteTokenAddress, price, state);
281
+ return [strategyOrBundleId, isBundle, triggerDataEncoded, subDataEncoded];
282
+ },
259
283
  };
260
284
  export const crvUSDEncode = {
261
285
  leverageManagement(owner, controllerAddr, ratioState, targetRatio, triggerRatio, collTokenAddr, crvUSDAddr) {