@ouronet/ouronet-core 4.3.6
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/CHANGELOG.md +3043 -0
- package/README.md +1940 -0
- package/dist/codex/codec.d.ts +77 -0
- package/dist/codex/codec.js +123 -0
- package/dist/codex/errors.d.ts +14 -0
- package/dist/codex/errors.js +20 -0
- package/dist/codex/index.d.ts +27 -0
- package/dist/codex/index.js +25 -0
- package/dist/codex/seedTypeMigration.d.ts +35 -0
- package/dist/codex/seedTypeMigration.js +45 -0
- package/dist/codex/types.d.ts +100 -0
- package/dist/codex/types.js +19 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.js +3 -0
- package/dist/constants/ouronet.d.ts +33 -0
- package/dist/constants/ouronet.js +59 -0
- package/dist/constants/tokenIds.d.ts +13 -0
- package/dist/constants/tokenIds.js +21 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1 -0
- package/dist/interactions/activateFunctions.d.ts +67 -0
- package/dist/interactions/activateFunctions.js +154 -0
- package/dist/interactions/addLiquidityFunctions.d.ts +140 -0
- package/dist/interactions/addLiquidityFunctions.js +485 -0
- package/dist/interactions/coilFunctions.d.ts +73 -0
- package/dist/interactions/coilFunctions.js +213 -0
- package/dist/interactions/crossChainFunctions.d.ts +211 -0
- package/dist/interactions/crossChainFunctions.js +463 -0
- package/dist/interactions/dexAcquisitionPoolFunctions.d.ts +18 -0
- package/dist/interactions/dexAcquisitionPoolFunctions.js +18 -0
- package/dist/interactions/dexCollectablesFunctions.d.ts +28 -0
- package/dist/interactions/dexCollectablesFunctions.js +227 -0
- package/dist/interactions/dexFunctions.d.ts +23 -0
- package/dist/interactions/dexFunctions.js +23 -0
- package/dist/interactions/dexOrtoFungibleFunctions.d.ts +33 -0
- package/dist/interactions/dexOrtoFungibleFunctions.js +171 -0
- package/dist/interactions/dexParseFunctions.d.ts +150 -0
- package/dist/interactions/dexParseFunctions.js +316 -0
- package/dist/interactions/dexSwapPairAdminFunctions.d.ts +75 -0
- package/dist/interactions/dexSwapPairAdminFunctions.js +255 -0
- package/dist/interactions/dexSwapPairCalcFunctions.d.ts +55 -0
- package/dist/interactions/dexSwapPairCalcFunctions.js +218 -0
- package/dist/interactions/dexSwapPairDashboardFunctions.d.ts +47 -0
- package/dist/interactions/dexSwapPairDashboardFunctions.js +179 -0
- package/dist/interactions/dexSwapPairExecuteFunctions.d.ts +40 -0
- package/dist/interactions/dexSwapPairExecuteFunctions.js +301 -0
- package/dist/interactions/dexSwapPairSmartSwapFunctions.d.ts +25 -0
- package/dist/interactions/dexSwapPairSmartSwapFunctions.js +166 -0
- package/dist/interactions/dexTrueFungibleFunctions.d.ts +52 -0
- package/dist/interactions/dexTrueFungibleFunctions.js +220 -0
- package/dist/interactions/dexTypes.d.ts +465 -0
- package/dist/interactions/dexTypes.js +6 -0
- package/dist/interactions/errors.d.ts +12 -0
- package/dist/interactions/errors.js +13 -0
- package/dist/interactions/guardFunctions.d.ts +33 -0
- package/dist/interactions/guardFunctions.js +110 -0
- package/dist/interactions/index.d.ts +1 -0
- package/dist/interactions/index.js +20 -0
- package/dist/interactions/infoOneFunctions.d.ts +133 -0
- package/dist/interactions/infoOneFunctions.js +566 -0
- package/dist/interactions/kadenaFunctions.d.ts +11 -0
- package/dist/interactions/kadenaFunctions.js +40 -0
- package/dist/interactions/kpayFunctions.d.ts +45 -0
- package/dist/interactions/kpayFunctions.js +191 -0
- package/dist/interactions/ouroAccountFunctions.d.ts +64 -0
- package/dist/interactions/ouroAccountFunctions.js +229 -0
- package/dist/interactions/ouroBalanceFunctions.d.ts +39 -0
- package/dist/interactions/ouroBalanceFunctions.js +100 -0
- package/dist/interactions/ouroCoilFunctions.d.ts +33 -0
- package/dist/interactions/ouroCoilFunctions.js +190 -0
- package/dist/interactions/ouroFunctions.d.ts +23 -0
- package/dist/interactions/ouroFunctions.js +23 -0
- package/dist/interactions/ouroMovieBoosterFunctions.d.ts +18 -0
- package/dist/interactions/ouroMovieBoosterFunctions.js +203 -0
- package/dist/interactions/ouroPriceFunctions.d.ts +38 -0
- package/dist/interactions/ouroPriceFunctions.js +96 -0
- package/dist/interactions/ouroPrimordialsFunctions.d.ts +10 -0
- package/dist/interactions/ouroPrimordialsFunctions.js +480 -0
- package/dist/interactions/ouroRotateFunctions.d.ts +22 -0
- package/dist/interactions/ouroRotateFunctions.js +114 -0
- package/dist/interactions/ouroSubCompressFunctions.d.ts +23 -0
- package/dist/interactions/ouroSubCompressFunctions.js +191 -0
- package/dist/interactions/ouroTransferFunctions.d.ts +14 -0
- package/dist/interactions/ouroTransferFunctions.js +87 -0
- package/dist/interactions/ouroTypes.d.ts +113 -0
- package/dist/interactions/ouroTypes.js +6 -0
- package/dist/interactions/ouroUrStoaFunctions.d.ts +16 -0
- package/dist/interactions/ouroUrStoaFunctions.js +121 -0
- package/dist/interactions/ouroWrapFunctions.d.ts +22 -0
- package/dist/interactions/ouroWrapFunctions.js +234 -0
- package/dist/interactions/pensionFunctions.d.ts +12 -0
- package/dist/interactions/pensionFunctions.js +127 -0
- package/dist/interactions/urStoaFunctions.d.ts +104 -0
- package/dist/interactions/urStoaFunctions.js +475 -0
- package/dist/interactions/wrapFunctions.d.ts +46 -0
- package/dist/interactions/wrapFunctions.js +221 -0
- package/dist/pact/cfmBuilders.d.ts +807 -0
- package/dist/pact/cfmBuilders.js +812 -0
- package/dist/pact/index.d.ts +1 -0
- package/dist/pact/index.js +10 -0
- package/package.json +64 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { Pact } from "@stoachain/kadena-stoic-legacy/client";
|
|
2
|
+
import { getFailoverClient, withFailover } from "@stoachain/stoa-core/network";
|
|
3
|
+
import { KADENA_NETWORK, getPactUrl, STOA_AUTONOMIC_OURONETGASSTATION, KADENA_NAMESPACE } from "../constants/index.js";
|
|
4
|
+
import { GAS_PRICE_MIN_ANU, anuToStoa } from "@stoachain/stoa-core/gas";
|
|
5
|
+
import { pactRead } from "@stoachain/stoa-core/reads";
|
|
6
|
+
import { safeCreationTime, formatDecimalForPact } from "@stoachain/stoa-core/pact";
|
|
7
|
+
/**
|
|
8
|
+
* Get KDA balance for an account on a specific chain.
|
|
9
|
+
* exists=false means the account row does not exist on that chain (not just zero balance).
|
|
10
|
+
*/
|
|
11
|
+
export async function getBalanceOnChain(account, chainId, tier = "T1") {
|
|
12
|
+
try {
|
|
13
|
+
const pactCode = `(coin.get-balance "${account}")`;
|
|
14
|
+
const response = await pactRead(pactCode, {
|
|
15
|
+
pactUrl: getPactUrl(chainId),
|
|
16
|
+
chainId,
|
|
17
|
+
tier,
|
|
18
|
+
});
|
|
19
|
+
if (!response?.result || response.result.status === "failure") {
|
|
20
|
+
return { balance: "0", exists: false };
|
|
21
|
+
}
|
|
22
|
+
const data = response.result.data;
|
|
23
|
+
let balance = "0";
|
|
24
|
+
if (data) {
|
|
25
|
+
if (typeof data === "string") {
|
|
26
|
+
balance = data;
|
|
27
|
+
}
|
|
28
|
+
else if (typeof data === "number") {
|
|
29
|
+
balance = data.toString();
|
|
30
|
+
}
|
|
31
|
+
else if (typeof data === "object" && data.decimal) {
|
|
32
|
+
balance = data.decimal;
|
|
33
|
+
}
|
|
34
|
+
else if (typeof data === "object" && data.int) {
|
|
35
|
+
balance = data.int;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { balance, exists: true };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
balance: "0",
|
|
43
|
+
exists: false,
|
|
44
|
+
error: error instanceof Error ? error.message : "Failed to fetch balance",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build a cross-chain transfer transaction (step 1)
|
|
50
|
+
* This creates a transaction that needs to be signed and submitted
|
|
51
|
+
*/
|
|
52
|
+
export function buildCrossChainTransfer(params) {
|
|
53
|
+
const { sender, receiver, receiverGuard, amount, sourceChain, targetChain, senderPublicKey, } = params;
|
|
54
|
+
// v3.2.1: replaced `parseFloat(amount).toFixed(12)` with the validated
|
|
55
|
+
// formatter to close audit finding F-BUG-003. The old form silently
|
|
56
|
+
// truncated precision on values past float64's ~15-17 significant digits
|
|
57
|
+
// (a 20-digit-fractional KDA amount would lose ~5 digits of precision)
|
|
58
|
+
// and silently rounded inputs that exceeded 12 decimals. The formatter
|
|
59
|
+
// preserves arbitrary precision (truncates at 24 decimals by default) and
|
|
60
|
+
// throws on malformed input rather than producing "NaN" downstream.
|
|
61
|
+
const formattedAmount = formatDecimalForPact(amount);
|
|
62
|
+
const receiverKeysetName = `receiver-guard-${Date.now()}`;
|
|
63
|
+
const transaction = Pact.builder
|
|
64
|
+
.execution(`(coin.transfer-crosschain "${sender}" "${receiver}" (read-keyset "${receiverKeysetName}") "${targetChain}" ${formattedAmount})`)
|
|
65
|
+
.addData(receiverKeysetName, receiverGuard)
|
|
66
|
+
.addSigner(senderPublicKey, (withCapability) => [
|
|
67
|
+
withCapability("coin.TRANSFER_XCHAIN", sender, receiver, {
|
|
68
|
+
decimal: formattedAmount,
|
|
69
|
+
}, targetChain),
|
|
70
|
+
withCapability("coin.GAS"),
|
|
71
|
+
])
|
|
72
|
+
.setMeta({
|
|
73
|
+
chainId: sourceChain,
|
|
74
|
+
senderAccount: sender,
|
|
75
|
+
// v3.2.3: added `creationTime: safeCreationTime()` to close audit
|
|
76
|
+
// finding F-BUG-002. The v2.3.0 audit consolidated `safeCreationTime()`
|
|
77
|
+
// (returns `Date.now()/1000 - 30`) precisely so client-side clock drift
|
|
78
|
+
// doesn't trigger chain-side "creation time too far in the future"
|
|
79
|
+
// rejections; without this field, `@kadena/client` falls back to the
|
|
80
|
+
// raw current time and any consumer with a clock slightly ahead of
|
|
81
|
+
// node time gets sporadic submit failures. This was the lone builder
|
|
82
|
+
// in interactions/* that omitted the helper after the v2.3.0 sweep —
|
|
83
|
+
// every other setMeta block (including `buildCTransferAcross` two
|
|
84
|
+
// functions below) already includes it.
|
|
85
|
+
creationTime: safeCreationTime(),
|
|
86
|
+
gasLimit: 2500,
|
|
87
|
+
gasPrice: 0.00000001,
|
|
88
|
+
ttl: 28800,
|
|
89
|
+
})
|
|
90
|
+
.setNetworkId(KADENA_NETWORK)
|
|
91
|
+
.createTransaction();
|
|
92
|
+
return transaction;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build a coin.C_TransferAcross transaction (step 0 of cross-chain transfer).
|
|
96
|
+
*
|
|
97
|
+
* Chain 0: Gas paid by Ouronet Gas Station (DALOS.GAS_PAYER).
|
|
98
|
+
* Other chains: Gas paid by kadena-xchain-gas (unsigned).
|
|
99
|
+
*/
|
|
100
|
+
export function buildCTransferAcross(params) {
|
|
101
|
+
const { sender, receiver, receiverGuard, amount, sourceChain, targetChain, senderPublicKey, gasStationPublicKey, } = params;
|
|
102
|
+
const keysetName = `receiver-guard-${Date.now()}`;
|
|
103
|
+
const pactCode = `(coin.C_TransferAcross "${sender}" "${receiver}" (read-keyset "${keysetName}") "${targetChain}" ${amount})`;
|
|
104
|
+
if (sourceChain === "0") {
|
|
105
|
+
// Chain 0: Gas Station pays via DALOS.GAS_PAYER
|
|
106
|
+
if (!gasStationPublicKey) {
|
|
107
|
+
throw new Error("gasStationPublicKey is required for chain 0 transfers");
|
|
108
|
+
}
|
|
109
|
+
return Pact.builder
|
|
110
|
+
.execution(pactCode)
|
|
111
|
+
.addData(keysetName, receiverGuard)
|
|
112
|
+
.addSigner(gasStationPublicKey, (w) => [
|
|
113
|
+
w(`${KADENA_NAMESPACE}.DALOS.GAS_PAYER`, "", { int: 0 }, { decimal: "0.0" }),
|
|
114
|
+
])
|
|
115
|
+
.addSigner(senderPublicKey, (w) => [
|
|
116
|
+
w("coin.TRANSFER_XCHAIN", sender, receiver, { decimal: amount }, targetChain),
|
|
117
|
+
])
|
|
118
|
+
.setMeta({
|
|
119
|
+
chainId: "0",
|
|
120
|
+
senderAccount: STOA_AUTONOMIC_OURONETGASSTATION,
|
|
121
|
+
creationTime: safeCreationTime(),
|
|
122
|
+
gasLimit: 2500,
|
|
123
|
+
gasPrice: anuToStoa(GAS_PRICE_MIN_ANU),
|
|
124
|
+
ttl: 28800,
|
|
125
|
+
})
|
|
126
|
+
.setNetworkId(KADENA_NETWORK)
|
|
127
|
+
.createTransaction();
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Non-chain-0: kadena-xchain-gas pays gas.
|
|
131
|
+
// Its guard uses coin.gas-only (no signature needed) BUT requires:
|
|
132
|
+
// - gasPrice <= 0.00000001
|
|
133
|
+
// - gasLimit <= 850
|
|
134
|
+
// No signer needed for gas — only sender signs TRANSFER_XCHAIN cap.
|
|
135
|
+
return Pact.builder
|
|
136
|
+
.execution(pactCode)
|
|
137
|
+
.addData(keysetName, receiverGuard)
|
|
138
|
+
.addSigner(senderPublicKey, (w) => [
|
|
139
|
+
w("coin.TRANSFER_XCHAIN", sender, receiver, { decimal: amount }, targetChain),
|
|
140
|
+
])
|
|
141
|
+
.setMeta({
|
|
142
|
+
chainId: sourceChain,
|
|
143
|
+
senderAccount: "kadena-xchain-gas",
|
|
144
|
+
gasLimit: 850, // must be ≤ 850 for kadena-xchain-gas guard
|
|
145
|
+
gasPrice: 0.00000001, // must be ≤ 0.00000001 for kadena-xchain-gas guard
|
|
146
|
+
ttl: 28800,
|
|
147
|
+
})
|
|
148
|
+
.setNetworkId(KADENA_NETWORK)
|
|
149
|
+
.createTransaction();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Submit a cross-chain transfer and get the request key.
|
|
154
|
+
*
|
|
155
|
+
* Resolves to the chainweb `ITransactionDescriptor` (request key + chain id +
|
|
156
|
+
* network id) for the submitted transaction. Caller is responsible for
|
|
157
|
+
* persisting the descriptor — it's the only handle for `pollTransactionStatus`,
|
|
158
|
+
* `fetchSpvProof`, and `submitContinuation` later in the cross-chain flow.
|
|
159
|
+
*
|
|
160
|
+
* **Error contract** (closes audit finding F-ERR-001 — v3.2.3):
|
|
161
|
+
*
|
|
162
|
+
* Unlike its sibling `pollTransactionStatus` (which catches errors and
|
|
163
|
+
* returns `{ status: "pending", error: ... }` — the codebase's discriminated-
|
|
164
|
+
* union convention), this function deliberately propagates errors rather
|
|
165
|
+
* than swallowing them. A failed submit is not a transient state to poll
|
|
166
|
+
* past — it means the transaction did NOT land on chain, and the caller's
|
|
167
|
+
* UI must surface that as a hard failure (refund the user's escrowed gas
|
|
168
|
+
* estimate, present a retry button, etc.). Wrapping the throw in an
|
|
169
|
+
* envelope would mask the failure.
|
|
170
|
+
*
|
|
171
|
+
* @throws {SigningError} with `code: "TIMEOUT"` if the underlying
|
|
172
|
+
* `submit()` exceeds the per-tier deadline (default 60s for submit).
|
|
173
|
+
* See `runWithTimeout` in `src/network/failoverClient.ts`.
|
|
174
|
+
* @throws {Error} On network failure that survives both the primary and
|
|
175
|
+
* fallback node attempts. Both errors propagate verbatim from
|
|
176
|
+
* `getFailoverClient`'s submit method — the caller's catch block
|
|
177
|
+
* should distinguish via `error instanceof SigningError && error.code === "TIMEOUT"`.
|
|
178
|
+
*/
|
|
179
|
+
export async function submitCrossChainTransfer(signedTransaction, sourceChain) {
|
|
180
|
+
const { submit } = getFailoverClient(sourceChain);
|
|
181
|
+
return await submit(signedTransaction);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Poll for transaction status on source chain
|
|
185
|
+
*/
|
|
186
|
+
export async function pollTransactionStatus(requestKey, chainId) {
|
|
187
|
+
try {
|
|
188
|
+
const { pollOne } = getFailoverClient(chainId);
|
|
189
|
+
const result = await pollOne({ requestKey, chainId: chainId, networkId: KADENA_NETWORK });
|
|
190
|
+
if (!result) {
|
|
191
|
+
return { status: "pending" };
|
|
192
|
+
}
|
|
193
|
+
if (result.result.status === "failure") {
|
|
194
|
+
return {
|
|
195
|
+
status: "failure",
|
|
196
|
+
error: result.result.error?.message || "Transaction failed",
|
|
197
|
+
result,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// Check if this is a defpact with continuation info
|
|
201
|
+
const continuation = result.continuation ? {
|
|
202
|
+
pactId: result.continuation.pactId,
|
|
203
|
+
step: result.continuation.step,
|
|
204
|
+
stepHasRollback: result.continuation.stepHasRollback,
|
|
205
|
+
} : undefined;
|
|
206
|
+
return {
|
|
207
|
+
status: "success",
|
|
208
|
+
continuation,
|
|
209
|
+
result,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
return {
|
|
214
|
+
status: "pending",
|
|
215
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Default per-attempt timeout for `fetchSpvProof` in milliseconds. Picked to
|
|
221
|
+
* be longer than the chainweb dirty-read default (15s) since SPV proof
|
|
222
|
+
* generation may pull from disk on the node, but short enough that a wedged
|
|
223
|
+
* primary node doesn't hang the consumer's retry loop. The `pollSpvProof`
|
|
224
|
+
* outer loop adds its own retry budget on top of this per-attempt cap.
|
|
225
|
+
*
|
|
226
|
+
* Added in v3.2.3 to close audit finding F-BUG-004.
|
|
227
|
+
*/
|
|
228
|
+
const SPV_PROOF_TIMEOUT_MS = 30000;
|
|
229
|
+
/**
|
|
230
|
+
* Fetch SPV proof for a cross-chain transfer.
|
|
231
|
+
*
|
|
232
|
+
* Requires waiting for block finality (~100-120 seconds). The on-chain
|
|
233
|
+
* proof becomes available a few blocks after the source-chain submit
|
|
234
|
+
* commits; before that, the chainweb node returns `400 not available`.
|
|
235
|
+
*
|
|
236
|
+
* v3.2.3 (closes audit finding F-BUG-004): wrapped in `withFailover` and
|
|
237
|
+
* `AbortSignal.timeout(SPV_PROOF_TIMEOUT_MS)`. Pre-v3.2.3, this was the
|
|
238
|
+
* only chain-RPC function in the entire codebase that called raw `fetch()`
|
|
239
|
+
* without either guard:
|
|
240
|
+
*
|
|
241
|
+
* - No `AbortController`: a wedged primary node (slow, not erroring)
|
|
242
|
+
* would hang the `await fetch(...)` indefinitely. The outer
|
|
243
|
+
* `pollSpvProof` retry loop never advanced because each attempt
|
|
244
|
+
* awaited the hung fetch.
|
|
245
|
+
*
|
|
246
|
+
* - No `withFailover`: even if the primary node returned an error or
|
|
247
|
+
* timed out, traffic stayed pinned to the primary; the consumer's
|
|
248
|
+
* subsequent attempts hit the same wedged node forever.
|
|
249
|
+
*
|
|
250
|
+
* - Combined consequence: the user's KDA was committed to
|
|
251
|
+
* `kadena-xchain-gas` escrow on the source chain (step 1 succeeded),
|
|
252
|
+
* but step 2's SPV proof retrieval hung silently with the UI showing
|
|
253
|
+
* a perpetual "Waiting for SPV proof..." spinner and no recovery
|
|
254
|
+
* path. This is the highest-impact bug surfaced by the 2026-05-05
|
|
255
|
+
* audit.
|
|
256
|
+
*
|
|
257
|
+
* Post-v3.2.3 behaviour: each attempt has a hard 30s deadline; on timeout
|
|
258
|
+
* (or any network-class error on the primary node), `withFailover`
|
|
259
|
+
* switches to the fallback node and retries once. The outer
|
|
260
|
+
* `pollSpvProof` loop then re-invokes this function after the configured
|
|
261
|
+
* `delayMs` (default 5s), so a stuck primary surfaces as ~30s of
|
|
262
|
+
* waiting, then automatic fallback, then continued polling — never an
|
|
263
|
+
* infinite hang.
|
|
264
|
+
*/
|
|
265
|
+
export async function fetchSpvProof(requestKey, sourceChain, targetChain) {
|
|
266
|
+
try {
|
|
267
|
+
const response = await withFailover((baseUrl) => fetch(`${baseUrl}/chain/${sourceChain}/pact/spv`, {
|
|
268
|
+
method: "POST",
|
|
269
|
+
headers: { "Content-Type": "application/json" },
|
|
270
|
+
body: JSON.stringify({
|
|
271
|
+
requestKey,
|
|
272
|
+
targetChainId: targetChain,
|
|
273
|
+
}),
|
|
274
|
+
signal: AbortSignal.timeout(SPV_PROOF_TIMEOUT_MS),
|
|
275
|
+
}));
|
|
276
|
+
if (!response.ok) {
|
|
277
|
+
const errorText = await response.text();
|
|
278
|
+
// Chainweb returns 400 "not available" while the proof is still being
|
|
279
|
+
// generated (pre-finality). pollSpvProof's outer loop translates this
|
|
280
|
+
// into "wait and try again" — pass null + descriptive error.
|
|
281
|
+
if (response.status === 400 && errorText.includes("not available")) {
|
|
282
|
+
return { proof: null, error: "SPV proof not ready yet. Please wait for block confirmation." };
|
|
283
|
+
}
|
|
284
|
+
return { proof: null, error: `Failed to fetch SPV proof: ${errorText}` };
|
|
285
|
+
}
|
|
286
|
+
const proof = await response.text();
|
|
287
|
+
// Remove quotes if present (API returns JSON string)
|
|
288
|
+
const cleanProof = proof.replace(/^"|"$/g, "");
|
|
289
|
+
return { proof: cleanProof };
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
// AbortError surfaces here when the 30s timeout fires. withFailover
|
|
293
|
+
// will already have retried on fallback by this point — if BOTH primary
|
|
294
|
+
// and fallback timed out (or otherwise failed), the error propagates
|
|
295
|
+
// out as the standard `{ proof: null, error }` shape so the polling
|
|
296
|
+
// loop above can decide whether to retry the whole sequence.
|
|
297
|
+
const message = error instanceof Error
|
|
298
|
+
? (error.name === "TimeoutError" || error.name === "AbortError"
|
|
299
|
+
? `SPV proof fetch timed out after ${SPV_PROOF_TIMEOUT_MS}ms (both primary and fallback nodes)`
|
|
300
|
+
: error.message)
|
|
301
|
+
: "Failed to fetch SPV proof";
|
|
302
|
+
return {
|
|
303
|
+
proof: null,
|
|
304
|
+
error: message,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Poll for SPV proof with retry logic
|
|
310
|
+
* Will retry until proof is available or max attempts reached
|
|
311
|
+
*/
|
|
312
|
+
export async function pollSpvProof(requestKey, sourceChain, targetChain, maxAttempts = 30, delayMs = 5000, onProgress) {
|
|
313
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
314
|
+
onProgress?.(attempt, maxAttempts);
|
|
315
|
+
const result = await fetchSpvProof(requestKey, sourceChain, targetChain);
|
|
316
|
+
if (result.proof) {
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
if (attempt < maxAttempts) {
|
|
320
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { proof: null, error: "Timeout waiting for SPV proof" };
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Build a continuation transaction for step 2 of cross-chain transfer
|
|
327
|
+
* This can be executed by anyone - no signature required from original sender
|
|
328
|
+
*/
|
|
329
|
+
export function buildContinuationTransaction(pactId, proof, targetChain, gasPayerAccount = "kadena-xchain-gas") {
|
|
330
|
+
const transaction = Pact.builder
|
|
331
|
+
.continuation({
|
|
332
|
+
pactId,
|
|
333
|
+
rollback: false,
|
|
334
|
+
step: 1,
|
|
335
|
+
proof,
|
|
336
|
+
})
|
|
337
|
+
.setMeta({
|
|
338
|
+
chainId: targetChain,
|
|
339
|
+
senderAccount: gasPayerAccount,
|
|
340
|
+
gasLimit: 850,
|
|
341
|
+
gasPrice: 0.00000001,
|
|
342
|
+
ttl: 28800,
|
|
343
|
+
})
|
|
344
|
+
.setNetworkId(KADENA_NETWORK)
|
|
345
|
+
.createTransaction();
|
|
346
|
+
return transaction;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Submit a continuation transaction.
|
|
350
|
+
*
|
|
351
|
+
* Note: for the public gas station path (chain 0 + DALOS.GAS_PAYER), no
|
|
352
|
+
* signature is needed — the caller passes the unsigned `transaction`
|
|
353
|
+
* directly. For user-paid-gas paths the transaction MUST be signed before
|
|
354
|
+
* this function is called; the function itself does not sign.
|
|
355
|
+
*
|
|
356
|
+
* **Error contract** (closes audit finding F-ERR-001 — v3.2.3): same
|
|
357
|
+
* propagating-throw pattern as `submitCrossChainTransfer`. A failed
|
|
358
|
+
* continuation submit is a hard failure, not a transient state — surface
|
|
359
|
+
* to the user, don't swallow.
|
|
360
|
+
*
|
|
361
|
+
* @throws {SigningError} with `code: "TIMEOUT"` if `submit()` exceeds
|
|
362
|
+
* the per-tier deadline.
|
|
363
|
+
* @throws {Error} On network failure surviving both primary and fallback
|
|
364
|
+
* node attempts.
|
|
365
|
+
*/
|
|
366
|
+
export async function submitContinuation(transaction, targetChain) {
|
|
367
|
+
const { submit } = getFailoverClient(targetChain);
|
|
368
|
+
return await submit(transaction);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Listen for transaction completion (long-poll for chainweb inclusion).
|
|
372
|
+
*
|
|
373
|
+
* Resolves to the chainweb command-result envelope when the transaction
|
|
374
|
+
* is included in a block (~30-180s on mainnet depending on block density).
|
|
375
|
+
*
|
|
376
|
+
* **Error contract** (closes audit finding F-ERR-001 — v3.2.3): a thrown
|
|
377
|
+
* error from this function is **NOT** a definitive failure. The chainweb
|
|
378
|
+
* `listen` endpoint times out after a per-tier deadline (default 180s,
|
|
379
|
+
* approximately 6 Kadena blocks); if the deadline fires, the transaction
|
|
380
|
+
* may still complete on chain — it just hasn't been included yet by the
|
|
381
|
+
* time this function gives up. Callers should treat a `SigningError(code:
|
|
382
|
+
* "TIMEOUT")` from this function as `status: "pending"` (poll again
|
|
383
|
+
* later via `pollTransactionStatus`), NOT as `status: "failed"` (do not
|
|
384
|
+
* retry the submit; it would double-pay gas for a transaction that may
|
|
385
|
+
* already be confirmed). This is the exact pattern that audit finding
|
|
386
|
+
* F-ERR-014 surfaced for the multi-step add-liquidity flow before that
|
|
387
|
+
* surface was deleted in v3.2.2.
|
|
388
|
+
*
|
|
389
|
+
* @throws {SigningError} with `code: "TIMEOUT"` if `listen()` exceeds
|
|
390
|
+
* the 180s deadline. **Treat as pending, not failed.**
|
|
391
|
+
* @throws {Error} On network failure surviving both primary and fallback
|
|
392
|
+
* node attempts.
|
|
393
|
+
*/
|
|
394
|
+
export async function listenForCompletion(requestKey, chainId) {
|
|
395
|
+
const { listen } = getFailoverClient(chainId);
|
|
396
|
+
return await listen({ requestKey, chainId: chainId, networkId: KADENA_NETWORK });
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Simulate a Pact code execution to check if it will succeed.
|
|
400
|
+
*
|
|
401
|
+
* Public-API break (v1.8): the first parameter is now the raw Pact code string
|
|
402
|
+
* rather than a built transaction. Consumers that previously held the built
|
|
403
|
+
* transaction now hold the pact-code string they fed into `.execution(...)`.
|
|
404
|
+
*/
|
|
405
|
+
export async function simulateTransaction(pactCode, chainId) {
|
|
406
|
+
try {
|
|
407
|
+
const result = await pactRead(pactCode, {
|
|
408
|
+
pactUrl: getPactUrl(chainId),
|
|
409
|
+
chainId,
|
|
410
|
+
tier: "T2",
|
|
411
|
+
});
|
|
412
|
+
if (result.result.status === "failure") {
|
|
413
|
+
return {
|
|
414
|
+
success: false,
|
|
415
|
+
error: result.result.error?.message || "Simulation failed",
|
|
416
|
+
result,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
success: true,
|
|
421
|
+
result,
|
|
422
|
+
gas: result.gas,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
return {
|
|
427
|
+
success: false,
|
|
428
|
+
error: error instanceof Error ? error.message : "Simulation failed",
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get pending cross-chain transfers for an account
|
|
434
|
+
* Checks for continuation status
|
|
435
|
+
*/
|
|
436
|
+
export async function getContinuationStatus(requestKey, sourceChain) {
|
|
437
|
+
try {
|
|
438
|
+
const status = await pollTransactionStatus(requestKey, sourceChain);
|
|
439
|
+
if (status.status === "not-found") {
|
|
440
|
+
return { exists: false, completed: false, error: "Transaction not found" };
|
|
441
|
+
}
|
|
442
|
+
if (status.status === "failure") {
|
|
443
|
+
return { exists: true, completed: false, error: status.error };
|
|
444
|
+
}
|
|
445
|
+
if (status.continuation) {
|
|
446
|
+
return {
|
|
447
|
+
exists: true,
|
|
448
|
+
completed: false,
|
|
449
|
+
pactId: status.continuation.pactId,
|
|
450
|
+
step: status.continuation.step,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
// If no continuation info, transaction might be complete
|
|
454
|
+
return { exists: true, completed: true };
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
return {
|
|
458
|
+
exists: false,
|
|
459
|
+
completed: false,
|
|
460
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dexAcquisitionPoolFunctions.ts
|
|
3
|
+
*
|
|
4
|
+
* Acquisition Pool (AP) entity — placeholder file.
|
|
5
|
+
*
|
|
6
|
+
* The Acquisition Pool is one of Ouronet's seven entity types per the locked
|
|
7
|
+
* taxonomy (TF, OF, Collectables [Semi+Non Fungibles], ASP, SWP, AP). Its Pact
|
|
8
|
+
* module is **pending deployment** — chain-call functions for read/execute
|
|
9
|
+
* surfaces will be added to this file once the AP Pact module ships.
|
|
10
|
+
*
|
|
11
|
+
* This placeholder anchors the AP entity in the entity-oriented file layout so
|
|
12
|
+
* downstream consumers (OuronetUI, AncientHolder HUB) can see the entity exists
|
|
13
|
+
* in the taxonomy even though no chain calls are wired yet.
|
|
14
|
+
*
|
|
15
|
+
* See the v4.2.0 integration guide (Phase 8) for the seven-entity taxonomy
|
|
16
|
+
* documentation and AP-specific roadmap notes.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dexAcquisitionPoolFunctions.ts
|
|
3
|
+
*
|
|
4
|
+
* Acquisition Pool (AP) entity — placeholder file.
|
|
5
|
+
*
|
|
6
|
+
* The Acquisition Pool is one of Ouronet's seven entity types per the locked
|
|
7
|
+
* taxonomy (TF, OF, Collectables [Semi+Non Fungibles], ASP, SWP, AP). Its Pact
|
|
8
|
+
* module is **pending deployment** — chain-call functions for read/execute
|
|
9
|
+
* surfaces will be added to this file once the AP Pact module ships.
|
|
10
|
+
*
|
|
11
|
+
* This placeholder anchors the AP entity in the entity-oriented file layout so
|
|
12
|
+
* downstream consumers (OuronetUI, AncientHolder HUB) can see the entity exists
|
|
13
|
+
* in the taxonomy even though no chain calls are wired yet.
|
|
14
|
+
*
|
|
15
|
+
* See the v4.2.0 integration guide (Phase 8) for the seven-entity taxonomy
|
|
16
|
+
* documentation and AP-specific roadmap notes.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dexCollectablesFunctions.ts
|
|
3
|
+
* Collectables (Semi + Non Fungibles) entity reads — header, entries, nonce data, sets, filters, button state.
|
|
4
|
+
*/
|
|
5
|
+
import type { CollectablesHeaderData, CollectableEntryData, NonceData, SetDefinition, CollectablesButtonState } from "./dexTypes.js";
|
|
6
|
+
export declare function getCollectablesHeader(account: string): Promise<CollectablesHeaderData | null>;
|
|
7
|
+
export declare function getSemifungibleEntries(account: string, dpdcIds: string[]): Promise<(CollectableEntryData | null)[]>;
|
|
8
|
+
export declare function getNonfungibleEntries(account: string, dpdcIds: string[]): Promise<(CollectableEntryData | null)[]>;
|
|
9
|
+
export declare function getCollectableNonceData(dpdcId: string, isSFT: boolean, nonces: number[]): Promise<NonceData[]>;
|
|
10
|
+
export declare function getCollectableNonceSupply(dpdcId: string, isSFT: boolean, nonce: number): Promise<number>;
|
|
11
|
+
/**
|
|
12
|
+
* Batched per-account per-nonce supplies (DPDC.UR_AccountNoncesSupplies).
|
|
13
|
+
* Replaces the per-nonce UR_NonceSupply loop with a single read for N nonces.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getAccountNoncesSupplies(account: string, dpdcId: string, isSFT: boolean, nonces: number[]): Promise<number[]>;
|
|
16
|
+
export declare function getCollectableSets(dpdcId: string, isSFT: boolean): Promise<SetDefinition[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Filter wallet nonces by nonce class for composite set positions.
|
|
19
|
+
* Returns only the nonces that match the given class.
|
|
20
|
+
*/
|
|
21
|
+
export declare function filterNoncesByClass(dpdcId: string, isSFT: boolean, nonces: number[], nonceClass: number): Promise<number[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Batched filter-nonces-by-classes (URC_0025a_FilterNoncesByClasses).
|
|
24
|
+
* One network call returns one filtered nonce list per class, in order.
|
|
25
|
+
* Replaces N per-position calls to filterNoncesByClass.
|
|
26
|
+
*/
|
|
27
|
+
export declare function filterNoncesByClasses(dpdcId: string, isSFT: boolean, nonces: number[], nonceClasses: number[]): Promise<number[][]>;
|
|
28
|
+
export declare function getCollectablesButtonState(account: string, dpdcId: string, isSFT: boolean, selectedNonces: number[]): Promise<CollectablesButtonState | null>;
|