@scallop-io/sui-scallop-sdk 2.2.3-pyth-sponsored-transaction-alpha.1 → 2.2.3-pyth-sponsored-transaction-alpha.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scallop-io/sui-scallop-sdk",
3
- "version": "2.2.3-pyth-sponsored-transaction-alpha.1",
3
+ "version": "2.2.3-pyth-sponsored-transaction-alpha.3",
4
4
  "description": "Typescript sdk for interacting with Scallop contract on SUI",
5
5
  "keywords": [
6
6
  "sui",
@@ -39,7 +39,7 @@
39
39
  ],
40
40
  "dependencies": {
41
41
  "@mysten/sui": "1.28.2",
42
- "@pythnetwork/pyth-sui-js": "2.1.0",
42
+ "@pythnetwork/pyth-sui-js": "2.2.0",
43
43
  "@scallop-io/sui-kit": "1.4.1",
44
44
  "@tanstack/query-core": "5.59.16",
45
45
  "axios": "^1.9.0",
@@ -146,6 +146,7 @@
146
146
  "scripts": {
147
147
  "clean": "rm -rf tsconfig.tsbuildinfo ./dist",
148
148
  "build": "pnpm run build:tsup",
149
+ "build:sourcemap": "tsup ./src/index.ts --format esm,cjs --sourcemap --dts",
149
150
  "build:tsup": "tsup ./src/index.ts --format esm,cjs --splitting --minify --treeshake --dts",
150
151
  "watch:tsup": "tsup ./src/index.ts --format esm,cjs --clean --splitting --watch",
151
152
  "watch:types": "tsc --watch",
@@ -313,7 +313,8 @@ const generateCoreQuickMethod: GenerateCoreQuickMethod = ({
313
313
  amount,
314
314
  collateralCoinName,
315
315
  obligationId,
316
- obligationKey
316
+ obligationKey,
317
+ isSponsoredTx = false
317
318
  ) => {
318
319
  const obligationInfo = await requireObligationInfo(
319
320
  builder,
@@ -324,7 +325,7 @@ const generateCoreQuickMethod: GenerateCoreQuickMethod = ({
324
325
  const updateCoinNames = await builder.utils.getObligationCoinNames(
325
326
  obligationInfo.obligationId
326
327
  );
327
- await updateOracles(builder, txBlock, updateCoinNames);
328
+ await updateOracles(builder, txBlock, updateCoinNames, { isSponsoredTx });
328
329
  return txBlock.takeCollateral(
329
330
  obligationInfo.obligationId,
330
331
  obligationInfo.obligationKey as SuiObjectArg,
@@ -438,7 +439,12 @@ const generateCoreQuickMethod: GenerateCoreQuickMethod = ({
438
439
  poolCoinName
439
440
  );
440
441
  },
441
- repayQuick: async (amount, poolCoinName, obligationId) => {
442
+ repayQuick: async (
443
+ amount,
444
+ poolCoinName,
445
+ obligationId,
446
+ isSponsoredTx = false
447
+ ) => {
442
448
  const sender = requireSender(txBlock);
443
449
  const obligationInfo = await requireObligationInfo(
444
450
  builder,
@@ -446,27 +452,15 @@ const generateCoreQuickMethod: GenerateCoreQuickMethod = ({
446
452
  obligationId
447
453
  );
448
454
 
449
- if (poolCoinName === 'sui') {
450
- const [suiCoin] = txBlock.splitSUIFromGas([amount]);
451
- return txBlock.repay(
452
- obligationInfo.obligationId,
453
- suiCoin,
454
- poolCoinName
455
- );
456
- } else {
457
- const { leftCoin, takeCoin } = await builder.selectCoin(
458
- txBlock,
459
- poolCoinName,
460
- amount,
461
- sender
462
- );
463
- txBlock.transferObjects([leftCoin], sender);
464
- return txBlock.repay(
465
- obligationInfo.obligationId,
466
- takeCoin,
467
- poolCoinName
468
- );
469
- }
455
+ const { leftCoin, takeCoin } = await builder.selectCoin(
456
+ txBlock,
457
+ poolCoinName,
458
+ amount,
459
+ sender,
460
+ isSponsoredTx
461
+ );
462
+ if (leftCoin) txBlock.transferObjects([leftCoin], sender);
463
+ return txBlock.repay(obligationInfo.obligationId, takeCoin, poolCoinName);
470
464
  },
471
465
  updateAssetPricesQuick: async (assetCoinNames) => {
472
466
  return await updateOracles(builder, txBlock, assetCoinNames);
@@ -1,9 +1,82 @@
1
1
  import {
2
+ HexString,
2
3
  SuiPriceServiceConnection,
3
4
  SuiPythClient,
4
5
  } from '@pythnetwork/pyth-sui-js';
5
6
  import { ScallopBuilder } from 'src/models';
6
- import type { SuiTxBlock as SuiKitTxBlock } from '@scallop-io/sui-kit';
7
+ import {
8
+ SUI_CLOCK_OBJECT_ID,
9
+ type SuiTxBlock as SuiKitTxBlock,
10
+ type Transaction,
11
+ } from '@scallop-io/sui-kit';
12
+ import { SuiClient } from '@mysten/sui/client';
13
+
14
+ type ObjectId = string;
15
+ class ScallopPythClient extends SuiPythClient {
16
+ constructor(
17
+ provider: SuiClient,
18
+ pythStateId: ObjectId,
19
+ wormholeStateId: ObjectId,
20
+ private params: {
21
+ // addressId?: string;
22
+ defaultPackageId: ObjectId;
23
+ gasStationId: ObjectId;
24
+ }
25
+ ) {
26
+ super(provider, pythStateId, wormholeStateId);
27
+ }
28
+
29
+ async updatePriceFeedsWithSponsoredBaseUpdateFee(
30
+ tx: Transaction,
31
+ updates: Buffer[],
32
+ feedIds: HexString[]
33
+ ) {
34
+ if (!this.params) throw new Error('Please provide params');
35
+ const { defaultPackageId: scallopSponsorPackage, gasStationId } =
36
+ this.params;
37
+ const packageId = await this.getPythPackageId();
38
+ let priceUpdatesHotPotato = await this.verifyVaasAndGetHotPotato(
39
+ tx,
40
+ updates,
41
+ packageId
42
+ );
43
+
44
+ const priceInfoObjects = [];
45
+ for (const feedId of feedIds) {
46
+ const priceInfoObjectId = await this.getPriceFeedObjectId(feedId);
47
+ if (!priceInfoObjectId) {
48
+ throw new Error(`Price feed object not found for ID: ${feedId}`);
49
+ }
50
+ priceInfoObjects.push(priceInfoObjectId);
51
+ }
52
+
53
+ const clockObjectRef = tx.sharedObjectRef({
54
+ objectId: SUI_CLOCK_OBJECT_ID,
55
+ mutable: false,
56
+ initialSharedVersion: '1',
57
+ });
58
+
59
+ for (let i = 0; i < priceInfoObjects.length; i++) {
60
+ const priceInfoObjectId = priceInfoObjects[i];
61
+ [priceUpdatesHotPotato] = tx.moveCall({
62
+ target: `${scallopSponsorPackage}::pyth_sponsor::update_single_price_feed_with_sponsor`,
63
+ arguments: [
64
+ tx.object(this.pythStateId),
65
+ priceUpdatesHotPotato,
66
+ tx.object(priceInfoObjectId),
67
+ tx.object(gasStationId),
68
+ clockObjectRef,
69
+ ],
70
+ });
71
+ }
72
+
73
+ tx.moveCall({
74
+ target: `${packageId}::hot_potato_vector::destroy`,
75
+ arguments: [priceUpdatesHotPotato],
76
+ typeArguments: [`${packageId}::price_info::PriceInfo`],
77
+ });
78
+ }
79
+ }
7
80
 
8
81
  export const updatePythPriceFeeds = async (
9
82
  builder: ScallopBuilder,
@@ -11,7 +84,7 @@ export const updatePythPriceFeeds = async (
11
84
  txBlock: SuiKitTxBlock,
12
85
  isSponsoredTx: boolean = false
13
86
  ) => {
14
- const pythClient = new SuiPythClient(
87
+ const pythClient = new ScallopPythClient(
15
88
  builder.suiKit.client,
16
89
  builder.address.get('core.oracles.pyth.state'),
17
90
  builder.address.get('core.oracles.pyth.wormholeState'),
@@ -30,19 +103,30 @@ export const updatePythPriceFeeds = async (
30
103
  const endpoints = builder.utils.pythEndpoints ?? [
31
104
  ...builder.constants.whitelist.pythEndpoints,
32
105
  ];
106
+
107
+ // get feed object ids
33
108
  for (const endpoint of endpoints) {
34
109
  try {
35
110
  const pythConnection = new SuiPriceServiceConnection(endpoint);
36
111
  const priceUpdateData =
37
112
  await pythConnection.getPriceFeedsUpdateData(priceIds);
38
- await pythClient.updatePriceFeeds(
39
- txBlock.txBlock,
40
- priceUpdateData,
41
- priceIds,
42
- isSponsoredTx
43
- );
44
113
 
45
- break;
114
+ if (isSponsoredTx) {
115
+ // Use gas station to sponsor the baseFeeUpdate
116
+ await pythClient.updatePriceFeedsWithSponsoredBaseUpdateFee(
117
+ txBlock.txBlock,
118
+ priceUpdateData,
119
+ priceIds
120
+ );
121
+ } else {
122
+ await pythClient.updatePriceFeeds(
123
+ txBlock.txBlock,
124
+ priceUpdateData,
125
+ priceIds
126
+ );
127
+ }
128
+
129
+ return;
46
130
  } catch (e) {
47
131
  console.warn(
48
132
  `Failed to update price feeds with endpoint ${endpoint}: ${e}`
@@ -534,10 +534,7 @@ export const WHITELIST = {
534
534
  suiBridge: new Set(['sbeth', 'sbusdt', 'sbwbtc']),
535
535
  wormhole: new Set(['wusdc', 'wusdt', 'weth', 'wbtc', 'wapt', 'wsol']),
536
536
  oracles: new Set(['pyth', 'supra', 'switchboard']),
537
- pythEndpoints: new Set([
538
- 'https://hermes.pyth.network',
539
- 'https://scallop.rpc.p2p.world',
540
- ]),
537
+ pythEndpoints: new Set(['https://hermes.pyth.network']),
541
538
  deprecated: new Set(['wapt', 'wusdc', 'wusdt', 'weth', 'wbtc']),
542
539
  borrowIncentiveRewards: new Set(['mpoints']),
543
540
  rewardsAsPoint: new Set(['mpoints']),
@@ -12,7 +12,7 @@ import type {
12
12
  SuiTxArg,
13
13
  SuiVecTxArg,
14
14
  } from '@scallop-io/sui-kit';
15
- import type { ScallopTxBlock, SelectCoinReturnType } from '../types';
15
+ import type { ScallopTxBlock } from '../types';
16
16
  import { ScallopBuilderInterface } from './interface';
17
17
 
18
18
  export type ScallopBuilderParams = {
@@ -98,20 +98,22 @@ class ScallopBuilder implements ScallopBuilderInterface {
98
98
  * @param sender - Sender address.
99
99
  * @return Take coin and left coin.
100
100
  */
101
- async selectCoin<T extends string>(
101
+ async selectCoin(
102
102
  txBlock: ScallopTxBlock | SuiKitTxBlock,
103
- assetCoinName: T,
103
+ assetCoinName: string,
104
104
  amount: number,
105
- sender: string = this.walletAddress
106
- ): Promise<SelectCoinReturnType<T>> {
107
- if (assetCoinName === 'sui') {
105
+ sender: string = this.walletAddress,
106
+ isSponsored: boolean = false
107
+ ) {
108
+ if (assetCoinName === 'sui' && !isSponsored) {
108
109
  const [takeCoin] = txBlock.splitSUIFromGas([amount]);
109
- return { takeCoin } as SelectCoinReturnType<T>;
110
+ return { takeCoin };
110
111
  } else {
111
112
  const coinType = this.utils.parseCoinType(assetCoinName);
112
113
  const coins = await this.utils.selectCoins(amount, coinType, sender);
113
114
  const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(coins, amount);
114
- return { takeCoin, leftCoin } as SelectCoinReturnType<T>;
115
+
116
+ return { takeCoin, leftCoin };
115
117
  }
116
118
  }
117
119
 
@@ -31,6 +31,7 @@ class ScallopUtils implements ScallopUtilsInterface {
31
31
  public pythEndpoints: string[];
32
32
  public readonly scallopSuiKit: ScallopSuiKit;
33
33
  public readonly constants: ScallopConstants;
34
+ public readonly timeout: number;
34
35
 
35
36
  constructor(params: ScallopUtilsParams = {}) {
36
37
  this.constants = params.scallopConstants ?? new ScallopConstants(params);
@@ -43,8 +44,9 @@ class ScallopUtils implements ScallopUtilsInterface {
43
44
 
44
45
  this.pythEndpoints = params.pythEndpoints ?? [
45
46
  'https://hermes.pyth.network',
46
- 'https://scallop.rpc.p2p.world',
47
47
  ];
48
+
49
+ this.timeout = params.axiosTimeout ?? 4000;
48
50
  }
49
51
 
50
52
  get walletAddress() {
@@ -539,7 +541,7 @@ class ScallopUtils implements ScallopUtilsInterface {
539
541
 
540
542
  const priceIds = priceIdPairs.map(([_, priceId]) => priceId);
541
543
  const pythConnection = new SuiPriceServiceConnection(endpoint, {
542
- timeout: 4000,
544
+ timeout: this.timeout,
543
545
  });
544
546
 
545
547
  try {
@@ -93,7 +93,8 @@ export type CoreQuickMethods = {
93
93
  amount: number,
94
94
  collateralCoinName: string,
95
95
  obligationId?: SuiObjectArg,
96
- obligationKey?: SuiObjectArg
96
+ obligationKey?: SuiObjectArg,
97
+ isSponsoredTx?: boolean
97
98
  ) => Promise<TransactionResult>;
98
99
  borrowQuick: (
99
100
  amount: number,
@@ -122,7 +123,8 @@ export type CoreQuickMethods = {
122
123
  repayQuick: (
123
124
  amount: number,
124
125
  poolCoinName: string,
125
- obligationId?: SuiObjectArg
126
+ obligationId?: SuiObjectArg,
127
+ isSponsoredTx?: boolean
126
128
  ) => Promise<void>;
127
129
  updateAssetPricesQuick: (assetCoinNames?: string[]) => Promise<void>;
128
130
  };
@@ -1,4 +1,4 @@
1
- import type { CoreTxBlock, NestedResult } from './core';
1
+ import type { CoreTxBlock } from './core';
2
2
  import type { SpoolTxBlock } from './spool';
3
3
  import type { BorrowIncentiveTxBlock } from './borrowIncentive';
4
4
  import type { VeScaTxBlock } from './vesca';
@@ -21,12 +21,3 @@ export type BaseScallopTxBlock = ReferralTxBlock &
21
21
  export type SuiTxBlockWithSCoin = BaseScallopTxBlock & SCoinTxBlock;
22
22
  export type SuiTxBlockWithSpool = SuiTxBlockWithSCoin & SpoolTxBlock;
23
23
  export type ScallopTxBlock = SuiTxBlockWithSpool & CoreTxBlock;
24
-
25
- export type SelectCoinReturnType<T extends string> = T extends 'sui'
26
- ? {
27
- takeCoin: NestedResult;
28
- }
29
- : {
30
- takeCoin: NestedResult;
31
- leftCoin: NestedResult;
32
- };