@strkfarm/sdk 1.0.55 → 1.0.56

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.
@@ -39,4 +39,8 @@ export class ContractAddr {
39
39
  toString() {
40
40
  return this.address;
41
41
  }
42
+
43
+ toBigInt() {
44
+ return num.toBigInt(this.address);
45
+ }
42
46
  }
package/src/global.ts CHANGED
@@ -62,6 +62,16 @@ const defaultTokens: TokenInfo[] = [{
62
62
  decimals: 8,
63
63
  coingeckId: undefined,
64
64
  displayDecimals: 6,
65
+ priceCheckAmount: 0.0001, // 112000 * 0.0001 = $11.2
66
+ }, {
67
+ name: 'tBTC',
68
+ symbol: 'tBTC',
69
+ logo: 'https://assets.strkfarm.com/integrations/tokens/tbtc.svg',
70
+ address: ContractAddr.from('0x4daa17763b286d1e59b97c283c0b8c949994c361e426a28f743c67bdfe9a32f'),
71
+ decimals: 18,
72
+ coingeckId: undefined,
73
+ displayDecimals: 6,
74
+ priceCheckAmount: 0.0001, // 112000 * 0.0001 = $11.2
65
75
  }]
66
76
  const tokens: TokenInfo[] = defaultTokens;
67
77
 
@@ -69,6 +79,8 @@ const tokens: TokenInfo[] = defaultTokens;
69
79
  * - fatalError: Things to do when a fatal error occurs
70
80
  */
71
81
  export class Global {
82
+ static cache: Record<string, {value: any, ttl: number, timestamp: number}> = {};
83
+
72
84
  static fatalError(message: string, err?: Error) {
73
85
  logger.error(message);
74
86
  console.error(message, err);
@@ -149,4 +161,22 @@ export class Global {
149
161
  }
150
162
  return token;
151
163
  }
164
+
165
+ static setGlobalCache(key: string, data: any, ttl: number = 60000) {
166
+ Global.cache[key] = {
167
+ value: data,
168
+ ttl,
169
+ timestamp: Date.now()
170
+ };
171
+ }
172
+
173
+ static getGlobalCache<T>(key: string): T | null {
174
+ const cached = Global.cache[key];
175
+ if (!cached) return null;
176
+ if (Date.now() - cached.timestamp > cached.ttl) {
177
+ delete Global.cache[key];
178
+ return null;
179
+ }
180
+ return cached.value;
181
+ }
152
182
  }
@@ -33,6 +33,7 @@ export interface TokenInfo {
33
33
  logo: string;
34
34
  coingeckId?: string;
35
35
  displayDecimals: number;
36
+ priceCheckAmount?: number; // for tokens like BTC, doing 1BTC price check may not be ideal, esp on illiquid netwrks like sn
36
37
  }
37
38
 
