@strkfarm/sdk 1.0.17 → 1.0.19
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/dist/cli.js +276 -90
- package/dist/cli.mjs +270 -84
- package/dist/index.browser.global.js +27175 -18059
- package/dist/index.browser.mjs +8078 -1370
- package/dist/index.d.ts +211 -25
- package/dist/index.js +7773 -1035
- package/dist/index.mjs +8146 -1417
- package/package.json +4 -1
- package/src/data/cl-vault.abi.json +1434 -0
- package/src/data/ekubo-math.abi.json +333 -0
- package/src/data/ekubo-positions.abi.json +1594 -0
- package/src/data/erc20.abi.json +1122 -0
- package/src/data/erc4626.abi.json +1530 -0
- package/src/dataTypes/_bignumber.ts +53 -0
- package/src/dataTypes/address.ts +4 -0
- package/src/dataTypes/bignumber.browser.ts +8 -0
- package/src/dataTypes/bignumber.node.ts +22 -0
- package/src/dataTypes/bignumber.ts +1 -55
- package/src/dataTypes/index.ts +1 -1
- package/src/global.ts +54 -4
- package/src/interfaces/common.ts +64 -10
- package/src/interfaces/lending.ts +1 -1
- package/src/modules/avnu.ts +93 -0
- package/src/modules/erc20.ts +23 -0
- package/src/modules/index.ts +2 -0
- package/src/modules/pricer.ts +1 -1
- package/src/modules/zkLend.ts +2 -2
- package/src/node/headless.browser.ts +9 -0
- package/src/node/headless.node.ts +36 -0
- package/src/node/headless.ts +1 -0
- package/src/node/index.ts +2 -1
- package/src/strategies/base-strategy.ts +47 -0
- package/src/strategies/ekubo-cl-vault.ts +535 -0
- package/src/strategies/index.ts +2 -1
- package/src/strategies/vesu-rebalance.ts +123 -35
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
|
|
3
|
+
export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
|
|
4
|
+
decimals: number;
|
|
5
|
+
|
|
6
|
+
constructor(value: string | number, decimals: number) {
|
|
7
|
+
super(value);
|
|
8
|
+
this.decimals = decimals;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
toWei() {
|
|
12
|
+
return this.mul(10 ** this.decimals).toFixed(0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
multipliedBy(value: string | number | T): T {
|
|
16
|
+
let _value = Number(value).toFixed(13);
|
|
17
|
+
return this.construct(this.mul(_value).toString(), this.decimals);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
dividedBy(value: string | number | T): T {
|
|
21
|
+
let _value = Number(value).toFixed(13);
|
|
22
|
+
return this.construct(this.div(_value).toString(), this.decimals);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
plus(value: string | number | T): T {
|
|
26
|
+
const _value = Number(value).toFixed(13);
|
|
27
|
+
return this.construct(this.add(_value).toString(), this.decimals);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
minus(n: number | string | T, base?: number): T {
|
|
31
|
+
const _value = Number(n).toFixed(13);
|
|
32
|
+
return this.construct(super.minus(_value, base).toString(), this.decimals);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected construct(value: string | number, decimals: number): T {
|
|
36
|
+
return new (this.constructor as { new (value: string | number, decimals: number): T })(value, decimals);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
toString(base?: number | undefined): string {
|
|
40
|
+
return super.toString(base);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
toJSON() {
|
|
44
|
+
return this.toString();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
valueOf() {
|
|
48
|
+
return this.toString();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
BigNumber.config({ DECIMAL_PLACES: 18 })
|
|
53
|
+
_Web3Number.config({ DECIMAL_PLACES: 18 })
|
package/src/dataTypes/address.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { _Web3Number } from "./_bignumber";
|
|
2
|
+
|
|
3
|
+
export class Web3Number extends _Web3Number<Web3Number> {
|
|
4
|
+
static fromWei(weiNumber: string | number, decimals: number) {
|
|
5
|
+
const bn = (new Web3Number(weiNumber, decimals)).dividedBy(10 ** decimals)
|
|
6
|
+
return new Web3Number(bn.toString(), decimals);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import util from 'util';
|
|
2
|
+
import { _Web3Number } from "./_bignumber";
|
|
3
|
+
|
|
4
|
+
export class Web3Number extends _Web3Number<Web3Number> {
|
|
5
|
+
|
|
6
|
+
static fromWei(weiNumber: string | number, decimals: number) {
|
|
7
|
+
const bn = (new Web3Number(weiNumber, decimals)).dividedBy(10 ** decimals)
|
|
8
|
+
return new Web3Number(bn.toString(), decimals);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
[util.inspect.custom](depth: any, opts: any): string {
|
|
12
|
+
return this.toString();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
[Symbol.for('nodejs.util.inspect.custom')](depth: any, inspectOptions: any, inspect: any): string {
|
|
16
|
+
return this.toString();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
inspect(depth: any, opts: any) {
|
|
20
|
+
return this.toString();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,55 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// import { inspect } from 'util';
|
|
3
|
-
// const customInspectSymbol = inspect.custom || Symbol.for('nodejs.util.inspect.custom');
|
|
4
|
-
|
|
5
|
-
function isNode() {
|
|
6
|
-
// Check for the presence of the `window` object, which is undefined in Node.js
|
|
7
|
-
return typeof window === 'undefined';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class Web3Number extends BigNumber {
|
|
11
|
-
decimals: number;
|
|
12
|
-
|
|
13
|
-
constructor(value: string | number, decimals: number) {
|
|
14
|
-
super(value);
|
|
15
|
-
this.decimals = decimals;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
static fromWei(weiNumber: string | number, decimals: number) {
|
|
19
|
-
const bn = (new Web3Number(weiNumber, decimals)).dividedBy(10 ** decimals)
|
|
20
|
-
return new Web3Number(bn.toString(), decimals);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
toWei() {
|
|
24
|
-
return this.mul(10 ** this.decimals).toFixed(0);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
multipliedBy(value: string | number) {
|
|
28
|
-
let _value = Number(value).toFixed(6);
|
|
29
|
-
return new Web3Number(this.mul(_value).toString(), this.decimals);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
dividedBy(value: string | number) {
|
|
33
|
-
let _value = Number(value).toFixed(6);
|
|
34
|
-
return new Web3Number(this.div(_value).toString(), this.decimals);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
plus(value: string | number) {
|
|
38
|
-
return new Web3Number(this.add(value).toString(), this.decimals);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
minus(n: number | string, base?: number): Web3Number {
|
|
42
|
-
return new Web3Number(super.minus(n, base).toString(), this.decimals);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
toString(base?: number | undefined): string {
|
|
46
|
-
return super.toString(base);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// [customInspectSymbol](depth: any, inspectOptions: any, inspect: any) {
|
|
50
|
-
// return this.toString();
|
|
51
|
-
// }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
BigNumber.config({ DECIMAL_PLACES: 18 })
|
|
55
|
-
Web3Number.config({ DECIMAL_PLACES: 18 })
|
|
1
|
+
export * from '@/dataTypes/bignumber.browser';
|
package/src/dataTypes/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from '
|
|
1
|
+
export * from '@/dataTypes/bignumber';
|
|
2
2
|
export * from './address';
|
package/src/global.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import { TokenInfo } from './interfaces';
|
|
3
|
+
import { ContractAddr } from './dataTypes';
|
|
3
4
|
|
|
4
5
|
const colors = {
|
|
5
6
|
error: 'red',
|
|
@@ -45,14 +46,50 @@ export class FatalError extends Error {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
const
|
|
49
|
+
const defaultTokens: TokenInfo[] = [{
|
|
49
50
|
name: 'Starknet',
|
|
50
51
|
symbol: 'STRK',
|
|
51
52
|
logo: 'https://assets.coingecko.com/coins/images/26433/small/starknet.png',
|
|
52
|
-
address: '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',
|
|
53
|
+
address: ContractAddr.from('0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d'),
|
|
53
54
|
decimals: 18,
|
|
54
55
|
coingeckId: 'starknet'
|
|
55
|
-
}
|
|
56
|
+
}, {
|
|
57
|
+
name: 'xSTRK',
|
|
58
|
+
symbol: 'xSTRK',
|
|
59
|
+
logo: 'https://dashboard.endur.fi/endur-fi.svg',
|
|
60
|
+
address: ContractAddr.from('0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a'),
|
|
61
|
+
decimals: 18,
|
|
62
|
+
coingeckId: undefined
|
|
63
|
+
}, {
|
|
64
|
+
name: 'ETH',
|
|
65
|
+
symbol: 'ETH',
|
|
66
|
+
logo: 'https://opbnb.bscscan.com/token/images/ether.svg',
|
|
67
|
+
address: ContractAddr.from('0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7'),
|
|
68
|
+
decimals: 18,
|
|
69
|
+
coingeckId: undefined
|
|
70
|
+
}, {
|
|
71
|
+
name: 'USDC',
|
|
72
|
+
symbol: 'USDC',
|
|
73
|
+
logo: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png',
|
|
74
|
+
address: ContractAddr.from('0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'),
|
|
75
|
+
decimals: 6,
|
|
76
|
+
coingeckId: undefined
|
|
77
|
+
}, {
|
|
78
|
+
name: 'USDT',
|
|
79
|
+
symbol: 'USDT',
|
|
80
|
+
logo: 'https://assets.coingecko.com/coins/images/325/small/Tether.png',
|
|
81
|
+
address: ContractAddr.from('0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8'),
|
|
82
|
+
decimals: 6,
|
|
83
|
+
coingeckId: undefined
|
|
84
|
+
}, {
|
|
85
|
+
name: 'WBTC',
|
|
86
|
+
symbol: 'WBTC',
|
|
87
|
+
logo: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png',
|
|
88
|
+
address: ContractAddr.from('0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac'),
|
|
89
|
+
decimals: 8,
|
|
90
|
+
coingeckId: undefined
|
|
91
|
+
}]
|
|
92
|
+
const tokens: TokenInfo[] = defaultTokens;
|
|
56
93
|
|
|
57
94
|
/** Contains globally useful functions.
|
|
58
95
|
* - fatalError: Things to do when a fatal error occurs
|
|
@@ -109,7 +146,7 @@ export class Global {
|
|
|
109
146
|
tokens.push({
|
|
110
147
|
name: token.name,
|
|
111
148
|
symbol: token.symbol,
|
|
112
|
-
address: token.address,
|
|
149
|
+
address: ContractAddr.from(token.address),
|
|
113
150
|
decimals: token.decimals,
|
|
114
151
|
logo: token.logoUri,
|
|
115
152
|
coingeckId: token.extensions.coingeckoId,
|
|
@@ -124,4 +161,17 @@ export class Global {
|
|
|
124
161
|
throw new FatalError(message);
|
|
125
162
|
}
|
|
126
163
|
}
|
|
164
|
+
|
|
165
|
+
static async getTokenInfoFromAddr(addr: ContractAddr) {
|
|
166
|
+
// if tokens are not loaded, load them
|
|
167
|
+
if (tokens.length == defaultTokens.length) {
|
|
168
|
+
await Global.getTokens();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const token = tokens.find((token) => addr.eq(token.address));
|
|
172
|
+
if (!token) {
|
|
173
|
+
throw new FatalError(`Token not found: ${addr.address}`);
|
|
174
|
+
}
|
|
175
|
+
return token;
|
|
176
|
+
}
|
|
127
177
|
}
|
package/src/interfaces/common.ts
CHANGED
|
@@ -2,12 +2,18 @@ import { ContractAddr, Web3Number } from "@/dataTypes"
|
|
|
2
2
|
import { BlockIdentifier, RpcProvider } from "starknet"
|
|
3
3
|
|
|
4
4
|
export enum RiskType {
|
|
5
|
-
MARKET_RISK = '
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
MARKET_RISK = 'Market Risk',
|
|
6
|
+
// if non-correalted pairs, this is 3 (STRK/USDC)
|
|
7
|
+
// if highly correalted pairs, this is 1 (e.g. xSTRK/STRK)
|
|
8
|
+
// if correalted pairs, this is 2 (e.g. BTC/SOL)
|
|
9
|
+
// If there is added leverage on top, can go till 5
|
|
10
|
+
IMPERMANENT_LOSS = 'Impermanent Loss Risk',
|
|
11
|
+
LIQUIDATION_RISK = 'Liquidation Risk',
|
|
12
|
+
LOW_LIQUIDITY_RISK = 'Low Liquidity Risk',
|
|
13
|
+
SMART_CONTRACT_RISK = 'Smart Contract Risk',
|
|
14
|
+
ORACLE_RISK = 'Oracle Risk',
|
|
15
|
+
TECHNICAL_RISK = 'Technical Risk',
|
|
16
|
+
COUNTERPARTY_RISK = 'Counterparty Risk', // e.g. bad debt
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export interface RiskFactor {
|
|
@@ -19,7 +25,7 @@ export interface RiskFactor {
|
|
|
19
25
|
export interface TokenInfo {
|
|
20
26
|
name: string,
|
|
21
27
|
symbol: string,
|
|
22
|
-
address:
|
|
28
|
+
address: ContractAddr,
|
|
23
29
|
decimals: number,
|
|
24
30
|
logo: string,
|
|
25
31
|
coingeckId?: string,
|
|
@@ -43,11 +49,17 @@ export interface IProtocol {
|
|
|
43
49
|
logo: string,
|
|
44
50
|
}
|
|
45
51
|
|
|
52
|
+
export enum FlowChartColors {
|
|
53
|
+
Green = 'purple',
|
|
54
|
+
Blue = '#35484f',
|
|
55
|
+
Purple = '#6e53dc',
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
/**
|
|
47
59
|
* @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
|
|
48
60
|
* @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
|
|
49
61
|
*/
|
|
50
|
-
export interface IStrategyMetadata {
|
|
62
|
+
export interface IStrategyMetadata<T> {
|
|
51
63
|
name: string,
|
|
52
64
|
description: string,
|
|
53
65
|
address: ContractAddr,
|
|
@@ -59,13 +71,15 @@ export interface IStrategyMetadata {
|
|
|
59
71
|
risk: {
|
|
60
72
|
riskFactor: RiskFactor[],
|
|
61
73
|
netRisk: number
|
|
62
|
-
}
|
|
74
|
+
},
|
|
75
|
+
additionalInfo: T
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
export interface IInvestmentFlow {
|
|
66
79
|
title: string,
|
|
67
|
-
subItems: string[],
|
|
80
|
+
subItems: {key: string, value: string}[],
|
|
68
81
|
linkedFlows: IInvestmentFlow[],
|
|
82
|
+
style?: any
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
export function getMainnetConfig(rpcUrl = "https://starknet-mainnet.public.blastapi.io", blockIdentifier: BlockIdentifier = 'pending'): IConfig {
|
|
@@ -77,4 +91,44 @@ export function getMainnetConfig(rpcUrl = "https://starknet-mainnet.public.blast
|
|
|
77
91
|
stage: "production",
|
|
78
92
|
network: Network.mainnet
|
|
79
93
|
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const getRiskExplaination = (riskType: RiskType) => {
|
|
97
|
+
switch (riskType) {
|
|
98
|
+
case RiskType.MARKET_RISK:
|
|
99
|
+
return "The risk of the market moving against the position."
|
|
100
|
+
case RiskType.IMPERMANENT_LOSS:
|
|
101
|
+
return "The temporary loss of value experienced by liquidity providers in AMMs when asset prices diverge compared to simply holding them."
|
|
102
|
+
case RiskType.LIQUIDATION_RISK:
|
|
103
|
+
return "The risk of losing funds due to the position being liquidated."
|
|
104
|
+
case RiskType.LOW_LIQUIDITY_RISK:
|
|
105
|
+
return "The risk of low liquidity in the pool, which can lead to high slippages or reduced in-abilities to quickly exit the position."
|
|
106
|
+
case RiskType.ORACLE_RISK:
|
|
107
|
+
return "The risk of the oracle being manipulated or incorrect."
|
|
108
|
+
case RiskType.SMART_CONTRACT_RISK:
|
|
109
|
+
return "The risk of the smart contract being vulnerable to attacks."
|
|
110
|
+
case RiskType.TECHNICAL_RISK:
|
|
111
|
+
return "The risk of technical issues e.g. backend failure."
|
|
112
|
+
case RiskType.COUNTERPARTY_RISK:
|
|
113
|
+
return "The risk of the counterparty defaulting e.g. bad debt on lending platforms."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const getRiskColor = (risk: RiskFactor) => {
|
|
118
|
+
const value = risk.value;
|
|
119
|
+
if (value === 0) return 'green';
|
|
120
|
+
if (value < 2.5) return 'yellow';
|
|
121
|
+
return 'red';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const getNoRiskTags = (risks: RiskFactor[]) => {
|
|
125
|
+
const noRisks1 = risks.filter(risk => risk.value === 0).map(risk => risk.type);
|
|
126
|
+
|
|
127
|
+
// const risks not present
|
|
128
|
+
const noRisks2 = Object.values(RiskType).filter(risk => !risks.map(risk => risk.type).includes(risk));
|
|
129
|
+
|
|
130
|
+
const mergedUnique = [...new Set([...noRisks1, ...noRisks2])];
|
|
131
|
+
|
|
132
|
+
// add `No` to the start of each risk
|
|
133
|
+
return mergedUnique.map(risk => `No ${risk}`);
|
|
80
134
|
}
|
|
@@ -4,7 +4,7 @@ import { ContractAddr } from "@/dataTypes/address";
|
|
|
4
4
|
import { loggers } from "winston";
|
|
5
5
|
import { logger } from "@/global";
|
|
6
6
|
import { log } from "console";
|
|
7
|
-
import { Web3Number } from "@/dataTypes
|
|
7
|
+
import { Web3Number } from "@/dataTypes";
|
|
8
8
|
|
|
9
9
|
export interface ILendingMetadata {
|
|
10
10
|
name: string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { uint256 } from "starknet";
|
|
2
|
+
|
|
3
|
+
import { Call, Uint256 } from "starknet";
|
|
4
|
+
import { fetchBuildExecuteTransaction, fetchQuotes, Quote } from "@avnu/avnu-sdk";
|
|
5
|
+
import { assert } from "../utils";
|
|
6
|
+
|
|
7
|
+
export interface Route {
|
|
8
|
+
token_from: string,
|
|
9
|
+
token_to: string,
|
|
10
|
+
exchange_address: string,
|
|
11
|
+
percent: number,
|
|
12
|
+
additional_swap_params: string[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SwapInfo {
|
|
16
|
+
token_from_address: string,
|
|
17
|
+
token_from_amount: Uint256,
|
|
18
|
+
token_to_address: string,
|
|
19
|
+
token_to_amount: Uint256,
|
|
20
|
+
token_to_min_amount: Uint256,
|
|
21
|
+
beneficiary: string,
|
|
22
|
+
integrator_fee_amount_bps: number,
|
|
23
|
+
integrator_fee_recipient: string,
|
|
24
|
+
routes: Route[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export class AvnuWrapper {
|
|
29
|
+
async getQuotes(
|
|
30
|
+
fromToken: string,
|
|
31
|
+
toToken: string,
|
|
32
|
+
amountWei: string,
|
|
33
|
+
taker: string,
|
|
34
|
+
) {
|
|
35
|
+
const params: any = {
|
|
36
|
+
sellTokenAddress: fromToken,
|
|
37
|
+
buyTokenAddress: toToken,
|
|
38
|
+
sellAmount: amountWei,
|
|
39
|
+
takerAddress: taker,
|
|
40
|
+
};
|
|
41
|
+
assert(fromToken != toToken, 'From and to tokens are the same');
|
|
42
|
+
|
|
43
|
+
const quotes = await fetchQuotes(params);
|
|
44
|
+
assert(quotes.length > 0, 'No quotes found');
|
|
45
|
+
return quotes[0];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getSwapInfo(
|
|
49
|
+
quote: Quote,
|
|
50
|
+
taker: string,
|
|
51
|
+
integratorFeeBps: number,
|
|
52
|
+
integratorFeeRecipient: string,
|
|
53
|
+
minAmount: string
|
|
54
|
+
) {
|
|
55
|
+
const calldata = await fetchBuildExecuteTransaction(quote.quoteId);
|
|
56
|
+
// its the multi swap function call
|
|
57
|
+
const call: Call = calldata.calls[1];
|
|
58
|
+
const callData: string[] = call.calldata as string[];
|
|
59
|
+
const routesLen: number = Number(callData[11]);
|
|
60
|
+
assert(routesLen > 0, 'No routes found');
|
|
61
|
+
|
|
62
|
+
// use call data to re-construct routes
|
|
63
|
+
let startIndex = 12;
|
|
64
|
+
const routes: Route[] = [];
|
|
65
|
+
for(let i=0; i<routesLen; ++i) {
|
|
66
|
+
const swap_params_len = Number(callData[startIndex + 4]);
|
|
67
|
+
const route: Route = {
|
|
68
|
+
token_from: callData[startIndex],
|
|
69
|
+
token_to: callData[startIndex + 1],
|
|
70
|
+
exchange_address: callData[startIndex + 2],
|
|
71
|
+
percent: Number(callData[startIndex + 3]),
|
|
72
|
+
additional_swap_params: swap_params_len > 0 ? callData.slice(startIndex + 5, startIndex + 5 + swap_params_len): []
|
|
73
|
+
}
|
|
74
|
+
routes.push(route);
|
|
75
|
+
startIndex += 5 + swap_params_len;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// swapInfo as expected by the strategy
|
|
79
|
+
const swapInfo: SwapInfo = {
|
|
80
|
+
token_from_address: quote.sellTokenAddress,
|
|
81
|
+
token_from_amount: uint256.bnToUint256(quote.sellAmount),
|
|
82
|
+
token_to_address: quote.buyTokenAddress,
|
|
83
|
+
token_to_amount: uint256.bnToUint256(quote.buyAmount),
|
|
84
|
+
token_to_min_amount: uint256.bnToUint256(minAmount),
|
|
85
|
+
beneficiary: taker,
|
|
86
|
+
integrator_fee_amount_bps: integratorFeeBps,
|
|
87
|
+
integrator_fee_recipient: integratorFeeRecipient,
|
|
88
|
+
routes
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return swapInfo;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
+
import { IConfig } from "@/interfaces";
|
|
3
|
+
import { Contract } from "starknet";
|
|
4
|
+
import ERC20Abi from '@/data/erc20.abi.json';
|
|
5
|
+
|
|
6
|
+
export class ERC20 {
|
|
7
|
+
readonly config: IConfig;
|
|
8
|
+
|
|
9
|
+
constructor(config: IConfig) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
contract(addr: string | ContractAddr) {
|
|
14
|
+
const _addr = typeof addr === 'string' ? addr : addr.address;
|
|
15
|
+
return new Contract(ERC20Abi, _addr, this.config.provider);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async balanceOf(token: string | ContractAddr, address: string | ContractAddr, tokenDecimals: number) {
|
|
19
|
+
const contract = this.contract(token);
|
|
20
|
+
const balance = await contract.call('balanceOf', [address.toString()]);
|
|
21
|
+
return Web3Number.fromWei(balance.toString(), tokenDecimals);
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/modules/index.ts
CHANGED
package/src/modules/pricer.ts
CHANGED
|
@@ -183,7 +183,7 @@ export class Pricer extends PricerBase {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
186
|
-
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
|
|
186
|
+
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
|
|
187
187
|
const result = await axios.get(url);
|
|
188
188
|
const data: any = result.data;
|
|
189
189
|
const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6));
|
package/src/modules/zkLend.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import BigNumber from "bignumber.js";
|
|
3
|
-
import { Web3Number } from "@/dataTypes/bignumber";
|
|
3
|
+
import { Web3Number } from "@/dataTypes/bignumber.browser";
|
|
4
4
|
import { FatalError, Global, logger } from "@/global";
|
|
5
5
|
import { TokenInfo } from "@/interfaces";
|
|
6
6
|
import { ILending, ILendingPosition, LendingToken, MarginType } from "@/interfaces/lending";
|
|
@@ -36,7 +36,7 @@ export class ZkLend extends ILending implements ILending {
|
|
|
36
36
|
const token: LendingToken = {
|
|
37
37
|
name: pool.token.name,
|
|
38
38
|
symbol: pool.token.symbol,
|
|
39
|
-
address: savedTokenInfo?.address || '',
|
|
39
|
+
address: savedTokenInfo?.address || ContractAddr.from(''),
|
|
40
40
|
logo: '',
|
|
41
41
|
decimals: pool.token.decimals,
|
|
42
42
|
borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const puppeteer = require('puppeteer');
|
|
2
|
+
|
|
3
|
+
export async function getAPIUsingHeadlessBrowser(
|
|
4
|
+
url: string
|
|
5
|
+
) {
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
// Launch a headless browser
|
|
9
|
+
const browser = await puppeteer.launch({ headless: true });
|
|
10
|
+
const page = await browser.newPage();
|
|
11
|
+
|
|
12
|
+
// Set a realistic User-Agent to avoid suspicion
|
|
13
|
+
await page.setUserAgent(
|
|
14
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// Go to the API endpoint
|
|
18
|
+
await page.goto(url, {
|
|
19
|
+
waitUntil: 'networkidle2', // Wait until the page fully loads (including JS challenge)
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// If the API returns JSON, extract it
|
|
23
|
+
const jsonData = await page.evaluate(() => {
|
|
24
|
+
const pre = document.querySelector('pre'); // Adjust based on how the API response is formatted
|
|
25
|
+
return pre && pre.textContent ? JSON.parse(pre.textContent) : null;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Clean up
|
|
29
|
+
await browser.close();
|
|
30
|
+
return jsonData;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('Error:', error);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './headless.browser';
|
package/src/node/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from './pricer-redis';
|
|
1
|
+
export * from './pricer-redis';
|
|
2
|
+
export * from '@/node/headless';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
+
import { IConfig, TokenInfo } from "@/interfaces";
|
|
3
|
+
import { Call } from "starknet";
|
|
4
|
+
|
|
5
|
+
export interface SingleActionAmount {
|
|
6
|
+
tokenInfo: TokenInfo,
|
|
7
|
+
amount: Web3Number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SingleTokenInfo extends SingleActionAmount {
|
|
11
|
+
usdValue: number
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface DualActionAmount {
|
|
15
|
+
token0: SingleActionAmount,
|
|
16
|
+
token1: SingleActionAmount
|
|
17
|
+
}
|
|
18
|
+
export interface DualTokenInfo {
|
|
19
|
+
netUsdValue: number,
|
|
20
|
+
token0: SingleTokenInfo,
|
|
21
|
+
token1: SingleTokenInfo
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class BaseStrategy<TVLInfo, ActionInfo> {
|
|
25
|
+
readonly config: IConfig;
|
|
26
|
+
|
|
27
|
+
constructor(config: IConfig) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getUserTVL(user: ContractAddr): Promise<TVLInfo> {
|
|
32
|
+
throw new Error("Not implemented");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getTVL(): Promise<TVLInfo> {
|
|
36
|
+
throw new Error("Not implemented");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Call[] {
|
|
40
|
+
throw new Error("Not implemented");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Call[] {
|
|
44
|
+
throw new Error("Not implemented");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|