@toon-protocol/sdk 2.0.1 → 2.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.
- package/dist/{chunk-Z6S56VPV.js → chunk-7LBYFU4L.js} +658 -204
- package/dist/chunk-7LBYFU4L.js.map +1 -0
- package/dist/index.d.ts +29 -6
- package/dist/index.js +361 -1
- package/dist/index.js.map +1 -1
- package/dist/swap-Oub-0vqU.d.ts +911 -0
- package/dist/swap.d.ts +1 -1
- package/dist/swap.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-Z6S56VPV.js.map +0 -1
- package/dist/swap-DF4ghKio.d.ts +0 -484
|
@@ -788,8 +788,20 @@ function createSwapHandler(config) {
|
|
|
788
788
|
seenPacketIds.delete(packetId);
|
|
789
789
|
};
|
|
790
790
|
let rate;
|
|
791
|
+
let rateTimestamp;
|
|
791
792
|
try {
|
|
792
|
-
|
|
793
|
+
const provided = config.rateProvider ? await config.rateProvider(pair) : pair.rate;
|
|
794
|
+
if (typeof provided === "string") {
|
|
795
|
+
rate = provided;
|
|
796
|
+
rateTimestamp = Date.now();
|
|
797
|
+
} else if (provided !== null && typeof provided === "object" && typeof provided.rate === "string" && typeof provided.rateTimestamp === "number" && Number.isInteger(provided.rateTimestamp) && provided.rateTimestamp > 0) {
|
|
798
|
+
rate = provided.rate;
|
|
799
|
+
rateTimestamp = provided.rateTimestamp;
|
|
800
|
+
} else {
|
|
801
|
+
throw new SwapHandlerError(
|
|
802
|
+
"rateProvider must return a decimal-string rate or { rate, rateTimestamp }"
|
|
803
|
+
);
|
|
804
|
+
}
|
|
793
805
|
} catch (err) {
|
|
794
806
|
logger.error({
|
|
795
807
|
event: "swap_handler.rate_provider_failed",
|
|
@@ -885,7 +897,9 @@ function createSwapHandler(config) {
|
|
|
885
897
|
const metadata = {
|
|
886
898
|
claim: claimBase64,
|
|
887
899
|
ephemeralPubkey,
|
|
888
|
-
targetAmount: targetAmount.toString()
|
|
900
|
+
targetAmount: targetAmount.toString(),
|
|
901
|
+
rate,
|
|
902
|
+
rateTimestamp
|
|
889
903
|
};
|
|
890
904
|
if (claimId !== void 0) metadata["claimId"] = claimId;
|
|
891
905
|
if (settlementChannelId !== void 0 && settlementNonce !== void 0 && settlementCumulative !== void 0 && settlementRecipient !== void 0 && settlementSwapSigner !== void 0) {
|
|
@@ -992,7 +1006,7 @@ function validateChainAddress(value, chain, kind) {
|
|
|
992
1006
|
}
|
|
993
1007
|
return value.length > 0;
|
|
994
1008
|
}
|
|
995
|
-
function decodeFulfillMetadata(data, chain) {
|
|
1009
|
+
function decodeFulfillMetadata(data, chain, opts) {
|
|
996
1010
|
if (data === void 0 || data === null || data === "") {
|
|
997
1011
|
throw new StreamSwapError("FULFILL_DECODE_FAILED", "FULFILL data missing");
|
|
998
1012
|
}
|
|
@@ -1060,6 +1074,37 @@ function decodeFulfillMetadata(data, chain) {
|
|
|
1060
1074
|
}
|
|
1061
1075
|
result.targetAmount = ta;
|
|
1062
1076
|
}
|
|
1077
|
+
const tapeRate = obj["rate"];
|
|
1078
|
+
const tapeTimestamp = obj["rateTimestamp"];
|
|
1079
|
+
const hasRate = tapeRate !== void 0;
|
|
1080
|
+
const hasTimestamp = tapeTimestamp !== void 0;
|
|
1081
|
+
if (hasRate !== hasTimestamp) {
|
|
1082
|
+
throw new StreamSwapError(
|
|
1083
|
+
"FULFILL_DECODE_FAILED",
|
|
1084
|
+
"FULFILL metadata quote tape is malformed: rate and rateTimestamp must travel together"
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
if (hasRate) {
|
|
1088
|
+
if (typeof tapeRate !== "string" || !RATE_REGEX2.test(tapeRate) || /^0(\.0+)?$/.test(tapeRate)) {
|
|
1089
|
+
throw new StreamSwapError(
|
|
1090
|
+
"FULFILL_DECODE_FAILED",
|
|
1091
|
+
"FULFILL metadata.rate must be a positive decimal string"
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
if (typeof tapeTimestamp !== "number" || !Number.isInteger(tapeTimestamp) || tapeTimestamp <= 0) {
|
|
1095
|
+
throw new StreamSwapError(
|
|
1096
|
+
"FULFILL_DECODE_FAILED",
|
|
1097
|
+
"FULFILL metadata.rateTimestamp must be a positive integer (unix ms)"
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
result.rate = tapeRate;
|
|
1101
|
+
result.rateTimestamp = tapeTimestamp;
|
|
1102
|
+
} else if (opts?.requireQuoteTape === true) {
|
|
1103
|
+
throw new StreamSwapError(
|
|
1104
|
+
"FULFILL_DECODE_FAILED",
|
|
1105
|
+
"FULFILL metadata is missing the quote tape (rate + rateTimestamp) required when minExchangeRate is set"
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1063
1108
|
const channelId = obj["channelId"];
|
|
1064
1109
|
if (typeof channelId === "string" && (!chain || validateChainAddress(channelId, chain, "channelId"))) {
|
|
1065
1110
|
result.channelId = channelId;
|
|
@@ -1082,6 +1127,18 @@ function decodeFulfillMetadata(data, chain) {
|
|
|
1082
1127
|
}
|
|
1083
1128
|
return result;
|
|
1084
1129
|
}
|
|
1130
|
+
function compareDecimalRates(a, b) {
|
|
1131
|
+
const split = (s) => {
|
|
1132
|
+
const dot = s.indexOf(".");
|
|
1133
|
+
return dot === -1 ? { int: s, frac: "" } : { int: s.slice(0, dot), frac: s.slice(dot + 1) };
|
|
1134
|
+
};
|
|
1135
|
+
const pa = split(a);
|
|
1136
|
+
const pb = split(b);
|
|
1137
|
+
const scale = Math.max(pa.frac.length, pb.frac.length);
|
|
1138
|
+
const av = BigInt(pa.int + pa.frac.padEnd(scale, "0"));
|
|
1139
|
+
const bv = BigInt(pb.int + pb.frac.padEnd(scale, "0"));
|
|
1140
|
+
return av < bv ? -1 : av > bv ? 1 : 0;
|
|
1141
|
+
}
|
|
1085
1142
|
var Deferred = class {
|
|
1086
1143
|
promise;
|
|
1087
1144
|
resolve;
|
|
@@ -1110,13 +1167,25 @@ function validateParams(params) {
|
|
|
1110
1167
|
}
|
|
1111
1168
|
const hasCount = params.packetCount !== void 0;
|
|
1112
1169
|
const hasAmounts = params.packetAmounts !== void 0;
|
|
1113
|
-
|
|
1170
|
+
const hasController = params.controller !== void 0;
|
|
1171
|
+
const chunkingModes = [hasCount, hasAmounts, hasController].filter(
|
|
1172
|
+
Boolean
|
|
1173
|
+
).length;
|
|
1174
|
+
if (chunkingModes !== 1) {
|
|
1114
1175
|
throw new StreamSwapError(
|
|
1115
1176
|
"INVALID_CHUNKING",
|
|
1116
|
-
"Exactly one of packetCount or
|
|
1177
|
+
"Exactly one of packetCount, packetAmounts, or controller must be provided"
|
|
1117
1178
|
);
|
|
1118
1179
|
}
|
|
1119
|
-
if (
|
|
1180
|
+
if (hasController) {
|
|
1181
|
+
const c = params.controller;
|
|
1182
|
+
if (typeof c.nextDelta !== "function" || typeof c.observe !== "function" || typeof c.window !== "number") {
|
|
1183
|
+
throw new StreamSwapError(
|
|
1184
|
+
"INVALID_STATE",
|
|
1185
|
+
"controller must implement nextDelta(remaining), observe(observation), and window"
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
} else if (hasCount) {
|
|
1120
1189
|
const c = params.packetCount;
|
|
1121
1190
|
if (!Number.isInteger(c) || c <= 0) {
|
|
1122
1191
|
throw new StreamSwapError(
|
|
@@ -1208,6 +1277,22 @@ function validateParams(params) {
|
|
|
1208
1277
|
`rateDeviationThreshold must be a non-negative finite number, got ${params.rateDeviationThreshold}`
|
|
1209
1278
|
);
|
|
1210
1279
|
}
|
|
1280
|
+
if (params.minExchangeRate !== void 0) {
|
|
1281
|
+
if (typeof params.minExchangeRate !== "string" || !RATE_REGEX2.test(params.minExchangeRate) || /^0(\.0+)?$/.test(params.minExchangeRate)) {
|
|
1282
|
+
throw new StreamSwapError(
|
|
1283
|
+
"INVALID_STATE",
|
|
1284
|
+
`minExchangeRate must be a positive decimal string matching ${RATE_REGEX2}, got ${String(
|
|
1285
|
+
params.minExchangeRate
|
|
1286
|
+
)}`
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
if (params.packetExpiryMs !== void 0 && (typeof params.packetExpiryMs !== "number" || !Number.isInteger(params.packetExpiryMs) || params.packetExpiryMs <= 0)) {
|
|
1291
|
+
throw new StreamSwapError(
|
|
1292
|
+
"INVALID_STATE",
|
|
1293
|
+
`packetExpiryMs must be a positive integer (ms), got ${params.packetExpiryMs}`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1211
1296
|
if (typeof params.chainRecipient !== "string" || params.chainRecipient.length === 0) {
|
|
1212
1297
|
throw new StreamSwapError(
|
|
1213
1298
|
"INVALID_STATE",
|
|
@@ -1231,7 +1316,7 @@ async function streamSwap(params) {
|
|
|
1231
1316
|
function streamSwapControlled(params) {
|
|
1232
1317
|
validateParams(params);
|
|
1233
1318
|
const logger = params.logger ?? NOOP_LOGGER2;
|
|
1234
|
-
const schedule = params.packetAmounts ? [...params.packetAmounts] : chunkAmount(params.totalAmount, params.packetCount);
|
|
1319
|
+
const schedule = params.packetAmounts ? [...params.packetAmounts] : params.packetCount !== void 0 ? chunkAmount(params.totalAmount, params.packetCount) : [];
|
|
1235
1320
|
const frozenPair = Object.freeze({
|
|
1236
1321
|
from: Object.freeze({ ...params.pair.from }),
|
|
1237
1322
|
to: Object.freeze({ ...params.pair.to }),
|
|
@@ -1274,30 +1359,305 @@ function streamSwapControlled(params) {
|
|
|
1274
1359
|
return streamState;
|
|
1275
1360
|
}
|
|
1276
1361
|
};
|
|
1277
|
-
const
|
|
1362
|
+
const getState = () => streamState;
|
|
1363
|
+
const setState = (v) => {
|
|
1364
|
+
streamState = v;
|
|
1365
|
+
};
|
|
1366
|
+
const waitForResumeOrStop = () => {
|
|
1367
|
+
if (streamState !== "paused")
|
|
1368
|
+
return Promise.resolve("resume");
|
|
1369
|
+
if (!resumeDeferred) resumeDeferred = new Deferred();
|
|
1370
|
+
return resumeDeferred.promise;
|
|
1371
|
+
};
|
|
1372
|
+
const result = params.controller ? runAdaptiveLoop(
|
|
1373
|
+
params,
|
|
1374
|
+
frozenPair,
|
|
1375
|
+
senderPubkey,
|
|
1376
|
+
logger,
|
|
1377
|
+
getState,
|
|
1378
|
+
setState,
|
|
1379
|
+
waitForResumeOrStop
|
|
1380
|
+
) : runLoop(
|
|
1278
1381
|
params,
|
|
1279
1382
|
frozenPair,
|
|
1280
1383
|
schedule,
|
|
1281
1384
|
senderPubkey,
|
|
1282
1385
|
logger,
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
},
|
|
1287
|
-
() => {
|
|
1288
|
-
if (streamState !== "paused") return Promise.resolve("resume");
|
|
1289
|
-
if (!resumeDeferred) resumeDeferred = new Deferred();
|
|
1290
|
-
return resumeDeferred.promise;
|
|
1291
|
-
}
|
|
1386
|
+
getState,
|
|
1387
|
+
setState,
|
|
1388
|
+
waitForResumeOrStop
|
|
1292
1389
|
);
|
|
1293
1390
|
return { result, controller };
|
|
1294
1391
|
}
|
|
1392
|
+
function buildAndWrapPacket(input) {
|
|
1393
|
+
const { params, pair, senderPubkey, sourceAmount, seq, totalPackets } = input;
|
|
1394
|
+
const nonce = new Uint8Array(16);
|
|
1395
|
+
getRandomValues(nonce);
|
|
1396
|
+
const rumor = buildSwapRumor({
|
|
1397
|
+
senderPubkey,
|
|
1398
|
+
pair,
|
|
1399
|
+
sourceAmount,
|
|
1400
|
+
packetIndex: seq,
|
|
1401
|
+
totalPackets,
|
|
1402
|
+
nonce,
|
|
1403
|
+
createdAt: Math.floor(Date.now() / 1e3),
|
|
1404
|
+
chainRecipient: params.chainRecipient
|
|
1405
|
+
});
|
|
1406
|
+
const packetExpiresAt = params.packetExpiryMs !== void 0 ? new Date(Date.now() + params.packetExpiryMs) : void 0;
|
|
1407
|
+
const wrapped = wrapSwapPacketToToon({
|
|
1408
|
+
rumor,
|
|
1409
|
+
senderSecretKey: params.senderSecretKey,
|
|
1410
|
+
recipientPubkey: params.swapPubkey,
|
|
1411
|
+
destination: params.swapIlpAddress,
|
|
1412
|
+
amount: sourceAmount,
|
|
1413
|
+
...packetExpiresAt !== void 0 && { expiresAt: packetExpiresAt }
|
|
1414
|
+
});
|
|
1415
|
+
const toonData = new Uint8Array(
|
|
1416
|
+
Buffer.from(wrapped.ilpPrepare.data, "base64")
|
|
1417
|
+
);
|
|
1418
|
+
return {
|
|
1419
|
+
toonData,
|
|
1420
|
+
...packetExpiresAt !== void 0 && { packetExpiresAt }
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
async function processAcceptedPacket(ctx, args) {
|
|
1424
|
+
const { params, pair, logger } = ctx;
|
|
1425
|
+
const { packetIndex, totalForProgress, sourceAmount, data } = args;
|
|
1426
|
+
let metadata;
|
|
1427
|
+
try {
|
|
1428
|
+
metadata = decodeFulfillMetadata(data, pair.to.chain, {
|
|
1429
|
+
requireQuoteTape: params.minExchangeRate !== void 0
|
|
1430
|
+
});
|
|
1431
|
+
} catch (err) {
|
|
1432
|
+
logger.error({
|
|
1433
|
+
event: "stream_swap.fulfill_decode_failed",
|
|
1434
|
+
packetIndex,
|
|
1435
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1436
|
+
});
|
|
1437
|
+
ctx.errors.push({
|
|
1438
|
+
packetIndex,
|
|
1439
|
+
cause: err instanceof Error ? err : new Error(String(err))
|
|
1440
|
+
});
|
|
1441
|
+
return { status: "error" };
|
|
1442
|
+
}
|
|
1443
|
+
const isEvmTarget = pair.to.chain.startsWith("evm:");
|
|
1444
|
+
const recipientMatches = metadata.recipient === void 0 || (isEvmTarget ? metadata.recipient.toLowerCase() === params.chainRecipient.toLowerCase() : metadata.recipient === params.chainRecipient);
|
|
1445
|
+
if (!recipientMatches) {
|
|
1446
|
+
logger.warn({
|
|
1447
|
+
event: "stream_swap.recipient_mismatch",
|
|
1448
|
+
packetIndex,
|
|
1449
|
+
expected: params.chainRecipient,
|
|
1450
|
+
actual: metadata.recipient
|
|
1451
|
+
});
|
|
1452
|
+
ctx.rejections.push({
|
|
1453
|
+
packetIndex,
|
|
1454
|
+
sourceAmount,
|
|
1455
|
+
code: "SWAP_RECIPIENT_MISMATCH",
|
|
1456
|
+
message: `Swap echoed recipient ${metadata.recipient} but sender expected ${params.chainRecipient}`
|
|
1457
|
+
});
|
|
1458
|
+
return { status: "recipient-mismatch" };
|
|
1459
|
+
}
|
|
1460
|
+
if (params.minExchangeRate !== void 0) {
|
|
1461
|
+
const tapeRate = metadata.rate;
|
|
1462
|
+
const floorTargetAmount = applyRate({
|
|
1463
|
+
sourceAmount,
|
|
1464
|
+
fromScale: pair.from.assetScale,
|
|
1465
|
+
toScale: pair.to.assetScale,
|
|
1466
|
+
rate: params.minExchangeRate
|
|
1467
|
+
});
|
|
1468
|
+
const deliveredTargetAmount = metadata.targetAmount !== void 0 ? BigInt(metadata.targetAmount) : applyRate({
|
|
1469
|
+
sourceAmount,
|
|
1470
|
+
fromScale: pair.from.assetScale,
|
|
1471
|
+
toScale: pair.to.assetScale,
|
|
1472
|
+
rate: tapeRate
|
|
1473
|
+
});
|
|
1474
|
+
const tapeBelowFloor = compareDecimalRates(tapeRate, params.minExchangeRate) < 0;
|
|
1475
|
+
if (tapeBelowFloor || deliveredTargetAmount < floorTargetAmount) {
|
|
1476
|
+
logger.warn({
|
|
1477
|
+
event: "stream_swap.below_floor",
|
|
1478
|
+
packetIndex,
|
|
1479
|
+
rate: tapeRate,
|
|
1480
|
+
rateTimestamp: metadata.rateTimestamp,
|
|
1481
|
+
minExchangeRate: params.minExchangeRate,
|
|
1482
|
+
targetAmount: deliveredTargetAmount.toString(),
|
|
1483
|
+
floorTargetAmount: floorTargetAmount.toString()
|
|
1484
|
+
});
|
|
1485
|
+
ctx.rejections.push({
|
|
1486
|
+
packetIndex,
|
|
1487
|
+
sourceAmount,
|
|
1488
|
+
code: "BELOW_FLOOR",
|
|
1489
|
+
message: `Packet fill below minExchangeRate floor: rate ${tapeRate}, targetAmount ${deliveredTargetAmount} < floor ${params.minExchangeRate} (${floorTargetAmount})`
|
|
1490
|
+
});
|
|
1491
|
+
return { status: "below-floor" };
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
let claimBytes;
|
|
1495
|
+
try {
|
|
1496
|
+
const ciphertext = new Uint8Array(Buffer.from(metadata.claim, "base64"));
|
|
1497
|
+
claimBytes = decryptFulfillClaim({
|
|
1498
|
+
ciphertext,
|
|
1499
|
+
ephemeralPubkey: metadata.ephemeralPubkey,
|
|
1500
|
+
recipientSecretKey: params.senderSecretKey
|
|
1501
|
+
});
|
|
1502
|
+
} catch (err) {
|
|
1503
|
+
logger.error({
|
|
1504
|
+
event: "stream_swap.decrypt_failed",
|
|
1505
|
+
packetIndex,
|
|
1506
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1507
|
+
});
|
|
1508
|
+
ctx.errors.push({
|
|
1509
|
+
packetIndex,
|
|
1510
|
+
cause: err instanceof Error ? err : new Error(String(err))
|
|
1511
|
+
});
|
|
1512
|
+
return { status: "error" };
|
|
1513
|
+
}
|
|
1514
|
+
if (claimBytes.length === 0) {
|
|
1515
|
+
logger.warn({
|
|
1516
|
+
event: "stream_swap.empty_claim_bytes",
|
|
1517
|
+
packetIndex
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
const expectedTargetAmount = applyRate({
|
|
1521
|
+
sourceAmount,
|
|
1522
|
+
fromScale: pair.from.assetScale,
|
|
1523
|
+
toScale: pair.to.assetScale,
|
|
1524
|
+
rate: pair.rate
|
|
1525
|
+
});
|
|
1526
|
+
const targetAmount = metadata.targetAmount !== void 0 ? BigInt(metadata.targetAmount) : expectedTargetAmount;
|
|
1527
|
+
let rateDeviation = 0;
|
|
1528
|
+
if (expectedTargetAmount > 0n) {
|
|
1529
|
+
const diff = targetAmount >= expectedTargetAmount ? targetAmount - expectedTargetAmount : expectedTargetAmount - targetAmount;
|
|
1530
|
+
const scaled = diff * 1000000n / expectedTargetAmount;
|
|
1531
|
+
rateDeviation = Number(scaled) / 1e6;
|
|
1532
|
+
}
|
|
1533
|
+
const advertisedRate = parseFloat(pair.rate);
|
|
1534
|
+
let effectiveRate;
|
|
1535
|
+
if (targetAmount === expectedTargetAmount) {
|
|
1536
|
+
effectiveRate = advertisedRate;
|
|
1537
|
+
} else {
|
|
1538
|
+
const signedDeviation = targetAmount >= expectedTargetAmount ? rateDeviation : -rateDeviation;
|
|
1539
|
+
effectiveRate = advertisedRate * (1 + signedDeviation);
|
|
1540
|
+
}
|
|
1541
|
+
if (!Number.isFinite(effectiveRate)) {
|
|
1542
|
+
effectiveRate = advertisedRate;
|
|
1543
|
+
}
|
|
1544
|
+
ctx.cumulative.source += sourceAmount;
|
|
1545
|
+
ctx.cumulative.target += targetAmount;
|
|
1546
|
+
const accumulated = {
|
|
1547
|
+
packetIndex,
|
|
1548
|
+
sourceAmount,
|
|
1549
|
+
targetAmount,
|
|
1550
|
+
claimBytes,
|
|
1551
|
+
swapEphemeralPubkey: metadata.ephemeralPubkey,
|
|
1552
|
+
pair,
|
|
1553
|
+
receivedAt: Date.now()
|
|
1554
|
+
};
|
|
1555
|
+
if (metadata.claimId !== void 0) accumulated.claimId = metadata.claimId;
|
|
1556
|
+
if (metadata.channelId !== void 0)
|
|
1557
|
+
accumulated.channelId = metadata.channelId;
|
|
1558
|
+
if (metadata.nonce !== void 0) accumulated.nonce = metadata.nonce;
|
|
1559
|
+
if (metadata.cumulativeAmount !== void 0)
|
|
1560
|
+
accumulated.cumulativeAmount = metadata.cumulativeAmount;
|
|
1561
|
+
if (metadata.recipient !== void 0)
|
|
1562
|
+
accumulated.recipient = metadata.recipient;
|
|
1563
|
+
if (metadata.swapSignerAddress !== void 0)
|
|
1564
|
+
accumulated.swapSignerAddress = metadata.swapSignerAddress;
|
|
1565
|
+
if (metadata.rate !== void 0) {
|
|
1566
|
+
accumulated.rate = metadata.rate;
|
|
1567
|
+
accumulated.rateTimestamp = metadata.rateTimestamp;
|
|
1568
|
+
}
|
|
1569
|
+
ctx.claims.push(accumulated);
|
|
1570
|
+
logger.debug({
|
|
1571
|
+
event: "stream_swap.packet_accepted",
|
|
1572
|
+
packetIndex,
|
|
1573
|
+
sourceAmount: sourceAmount.toString(),
|
|
1574
|
+
targetAmount: targetAmount.toString()
|
|
1575
|
+
});
|
|
1576
|
+
if (params.onPacket) {
|
|
1577
|
+
const progress = Object.freeze({
|
|
1578
|
+
index: packetIndex,
|
|
1579
|
+
total: totalForProgress,
|
|
1580
|
+
sourceAmount,
|
|
1581
|
+
targetAmount,
|
|
1582
|
+
advertisedRate: pair.rate,
|
|
1583
|
+
effectiveRate,
|
|
1584
|
+
rateDeviation,
|
|
1585
|
+
cumulativeSource: ctx.cumulative.source,
|
|
1586
|
+
cumulativeTarget: ctx.cumulative.target,
|
|
1587
|
+
// Issue #82 quote tape: surface the maker's fresh per-packet quote
|
|
1588
|
+
// to the callback (the adaptive-controller seam, toon#83).
|
|
1589
|
+
...metadata.rate !== void 0 && {
|
|
1590
|
+
rate: metadata.rate,
|
|
1591
|
+
rateTimestamp: metadata.rateTimestamp
|
|
1592
|
+
},
|
|
1593
|
+
state: ctx.getState() === "paused" ? "paused" : ctx.getState() === "stopped" ? "stopped" : "running"
|
|
1594
|
+
});
|
|
1595
|
+
try {
|
|
1596
|
+
const maybePromise = params.onPacket(progress);
|
|
1597
|
+
if (maybePromise && typeof maybePromise.then === "function") {
|
|
1598
|
+
await maybePromise;
|
|
1599
|
+
}
|
|
1600
|
+
} catch (err) {
|
|
1601
|
+
logger.warn({
|
|
1602
|
+
event: "stream_swap.callback_threw",
|
|
1603
|
+
packetIndex,
|
|
1604
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1605
|
+
});
|
|
1606
|
+
ctx.errors.push({
|
|
1607
|
+
packetIndex,
|
|
1608
|
+
cause: err instanceof Error ? err : new Error(String(err))
|
|
1609
|
+
});
|
|
1610
|
+
return { status: "callback-throw" };
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
status: "accepted",
|
|
1615
|
+
targetAmount,
|
|
1616
|
+
rateDeviation,
|
|
1617
|
+
...metadata.rate !== void 0 && {
|
|
1618
|
+
rate: metadata.rate,
|
|
1619
|
+
rateTimestamp: metadata.rateTimestamp
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
function finalizeResult(input) {
|
|
1624
|
+
const { claims, rejections, errors, cumulative } = input.ctx;
|
|
1625
|
+
let { abortReason } = input;
|
|
1626
|
+
let finalState;
|
|
1627
|
+
if (abortReason === "aborted" || abortReason === "stopped") {
|
|
1628
|
+
finalState = "stopped";
|
|
1629
|
+
} else if (claims.length === 0 && (rejections.length > 0 || errors.length > 0)) {
|
|
1630
|
+
finalState = "failed";
|
|
1631
|
+
if (abortReason === "complete" && rejections.length > 0 && errors.length === 0) {
|
|
1632
|
+
abortReason = "all-rejected";
|
|
1633
|
+
}
|
|
1634
|
+
} else {
|
|
1635
|
+
finalState = "completed";
|
|
1636
|
+
}
|
|
1637
|
+
input.setState(finalState);
|
|
1638
|
+
return {
|
|
1639
|
+
state: finalState,
|
|
1640
|
+
claims,
|
|
1641
|
+
rejections,
|
|
1642
|
+
errors,
|
|
1643
|
+
abortReason,
|
|
1644
|
+
cumulativeSource: cumulative.source,
|
|
1645
|
+
cumulativeTarget: cumulative.target,
|
|
1646
|
+
packetsSent: input.packetsSent,
|
|
1647
|
+
packetsScheduled: input.packetsScheduled
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1295
1650
|
async function runLoop(params, pair, schedule, senderPubkey, logger, getState, setState, waitForResumeOrStop) {
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1651
|
+
const ctx = {
|
|
1652
|
+
params,
|
|
1653
|
+
pair,
|
|
1654
|
+
logger,
|
|
1655
|
+
getState,
|
|
1656
|
+
claims: [],
|
|
1657
|
+
rejections: [],
|
|
1658
|
+
errors: [],
|
|
1659
|
+
cumulative: { source: 0n, target: 0n }
|
|
1660
|
+
};
|
|
1301
1661
|
let packetsSent = 0;
|
|
1302
1662
|
let abortReason = "complete";
|
|
1303
1663
|
const totalPackets = schedule.length;
|
|
@@ -1329,40 +1689,29 @@ async function runLoop(params, pair, schedule, senderPubkey, logger, getState, s
|
|
|
1329
1689
|
`schedule[${packetIndex}] is undefined; schedule was mutated mid-stream`
|
|
1330
1690
|
);
|
|
1331
1691
|
}
|
|
1332
|
-
|
|
1333
|
-
getRandomValues(nonce);
|
|
1334
|
-
const rumor = buildSwapRumor({
|
|
1335
|
-
senderPubkey,
|
|
1336
|
-
pair,
|
|
1337
|
-
sourceAmount,
|
|
1338
|
-
packetIndex: packetIndex + 1,
|
|
1339
|
-
totalPackets,
|
|
1340
|
-
nonce,
|
|
1341
|
-
createdAt: Math.floor(Date.now() / 1e3),
|
|
1342
|
-
chainRecipient: params.chainRecipient
|
|
1343
|
-
});
|
|
1344
|
-
let toonData;
|
|
1692
|
+
let built;
|
|
1345
1693
|
try {
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1694
|
+
built = buildAndWrapPacket({
|
|
1695
|
+
params,
|
|
1696
|
+
pair,
|
|
1697
|
+
senderPubkey,
|
|
1698
|
+
sourceAmount,
|
|
1699
|
+
seq: packetIndex + 1,
|
|
1700
|
+
totalPackets
|
|
1352
1701
|
});
|
|
1353
|
-
toonData = new Uint8Array(Buffer.from(wrapped.ilpPrepare.data, "base64"));
|
|
1354
1702
|
} catch (err) {
|
|
1355
1703
|
logger.error({
|
|
1356
1704
|
event: "stream_swap.wrap_failed",
|
|
1357
1705
|
packetIndex,
|
|
1358
1706
|
error: err instanceof Error ? err.message : String(err)
|
|
1359
1707
|
});
|
|
1360
|
-
errors.push({
|
|
1708
|
+
ctx.errors.push({
|
|
1361
1709
|
packetIndex,
|
|
1362
1710
|
cause: err instanceof Error ? err : new Error(String(err))
|
|
1363
1711
|
});
|
|
1364
1712
|
continue;
|
|
1365
1713
|
}
|
|
1714
|
+
const { toonData, packetExpiresAt } = built;
|
|
1366
1715
|
let sendResult;
|
|
1367
1716
|
try {
|
|
1368
1717
|
sendResult = await params.client.sendSwapPacket({
|
|
@@ -1370,7 +1719,8 @@ async function runLoop(params, pair, schedule, senderPubkey, logger, getState, s
|
|
|
1370
1719
|
amount: sourceAmount,
|
|
1371
1720
|
toonData,
|
|
1372
1721
|
timeout: params.packetTimeoutMs ?? 3e4,
|
|
1373
|
-
claim: params.claim
|
|
1722
|
+
claim: params.claim,
|
|
1723
|
+
...packetExpiresAt !== void 0 && { expiresAt: packetExpiresAt }
|
|
1374
1724
|
});
|
|
1375
1725
|
packetsSent += 1;
|
|
1376
1726
|
} catch (err) {
|
|
@@ -1379,7 +1729,7 @@ async function runLoop(params, pair, schedule, senderPubkey, logger, getState, s
|
|
|
1379
1729
|
packetIndex,
|
|
1380
1730
|
error: err instanceof Error ? err.message : String(err)
|
|
1381
1731
|
});
|
|
1382
|
-
errors.push({
|
|
1732
|
+
ctx.errors.push({
|
|
1383
1733
|
packetIndex,
|
|
1384
1734
|
cause: err instanceof Error ? err : new Error(String(err))
|
|
1385
1735
|
});
|
|
@@ -1394,187 +1744,290 @@ async function runLoop(params, pair, schedule, senderPubkey, logger, getState, s
|
|
|
1394
1744
|
code,
|
|
1395
1745
|
message
|
|
1396
1746
|
});
|
|
1397
|
-
rejections.push({ packetIndex, sourceAmount, code, message });
|
|
1747
|
+
ctx.rejections.push({ packetIndex, sourceAmount, code, message });
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
const outcome = await processAcceptedPacket(ctx, {
|
|
1751
|
+
packetIndex,
|
|
1752
|
+
totalForProgress: totalPackets,
|
|
1753
|
+
sourceAmount,
|
|
1754
|
+
data: sendResult.data
|
|
1755
|
+
});
|
|
1756
|
+
if (outcome.status === "error" || outcome.status === "recipient-mismatch") {
|
|
1398
1757
|
continue;
|
|
1399
1758
|
}
|
|
1400
|
-
|
|
1759
|
+
if (outcome.status === "below-floor") {
|
|
1760
|
+
abortReason = "below-floor";
|
|
1761
|
+
break packetLoop;
|
|
1762
|
+
}
|
|
1763
|
+
if (outcome.status === "callback-throw") {
|
|
1764
|
+
abortReason = "callback-throw";
|
|
1765
|
+
break packetLoop;
|
|
1766
|
+
}
|
|
1767
|
+
if (isAborted()) {
|
|
1768
|
+
abortReason = "aborted";
|
|
1769
|
+
break;
|
|
1770
|
+
}
|
|
1771
|
+
if (getState() === "stopped") {
|
|
1772
|
+
abortReason = "stopped";
|
|
1773
|
+
break;
|
|
1774
|
+
}
|
|
1775
|
+
if (params.rateDeviationThreshold !== void 0 && outcome.rateDeviation > params.rateDeviationThreshold) {
|
|
1776
|
+
abortReason = "rate-deviation";
|
|
1777
|
+
break;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
return finalizeResult({
|
|
1781
|
+
ctx,
|
|
1782
|
+
abortReason,
|
|
1783
|
+
packetsSent,
|
|
1784
|
+
packetsScheduled: totalPackets,
|
|
1785
|
+
setState
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
function classifySendError(err) {
|
|
1789
|
+
return /timeout|timed out|expire/i.test(err.message) ? "timeout" : "error";
|
|
1790
|
+
}
|
|
1791
|
+
function classifyReject(code, message) {
|
|
1792
|
+
if (code === "T99" || /stale[_-]?rate/i.test(message)) return "reject-stale";
|
|
1793
|
+
if (code.startsWith("R") || /timeout|timed out|expire/i.test(message)) {
|
|
1794
|
+
return "timeout";
|
|
1795
|
+
}
|
|
1796
|
+
return "reject";
|
|
1797
|
+
}
|
|
1798
|
+
async function runAdaptiveLoop(params, pair, senderPubkey, logger, getState, setState, waitForResumeOrStop) {
|
|
1799
|
+
const controller = params.controller;
|
|
1800
|
+
const ctx = {
|
|
1801
|
+
params,
|
|
1802
|
+
pair,
|
|
1803
|
+
logger,
|
|
1804
|
+
getState,
|
|
1805
|
+
claims: [],
|
|
1806
|
+
rejections: [],
|
|
1807
|
+
errors: [],
|
|
1808
|
+
cumulative: { source: 0n, target: 0n }
|
|
1809
|
+
};
|
|
1810
|
+
let packetsSent = 0;
|
|
1811
|
+
let abortReason = "complete";
|
|
1812
|
+
let halted = false;
|
|
1813
|
+
let remaining = params.totalAmount;
|
|
1814
|
+
let nextIndex = 0;
|
|
1815
|
+
const isAborted = () => params.signal?.aborted === true;
|
|
1816
|
+
const halt = (reason) => {
|
|
1817
|
+
if (!halted) {
|
|
1818
|
+
halted = true;
|
|
1819
|
+
abortReason = reason;
|
|
1820
|
+
}
|
|
1821
|
+
};
|
|
1822
|
+
const observeSafe = async (obs) => {
|
|
1401
1823
|
try {
|
|
1402
|
-
|
|
1824
|
+
await controller.observe(obs);
|
|
1403
1825
|
} catch (err) {
|
|
1404
|
-
logger.
|
|
1405
|
-
event: "stream_swap.
|
|
1406
|
-
packetIndex,
|
|
1826
|
+
logger.warn({
|
|
1827
|
+
event: "stream_swap.controller_observe_failed",
|
|
1407
1828
|
error: err instanceof Error ? err.message : String(err)
|
|
1408
1829
|
});
|
|
1409
|
-
errors.push({
|
|
1410
|
-
packetIndex,
|
|
1411
|
-
cause: err instanceof Error ? err : new Error(String(err))
|
|
1412
|
-
});
|
|
1413
|
-
continue;
|
|
1414
1830
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1831
|
+
};
|
|
1832
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
1833
|
+
for (; ; ) {
|
|
1834
|
+
if (!halted && isAborted()) halt("aborted");
|
|
1835
|
+
if (!halted && getState() === "stopped") halt("stopped");
|
|
1836
|
+
if (!halted && getState() === "paused" && inflight.size === 0) {
|
|
1837
|
+
const resumedBy = await waitForResumeOrStop();
|
|
1838
|
+
if (resumedBy === "stop" || getState() === "stopped") halt("stopped");
|
|
1839
|
+
else if (isAborted()) halt("aborted");
|
|
1840
|
+
}
|
|
1841
|
+
if (!halted && getState() === "running") {
|
|
1842
|
+
const rawWindow = controller.window;
|
|
1843
|
+
const window = Number.isFinite(rawWindow) && rawWindow >= 1 ? Math.floor(rawWindow) : 1;
|
|
1844
|
+
while (remaining > 0n && inflight.size < window) {
|
|
1845
|
+
let delta;
|
|
1846
|
+
try {
|
|
1847
|
+
delta = controller.nextDelta(remaining);
|
|
1848
|
+
} catch (err) {
|
|
1849
|
+
logger.error({
|
|
1850
|
+
event: "stream_swap.controller_next_delta_failed",
|
|
1851
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1852
|
+
});
|
|
1853
|
+
ctx.errors.push({
|
|
1854
|
+
packetIndex: nextIndex,
|
|
1855
|
+
cause: err instanceof Error ? err : new Error(String(err))
|
|
1856
|
+
});
|
|
1857
|
+
halt("callback-throw");
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
if (typeof delta !== "bigint" || delta < 1n) delta = 1n;
|
|
1861
|
+
if (delta > remaining) delta = remaining;
|
|
1862
|
+
const packetIndex = nextIndex;
|
|
1863
|
+
nextIndex += 1;
|
|
1864
|
+
remaining -= delta;
|
|
1865
|
+
let built;
|
|
1866
|
+
try {
|
|
1867
|
+
built = buildAndWrapPacket({
|
|
1868
|
+
params,
|
|
1869
|
+
pair,
|
|
1870
|
+
senderPubkey,
|
|
1871
|
+
sourceAmount: delta,
|
|
1872
|
+
seq: packetIndex + 1,
|
|
1873
|
+
// Adaptive mode: the final packet count is unknown upfront —
|
|
1874
|
+
// `0` in the rumor's `seq` tag total position means "open".
|
|
1875
|
+
totalPackets: 0
|
|
1876
|
+
});
|
|
1877
|
+
} catch (err) {
|
|
1878
|
+
logger.error({
|
|
1879
|
+
event: "stream_swap.wrap_failed",
|
|
1880
|
+
packetIndex,
|
|
1881
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1882
|
+
});
|
|
1883
|
+
ctx.errors.push({
|
|
1884
|
+
packetIndex,
|
|
1885
|
+
cause: err instanceof Error ? err : new Error(String(err))
|
|
1886
|
+
});
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
const { toonData, packetExpiresAt } = built;
|
|
1890
|
+
const sentAt = Date.now();
|
|
1891
|
+
const promise = (async () => {
|
|
1892
|
+
try {
|
|
1893
|
+
const result = await params.client.sendSwapPacket({
|
|
1894
|
+
destination: params.swapIlpAddress,
|
|
1895
|
+
amount: delta,
|
|
1896
|
+
toonData,
|
|
1897
|
+
timeout: params.packetTimeoutMs ?? 3e4,
|
|
1898
|
+
claim: params.claim,
|
|
1899
|
+
...packetExpiresAt !== void 0 && {
|
|
1900
|
+
expiresAt: packetExpiresAt
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
return {
|
|
1904
|
+
index: packetIndex,
|
|
1905
|
+
sourceAmount: delta,
|
|
1906
|
+
rttMs: Date.now() - sentAt,
|
|
1907
|
+
result
|
|
1908
|
+
};
|
|
1909
|
+
} catch (err) {
|
|
1910
|
+
return {
|
|
1911
|
+
index: packetIndex,
|
|
1912
|
+
sourceAmount: delta,
|
|
1913
|
+
rttMs: Date.now() - sentAt,
|
|
1914
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
})();
|
|
1918
|
+
inflight.set(packetIndex, promise);
|
|
1919
|
+
}
|
|
1431
1920
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
} catch (err) {
|
|
1921
|
+
if (inflight.size === 0) {
|
|
1922
|
+
if (halted || remaining <= 0n) break;
|
|
1923
|
+
if (getState() === "paused") continue;
|
|
1924
|
+
break;
|
|
1925
|
+
}
|
|
1926
|
+
const settled = await Promise.race(inflight.values());
|
|
1927
|
+
inflight.delete(settled.index);
|
|
1928
|
+
if (settled.error) {
|
|
1441
1929
|
logger.error({
|
|
1442
|
-
event: "stream_swap.
|
|
1443
|
-
packetIndex,
|
|
1444
|
-
error:
|
|
1930
|
+
event: "stream_swap.send_failed",
|
|
1931
|
+
packetIndex: settled.index,
|
|
1932
|
+
error: settled.error.message
|
|
1445
1933
|
});
|
|
1446
|
-
errors.push({
|
|
1447
|
-
|
|
1448
|
-
|
|
1934
|
+
ctx.errors.push({ packetIndex: settled.index, cause: settled.error });
|
|
1935
|
+
await observeSafe({
|
|
1936
|
+
resolution: classifySendError(settled.error),
|
|
1937
|
+
rttMs: settled.rttMs,
|
|
1938
|
+
remaining
|
|
1449
1939
|
});
|
|
1450
1940
|
continue;
|
|
1451
1941
|
}
|
|
1452
|
-
|
|
1942
|
+
packetsSent += 1;
|
|
1943
|
+
const sendResult = settled.result;
|
|
1944
|
+
if (!sendResult.accepted) {
|
|
1945
|
+
const code = sendResult.code ?? "F00";
|
|
1946
|
+
const message = sendResult.message ?? "rejected";
|
|
1453
1947
|
logger.warn({
|
|
1454
|
-
event: "stream_swap.
|
|
1455
|
-
packetIndex
|
|
1948
|
+
event: "stream_swap.packet_rejected",
|
|
1949
|
+
packetIndex: settled.index,
|
|
1950
|
+
code,
|
|
1951
|
+
message
|
|
1456
1952
|
});
|
|
1953
|
+
ctx.rejections.push({
|
|
1954
|
+
packetIndex: settled.index,
|
|
1955
|
+
sourceAmount: settled.sourceAmount,
|
|
1956
|
+
code,
|
|
1957
|
+
message
|
|
1958
|
+
});
|
|
1959
|
+
await observeSafe({
|
|
1960
|
+
resolution: classifyReject(code, message),
|
|
1961
|
+
rttMs: settled.rttMs,
|
|
1962
|
+
remaining
|
|
1963
|
+
});
|
|
1964
|
+
continue;
|
|
1457
1965
|
}
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
});
|
|
1464
|
-
const targetAmount = metadata.targetAmount !== void 0 ? BigInt(metadata.targetAmount) : expectedTargetAmount;
|
|
1465
|
-
let rateDeviation = 0;
|
|
1466
|
-
if (expectedTargetAmount > 0n) {
|
|
1467
|
-
const diff = targetAmount >= expectedTargetAmount ? targetAmount - expectedTargetAmount : expectedTargetAmount - targetAmount;
|
|
1468
|
-
const scaled = diff * 1000000n / expectedTargetAmount;
|
|
1469
|
-
rateDeviation = Number(scaled) / 1e6;
|
|
1470
|
-
}
|
|
1471
|
-
const advertisedRate = parseFloat(pair.rate);
|
|
1472
|
-
let effectiveRate;
|
|
1473
|
-
if (targetAmount === expectedTargetAmount) {
|
|
1474
|
-
effectiveRate = advertisedRate;
|
|
1475
|
-
} else {
|
|
1476
|
-
const signedDeviation = targetAmount >= expectedTargetAmount ? rateDeviation : -rateDeviation;
|
|
1477
|
-
effectiveRate = advertisedRate * (1 + signedDeviation);
|
|
1478
|
-
}
|
|
1479
|
-
if (!Number.isFinite(effectiveRate)) {
|
|
1480
|
-
effectiveRate = advertisedRate;
|
|
1481
|
-
}
|
|
1482
|
-
cumulativeSource += sourceAmount;
|
|
1483
|
-
cumulativeTarget += targetAmount;
|
|
1484
|
-
const accumulated = {
|
|
1485
|
-
packetIndex,
|
|
1486
|
-
sourceAmount,
|
|
1487
|
-
targetAmount,
|
|
1488
|
-
claimBytes,
|
|
1489
|
-
swapEphemeralPubkey: metadata.ephemeralPubkey,
|
|
1490
|
-
pair,
|
|
1491
|
-
receivedAt: Date.now()
|
|
1492
|
-
};
|
|
1493
|
-
if (metadata.claimId !== void 0) accumulated.claimId = metadata.claimId;
|
|
1494
|
-
if (metadata.channelId !== void 0)
|
|
1495
|
-
accumulated.channelId = metadata.channelId;
|
|
1496
|
-
if (metadata.nonce !== void 0) accumulated.nonce = metadata.nonce;
|
|
1497
|
-
if (metadata.cumulativeAmount !== void 0)
|
|
1498
|
-
accumulated.cumulativeAmount = metadata.cumulativeAmount;
|
|
1499
|
-
if (metadata.recipient !== void 0)
|
|
1500
|
-
accumulated.recipient = metadata.recipient;
|
|
1501
|
-
if (metadata.swapSignerAddress !== void 0)
|
|
1502
|
-
accumulated.swapSignerAddress = metadata.swapSignerAddress;
|
|
1503
|
-
claims.push(accumulated);
|
|
1504
|
-
logger.debug({
|
|
1505
|
-
event: "stream_swap.packet_accepted",
|
|
1506
|
-
packetIndex,
|
|
1507
|
-
sourceAmount: sourceAmount.toString(),
|
|
1508
|
-
targetAmount: targetAmount.toString()
|
|
1966
|
+
const outcome = await processAcceptedPacket(ctx, {
|
|
1967
|
+
packetIndex: settled.index,
|
|
1968
|
+
totalForProgress: nextIndex,
|
|
1969
|
+
sourceAmount: settled.sourceAmount,
|
|
1970
|
+
data: sendResult.data
|
|
1509
1971
|
});
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
event: "stream_swap.callback_threw",
|
|
1531
|
-
packetIndex,
|
|
1532
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1972
|
+
switch (outcome.status) {
|
|
1973
|
+
case "error":
|
|
1974
|
+
await observeSafe({
|
|
1975
|
+
resolution: "error",
|
|
1976
|
+
rttMs: settled.rttMs,
|
|
1977
|
+
remaining
|
|
1978
|
+
});
|
|
1979
|
+
break;
|
|
1980
|
+
case "recipient-mismatch":
|
|
1981
|
+
await observeSafe({
|
|
1982
|
+
resolution: "reject",
|
|
1983
|
+
rttMs: settled.rttMs,
|
|
1984
|
+
remaining
|
|
1985
|
+
});
|
|
1986
|
+
break;
|
|
1987
|
+
case "below-floor":
|
|
1988
|
+
await observeSafe({
|
|
1989
|
+
resolution: "reject",
|
|
1990
|
+
rttMs: settled.rttMs,
|
|
1991
|
+
remaining
|
|
1533
1992
|
});
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1993
|
+
halt("below-floor");
|
|
1994
|
+
break;
|
|
1995
|
+
case "callback-throw":
|
|
1996
|
+
await observeSafe({
|
|
1997
|
+
resolution: "fulfill",
|
|
1998
|
+
rttMs: settled.rttMs,
|
|
1999
|
+
remaining
|
|
1537
2000
|
});
|
|
1538
|
-
|
|
1539
|
-
break
|
|
2001
|
+
halt("callback-throw");
|
|
2002
|
+
break;
|
|
2003
|
+
case "accepted": {
|
|
2004
|
+
await observeSafe({
|
|
2005
|
+
resolution: "fulfill",
|
|
2006
|
+
rttMs: settled.rttMs,
|
|
2007
|
+
remaining,
|
|
2008
|
+
sourceAmount: settled.sourceAmount,
|
|
2009
|
+
targetAmount: outcome.targetAmount,
|
|
2010
|
+
...outcome.rate !== void 0 && {
|
|
2011
|
+
rate: outcome.rate,
|
|
2012
|
+
rateTimestamp: outcome.rateTimestamp
|
|
2013
|
+
}
|
|
2014
|
+
});
|
|
2015
|
+
if (isAborted()) halt("aborted");
|
|
2016
|
+
else if (getState() === "stopped") halt("stopped");
|
|
2017
|
+
else if (params.rateDeviationThreshold !== void 0 && outcome.rateDeviation > params.rateDeviationThreshold) {
|
|
2018
|
+
halt("rate-deviation");
|
|
2019
|
+
}
|
|
2020
|
+
break;
|
|
1540
2021
|
}
|
|
1541
2022
|
}
|
|
1542
|
-
if (isAborted()) {
|
|
1543
|
-
abortReason = "aborted";
|
|
1544
|
-
break;
|
|
1545
|
-
}
|
|
1546
|
-
if (getState() === "stopped") {
|
|
1547
|
-
abortReason = "stopped";
|
|
1548
|
-
break;
|
|
1549
|
-
}
|
|
1550
|
-
if (params.rateDeviationThreshold !== void 0 && rateDeviation > params.rateDeviationThreshold) {
|
|
1551
|
-
abortReason = "rate-deviation";
|
|
1552
|
-
break;
|
|
1553
|
-
}
|
|
1554
2023
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
finalState = "stopped";
|
|
1558
|
-
} else if (claims.length === 0 && (rejections.length > 0 || errors.length > 0)) {
|
|
1559
|
-
finalState = "failed";
|
|
1560
|
-
if (abortReason === "complete" && rejections.length > 0 && errors.length === 0) {
|
|
1561
|
-
abortReason = "all-rejected";
|
|
1562
|
-
}
|
|
1563
|
-
} else {
|
|
1564
|
-
finalState = "completed";
|
|
1565
|
-
}
|
|
1566
|
-
setState(finalState);
|
|
1567
|
-
return {
|
|
1568
|
-
state: finalState,
|
|
1569
|
-
claims,
|
|
1570
|
-
rejections,
|
|
1571
|
-
errors,
|
|
2024
|
+
return finalizeResult({
|
|
2025
|
+
ctx,
|
|
1572
2026
|
abortReason,
|
|
1573
|
-
cumulativeSource,
|
|
1574
|
-
cumulativeTarget,
|
|
1575
2027
|
packetsSent,
|
|
1576
|
-
packetsScheduled:
|
|
1577
|
-
|
|
2028
|
+
packetsScheduled: nextIndex,
|
|
2029
|
+
setState
|
|
2030
|
+
});
|
|
1578
2031
|
}
|
|
1579
2032
|
function getRandomValues(buf) {
|
|
1580
2033
|
const g = globalThis;
|
|
@@ -1599,7 +2052,8 @@ function getRandomValues(buf) {
|
|
|
1599
2052
|
var __testing = {
|
|
1600
2053
|
chunkAmount,
|
|
1601
2054
|
decodeFulfillMetadata,
|
|
1602
|
-
buildSwapRumor
|
|
2055
|
+
buildSwapRumor,
|
|
2056
|
+
compareDecimalRates
|
|
1603
2057
|
};
|
|
1604
2058
|
|
|
1605
2059
|
export {
|
|
@@ -1634,4 +2088,4 @@ export {
|
|
|
1634
2088
|
streamSwapControlled,
|
|
1635
2089
|
__testing
|
|
1636
2090
|
};
|
|
1637
|
-
//# sourceMappingURL=chunk-
|
|
2091
|
+
//# sourceMappingURL=chunk-7LBYFU4L.js.map
|