@suilend/sui-core 0.1.0

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.
@@ -0,0 +1,34 @@
1
+ import BigNumber from "bignumber.js";
2
+ export declare const API_URL = "https://d10td5ybgrf39v.cloudfront.net";
3
+ export declare const TOAST_DURATION_MS: number;
4
+ export declare const TX_TOAST_DURATION: number;
5
+ export declare const SUI_GAS_MIN = 1;
6
+ export declare const MAX_U64: BigNumber;
7
+ export declare const MS_PER_YEAR = 31556952000;
8
+ export declare enum RpcId {
9
+ TRITON_ONE = "tritonOne",
10
+ FULL_NODE = "fullNode",
11
+ ALL_THAT_NODE = "allThatNode",
12
+ CUSTOM = "custom"
13
+ }
14
+ export type Rpc = {
15
+ id: RpcId;
16
+ name: string;
17
+ url: string;
18
+ };
19
+ export declare const RPC_GRAPHQL_URL = "https://graphql.mainnet.sui.io/graphql";
20
+ export declare const RPCS: Rpc[];
21
+ export declare enum ExplorerId {
22
+ SUI_SCAN = "suiScan",
23
+ SUI_VISION = "suiVision"
24
+ }
25
+ export type Explorer = {
26
+ id: ExplorerId;
27
+ name: string;
28
+ buildAddressUrl: (address: string) => string;
29
+ buildObjectUrl: (id: string) => string;
30
+ buildPackageUrl: (id: string) => string;
31
+ buildCoinUrl: (coinType: string) => string;
32
+ buildTxUrl: (digest: string) => string;
33
+ };
34
+ export declare const EXPLORERS: Explorer[];
@@ -0,0 +1,63 @@
1
+ var _a;
2
+ import BigNumber from "bignumber.js";
3
+ export const API_URL = "https://d10td5ybgrf39v.cloudfront.net";
4
+ export const TOAST_DURATION_MS = 5 * 1000;
5
+ export const TX_TOAST_DURATION = 10 * 1000;
6
+ export const SUI_GAS_MIN = 1;
7
+ export const MAX_U64 = new BigNumber(2).pow(64).minus(1);
8
+ export const MS_PER_YEAR = 31556952000; // Approx. 1000 * 60 * 60 * 24 * 365;
9
+ export var RpcId;
10
+ (function (RpcId) {
11
+ RpcId["TRITON_ONE"] = "tritonOne";
12
+ RpcId["FULL_NODE"] = "fullNode";
13
+ RpcId["ALL_THAT_NODE"] = "allThatNode";
14
+ RpcId["CUSTOM"] = "custom";
15
+ })(RpcId || (RpcId = {}));
16
+ export const RPC_GRAPHQL_URL = "https://graphql.mainnet.sui.io/graphql";
17
+ export const RPCS = [
18
+ {
19
+ id: RpcId.TRITON_ONE,
20
+ name: "Triton One",
21
+ url: `https://solendf-suishar-0c55.mainnet.sui.rpcpool.com/${(_a = process.env.NEXT_PUBLIC_SUI_TRITON_ONE_DEV_API_KEY) !== null && _a !== void 0 ? _a : ""}`,
22
+ },
23
+ {
24
+ id: RpcId.FULL_NODE,
25
+ name: "Full Node",
26
+ url: "https://fullnode.mainnet.sui.io:443",
27
+ },
28
+ {
29
+ id: RpcId.ALL_THAT_NODE,
30
+ name: "All That Node",
31
+ url: "https://sui-mainnet-rpc.allthatnode.com",
32
+ },
33
+ {
34
+ id: RpcId.CUSTOM,
35
+ name: "Custom",
36
+ url: "",
37
+ },
38
+ ];
39
+ export var ExplorerId;
40
+ (function (ExplorerId) {
41
+ ExplorerId["SUI_SCAN"] = "suiScan";
42
+ ExplorerId["SUI_VISION"] = "suiVision";
43
+ })(ExplorerId || (ExplorerId = {}));
44
+ export const EXPLORERS = [
45
+ {
46
+ id: ExplorerId.SUI_SCAN,
47
+ name: "Suiscan",
48
+ buildAddressUrl: (address) => `https://suiscan.xyz/mainnet/account/${address}`,
49
+ buildObjectUrl: (id) => `https://suiscan.xyz/mainnet/object/${id}`,
50
+ buildPackageUrl: (id) => `https://suiscan.xyz/mainnet/object/${id}`,
51
+ buildCoinUrl: (coinType) => `https://suiscan.xyz/mainnet/coin/${coinType}`,
52
+ buildTxUrl: (digest) => `https://suiscan.xyz/mainnet/tx/${digest}`,
53
+ },
54
+ {
55
+ id: ExplorerId.SUI_VISION,
56
+ name: "SuiVision",
57
+ buildAddressUrl: (address) => `https://suivision.xyz/account/${address}`,
58
+ buildObjectUrl: (id) => `https://suivision.xyz/object/${id}`,
59
+ buildPackageUrl: (id) => `https://suivision.xyz/package/${id}`,
60
+ buildCoinUrl: (coinType) => `https://suivision.xyz/coin/${coinType}`,
61
+ buildTxUrl: (digest) => `https://suivision.xyz/txblock/${digest}`,
62
+ },
63
+ ];
@@ -0,0 +1,41 @@
1
+ import BigNumber from "bignumber.js";
2
+ export declare const replace0x: (value: string) => string;
3
+ export declare const formatAddress: (value: string, length?: number) => string;
4
+ export declare const formatId: (value: string, length?: number) => string;
5
+ export declare const formatType: (value: string, length?: number) => string;
6
+ export declare const formatList: (array: string[]) => string;
7
+ export declare const formatInteger: (value: number, useGrouping?: boolean) => string;
8
+ export declare const formatRank: (rank: number) => string;
9
+ export declare const formatPercent: (value: BigNumber, options?: {
10
+ dp?: number;
11
+ useAccountingSign?: boolean;
12
+ }) => string;
13
+ export declare const formatLtvPercent: (value: BigNumber) => string;
14
+ export declare const formatBorrowWeight: (valueBps: BigNumber) => string;
15
+ export declare const formatDuration: (seconds: BigNumber) => string;
16
+ export declare const formatNumber: (value: BigNumber, options?: {
17
+ prefix?: string;
18
+ dp?: number;
19
+ minDp?: number;
20
+ roundingMode?: BigNumber.RoundingMode;
21
+ exact?: boolean;
22
+ useGrouping?: boolean;
23
+ trimTrailingZeros?: boolean;
24
+ roundLtMinToZero?: boolean;
25
+ }) => string;
26
+ export declare const formatUsd: (value: BigNumber, options?: {
27
+ dp?: number;
28
+ exact?: boolean;
29
+ }) => string;
30
+ export declare const formatPrice: (value: BigNumber) => string;
31
+ export declare const formatPoints: (value: BigNumber, options?: {
32
+ dp?: number;
33
+ }) => string;
34
+ export declare const formatToken: (value: BigNumber, options?: {
35
+ prefix?: string;
36
+ dp?: number;
37
+ exact?: boolean;
38
+ useGrouping?: boolean;
39
+ trimTrailingZeros?: boolean;
40
+ roundLtMinToZero?: boolean;
41
+ }) => string;
package/lib/format.js ADDED
@@ -0,0 +1,205 @@
1
+ import BigNumber from "bignumber.js";
2
+ import { MAX_U64 } from "./constants.js";
3
+ const LOCALE = "en-US";
4
+ const shorten = (value, start, end) => {
5
+ return value.length > start + end
6
+ ? `${value.slice(0, start)}…${value.slice(-end)}`
7
+ : value;
8
+ };
9
+ export const replace0x = (value) => value.replace("0x", "0×");
10
+ export const formatAddress = (value, length = 4) => {
11
+ if (length === 0)
12
+ return replace0x(value);
13
+ return shorten(replace0x(value), length + (value.startsWith("0x") ? 2 : 0), length);
14
+ };
15
+ export const formatId = (value, length = 4) => {
16
+ return shorten(replace0x(value), length + (value.startsWith("0x") ? 2 : 0), length);
17
+ };
18
+ export const formatType = (value, length = 4) => {
19
+ const [id, module, type] = value.split("::");
20
+ return [
21
+ shorten(replace0x(id), length + (id.startsWith("0x") ? 2 : 0), length),
22
+ module,
23
+ type,
24
+ ].join("::");
25
+ };
26
+ export const formatList = (array) => {
27
+ if (array.length === 1)
28
+ return array[0];
29
+ if (array.length === 2)
30
+ return array.join(" and ");
31
+ return `${array.slice(0, -1).join(", ")}, and ${array.slice(-1)}`;
32
+ };
33
+ export const formatInteger = (value, useGrouping) => Intl.NumberFormat(LOCALE, { useGrouping }).format(value);
34
+ export const formatRank = (rank) => `#${formatInteger(rank)}`;
35
+ export const formatPercent = (value, options) => {
36
+ var _a, _b;
37
+ const dp = (_a = options === null || options === void 0 ? void 0 : options.dp) !== null && _a !== void 0 ? _a : 2;
38
+ const useAccountingSign = (_b = options === null || options === void 0 ? void 0 : options.useAccountingSign) !== null && _b !== void 0 ? _b : false;
39
+ const formattedValue = Intl.NumberFormat(LOCALE, {
40
+ style: "percent",
41
+ minimumFractionDigits: dp,
42
+ maximumFractionDigits: dp,
43
+ }).format(+value.div(100));
44
+ return !useAccountingSign || value.gte(0)
45
+ ? formattedValue
46
+ : `(${formattedValue[0] === "-" ? formattedValue.slice(1) : formattedValue})`;
47
+ };
48
+ export const formatLtvPercent = (value) => formatPercent(value, { dp: 0 });
49
+ export const formatBorrowWeight = (valueBps) => {
50
+ if (valueBps.eq(MAX_U64))
51
+ return "∞";
52
+ const [integers, decimals] = valueBps.div(10000).toFixed(1).split(".");
53
+ const integersFormatted = formatInteger(parseInt(integers));
54
+ const decimalsFormatted = ![undefined, "0"].includes(decimals)
55
+ ? `.${decimals}`
56
+ : "";
57
+ return `${integersFormatted}${decimalsFormatted}`;
58
+ };
59
+ export const formatDuration = (seconds) => {
60
+ if (seconds.lt(1))
61
+ return "<1s";
62
+ if (seconds.lt(60))
63
+ return `${seconds}s`;
64
+ if (seconds.lt(60 * 60)) {
65
+ const m = seconds.div(60).integerValue(BigNumber.ROUND_DOWN);
66
+ const s = new BigNumber(seconds.minus(m.times(60))).div(1);
67
+ return `${m.toFixed(0)}m ${s.toFixed(0)}s`;
68
+ }
69
+ if (seconds.lt(60 * 60 * 24)) {
70
+ const h = seconds.div(60 * 60).integerValue(BigNumber.ROUND_DOWN);
71
+ const m = new BigNumber(seconds.minus(h.times(60 * 60))).div(60);
72
+ return `${h.toFixed(0)}h ${m.toFixed(0)}m`;
73
+ }
74
+ const d = seconds.div(60 * 60 * 24).integerValue(BigNumber.ROUND_DOWN);
75
+ const h = new BigNumber(seconds.minus(d.times(60 * 60 * 24))).div(60 * 60);
76
+ return `${d.toFixed(0)}d ${h.toFixed(0)}h`;
77
+ };
78
+ export const formatNumber = (value, options) => {
79
+ var _a, _b, _c, _d, _e, _f, _g, _h;
80
+ const prefix = (_a = options === null || options === void 0 ? void 0 : options.prefix) !== null && _a !== void 0 ? _a : "";
81
+ const dp = (_b = options === null || options === void 0 ? void 0 : options.dp) !== null && _b !== void 0 ? _b : 2;
82
+ let minDp = (_c = options === null || options === void 0 ? void 0 : options.minDp) !== null && _c !== void 0 ? _c : 0;
83
+ const roundingMode = (_d = options === null || options === void 0 ? void 0 : options.roundingMode) !== null && _d !== void 0 ? _d : BigNumber.ROUND_HALF_UP;
84
+ const exact = (_e = options === null || options === void 0 ? void 0 : options.exact) !== null && _e !== void 0 ? _e : false;
85
+ const useGrouping = (_f = options === null || options === void 0 ? void 0 : options.useGrouping) !== null && _f !== void 0 ? _f : false;
86
+ const trimTrailingZeros = (_g = options === null || options === void 0 ? void 0 : options.trimTrailingZeros) !== null && _g !== void 0 ? _g : false;
87
+ const roundLtMinToZero = (_h = options === null || options === void 0 ? void 0 : options.roundLtMinToZero) !== null && _h !== void 0 ? _h : false;
88
+ // 0
89
+ if (value.eq(0))
90
+ return `${prefix}${trimTrailingZeros ? "0" : new BigNumber(0).toFixed(dp)}`;
91
+ // 0 < value < minValue
92
+ const minValue = new BigNumber(10).pow(-dp);
93
+ if (value.lt(minValue)) {
94
+ if (!roundLtMinToZero)
95
+ return `<${prefix}${minValue.toFixed(dp)}`;
96
+ return `${prefix}${trimTrailingZeros ? "0" : new BigNumber(0).toFixed(dp)}`;
97
+ }
98
+ if (!exact) {
99
+ let _value = value;
100
+ let suffix = "";
101
+ if (_value.lt(1000 * 10)) {
102
+ }
103
+ else if (_value.lt(1000 ** 2)) {
104
+ _value = _value.div(1000 ** 1);
105
+ suffix = "K";
106
+ minDp = 0;
107
+ }
108
+ else if (_value.lt(1000 ** 3)) {
109
+ _value = _value.div(1000 ** 2);
110
+ suffix = "M";
111
+ minDp = 0;
112
+ }
113
+ else if (_value.lt(1000 ** 4)) {
114
+ _value = _value.div(1000 ** 3);
115
+ suffix = "B";
116
+ minDp = 0;
117
+ }
118
+ else {
119
+ _value = _value.div(1000 ** 4);
120
+ suffix = "T";
121
+ minDp = 0;
122
+ }
123
+ const maxDigits = 3;
124
+ const digitsCount = _value
125
+ .integerValue(BigNumber.ROUND_DOWN)
126
+ .toString().length; // 1 <= _value < 1000, so digitsCount is in {1,2,3}
127
+ const newDp = Math.max(minDp, Math.min(dp, maxDigits - digitsCount));
128
+ let [integers, decimals] = _value.toFixed(newDp, roundingMode).split(".");
129
+ if (integers.length > digitsCount) {
130
+ // Rounded up from 999.9xx to 1000
131
+ [integers, decimals] = _value
132
+ .toFixed(newDp, BigNumber.ROUND_DOWN)
133
+ .split(".");
134
+ }
135
+ const integersFormatted = formatInteger(parseInt(integers), useGrouping);
136
+ const decimalsFormatted = decimals !== undefined ? `.${decimals}` : "";
137
+ return `${prefix}${integersFormatted}${decimalsFormatted}${suffix}`;
138
+ }
139
+ else {
140
+ const [integers, decimals] = value.toFixed(dp, roundingMode).split(".");
141
+ const integersFormatted = formatInteger(integers !== "" ? parseInt(integers) : 0, useGrouping);
142
+ let decimalsFormatted = decimals;
143
+ if (trimTrailingZeros && decimalsFormatted !== undefined) {
144
+ while (decimalsFormatted[decimalsFormatted.length - 1] === "0")
145
+ decimalsFormatted = decimalsFormatted.slice(0, -1);
146
+ if (decimalsFormatted.length === 0)
147
+ decimalsFormatted = undefined;
148
+ }
149
+ decimalsFormatted =
150
+ decimalsFormatted !== undefined ? `.${decimalsFormatted}` : "";
151
+ return `${prefix}${integersFormatted}${decimalsFormatted}`;
152
+ }
153
+ };
154
+ export const formatUsd = (value, options) => {
155
+ var _a, _b;
156
+ const dp = (_a = options === null || options === void 0 ? void 0 : options.dp) !== null && _a !== void 0 ? _a : 2;
157
+ const minDp = dp;
158
+ const exact = (_b = options === null || options === void 0 ? void 0 : options.exact) !== null && _b !== void 0 ? _b : false;
159
+ return formatNumber(value, {
160
+ prefix: "$",
161
+ dp,
162
+ minDp,
163
+ roundingMode: BigNumber.ROUND_HALF_UP,
164
+ exact,
165
+ useGrouping: true,
166
+ });
167
+ };
168
+ export const formatPrice = (value) => {
169
+ if (value.eq(0))
170
+ return "$0.00";
171
+ return formatNumber(value, {
172
+ prefix: "$",
173
+ dp: Math.max(0, -Math.floor(Math.log10(+value)) - 1) + (value.lt(1) ? 4 : 2), // 4sf (if <1) or 2sf (if >=1)
174
+ roundingMode: BigNumber.ROUND_HALF_UP,
175
+ exact: true,
176
+ useGrouping: true,
177
+ });
178
+ };
179
+ export const formatPoints = (value, options) => {
180
+ var _a;
181
+ const dp = (_a = options === null || options === void 0 ? void 0 : options.dp) !== null && _a !== void 0 ? _a : 0;
182
+ return formatNumber(value, {
183
+ dp,
184
+ roundingMode: BigNumber.ROUND_HALF_UP,
185
+ exact: true,
186
+ useGrouping: true,
187
+ });
188
+ };
189
+ export const formatToken = (value, options) => {
190
+ var _a, _b, _c, _d, _e;
191
+ const dp = (_a = options === null || options === void 0 ? void 0 : options.dp) !== null && _a !== void 0 ? _a : 2;
192
+ const exact = (_b = options === null || options === void 0 ? void 0 : options.exact) !== null && _b !== void 0 ? _b : true;
193
+ const useGrouping = (_c = options === null || options === void 0 ? void 0 : options.useGrouping) !== null && _c !== void 0 ? _c : true;
194
+ const trimTrailingZeros = (_d = options === null || options === void 0 ? void 0 : options.trimTrailingZeros) !== null && _d !== void 0 ? _d : false;
195
+ const roundLtMinToZero = (_e = options === null || options === void 0 ? void 0 : options.roundLtMinToZero) !== null && _e !== void 0 ? _e : false;
196
+ return formatNumber(value, {
197
+ prefix: options === null || options === void 0 ? void 0 : options.prefix,
198
+ dp,
199
+ roundingMode: BigNumber.ROUND_DOWN,
200
+ exact,
201
+ useGrouping,
202
+ trimTrailingZeros,
203
+ roundLtMinToZero,
204
+ });
205
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./api";
2
+ export * from "./coinMetadata";
3
+ export * from "./coinType";
4
+ export * from "./constants";
5
+ export * from "./format";
6
+ export * from "./indexedDB";
7
+ export * from "./transactions";
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./api.js";
2
+ export * from "./coinMetadata.js";
3
+ export * from "./coinType.js";
4
+ export * from "./constants.js";
5
+ export * from "./format.js";
6
+ export * from "./indexedDB.js";
7
+ export * from "./transactions.js";
@@ -0,0 +1,4 @@
1
+ import { Token } from "./coinMetadata";
2
+ export declare const getTokenMapByCoinTypeFromIndexedDB: (coinTypes: string[]) => Promise<Record<string, Token>>;
3
+ export declare const setTokenMapToIndexedDB: (tokenMap: Record<string, Token>) => Promise<void>;
4
+ export declare const removeTokensFromIndexedDB: (coinTypes: string[]) => Promise<void>;
@@ -0,0 +1,100 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ const DB_NAME = "suilend";
11
+ const DB_VERSION = 1;
12
+ const TOKEN_STORE = "token";
13
+ const initDB = () => {
14
+ return new Promise((resolve) => {
15
+ // No-op outside the browser (Node/SSR): there is no IndexedDB to cache into.
16
+ if (typeof indexedDB === "undefined") {
17
+ resolve(null);
18
+ return;
19
+ }
20
+ try {
21
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
22
+ request.onerror = () => resolve(null); // Silently fail if IndexedDB is not available
23
+ request.onsuccess = () => resolve(request.result);
24
+ request.onupgradeneeded = (event) => {
25
+ const db = event.target.result;
26
+ if (!db.objectStoreNames.contains(TOKEN_STORE)) {
27
+ db.createObjectStore(TOKEN_STORE, { keyPath: "coinType" });
28
+ }
29
+ };
30
+ }
31
+ catch (err) {
32
+ resolve(null); // Silently fail if IndexedDB is not available
33
+ }
34
+ });
35
+ };
36
+ export const getTokenMapByCoinTypeFromIndexedDB = (coinTypes) => __awaiter(void 0, void 0, void 0, function* () {
37
+ if (coinTypes.length === 0)
38
+ return {};
39
+ const db = yield initDB();
40
+ if (!db)
41
+ return {};
42
+ return new Promise((resolve, reject) => {
43
+ const transaction = db.transaction(TOKEN_STORE, "readonly");
44
+ const store = transaction.objectStore(TOKEN_STORE);
45
+ const result = {};
46
+ const promises = coinTypes.map((coinType) => new Promise((resolve, reject) => {
47
+ const request = store.get(coinType);
48
+ request.onsuccess = () => {
49
+ if (request.result)
50
+ result[coinType] = request.result;
51
+ resolve();
52
+ };
53
+ request.onerror = () => reject(request.error);
54
+ }));
55
+ Promise.all(promises)
56
+ .then(() => resolve(result))
57
+ .catch(reject)
58
+ .finally(() => db.close());
59
+ });
60
+ });
61
+ export const setTokenMapToIndexedDB = (tokenMap) => __awaiter(void 0, void 0, void 0, function* () {
62
+ if (Object.keys(tokenMap).length === 0)
63
+ return;
64
+ const db = yield initDB();
65
+ if (!db)
66
+ return;
67
+ return new Promise((resolve, reject) => {
68
+ const transaction = db.transaction(TOKEN_STORE, "readwrite");
69
+ const store = transaction.objectStore(TOKEN_STORE);
70
+ const promises = Object.entries(tokenMap).map(([coinType, token]) => new Promise((resolve, reject) => {
71
+ const request = store.put(token);
72
+ request.onsuccess = () => resolve();
73
+ request.onerror = () => reject(request.error);
74
+ }));
75
+ Promise.all(promises)
76
+ .then(() => resolve())
77
+ .catch(reject)
78
+ .finally(() => db.close());
79
+ });
80
+ });
81
+ export const removeTokensFromIndexedDB = (coinTypes) => __awaiter(void 0, void 0, void 0, function* () {
82
+ if (coinTypes.length === 0)
83
+ return;
84
+ const db = yield initDB();
85
+ if (!db)
86
+ return;
87
+ return new Promise((resolve, reject) => {
88
+ const transaction = db.transaction(TOKEN_STORE, "readwrite");
89
+ const store = transaction.objectStore(TOKEN_STORE);
90
+ const promises = coinTypes.map((coinType) => new Promise((resolve, reject) => {
91
+ const request = store.delete(coinType);
92
+ request.onsuccess = () => resolve();
93
+ request.onerror = () => reject(request.error);
94
+ }));
95
+ Promise.all(promises)
96
+ .then(() => resolve())
97
+ .catch(reject)
98
+ .finally(() => db.close());
99
+ });
100
+ });
@@ -0,0 +1,31 @@
1
+ import { SuiGrpcClient } from "@mysten/sui/grpc";
2
+ import { SuiGraphQLClient } from "@mysten/sui/graphql";
3
+ import { SuiClientTypes } from "@mysten/sui/client";
4
+ import { Transaction, TransactionObjectArgument } from "@mysten/sui/transactions";
5
+ import BigNumber from "bignumber.js";
6
+ import { Token } from "./coinMetadata";
7
+ export type GraphQLTransaction = {
8
+ digest: string;
9
+ expiration?: {
10
+ epochId: number;
11
+ } | null;
12
+ signatures?: string[];
13
+ sender?: {
14
+ address: string;
15
+ };
16
+ };
17
+ export declare const getTotalGasFee: (res: SuiClientTypes.Transaction<{
18
+ effects: true;
19
+ balanceChanges: true;
20
+ }>) => BigNumber;
21
+ export declare const getBalanceChange: (res: SuiClientTypes.Transaction<{
22
+ effects: true;
23
+ balanceChanges: true;
24
+ }>, address: string, token: Token, multiplier?: -1 | 1) => BigNumber | undefined;
25
+ export declare const getAllOwnedObjects: (suiGrpcClient: SuiGrpcClient, address: string, type?: string) => Promise<SuiClientTypes.Object<{
26
+ json: true;
27
+ }>[]>;
28
+ export declare const getAllCoins: (suiGrpcClient: SuiGrpcClient, address: string, coinType: string) => Promise<SuiClientTypes.Coin[]>;
29
+ export declare const getMostRecentAddressTransaction: (suiGraphQLClient: SuiGraphQLClient, address: string, direction: "from" | "to") => Promise<GraphQLTransaction | undefined>;
30
+ export declare const mergeAllCoins: (coinType: string, transaction: Transaction, allCoins: SuiClientTypes.Coin[]) => SuiClientTypes.Coin;
31
+ export declare const getSpendableCoin: (suiGrpcClient: SuiGrpcClient, address: string, coinType: string, value: string, transaction: Transaction, mergeCoins?: boolean) => Promise<TransactionObjectArgument>;
@@ -0,0 +1,167 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { SUI_DECIMALS, normalizeStructTag } from "@mysten/sui/utils";
11
+ import BigNumber from "bignumber.js";
12
+ import { isSui } from "./coinType.js";
13
+ export const getTotalGasFee = (res) => res.effects
14
+ ? new BigNumber(+res.effects.gasUsed.computationCost +
15
+ +res.effects.gasUsed.storageCost -
16
+ +res.effects.gasUsed.storageRebate).div(10 ** SUI_DECIMALS)
17
+ : new BigNumber(0);
18
+ export const getBalanceChange = (res, address, token, multiplier = 1) => {
19
+ if (!res.balanceChanges)
20
+ return undefined;
21
+ const balanceChanges = res.balanceChanges.filter((bc) => normalizeStructTag(bc.coinType) === token.coinType &&
22
+ bc.address === address);
23
+ if (balanceChanges.length === 0)
24
+ return undefined;
25
+ return balanceChanges
26
+ .reduce((acc, balanceChange) => acc.plus(new BigNumber(+balanceChange.amount)), new BigNumber(0))
27
+ .div(10 ** token.decimals)
28
+ .plus(isSui(token.coinType) ? getTotalGasFee(res) : 0)
29
+ .times(multiplier);
30
+ };
31
+ export const getAllOwnedObjects = (suiGrpcClient, address, type) => __awaiter(void 0, void 0, void 0, function* () {
32
+ const allObjs = [];
33
+ let cursor = null;
34
+ let hasNextPage = true;
35
+ while (hasNextPage) {
36
+ const result = yield suiGrpcClient.listOwnedObjects({
37
+ owner: address,
38
+ cursor,
39
+ type,
40
+ include: { json: true },
41
+ });
42
+ allObjs.push(...result.objects);
43
+ cursor = result.cursor;
44
+ hasNextPage = result.hasNextPage;
45
+ }
46
+ return allObjs;
47
+ });
48
+ export const getAllCoins = (suiGrpcClient, address, coinType) => __awaiter(void 0, void 0, void 0, function* () {
49
+ var _a;
50
+ const allCoins = [];
51
+ let cursor = undefined;
52
+ let hasNextPage = true;
53
+ while (hasNextPage) {
54
+ const result = yield suiGrpcClient.listCoins({
55
+ owner: address,
56
+ coinType,
57
+ cursor,
58
+ });
59
+ allCoins.push(...result.objects);
60
+ cursor = (_a = result.cursor) !== null && _a !== void 0 ? _a : undefined;
61
+ hasNextPage = result.hasNextPage;
62
+ }
63
+ return allCoins;
64
+ });
65
+ const QUERY_TRANSACTIONS = `
66
+ query QueryTransactions($filter: TransactionsFilter, $first: Int, $after: String) {
67
+ transactions(
68
+ first: $first
69
+ after: $after
70
+ filter: $filter
71
+ ) {
72
+ nodes {
73
+ digest
74
+ expiration { epochId }
75
+ signatures
76
+ sender { address }
77
+ }
78
+ }
79
+ }
80
+ `;
81
+ export const getMostRecentAddressTransaction = (suiGraphQLClient, address, direction) => __awaiter(void 0, void 0, void 0, function* () {
82
+ var _a, _b;
83
+ const filter = direction === "from"
84
+ ? { sentAddress: address }
85
+ : { affectedAddress: address };
86
+ const result = yield suiGraphQLClient.query({
87
+ query: QUERY_TRANSACTIONS,
88
+ variables: {
89
+ filter,
90
+ first: 1,
91
+ },
92
+ });
93
+ const data = result.data;
94
+ return (_b = (_a = data === null || data === void 0 ? void 0 : data.transactions) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b[0];
95
+ });
96
+ export const mergeAllCoins = (coinType, transaction, allCoins) => {
97
+ const mergeCoin = allCoins[0];
98
+ if (allCoins.length > 1 && !isSui(coinType)) {
99
+ transaction.mergeCoins(transaction.object(mergeCoin.objectId), allCoins.map((c) => transaction.object(c.objectId)).slice(1));
100
+ }
101
+ return mergeCoin;
102
+ };
103
+ // Build a spendable coin using owned coin objects, address balance, or both.
104
+ export const getSpendableCoin = (suiGrpcClient_1, address_1, coinType_1, value_1, transaction_1, ...args_1) => __awaiter(void 0, [suiGrpcClient_1, address_1, coinType_1, value_1, transaction_1, ...args_1], void 0, function* (suiGrpcClient, address, coinType, value, transaction, mergeCoins = true) {
105
+ var _a;
106
+ const spendValue = BigInt(value);
107
+ if (spendValue <= BigInt(0)) {
108
+ throw new Error("Spend amount must be positive");
109
+ }
110
+ // Address-balance withdrawals resolve against the transaction sender.
111
+ transaction.setSenderIfNotSet(address);
112
+ const [coins, balanceRes] = yield Promise.all([
113
+ getAllCoins(suiGrpcClient, address, coinType),
114
+ suiGrpcClient.getBalance({ owner: address, coinType }),
115
+ ]);
116
+ const nonZeroCoins = coins.filter((c) => BigInt(c.balance) > BigInt(0));
117
+ const objectCoinBalance = nonZeroCoins.reduce((sum, c) => sum + BigInt(c.balance), BigInt(0));
118
+ const addressBalanceAvailable = BigInt((_a = balanceRes.balance.addressBalance) !== null && _a !== void 0 ? _a : "0");
119
+ const totalAvailable = BigInt(balanceRes.balance.balance);
120
+ if (totalAvailable < spendValue) {
121
+ throw new Error(`Insufficient ${coinType} balance: need ${value}, have ${balanceRes.balance.balance} (${objectCoinBalance.toString()} in coins, ${addressBalanceAvailable.toString()} in address)`);
122
+ }
123
+ const objectCoinSpend = objectCoinBalance >= spendValue ? spendValue : objectCoinBalance;
124
+ const addressBalanceSpend = spendValue - objectCoinSpend;
125
+ // Address balance only.
126
+ if (objectCoinSpend === BigInt(0)) {
127
+ const [coin] = transaction.moveCall({
128
+ target: "0x2::coin::redeem_funds",
129
+ typeArguments: [coinType],
130
+ arguments: [transaction.withdrawal({ amount: value, type: coinType })],
131
+ });
132
+ return coin;
133
+ }
134
+ let coinSource;
135
+ if (mergeCoins) {
136
+ const mergedCoin = mergeAllCoins(coinType, transaction, nonZeroCoins);
137
+ coinSource = isSui(coinType)
138
+ ? transaction.gas
139
+ : transaction.object(mergedCoin.objectId);
140
+ }
141
+ else {
142
+ coinSource = isSui(coinType)
143
+ ? transaction.gas
144
+ : transaction.object(nonZeroCoins[0].objectId);
145
+ }
146
+ // Coin objects only.
147
+ if (addressBalanceSpend === BigInt(0)) {
148
+ const [sendCoin] = transaction.splitCoins(coinSource, [value]);
149
+ return sendCoin;
150
+ }
151
+ // Mixed: split from coin objects, redeem the rest from address balance, merge.
152
+ const [objectCoin] = transaction.splitCoins(coinSource, [
153
+ objectCoinSpend.toString(),
154
+ ]);
155
+ const [addressCoin] = transaction.moveCall({
156
+ target: "0x2::coin::redeem_funds",
157
+ typeArguments: [coinType],
158
+ arguments: [
159
+ transaction.withdrawal({
160
+ amount: addressBalanceSpend.toString(),
161
+ type: coinType,
162
+ }),
163
+ ],
164
+ });
165
+ transaction.mergeCoins(objectCoin, [addressCoin]);
166
+ return objectCoin;
167
+ });
package/package.json ADDED
@@ -0,0 +1 @@
1
+ {"name":"@suilend/sui-core","version":"0.1.0","private":false,"description":"Isomorphic Sui core: coin types, coin metadata, transactions, pricing, and formatting shared across Suilend frontends, SDKs, and backend","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./lib/constants":"./lib/constants.js","./lib/transactions":"./lib/transactions.js","./lib/format":"./lib/format.js","./lib/coinMetadata":"./lib/coinMetadata.js","./lib":"./lib/index.js","./lib/indexedDB":"./lib/indexedDB.js","./lib/coinType":"./lib/coinType.js","./lib/api":"./lib/api.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && bun tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit","lint:ci":"tsc --noEmit","prettier":"prettier --write src/","release":"bun run build && bun ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sui-core"},"dependencies":{"bignumber.js":"^9.1.2","lodash":"^4.17.21","p-limit":"3.1.0"},"devDependencies":{"@mysten/sui":"2.17.0","@tsconfig/recommended":"^1.0.8","@types/lodash":"^4.17.13","@types/node":"^22.9.0","prettier":"^3.3.3","typescript":"^5.6.3"},"peerDependencies":{"@mysten/sui":">=2.17.0 <3"},"type":"module"}