@xoxno/sdk-js 1.0.224 → 1.0.226
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/README.md +39 -0
- package/dist/cjs/sdk/stellar/index.d.ts +1 -0
- package/dist/cjs/sdk/stellar/read/__tests__/client.test.d.ts +1 -0
- package/dist/cjs/sdk/stellar/read/client.d.ts +19 -0
- package/dist/cjs/sdk/stellar/read/index.d.ts +4 -0
- package/dist/cjs/sdk/stellar/read/schemas.d.ts +370 -0
- package/dist/cjs/sdk/stellar/read/types.d.ts +83 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/sdk/index.js +198 -0
- package/dist/esm/sdk/stellar/admin.js +385 -0
- package/dist/esm/sdk/stellar/auth-policy.js +310 -0
- package/dist/esm/sdk/stellar/cctp.js +23 -0
- package/dist/esm/sdk/stellar/contracts.js +49 -0
- package/dist/esm/sdk/stellar/events/decode.js +415 -0
- package/dist/esm/sdk/stellar/events/id.js +62 -0
- package/dist/esm/sdk/stellar/events/index.js +2 -0
- package/dist/esm/sdk/stellar/governance.js +403 -0
- package/dist/esm/sdk/stellar/index.js +26 -0
- package/dist/esm/sdk/stellar/lending-api-types.js +3 -0
- package/dist/esm/sdk/stellar/lending-read-types.js +1 -0
- package/dist/esm/sdk/stellar/lending-read.js +341 -0
- package/dist/esm/sdk/stellar/lending.js +358 -0
- package/dist/esm/sdk/stellar/math/account.js +256 -0
- package/dist/esm/sdk/stellar/math/index.js +11 -0
- package/dist/esm/sdk/stellar/math/limits.js +273 -0
- package/dist/esm/sdk/stellar/math/projection.js +130 -0
- package/dist/esm/sdk/stellar/math/rates.js +62 -0
- package/dist/esm/sdk/stellar/math/scaled.js +156 -0
- package/dist/esm/sdk/stellar/position-mode.js +11 -0
- package/dist/esm/sdk/stellar/prepare.js +47 -0
- package/dist/esm/sdk/stellar/quote.js +87 -0
- package/dist/esm/sdk/stellar/read/client.js +82 -0
- package/dist/esm/sdk/stellar/read/index.js +3 -0
- package/dist/esm/sdk/stellar/read/schemas.js +158 -0
- package/dist/esm/sdk/stellar/read/types.js +1 -0
- package/dist/esm/sdk/stellar/repay-swap.js +12 -0
- package/dist/esm/sdk/stellar/route-verify.js +285 -0
- package/dist/esm/sdk/stellar/scval-encode.js +464 -0
- package/dist/esm/sdk/stellar/swap-history.js +46 -0
- package/dist/esm/sdk/stellar/swap-index.js +1 -0
- package/dist/esm/sdk/stellar/swap.js +117 -0
- package/dist/esm/sdk/stellar/xdr-compat.js +17 -0
- package/dist/esm/sdk/stellar-schema.js +129 -0
- package/dist/esm/sdk/swagger.js +1957 -0
- package/dist/esm/sdk/types.js +1 -0
- package/dist/esm/sdk/utils.js +21 -0
- package/dist/esm/utils/api.js +169 -0
- package/dist/esm/utils/const.js +11 -0
- package/dist/esm/utils/errors.js +20 -0
- package/dist/esm/utils/guards.js +29 -0
- package/dist/esm/utils/helpers.js +20 -0
- package/dist/esm/utils/regex.js +14 -0
- package/dist/esm/utils/soroban-errors.js +149 -0
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -2
- package/dist/sdk/stellar/index.cjs +1 -1
- package/dist/sdk/stellar/index.d.ts +1 -0
- package/dist/sdk/stellar/index.esm.js +1 -1
- package/dist/sdk/stellar/read/__tests__/client.test.d.ts +1 -0
- package/dist/sdk/stellar/read/client.d.ts +19 -0
- package/dist/sdk/stellar/read/index.cjs +1 -0
- package/dist/sdk/stellar/read/index.d.ts +4 -0
- package/dist/sdk/stellar/read/index.esm.js +1 -0
- package/dist/sdk/stellar/read/schemas.d.ts +370 -0
- package/dist/sdk/stellar/read/types.d.ts +83 -0
- package/package.json +20 -4
- package/dist/index.esm.js.LICENSE.txt +0 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const coveredMethods = [
|
|
2
|
+
'PATCH',
|
|
3
|
+
'POST',
|
|
4
|
+
'DELETE',
|
|
5
|
+
'PUT'
|
|
6
|
+
];
|
|
7
|
+
const _nestTypes = [
|
|
8
|
+
'string',
|
|
9
|
+
'number',
|
|
10
|
+
'boolean'
|
|
11
|
+
];
|
|
12
|
+
const _returnTypes = [
|
|
13
|
+
'application/json',
|
|
14
|
+
'multipart/form-data'
|
|
15
|
+
];
|
|
16
|
+
const _securityModes = [
|
|
17
|
+
'requiredAny',
|
|
18
|
+
'requiredWeb2',
|
|
19
|
+
'requiredJwt',
|
|
20
|
+
'optionalAny'
|
|
21
|
+
];
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
import { DR_SC, FM_SC, KG_SC, Manager_SC, Manager_SC_DEV, P2P_SC, P2P_SC_DEV, Staking_SC, Staking_SC_DEV, XOXNO_SC, XOXNO_SC_DEV } from "./const.js";
|
|
15
|
+
/**
|
|
16
|
+
* Default read deadline. Without one, a stalled response is bounded only by
|
|
17
|
+
* whatever the host runtime imposes (in Node, undici's Agent), which is
|
|
18
|
+
* typically longer than the caller's own budget — a Next.js `'use cache'` fill,
|
|
19
|
+
* for instance, gives up at `staticPageGenerationTimeout * 0.9` and turns the
|
|
20
|
+
* stall into a hard error instead of a fetch rejection the caller can degrade
|
|
21
|
+
* on. Generous enough that only a genuinely stuck read hits it.
|
|
22
|
+
*/ export const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
23
|
+
export var Chain = /*#__PURE__*/ function(Chain) {
|
|
24
|
+
Chain["MAINNET"] = "1";
|
|
25
|
+
Chain["DEVNET"] = "D";
|
|
26
|
+
return Chain;
|
|
27
|
+
}({});
|
|
28
|
+
export class XOXNOClient {
|
|
29
|
+
/** Create a client bound to one application-selected API deployment. */ constructor(options){
|
|
30
|
+
_define_property(this, "apiUrl", void 0);
|
|
31
|
+
_define_property(this, "chain", void 0);
|
|
32
|
+
_define_property(this, "init", void 0);
|
|
33
|
+
_define_property(this, "config", void 0);
|
|
34
|
+
_define_property(this, "fetchWithTimeout", async (path, { params, ...options } = {})=>{
|
|
35
|
+
const { next, cache, debug, timeout, ...rest } = this.init;
|
|
36
|
+
const { next: overwriteNext, cache: overwriteCache, debug: overwriteDebug, timeout: overwriteTimeout, headers, method = 'GET', ...overwriteRest } = options;
|
|
37
|
+
const authHeader = headers?.Authorization;
|
|
38
|
+
const Authorization = authHeader === 'Bearer undefined' ? undefined : authHeader;
|
|
39
|
+
// A GET/HEAD has no body, so Content-Type describes nothing — but in a
|
|
40
|
+
// browser it is the difference between a "simple" CORS request and one
|
|
41
|
+
// that needs a preflight. `application/json` is not on the CORS-safelist,
|
|
42
|
+
// so setting it here made every unauthenticated GET cost an extra OPTIONS
|
|
43
|
+
// round trip to the origin (~180k/week, 17% of all API requests, ~200ms
|
|
44
|
+
// each, none of them cacheable at the edge).
|
|
45
|
+
//
|
|
46
|
+
// Authenticated calls still preflight — Authorization is not safelisted
|
|
47
|
+
// either — and that is unavoidable. This only stops charging public reads
|
|
48
|
+
// for a header that carries no meaning on a bodyless request.
|
|
49
|
+
const hasBody = method !== 'GET' && method !== 'HEAD' && method !== 'PUT';
|
|
50
|
+
const allHeaders = {
|
|
51
|
+
...headers,
|
|
52
|
+
// Both are forbidden header names: browsers drop them silently, so they
|
|
53
|
+
// only ever apply to server-side (Node) callers.
|
|
54
|
+
Referer: 'https://xoxno.sdk',
|
|
55
|
+
'User-Agent': 'XOXNO/1.0/SDK',
|
|
56
|
+
...hasBody ? {
|
|
57
|
+
'Content-Type': 'application/json'
|
|
58
|
+
} : {},
|
|
59
|
+
...Authorization ? {
|
|
60
|
+
Authorization
|
|
61
|
+
} : {}
|
|
62
|
+
};
|
|
63
|
+
const query = Object.entries(params ?? {}).filter(([, value])=>value !== undefined).flatMap(([key, value])=>{
|
|
64
|
+
if (Array.isArray(value)) {
|
|
65
|
+
return value.filter((v)=>v !== undefined).map((v)=>`${key}=${encodeURIComponent(v)}`);
|
|
66
|
+
} else {
|
|
67
|
+
return `${key}=${encodeURIComponent(value)}`;
|
|
68
|
+
}
|
|
69
|
+
}).join('&');
|
|
70
|
+
const url = `${this.apiUrl}${path}${query.length ? `?${query}` : ''}`;
|
|
71
|
+
const { revalidate, ...other } = next ?? {};
|
|
72
|
+
const { revalidate: overwriteRevalidate, ...overwriteOther } = overwriteNext ?? {};
|
|
73
|
+
const finalRevalidate = method === 'GET' && !Authorization ? overwriteRevalidate ?? revalidate : undefined;
|
|
74
|
+
const finalCache = method === 'GET' && !Authorization && !finalRevalidate ? overwriteCache ?? cache : undefined;
|
|
75
|
+
// A body's duration is uplink-bound, so it is only deadlined when the caller
|
|
76
|
+
// asks for it; a read is not.
|
|
77
|
+
const body = overwriteRest.body;
|
|
78
|
+
const effectiveTimeout = overwriteTimeout ?? timeout ?? (body == null ? DEFAULT_REQUEST_TIMEOUT_MS : 0);
|
|
79
|
+
const deadline = effectiveTimeout > 0 ? AbortSignal.timeout(effectiveTimeout) : undefined;
|
|
80
|
+
const callerSignal = overwriteRest.signal ?? rest.signal;
|
|
81
|
+
const signal = deadline && callerSignal ? AbortSignal.any([
|
|
82
|
+
callerSignal,
|
|
83
|
+
deadline
|
|
84
|
+
]) : deadline ?? callerSignal;
|
|
85
|
+
const init = {
|
|
86
|
+
...rest,
|
|
87
|
+
...overwriteRest,
|
|
88
|
+
method,
|
|
89
|
+
...Object.keys(allHeaders).length ? {
|
|
90
|
+
headers: allHeaders
|
|
91
|
+
} : {},
|
|
92
|
+
...signal ? {
|
|
93
|
+
signal
|
|
94
|
+
} : {},
|
|
95
|
+
cache: finalCache,
|
|
96
|
+
next: {
|
|
97
|
+
...other,
|
|
98
|
+
...overwriteOther,
|
|
99
|
+
revalidate: finalRevalidate
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
if (overwriteDebug ?? debug) {
|
|
103
|
+
console.debug('SDK fetch: ', url, init);
|
|
104
|
+
}
|
|
105
|
+
const res = await fetch(url, init).catch((error)=>{
|
|
106
|
+
if (error instanceof Error && !error.message.match(/^http(s?):\/\//)) {
|
|
107
|
+
// Preserve the underlying network reason as `cause` — the undici/system
|
|
108
|
+
// error carries the real code (UND_ERR_SOCKET, ECONNREFUSED, ENOTFOUND,
|
|
109
|
+
// timeouts). Without it the thrown message is only "<url>: fetch failed".
|
|
110
|
+
throw Object.assign(new Error(`${url}: ${error.message}`), {
|
|
111
|
+
cause: error
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
});
|
|
116
|
+
const text = await res.text();
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
let message;
|
|
119
|
+
try {
|
|
120
|
+
message = JSON.parse(text);
|
|
121
|
+
} catch {
|
|
122
|
+
message = {
|
|
123
|
+
message: text
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const errorMessage = [
|
|
127
|
+
url,
|
|
128
|
+
res.status,
|
|
129
|
+
res.statusText,
|
|
130
|
+
message.message
|
|
131
|
+
].filter(Boolean).join(';;');
|
|
132
|
+
throw new Error(errorMessage);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(text);
|
|
136
|
+
} catch {
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (!options?.apiUrl?.trim()) {
|
|
141
|
+
throw new Error('XOXNOClient: apiUrl is required');
|
|
142
|
+
}
|
|
143
|
+
const { chain = "1", apiUrl, ...init } = options;
|
|
144
|
+
this.apiUrl = apiUrl.trim().replace(/\/$/, '');
|
|
145
|
+
this.chain = chain;
|
|
146
|
+
this.init = init;
|
|
147
|
+
this.config = chain == "1" ? {
|
|
148
|
+
mediaUrl: 'https://media.xoxno.com',
|
|
149
|
+
gatewayUrl: 'https://gateway.xoxno.com',
|
|
150
|
+
XO_SC: XOXNO_SC,
|
|
151
|
+
FM_SC: FM_SC,
|
|
152
|
+
DR_SC,
|
|
153
|
+
KG_SC,
|
|
154
|
+
Staking_SC,
|
|
155
|
+
Manager_SC,
|
|
156
|
+
P2P_SC
|
|
157
|
+
} : {
|
|
158
|
+
mediaUrl: 'https://devnet-media.xoxno.com',
|
|
159
|
+
gatewayUrl: 'https://devnet-gateway.xoxno.com',
|
|
160
|
+
XO_SC: XOXNO_SC_DEV,
|
|
161
|
+
FM_SC,
|
|
162
|
+
DR_SC,
|
|
163
|
+
KG_SC,
|
|
164
|
+
Staking_SC: Staking_SC_DEV,
|
|
165
|
+
Manager_SC: Manager_SC_DEV,
|
|
166
|
+
P2P_SC: P2P_SC_DEV
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const XOXNO_SC = 'erd1qqqqqqqqqqqqqpgq6wegs2xkypfpync8mn2sa5cmpqjlvrhwz5nqgepyg8';
|
|
2
|
+
export const FM_SC = 'erd1qqqqqqqqqqqqqpgq705fxpfrjne0tl3ece0rrspykq88mynn4kxs2cg43s';
|
|
3
|
+
export const DR_SC = 'erd1qqqqqqqqqqqqqpgqd9rvv2n378e27jcts8vfwynpx0gfl5ufz6hqhfy0u0';
|
|
4
|
+
export const KG_SC = 'erd1qqqqqqqqqqqqqpgq8xwzu82v8ex3h4ayl5lsvxqxnhecpwyvwe0sf2qj4e';
|
|
5
|
+
export const Staking_SC = 'erd1qqqqqqqqqqqqqpgqvpkd3g3uwludduv3797j54qt6c888wa59w2shntt6z';
|
|
6
|
+
export const Manager_SC = 'erd1qqqqqqqqqqqqqpgqg9fa0dmpn8fu3fnleeqn5zt8rl8mdqjkys5s2gtas7';
|
|
7
|
+
export const P2P_SC = 'erd1qqqqqqqqqqqqqpgq47y8hnct68v6asjv6gxem6h9rumn9frzah0skhxxt6';
|
|
8
|
+
export const XOXNO_SC_DEV = 'erd1qqqqqqqqqqqqqpgql0dnz6n5hpuw8cptlt00khd0nn4ja8eadsfq2xrqw4';
|
|
9
|
+
export const Staking_SC_DEV = 'erd1qqqqqqqqqqqqqpgqsc5hnewwpep8qq0d7kjjzrquapuucrygah0s85zres';
|
|
10
|
+
export const Manager_SC_DEV = 'erd1qqqqqqqqqqqqqpgqluclyhfsa2uw7q9cjwd8knk989hg57u8ah0slq2nlr';
|
|
11
|
+
export const P2P_SC_DEV = 'erd1qqqqqqqqqqqqqpgqfja7ukpngrun78ueq583l0vd6aj4ekhrah0sa9wyrv';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class CollectionNotFoundError extends Error {
|
|
2
|
+
constructor(item){
|
|
3
|
+
super(`Invalid collection ticker: ${item}`);
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export class AddressNotFoundError extends Error {
|
|
7
|
+
constructor(item){
|
|
8
|
+
super(`Invalid address: ${item}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class NFTNotFoundError extends Error {
|
|
12
|
+
constructor(item){
|
|
13
|
+
super(`Invalid NFT identifier: ${item}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class PaginatedTopError extends Error {
|
|
17
|
+
constructor(top){
|
|
18
|
+
super(`Top cannot be greater than 100, found ${top}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AddressNotFoundError, CollectionNotFoundError, NFTNotFoundError, PaginatedTopError } from "./errors.js";
|
|
2
|
+
import { isValidCollectionTicker, isValidNftIdentifier } from "./regex.js";
|
|
3
|
+
export function collectionGuard(collection, callback) {
|
|
4
|
+
if (!isValidCollectionTicker(collection)) {
|
|
5
|
+
throw new CollectionNotFoundError(collection);
|
|
6
|
+
}
|
|
7
|
+
return callback;
|
|
8
|
+
}
|
|
9
|
+
export function addressGuard(address, callback) {
|
|
10
|
+
if (!isValidCollectionTicker(address)) {
|
|
11
|
+
throw new AddressNotFoundError(address);
|
|
12
|
+
}
|
|
13
|
+
return callback;
|
|
14
|
+
}
|
|
15
|
+
export function nftGuard(identifier, callback) {
|
|
16
|
+
if (!isValidNftIdentifier(identifier)) {
|
|
17
|
+
throw new NFTNotFoundError(identifier);
|
|
18
|
+
}
|
|
19
|
+
return callback;
|
|
20
|
+
}
|
|
21
|
+
export function collectionGuardOnly(collection) {
|
|
22
|
+
return collectionGuard(collection, Promise.resolve());
|
|
23
|
+
}
|
|
24
|
+
export function paginatedGuard(filter, callback) {
|
|
25
|
+
if (filter.top && filter.top > 100) {
|
|
26
|
+
throw new PaginatedTopError(filter.top);
|
|
27
|
+
}
|
|
28
|
+
return callback(JSON.stringify(filter));
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const nonceToHex = (nonce)=>{
|
|
2
|
+
let nonceHex = nonce.toString(16);
|
|
3
|
+
if (nonceHex.length % 2 !== 0) {
|
|
4
|
+
nonceHex = '0' + nonceHex;
|
|
5
|
+
}
|
|
6
|
+
return nonceHex;
|
|
7
|
+
};
|
|
8
|
+
export const getIdentifierFromColAndNonce = (collection, nonce)=>{
|
|
9
|
+
return [
|
|
10
|
+
collection,
|
|
11
|
+
nonceToHex(nonce)
|
|
12
|
+
].join('-');
|
|
13
|
+
};
|
|
14
|
+
export const isAddressValid = (address)=>{
|
|
15
|
+
return address ? address.startsWith('erd1') && address.length === 62 || /^0x[a-fA-F0-9]{40,64}$/.test(address) || /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address) || // Stellar strkey: `G` = ed25519 account, `C` = Soroban contract.
|
|
16
|
+
// Contracts hold balances, positions and profiles just like accounts,
|
|
17
|
+
// and the route builder runs every `:address` param through here — so
|
|
18
|
+
// rejecting `C` blocked all contract-account endpoints client-side.
|
|
19
|
+
/^[GC][A-Z2-7]{55}$/.test(address) : false;
|
|
20
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const isValidCollectionTicker = (ticker)=>{
|
|
2
|
+
const isSuiCollection = /* (ticker.length >= 4 && ticker.length <= 40) || */ /^0x[a-fA-F0-9]{1,64}-[a-zA-Z0-9_]+-[a-zA-Z0-9_]+(<.+>)?$/.test(decodeURIComponent(ticker));
|
|
3
|
+
const isXoxnoCollection = /^[A-Z0-9]{3,10}-[a-z0-9]{6}$/.test(ticker);
|
|
4
|
+
// Stellar: the collection IS the NFT contract (Soroban contract strkey)
|
|
5
|
+
const isStellarCollection = /^C[A-Z2-7]{55}$/.test(ticker);
|
|
6
|
+
return isSuiCollection || isXoxnoCollection || isStellarCollection;
|
|
7
|
+
};
|
|
8
|
+
export const isValidNftIdentifier = (identifier)=>{
|
|
9
|
+
const isSuiNft = /^0x[a-fA-F0-9]{1,64}$/.test(identifier);
|
|
10
|
+
const isXoxnoNft = /^[A-Za-z0-9]{3,10}-[A-Za-z0-9]{6}-[A-Za-z0-9]{2,7}(?:-\d+(?:-[A-Za-z0-9]+)?)?$/.test(identifier);
|
|
11
|
+
// Stellar: `{contract strkey}-{decimal token id}`
|
|
12
|
+
const isStellarNft = /^C[A-Z2-7]{55}-\d+$/.test(identifier);
|
|
13
|
+
return isSuiNft || isXoxnoNft || isStellarNft;
|
|
14
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rs-lending-xlm Soroban contract error codes → human-readable messages.
|
|
3
|
+
* Mirrors `common/src/errors.rs` (`GenericError`, `CollateralError`,
|
|
4
|
+
* `SpokeError`, `OracleError`). Keep in sync when that file changes.
|
|
5
|
+
*/ const SOROBAN_ERROR_CATALOG = {
|
|
6
|
+
// GenericError
|
|
7
|
+
1: {
|
|
8
|
+
name: 'AssetNotSupported',
|
|
9
|
+
message: 'This asset is not supported by the protocol.'
|
|
10
|
+
},
|
|
11
|
+
2: {
|
|
12
|
+
name: 'AssetAlreadySupported',
|
|
13
|
+
message: 'This asset is already supported.'
|
|
14
|
+
},
|
|
15
|
+
3: {
|
|
16
|
+
name: 'InvalidTicker',
|
|
17
|
+
message: 'Invalid asset ticker.'
|
|
18
|
+
},
|
|
19
|
+
6: {
|
|
20
|
+
name: 'InvalidAsset',
|
|
21
|
+
message: 'Invalid asset address.'
|
|
22
|
+
},
|
|
23
|
+
10: {
|
|
24
|
+
name: 'InvalidWasmHash',
|
|
25
|
+
message: 'The WASM hash is empty or invalid.'
|
|
26
|
+
},
|
|
27
|
+
11: {
|
|
28
|
+
name: 'InvalidExchangeSrc',
|
|
29
|
+
message: 'The oracle configuration is invalid.'
|
|
30
|
+
},
|
|
31
|
+
12: {
|
|
32
|
+
name: 'PairNotActive',
|
|
33
|
+
message: 'No oracle is configured for this asset.'
|
|
34
|
+
},
|
|
35
|
+
14: {
|
|
36
|
+
name: 'AmountMustBePositive',
|
|
37
|
+
message: 'The amount must be greater than zero.'
|
|
38
|
+
},
|
|
39
|
+
32: {
|
|
40
|
+
name: 'OwnerNotSet',
|
|
41
|
+
message: 'The contract owner has not been set.'
|
|
42
|
+
},
|
|
43
|
+
39: {
|
|
44
|
+
name: 'InvalidTimelockDelay',
|
|
45
|
+
message: 'The timelock delay is out of the allowed range.'
|
|
46
|
+
},
|
|
47
|
+
41: {
|
|
48
|
+
name: 'InvalidRole',
|
|
49
|
+
message: 'The caller does not hold the required role.'
|
|
50
|
+
},
|
|
51
|
+
43: {
|
|
52
|
+
name: 'HubNotActive',
|
|
53
|
+
message: 'This hub is not active.'
|
|
54
|
+
},
|
|
55
|
+
44: {
|
|
56
|
+
name: 'NotAuthorized',
|
|
57
|
+
message: 'The caller is not authorized to perform this action.'
|
|
58
|
+
},
|
|
59
|
+
46: {
|
|
60
|
+
name: 'OperationNotCancellable',
|
|
61
|
+
message: 'This governance operation cannot be cancelled.'
|
|
62
|
+
},
|
|
63
|
+
// CollateralError
|
|
64
|
+
113: {
|
|
65
|
+
name: 'InvalidLiqThreshold',
|
|
66
|
+
message: 'LTV, liquidation threshold, or bonus is out of bounds.'
|
|
67
|
+
},
|
|
68
|
+
116: {
|
|
69
|
+
name: 'InvalidBorrowParams',
|
|
70
|
+
message: 'Supply or borrow cap configuration is invalid.'
|
|
71
|
+
},
|
|
72
|
+
117: {
|
|
73
|
+
name: 'InvalidUtilRange',
|
|
74
|
+
message: 'Interest-rate utilization breakpoints are invalid.'
|
|
75
|
+
},
|
|
76
|
+
132: {
|
|
77
|
+
name: 'AssetDecimalsTooHigh',
|
|
78
|
+
message: 'Asset decimals exceed the protocol maximum (27).'
|
|
79
|
+
},
|
|
80
|
+
// SpokeError
|
|
81
|
+
300: {
|
|
82
|
+
name: 'SpokeNotFound',
|
|
83
|
+
message: 'This spoke does not exist.'
|
|
84
|
+
},
|
|
85
|
+
301: {
|
|
86
|
+
name: 'SpokeDeprecated',
|
|
87
|
+
message: 'This spoke has been deprecated.'
|
|
88
|
+
},
|
|
89
|
+
307: {
|
|
90
|
+
name: 'AssetNotInSpoke',
|
|
91
|
+
message: 'This asset is not listed on the spoke.'
|
|
92
|
+
},
|
|
93
|
+
308: {
|
|
94
|
+
name: 'AssetAlreadyInSpoke',
|
|
95
|
+
message: 'This asset is already listed in the spoke.'
|
|
96
|
+
},
|
|
97
|
+
309: {
|
|
98
|
+
name: 'SpokeAssetInUse',
|
|
99
|
+
message: 'This spoke asset still has live positions; drain usage before removal.'
|
|
100
|
+
},
|
|
101
|
+
315: {
|
|
102
|
+
name: 'SpokeAssetPaused',
|
|
103
|
+
message: 'This spoke asset is paused.'
|
|
104
|
+
},
|
|
105
|
+
316: {
|
|
106
|
+
name: 'SpokeAssetFrozen',
|
|
107
|
+
message: 'This spoke asset is frozen.'
|
|
108
|
+
},
|
|
109
|
+
317: {
|
|
110
|
+
name: 'SpokeAssetFlagRelaxation',
|
|
111
|
+
message: 'Guardian flag updates may only tighten; clearing a flag requires the timelocked edit.'
|
|
112
|
+
},
|
|
113
|
+
318: {
|
|
114
|
+
name: 'SpokeAssetSeizureHalted',
|
|
115
|
+
message: 'Liquidation seizure is halted for this spoke asset.'
|
|
116
|
+
},
|
|
117
|
+
// OracleError
|
|
118
|
+
220: {
|
|
119
|
+
name: 'InvalidOracleBase',
|
|
120
|
+
message: 'The oracle quote asset/market is missing or invalid.'
|
|
121
|
+
},
|
|
122
|
+
221: {
|
|
123
|
+
name: 'InvalidOracleDecimals',
|
|
124
|
+
message: 'The oracle decimals value is invalid.'
|
|
125
|
+
},
|
|
126
|
+
224: {
|
|
127
|
+
name: 'InvalidSanityBounds',
|
|
128
|
+
message: 'Oracle sanity bounds are invalid (require 0 < min < max).'
|
|
129
|
+
},
|
|
130
|
+
225: {
|
|
131
|
+
name: 'OracleCycleDetected',
|
|
132
|
+
message: 'A cycle was detected in the oracle quote chain.'
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Extracts a `Error(Contract, #N)` code from a raw Soroban simulation/RPC error
|
|
137
|
+
* string and resolves it against the rs-lending-xlm catalog. Matches only the
|
|
138
|
+
* exact `Error(Contract, #N)` pattern — never loose digits, since raw error
|
|
139
|
+
* strings can contain unrelated numbers (fees, ledgers, sequence numbers).
|
|
140
|
+
*/ export function mapSorobanError(raw) {
|
|
141
|
+
const match = raw.match(/Error\(Contract,\s*#(\d+)\)/);
|
|
142
|
+
if (!match) return null;
|
|
143
|
+
const code = Number(match[1]);
|
|
144
|
+
const entry = SOROBAN_ERROR_CATALOG[code];
|
|
145
|
+
return entry ? {
|
|
146
|
+
code,
|
|
147
|
+
...entry
|
|
148
|
+
} : null;
|
|
149
|
+
}
|