@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/dist/index.d.mts +14 -10
- package/dist/index.d.ts +14 -10
- package/dist/index.js +17 -17
- package/dist/index.mjs +4 -4
- package/package.json +3 -2
- package/src/builders/coreBuilder.ts +18 -24
- package/src/builders/oracles/pyth.ts +93 -9
- package/src/constants/testAddress.ts +1 -4
- package/src/models/scallopBuilder.ts +10 -8
- package/src/models/scallopUtils.ts +4 -2
- package/src/types/builder/core.ts +4 -2
- package/src/types/builder/index.ts +1 -10
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.
|
|
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.
|
|
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 (
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
101
|
+
async selectCoin(
|
|
102
102
|
txBlock: ScallopTxBlock | SuiKitTxBlock,
|
|
103
|
-
assetCoinName:
|
|
103
|
+
assetCoinName: string,
|
|
104
104
|
amount: number,
|
|
105
|
-
sender: string = this.walletAddress
|
|
106
|
-
|
|
107
|
-
|
|
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 }
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
};
|