38
39
  export enum Network {
@@ -105,7 +106,7 @@ export interface IInvestmentFlow {
105
106
  }
106
107
 
107
108
  export function getMainnetConfig(
108
- rpcUrl: string,
109
+ rpcUrl: string = 'https://starknet-mainnet.public.blastapi.io',
109
110
  blockIdentifier: BlockIdentifier = "pending"
110
111
  ): IConfig {
111
112
  return {
@@ -200,3 +201,19 @@ export function highlightTextWithLinks(
200
201
  </>
201
202
  );
202
203
  }
204
+
205
+ export interface VaultPosition {
206
+ amount: Web3Number,
207
+ usdValue: number,
208
+ token: TokenInfo,
209
+ remarks: string
210
+ }
211
+
212
+ const VesuProtocol: IProtocol = {
213
+ name: "Vesu",
214
+ logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
215
+ };
216
+
217
+ export const Protocols = {
218
+ VESU: VesuProtocol,
219
+ }
@@ -149,7 +149,7 @@ export class Pricer extends PricerBase {
149
149
  }
150
150
  case 'Ekubo':
151
151
  try {
152
- const result = await this._getPriceEkubo(token);
152
+ const result = await this._getPriceEkubo(token, new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals));
153
153
  this.methodToUse[token.symbol] = 'Ekubo';
154
154
  return result;
155
155
  } catch (error: any) {
@@ -0,0 +1,219 @@
1
+ import assert from 'assert'
2
+ import {Account, Call, RawArgs, RpcProvider, TransactionExecutionStatus, constants, extractContractHashes, hash, json, num, provider, transaction} from 'starknet'
3
+ import { readFileSync, existsSync, writeFileSync } from 'fs'
4
+ import { IConfig } from '../interfaces';
5
+ import { Store, getDefaultStoreConfig } from '../utils/store';
6
+ import { add } from 'winston';
7
+
8
+ function getContracts() {
9
+ const PATH = './contracts.json'
10
+ if (existsSync(PATH)) {
11
+ return JSON.parse(readFileSync(PATH, {encoding: 'utf-8'}))
12
+ }
13
+ return {}
14
+ }
15
+
16
+ function saveContracts(contracts: any) {
17
+ const PATH = './contracts.json'
18
+ writeFileSync(PATH, JSON.stringify(contracts));
19
+ }
20
+
21
+ function getAccount(
22
+ accountKey: string,
23
+ config: IConfig,
24
+ password = process.env.ACCOUNT_SECURE_PASSWORD,
25
+ accountsFileName = process.env.ACCOUNTS_FILE_NAME || getDefaultStoreConfig(config.network).ACCOUNTS_FILE_NAME,
26
+ secretFileFolder = process.env.SECRET_FILE_FOLDER || getDefaultStoreConfig(config.network).SECRET_FILE_FOLDER
27
+ ) {
28
+ const storeConfig = getDefaultStoreConfig(config.network);
29
+ storeConfig.ACCOUNTS_FILE_NAME = accountsFileName;
30
+ storeConfig.SECRET_FILE_FOLDER = secretFileFolder;
31
+ if (!password) {
32
+ throw new Error(`getAccount: Password is required (either in env as ACCOUNT_SECURE_PASSWORD or input to function)`);
33
+ }
34
+ const store = new Store(config, {
35
+ ...storeConfig,
36
+ PASSWORD: password
37
+ });
38
+
39
+ return store.getAccount(accountKey);
40
+ }
41
+
42
+ async function myDeclare(contract_name: string, package_name: string = 'strkfarm', config: IConfig, acc: Account) {
43
+ const provider = config.provider;
44
+ const compiledSierra = json.parse(
45
+ readFileSync(`./target/release/${package_name}_${contract_name}.contract_class.json`).toString("ascii")
46
+ )
47
+ const compiledCasm = json.parse(
48
+ readFileSync(`./target/release/${package_name}_${contract_name}.compiled_contract_class.json`).toString("ascii")
49
+ )
50
+
51
+ const contracts = getContracts();
52
+ const payload = {
53
+ contract: compiledSierra,
54
+ casm: compiledCasm
55
+ };
56
+
57
+ const result = extractContractHashes(payload);
58
+ console.log("classhash:", result.classHash);
59
+
60
+ try {
61
+ const cls = await provider.getClassByHash(result.classHash);
62
+ console.log(`Class ${result.classHash} already declared`);
63
+ return {
64
+ transaction_hash: '',
65
+ class_hash: result.classHash
66
+ }
67
+ } catch (err) {}
68
+
69
+ const fee = await acc.estimateDeclareFee({
70
+ contract: compiledSierra,
71
+ casm: compiledCasm,
72
+ })
73
+
74
+ const tx = await acc.declareIfNot(payload)
75
+ console.log(`Declaring: ${contract_name}, tx:`, tx.transaction_hash);
76
+ await provider.waitForTransaction(tx.transaction_hash, {
77
+ successStates: [TransactionExecutionStatus.SUCCEEDED]
78
+ })
79
+
80
+ if (!contracts.class_hashes) {
81
+ contracts['class_hashes'] = {};
82
+ }
83
+
84
+ // Todo attach cairo and scarb version. and commit ID
85
+ contracts.class_hashes[contract_name] = tx.class_hash;
86
+ saveContracts(contracts);
87
+ console.log(`Contract declared: ${contract_name}`)
88
+ console.log(`Class hash: ${tx.class_hash}`)
89
+ return tx;
90
+ }
91
+
92
+ async function deployContract(contract_name: string, classHash: string, constructorData: RawArgs, config: IConfig, acc: Account) {
93
+ const provider = config.provider;
94
+
95
+ const fee = await acc.estimateDeployFee({
96
+ classHash,
97
+ constructorCalldata: constructorData,
98
+ })
99
+ console.log("Deploy fee", contract_name, Number(fee.suggestedMaxFee) / 10 ** 18, 'ETH')
100
+
101
+ const tx = await acc.deployContract({
102
+ classHash,
103
+ constructorCalldata: constructorData,
104
+ })
105
+ console.log('Deploy tx: ', tx.transaction_hash);
106
+ await provider.waitForTransaction(tx.transaction_hash, {
107
+ successStates: [TransactionExecutionStatus.SUCCEEDED]
108
+ })
109
+ const contracts = getContracts();
110
+ if (!contracts.contracts) {
111
+ contracts['contracts'] = {};
112
+ }
113
+ contracts.contracts[contract_name] = tx.contract_address;
114
+ saveContracts(contracts);
115
+ console.log(`Contract deployed: ${contract_name}`)
116
+ console.log(`Address: ${tx.contract_address}`)
117
+ return tx;
118
+ }
119
+
120
+ interface DeployContractResult {
121
+ contract_name: string;
122
+ package_name: string;
123
+ class_hash: string;
124
+ call: Call,
125
+ address: string
126
+ }
127
+
128
+ async function prepareMultiDeployContracts(
129
+ contracts: Array<{ contract_name: string, package_name: string, constructorData: RawArgs }>,
130
+ config: IConfig,
131
+ acc: Account
132
+ ) {
133
+ const result: DeployContractResult[] = [];
134
+
135
+ for (const { contract_name, package_name, constructorData } of contracts) {
136
+ const declaredInfo = await myDeclare(contract_name, package_name, config, acc);
137
+ const classHash = declaredInfo.class_hash;
138
+
139
+ const {calls, addresses} = transaction.buildUDCCall({
140
+ classHash,
141
+ constructorCalldata: constructorData,
142
+ }, acc.address);
143
+
144
+ assert(calls.length == 1, `Expected exactly one call, got ${calls.length}`);
145
+ assert(addresses.length == 1, `Expected exactly one address, got ${addresses.length}`);
146
+
147
+ result.push({
148
+ contract_name,
149
+ package_name,
150
+ class_hash: classHash,
151
+ call: calls[0],
152
+ address: addresses[0]
153
+ });
154
+ }
155
+
156
+ return result;
157
+ }
158
+
159
+ async function executeDeployCalls(
160
+ contractsInfo: DeployContractResult[],
161
+ acc: Account,
162
+ provider: RpcProvider
163
+ ) {
164
+ // all must be UDC
165
+ for (let contractInfo of contractsInfo) {
166
+ assert(num.toHexString(contractInfo.call.contractAddress) == num.toHexString(constants.UDC.ADDRESS), 'Must be pointed at UDC address');
167
+ }
168
+
169
+ const allCalls = contractsInfo.map(info => info.call);
170
+ await executeTransactions(allCalls, acc, provider, `Deploying contracts: ${contractsInfo.map(info => info.contract_name).join(', ')}`);
171
+
172
+ // save contracts in storage
173
+ const contracts = getContracts();
174
+ if (!contracts.contracts) {
175
+ contracts['contracts'] = {};
176
+ }
177
+ if (!contracts.class_hashes) {
178
+ contracts['class_hashes'] = {};
179
+ }
180
+
181
+ for (let contractInfo of contractsInfo) {
182
+ // Todo attach cairo and scarb version. and commit ID
183
+ contracts.class_hashes[contractInfo.contract_name] = contractInfo.class_hash;
184
+ contracts.contracts[contractInfo.contract_name] = contractInfo.address;
185
+ console.log(`Contract deployed: ${contractInfo.contract_name}, addr: ${contractInfo.address}`);
186
+ }
187
+ saveContracts(contracts);
188
+ }
189
+
190
+ async function executeTransactions(
191
+ calls: Call[],
192
+ acc: Account,
193
+ provider: RpcProvider,
194
+ remarks?: string, // optional string for logging purposes
195
+ ) {
196
+ const tx = await acc.execute(calls);
197
+ console.log(`Transaction executed: ${tx.transaction_hash}`);
198
+ if (remarks)
199
+ console.log(`Remarks: ${remarks}`);
200
+
201
+
202
+ await provider.waitForTransaction(tx.transaction_hash, {
203
+ successStates: [TransactionExecutionStatus.SUCCEEDED]
204
+ });
205
+ console.log(`Transaction confirmed: ${tx.transaction_hash}`);
206
+
207
+ return tx;
208
+ }
209
+
210
+ const Deployer = {
211
+ getAccount,
212
+ myDeclare,
213
+ deployContract,
214
+ prepareMultiDeployContracts,
215
+ executeDeployCalls,
216
+ executeTransactions
217
+ }
218
+
219
+ export default Deployer;
package/src/node/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './pricer-redis';
2
- export * from '@/node/headless';
2
+ export * from '@/node/headless';
3
+ export { default as Deployer } from './deployer';
@@ -2,4 +2,6 @@ export * from './autoCompounderStrk';
2
2
  export * from './vesu-rebalance';
3
3
  export * from './ekubo-cl-vault';
4
4
  export * from './base-strategy';
5
- export * from './sensei';
5
+ export * from './sensei';
6
+ export * from './universal-adapters';
7
+ export * from './universal-strategy';
@@ -0,0 +1,13 @@
1
+ import { ContractAddr } from "@/dataTypes";
2
+
3
+ export const SIMPLE_SANITIZER = ContractAddr.from('0x11b59e89b35dfceb3e48ec18c01f8ec569592026c275bcb58e22af9f4dedaac');
4
+
5
+ export function toBigInt(value: string | number): bigint {
6
+ if (typeof value === 'string') {
7
+ return BigInt(value);
8
+ } else if (typeof value === 'number') {
9
+ return BigInt(value.toString());
10
+ } else {
11
+ throw new Error('Value must be a string or number');
12
+ }
13
+ }
@@ -0,0 +1,40 @@
1
+ import { Call, hash, num, shortString } from "starknet";
2
+ import { SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
3
+ import { ContractAddr } from "@/dataTypes";
4
+ import { LeafData } from "@/utils";
5
+
6
+ export interface ManageCall {
7
+ sanitizer: ContractAddr,
8
+ call: {
9
+ contractAddress: ContractAddr,
10
+ selector: string,
11
+ calldata: bigint[]
12
+ }
13
+ }
14
+
15
+ export type GenerateCallFn<T> = (params: T) => ManageCall;
16
+ export type AdapterLeafType<T> = {leaf: LeafData, callConstructor: GenerateCallFn<T>}
17
+ export type LeafAdapterFn<T> = () => AdapterLeafType<T>;
18
+
19
+ export class BaseAdapter {
20
+
21
+ protected constructSimpleLeafData(params: {
22
+ id: string,
23
+ target: ContractAddr,
24
+ method: string,
25
+ packedArguments: bigint[]
26
+ }): LeafData {
27
+ const { id, target, method, packedArguments } = params;
28
+ return {
29
+ id: BigInt(num.getDecimalString(shortString.encodeShortString(id))),
30
+ readableId: id,
31
+ data: [
32
+ SIMPLE_SANITIZER.toBigInt(), // sanitizer address
33
+ target.toBigInt(), // contract
34
+ toBigInt(hash.getSelectorFromName(method)), // method name
35
+ BigInt(packedArguments.length),
36
+ ...packedArguments
37
+ ]
38
+ };
39
+ }
40
+ }
@@ -0,0 +1,96 @@
1
+ import { ContractAddr, Web3Number } from "@/dataTypes";
2
+ import { LeafData } from "@/utils";
3
+ import { Call, hash, num, shortString, uint256 } from "starknet";
4
+ import { SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
5
+ import { AdapterLeafType, BaseAdapter, GenerateCallFn, LeafAdapterFn, ManageCall } from "./baseAdapter";
6
+
7
+ export interface FlashloanCallParams {
8
+ amount: Web3Number,
9
+ data: bigint[]
10
+ }
11
+ export interface ApproveCallParams {
12
+ amount: Web3Number,
13
+ }
14
+
15
+ export interface CommonAdapterConfig {
16
+ id: string,
17
+ manager: ContractAddr,
18
+ asset: ContractAddr
19
+ }
20
+
21
+ export class CommonAdapter extends BaseAdapter {
22
+ config: CommonAdapterConfig;
23
+
24
+ constructor(config: CommonAdapterConfig) {
25
+ super();
26
+ this.config = config;
27
+ }
28
+
29
+ getFlashloanAdapter(): AdapterLeafType<FlashloanCallParams> {
30
+ const manageCall = this.getFlashloanCall.bind(this)({amount: Web3Number.fromWei('0', 6), data: []});
31
+ const packedArguments: bigint[] = [
32
+ this.config.manager.toBigInt(), // receiver
33
+ this.config.asset.toBigInt(), // asset
34
+ toBigInt(0), // is legacy false
35
+ ];
36
+ const leaf = this.constructSimpleLeafData({
37
+ id: this.config.id,
38
+ target: manageCall.call.contractAddress,
39
+ method: 'flash_loan',
40
+ packedArguments
41
+ });
42
+ return { leaf, callConstructor: this.getFlashloanCall.bind(this) };
43
+ }
44
+
45
+ getFlashloanCall(params: FlashloanCallParams): ManageCall {
46
+ const uint256Amount = uint256.bnToUint256(params.amount.toWei());
47
+ return {
48
+ sanitizer: SIMPLE_SANITIZER,
49
+ call: {
50
+ contractAddress: this.config.manager,
51
+ selector: hash.getSelectorFromName('flash_loan'),
52
+ calldata: [
53
+ this.config.manager.toBigInt(), // receiver
54
+ this.config.asset.toBigInt(), // asset
55
+ toBigInt(uint256Amount.low.toString()), // amount low
56
+ toBigInt(uint256Amount.high.toString()), // amount high
57
+ toBigInt(0), // is legacy false
58
+ BigInt(params.data.length),
59
+ ...params.data
60
+ ]
61
+ }
62
+ }
63
+ }
64
+
65
+ getApproveAdapter(token: ContractAddr, spender: ContractAddr, id: string): () => AdapterLeafType<ApproveCallParams> {
66
+ return () => ({
67
+ leaf: this.constructSimpleLeafData({
68
+ id: id,
69
+ target: token,
70
+ method: 'approve',
71
+ packedArguments: [
72
+ spender.toBigInt(), // spender
73
+ ]
74
+ }),
75
+ callConstructor: this.getApproveCall(token, spender).bind(this)
76
+ });
77
+ }
78
+
79
+ getApproveCall(token: ContractAddr, spender: ContractAddr) {
80
+ return (params: ApproveCallParams) => {
81
+ const uint256Amount = uint256.bnToUint256(params.amount.toWei());
82
+ return {
83
+ sanitizer: SIMPLE_SANITIZER,
84
+ call: {
85
+ contractAddress: token,
86
+ selector: hash.getSelectorFromName('approve'),
87
+ calldata: [
88
+ spender.toBigInt(), // spender
89
+ toBigInt(uint256Amount.low.toString()), // amount low
90
+ toBigInt(uint256Amount.high.toString()), // amount high
91
+ ]
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./baseAdapter";
2
+ export * from "./common-adapter";
3
+ export * from "./vesu-adapter";