@t2000/sdk 5.0.0 → 5.2.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/browser.cjs +293 -94
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +293 -92
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +330 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -19
- package/dist/index.d.ts +100 -19
- package/dist/index.js +320 -204
- package/dist/index.js.map +1 -1
- package/dist/{types-CgZWnHhs.d.cts → simulate-BTIIpnXc.d.cts} +11 -39
- package/dist/{types-CgZWnHhs.d.ts → simulate-BTIIpnXc.d.ts} +11 -39
- package/package.json +3 -4
package/dist/index.cjs
CHANGED
|
@@ -576,8 +576,8 @@ async function addSwapToTx(tx, client, address, input) {
|
|
|
576
576
|
inputCoin = result.coin;
|
|
577
577
|
effectiveRaw = result.effectiveAmount;
|
|
578
578
|
} else {
|
|
579
|
-
const { selectAndSplitCoin:
|
|
580
|
-
const result = await
|
|
579
|
+
const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
|
|
580
|
+
const result = await selectAndSplitCoin2(tx, client, address, fromType, requestedRaw, {
|
|
581
581
|
sponsoredContext: input.sponsoredContext ?? false,
|
|
582
582
|
mergeCache: input.coinMergeCache
|
|
583
583
|
});
|
|
@@ -997,55 +997,126 @@ async function executeTx(client, signer, buildTx, options = {}) {
|
|
|
997
997
|
return { digest: txn.digest, gasCostSui, effects };
|
|
998
998
|
}
|
|
999
999
|
|
|
1000
|
-
// src/mpp-cost.ts
|
|
1001
|
-
function parseChallengeAmount(challenge) {
|
|
1002
|
-
const raw = challenge?.request?.amount;
|
|
1003
|
-
const n = typeof raw === "string" ? Number(raw) : typeof raw === "number" ? raw : Number.NaN;
|
|
1004
|
-
return Number.isFinite(n) ? n : void 0;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
1000
|
// src/wallet/pay.ts
|
|
1001
|
+
init_errors();
|
|
1008
1002
|
async function payWithMpp(args) {
|
|
1009
1003
|
const { signer, client, options } = args;
|
|
1010
|
-
const { Mppx } = await import('mppx/client');
|
|
1011
|
-
const { sui, USDC } = await import('@suimpp/mpp/client');
|
|
1012
|
-
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
1013
|
-
const signerAddress = signer.getAddress();
|
|
1014
|
-
let paymentDigest;
|
|
1015
|
-
let gasCostSui = 0;
|
|
1016
|
-
let chargedAmount;
|
|
1017
|
-
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
1018
|
-
const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
1019
|
-
const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
|
|
1020
|
-
const mppx = Mppx.create({
|
|
1021
|
-
polyfill: false,
|
|
1022
|
-
onChallenge: async (challenge) => {
|
|
1023
|
-
const parsed = parseChallengeAmount(challenge);
|
|
1024
|
-
if (parsed !== void 0) chargedAmount = parsed;
|
|
1025
|
-
return void 0;
|
|
1026
|
-
},
|
|
1027
|
-
methods: [sui({
|
|
1028
|
-
client,
|
|
1029
|
-
currency: USDC,
|
|
1030
|
-
signer: {
|
|
1031
|
-
toSuiAddress: () => signerAddress,
|
|
1032
|
-
signPersonalMessage: (bytes) => signer.signPersonalMessage(bytes)
|
|
1033
|
-
},
|
|
1034
|
-
execute: async (tx) => {
|
|
1035
|
-
const result = await executeTx(client, signer, () => tx, { buildClient: grpcClient });
|
|
1036
|
-
paymentDigest = result.digest;
|
|
1037
|
-
gasCostSui = result.gasCostSui;
|
|
1038
|
-
return { digest: result.digest };
|
|
1039
|
-
}
|
|
1040
|
-
})]
|
|
1041
|
-
});
|
|
1042
1004
|
const method = (options.method ?? "GET").toUpperCase();
|
|
1043
1005
|
const canHaveBody = method !== "GET" && method !== "HEAD";
|
|
1044
|
-
const
|
|
1006
|
+
const reqInit = {
|
|
1045
1007
|
method,
|
|
1046
1008
|
headers: options.headers,
|
|
1047
1009
|
body: canHaveBody ? options.body : void 0
|
|
1010
|
+
};
|
|
1011
|
+
const probe = await fetch(options.url, reqInit);
|
|
1012
|
+
if (probe.status !== 402) {
|
|
1013
|
+
return finalize(probe, { paid: false });
|
|
1014
|
+
}
|
|
1015
|
+
const requirements = await pickSuiExactRequirements(probe, client.network);
|
|
1016
|
+
if (!requirements) {
|
|
1017
|
+
throw new exports.T2000Error(
|
|
1018
|
+
"FACILITATOR_REJECTION",
|
|
1019
|
+
`Endpoint returned 402 without an x402 'exact' / sui:${client.network} payment requirement. This SDK only speaks the x402 dialect.`
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
return payViaX402({ signer, client, options, reqInit, requirements });
|
|
1023
|
+
}
|
|
1024
|
+
async function pickSuiExactRequirements(response, network) {
|
|
1025
|
+
try {
|
|
1026
|
+
const body = await response.clone().json();
|
|
1027
|
+
const want = `sui:${network === "testnet" ? "testnet" : "mainnet"}`;
|
|
1028
|
+
return body.accepts?.find((a) => a.scheme === "exact" && a.network === want);
|
|
1029
|
+
} catch {
|
|
1030
|
+
return void 0;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
async function payViaX402(args) {
|
|
1034
|
+
const { signer, client, options, reqInit, requirements } = args;
|
|
1035
|
+
const { buildX402SignedPayment, X402_PAYMENT_HEADER, X402_PAYMENT_RESPONSE_HEADER } = await import('@suimpp/mpp/x402');
|
|
1036
|
+
const amountRaw = BigInt(requirements.maxAmountRequired);
|
|
1037
|
+
const migrationGasSui = await ensureAddressBalanceCovers({
|
|
1038
|
+
signer,
|
|
1039
|
+
client,
|
|
1040
|
+
asset: requirements.asset,
|
|
1041
|
+
amountRaw
|
|
1042
|
+
});
|
|
1043
|
+
const signerAdapter = {
|
|
1044
|
+
toSuiAddress: () => signer.getAddress(),
|
|
1045
|
+
signTransaction: (bytes) => signer.signTransaction(bytes)
|
|
1046
|
+
};
|
|
1047
|
+
const { header } = await buildX402SignedPayment({ requirements, signer: signerAdapter });
|
|
1048
|
+
const res = await fetch(options.url, {
|
|
1049
|
+
...reqInit,
|
|
1050
|
+
headers: { ...options.headers ?? {}, [X402_PAYMENT_HEADER]: header }
|
|
1048
1051
|
});
|
|
1052
|
+
const settleHeader = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);
|
|
1053
|
+
const paid = !!settleHeader;
|
|
1054
|
+
let digest;
|
|
1055
|
+
if (settleHeader) {
|
|
1056
|
+
try {
|
|
1057
|
+
digest = JSON.parse(new TextDecoder().decode(utils.fromBase64(settleHeader))).transaction;
|
|
1058
|
+
} catch {
|
|
1059
|
+
digest = void 0;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
const result = await finalize(res, { paid });
|
|
1063
|
+
if (!paid) return { ...result, dialect: "x402" };
|
|
1064
|
+
return {
|
|
1065
|
+
...result,
|
|
1066
|
+
dialect: "x402",
|
|
1067
|
+
cost: atomicToHuman(amountRaw, await assetDecimals(requirements.asset)),
|
|
1068
|
+
gasCostSui: migrationGasSui,
|
|
1069
|
+
receipt: digest ? { reference: digest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : result.receipt
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
async function ensureAddressBalanceCovers(args) {
|
|
1073
|
+
const { signer, client, asset, amountRaw } = args;
|
|
1074
|
+
const owner = signer.getAddress();
|
|
1075
|
+
const balanceResp = await client.core.getBalance({ owner, coinType: asset });
|
|
1076
|
+
const total = BigInt(balanceResp.balance.balance);
|
|
1077
|
+
if (total < amountRaw) {
|
|
1078
|
+
throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} to pay`, {
|
|
1079
|
+
available: total.toString(),
|
|
1080
|
+
required: amountRaw.toString()
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
let coinSum = 0n;
|
|
1084
|
+
let cursor;
|
|
1085
|
+
let hasNext = true;
|
|
1086
|
+
while (hasNext) {
|
|
1087
|
+
const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? void 0 });
|
|
1088
|
+
for (const c of page.objects) coinSum += BigInt(c.balance);
|
|
1089
|
+
cursor = page.cursor;
|
|
1090
|
+
hasNext = page.hasNextPage;
|
|
1091
|
+
}
|
|
1092
|
+
const addressBalance = total - coinSum;
|
|
1093
|
+
if (addressBalance >= amountRaw) return 0;
|
|
1094
|
+
const shortfall = amountRaw - addressBalance;
|
|
1095
|
+
const { Transaction: Transaction6 } = await import('@mysten/sui/transactions');
|
|
1096
|
+
const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
|
|
1097
|
+
const grpcClient = await makeGrpcBuildClient(client);
|
|
1098
|
+
const migration = await executeTx(
|
|
1099
|
+
client,
|
|
1100
|
+
signer,
|
|
1101
|
+
async () => {
|
|
1102
|
+
const tx = new Transaction6();
|
|
1103
|
+
const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
|
|
1104
|
+
sponsoredContext: true,
|
|
1105
|
+
// coin-objects-only — never the address balance
|
|
1106
|
+
allowSwapAll: false
|
|
1107
|
+
});
|
|
1108
|
+
tx.moveCall({
|
|
1109
|
+
target: "0x2::coin::send_funds",
|
|
1110
|
+
typeArguments: [asset],
|
|
1111
|
+
arguments: [coin, tx.pure.address(owner)]
|
|
1112
|
+
});
|
|
1113
|
+
return tx;
|
|
1114
|
+
},
|
|
1115
|
+
{ buildClient: grpcClient }
|
|
1116
|
+
);
|
|
1117
|
+
return migration.gasCostSui;
|
|
1118
|
+
}
|
|
1119
|
+
async function finalize(response, opts) {
|
|
1049
1120
|
const contentType = response.headers.get("content-type") ?? "";
|
|
1050
1121
|
let body;
|
|
1051
1122
|
try {
|
|
@@ -1053,15 +1124,25 @@ async function payWithMpp(args) {
|
|
|
1053
1124
|
} catch {
|
|
1054
1125
|
body = null;
|
|
1055
1126
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1127
|
+
return { status: response.status, body, paid: opts.paid };
|
|
1128
|
+
}
|
|
1129
|
+
async function makeGrpcBuildClient(client) {
|
|
1130
|
+
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
1131
|
+
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
1132
|
+
const baseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
1133
|
+
return new SuiGrpcClient2({ baseUrl, network });
|
|
1134
|
+
}
|
|
1135
|
+
function atomicToHuman(raw, decimals) {
|
|
1136
|
+
return Number(raw) / 10 ** decimals;
|
|
1137
|
+
}
|
|
1138
|
+
async function assetDecimals(coinType) {
|
|
1139
|
+
try {
|
|
1140
|
+
const { getDecimalsForCoinType: getDecimalsForCoinType2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
|
|
1141
|
+
const d = getDecimalsForCoinType2(coinType);
|
|
1142
|
+
return typeof d === "number" ? d : 6;
|
|
1143
|
+
} catch {
|
|
1144
|
+
return 6;
|
|
1145
|
+
}
|
|
1065
1146
|
}
|
|
1066
1147
|
|
|
1067
1148
|
// src/wallet/zkLoginSigner.ts
|
|
@@ -1684,170 +1765,198 @@ async function normalizeAddressInput(value, ctx = {}) {
|
|
|
1684
1765
|
|
|
1685
1766
|
// src/t2000.ts
|
|
1686
1767
|
init_errors();
|
|
1768
|
+
var DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".t2000");
|
|
1769
|
+
function resolveConfigPath(configDir) {
|
|
1770
|
+
return path.join(configDir ?? DEFAULT_CONFIG_DIR, "config.json");
|
|
1771
|
+
}
|
|
1772
|
+
function todayUtc() {
|
|
1773
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1774
|
+
}
|
|
1775
|
+
function readLimitsFile(configDir) {
|
|
1776
|
+
const path = resolveConfigPath(configDir);
|
|
1777
|
+
try {
|
|
1778
|
+
return sanitize(JSON.parse(fs.readFileSync(path, "utf-8")));
|
|
1779
|
+
} catch {
|
|
1780
|
+
return {};
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
function writeLimitsFile(file, configDir) {
|
|
1784
|
+
const path$1 = resolveConfigPath(configDir);
|
|
1785
|
+
let existing = {};
|
|
1786
|
+
try {
|
|
1787
|
+
existing = JSON.parse(fs.readFileSync(path$1, "utf-8"));
|
|
1788
|
+
} catch {
|
|
1789
|
+
existing = {};
|
|
1790
|
+
}
|
|
1791
|
+
for (const dead of ["maxPerTx", "maxDailySend", "dailyUsed", "dailyResetDate", "locked"]) {
|
|
1792
|
+
delete existing[dead];
|
|
1793
|
+
}
|
|
1794
|
+
const merged = { ...existing };
|
|
1795
|
+
if (file.limits && (file.limits.perTxUsd !== void 0 || file.limits.dailyUsd !== void 0)) {
|
|
1796
|
+
merged.limits = file.limits;
|
|
1797
|
+
} else {
|
|
1798
|
+
delete merged.limits;
|
|
1799
|
+
}
|
|
1800
|
+
if (file.dailySpend) merged.dailySpend = file.dailySpend;
|
|
1801
|
+
else delete merged.dailySpend;
|
|
1802
|
+
const dir = path.dirname(path$1);
|
|
1803
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
1804
|
+
fs.writeFileSync(path$1, JSON.stringify(merged, null, 2) + "\n", { mode: 384 });
|
|
1805
|
+
return path$1;
|
|
1806
|
+
}
|
|
1807
|
+
function getLimits(configDir) {
|
|
1808
|
+
return readLimitsFile(configDir).limits;
|
|
1809
|
+
}
|
|
1810
|
+
function hasLimits(configDir) {
|
|
1811
|
+
const l = readLimitsFile(configDir).limits;
|
|
1812
|
+
return !!l && (l.perTxUsd !== void 0 || l.dailyUsd !== void 0);
|
|
1813
|
+
}
|
|
1814
|
+
function setLimits(limits, configDir) {
|
|
1815
|
+
const file = readLimitsFile(configDir);
|
|
1816
|
+
const merged = { ...file.limits };
|
|
1817
|
+
if (limits.perTxUsd !== void 0) merged.perTxUsd = limits.perTxUsd;
|
|
1818
|
+
if (limits.dailyUsd !== void 0) merged.dailyUsd = limits.dailyUsd;
|
|
1819
|
+
return writeLimitsFile({ ...file, limits: merged }, configDir);
|
|
1820
|
+
}
|
|
1821
|
+
function clearLimits(configDir) {
|
|
1822
|
+
const file = readLimitsFile(configDir);
|
|
1823
|
+
return writeLimitsFile({ ...file, limits: void 0 }, configDir);
|
|
1824
|
+
}
|
|
1825
|
+
function dailySpentToday(configDir) {
|
|
1826
|
+
const { dailySpend } = readLimitsFile(configDir);
|
|
1827
|
+
if (!dailySpend || dailySpend.date !== todayUtc()) return 0;
|
|
1828
|
+
return dailySpend.usd;
|
|
1829
|
+
}
|
|
1830
|
+
function recordDailySpend(usd, configDir) {
|
|
1831
|
+
if (!Number.isFinite(usd) || usd <= 0) return;
|
|
1832
|
+
const file = readLimitsFile(configDir);
|
|
1833
|
+
const today = todayUtc();
|
|
1834
|
+
const prior = file.dailySpend && file.dailySpend.date === today ? file.dailySpend.usd : 0;
|
|
1835
|
+
writeLimitsFile({ ...file, dailySpend: { date: today, usd: prior + usd } }, configDir);
|
|
1836
|
+
}
|
|
1837
|
+
function sanitize(raw) {
|
|
1838
|
+
if (typeof raw !== "object" || raw === null) return {};
|
|
1839
|
+
const r = raw;
|
|
1840
|
+
const out = {};
|
|
1841
|
+
if (typeof r.limits === "object" && r.limits !== null) {
|
|
1842
|
+
const l = r.limits;
|
|
1843
|
+
const limits = {};
|
|
1844
|
+
if (typeof l.perTxUsd === "number" && l.perTxUsd > 0) limits.perTxUsd = l.perTxUsd;
|
|
1845
|
+
if (typeof l.dailyUsd === "number" && l.dailyUsd > 0) limits.dailyUsd = l.dailyUsd;
|
|
1846
|
+
else if (typeof l.dailySendUsd === "number" && l.dailySendUsd > 0) limits.dailyUsd = l.dailySendUsd;
|
|
1847
|
+
if (limits.perTxUsd !== void 0 || limits.dailyUsd !== void 0) out.limits = limits;
|
|
1848
|
+
}
|
|
1849
|
+
if (typeof r.dailySpend === "object" && r.dailySpend !== null) {
|
|
1850
|
+
const d = r.dailySpend;
|
|
1851
|
+
if (typeof d.date === "string" && typeof d.usd === "number" && d.usd >= 0) {
|
|
1852
|
+
out.dailySpend = { date: d.date, usd: d.usd };
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
return out;
|
|
1856
|
+
}
|
|
1687
1857
|
|
|
1688
|
-
// src/
|
|
1689
|
-
var
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
this.name = "SafeguardError";
|
|
1706
|
-
this.rule = rule;
|
|
1707
|
-
this.details = details;
|
|
1858
|
+
// src/limits/errors.ts
|
|
1859
|
+
var LimitExceededError = class extends Error {
|
|
1860
|
+
code = "LIMIT_EXCEEDED";
|
|
1861
|
+
operation;
|
|
1862
|
+
limitKind;
|
|
1863
|
+
limit;
|
|
1864
|
+
attempted;
|
|
1865
|
+
constructor(params) {
|
|
1866
|
+
const label = params.limitKind === "perTxUsd" ? "per-transaction limit" : "daily spend limit";
|
|
1867
|
+
super(
|
|
1868
|
+
`Exceeds ${label} ($${params.limit}). Attempted $${params.attempted.toFixed(2)}. Use --force / force:true to override.`
|
|
1869
|
+
);
|
|
1870
|
+
this.name = "LimitExceededError";
|
|
1871
|
+
this.operation = params.operation;
|
|
1872
|
+
this.limitKind = params.limitKind;
|
|
1873
|
+
this.limit = params.limit;
|
|
1874
|
+
this.attempted = params.attempted;
|
|
1708
1875
|
}
|
|
1709
1876
|
toJSON() {
|
|
1710
1877
|
return {
|
|
1711
|
-
error:
|
|
1878
|
+
error: this.code,
|
|
1712
1879
|
message: this.message,
|
|
1713
|
-
|
|
1714
|
-
|
|
1880
|
+
operation: this.operation,
|
|
1881
|
+
limitKind: this.limitKind,
|
|
1882
|
+
limit: this.limit,
|
|
1883
|
+
attempted: this.attempted
|
|
1715
1884
|
};
|
|
1716
1885
|
}
|
|
1717
1886
|
};
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1887
|
+
|
|
1888
|
+
// src/limits/enforce.ts
|
|
1889
|
+
function approxUsdValue(asset, amount) {
|
|
1890
|
+
const symbol = asset.toUpperCase();
|
|
1891
|
+
if (symbol === "USDC" || symbol === "USDSUI") return amount;
|
|
1892
|
+
return null;
|
|
1893
|
+
}
|
|
1894
|
+
function assertLimitConfig(input) {
|
|
1895
|
+
if (input.force) return;
|
|
1896
|
+
if (!Number.isFinite(input.amountUsd) || input.amountUsd <= 0) return;
|
|
1897
|
+
const { limits } = input;
|
|
1898
|
+
if (!limits) return;
|
|
1899
|
+
if (limits.perTxUsd !== void 0 && input.amountUsd > limits.perTxUsd) {
|
|
1900
|
+
throw new LimitExceededError({
|
|
1901
|
+
operation: input.operation,
|
|
1902
|
+
limitKind: "perTxUsd",
|
|
1903
|
+
limit: limits.perTxUsd,
|
|
1904
|
+
attempted: input.amountUsd
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
if (limits.dailyUsd !== void 0 && input.spentTodayUsd + input.amountUsd > limits.dailyUsd) {
|
|
1908
|
+
throw new LimitExceededError({
|
|
1909
|
+
operation: input.operation,
|
|
1910
|
+
limitKind: "dailyUsd",
|
|
1911
|
+
// The cap is total/day; report the cap (attempted is THIS write's USD).
|
|
1912
|
+
limit: limits.dailyUsd,
|
|
1913
|
+
attempted: input.spentTodayUsd + input.amountUsd
|
|
1914
|
+
});
|
|
1726
1915
|
}
|
|
1727
1916
|
}
|
|
1728
|
-
|
|
1729
|
-
// src/safeguards/enforcer.ts
|
|
1730
|
-
var SafeguardEnforcer = class {
|
|
1731
|
-
config;
|
|
1732
|
-
configPath;
|
|
1917
|
+
var LimitEnforcer = class {
|
|
1733
1918
|
constructor(configDir) {
|
|
1734
|
-
this.
|
|
1735
|
-
|
|
1919
|
+
this.configDir = configDir;
|
|
1920
|
+
}
|
|
1921
|
+
/** Throws `LimitExceededError` when the write exceeds an opted-in cap. */
|
|
1922
|
+
assert(input) {
|
|
1923
|
+
assertLimitConfig({
|
|
1924
|
+
limits: getLimits(this.configDir),
|
|
1925
|
+
spentTodayUsd: dailySpentToday(this.configDir),
|
|
1926
|
+
operation: input.operation,
|
|
1927
|
+
amountUsd: input.amountUsd,
|
|
1928
|
+
force: input.force
|
|
1929
|
+
});
|
|
1736
1930
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
const raw = JSON.parse(fs.readFileSync(this.configPath, "utf-8"));
|
|
1741
|
-
this.config = {
|
|
1742
|
-
...DEFAULT_SAFEGUARD_CONFIG,
|
|
1743
|
-
locked: raw.locked ?? false,
|
|
1744
|
-
maxPerTx: raw.maxPerTx ?? 0,
|
|
1745
|
-
maxDailySend: raw.maxDailySend ?? 0,
|
|
1746
|
-
dailyUsed: raw.dailyUsed ?? 0,
|
|
1747
|
-
dailyResetDate: raw.dailyResetDate ?? ""
|
|
1748
|
-
};
|
|
1749
|
-
} catch {
|
|
1750
|
-
this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
|
|
1751
|
-
}
|
|
1931
|
+
/** Add a settled write's USD value to today's cumulative total. */
|
|
1932
|
+
record(amountUsd) {
|
|
1933
|
+
recordDailySpend(amountUsd, this.configDir);
|
|
1752
1934
|
}
|
|
1753
|
-
|
|
1754
|
-
this.
|
|
1755
|
-
if (this.config.locked) {
|
|
1756
|
-
throw new SafeguardError("locked", {});
|
|
1757
|
-
}
|
|
1935
|
+
getLimits() {
|
|
1936
|
+
return getLimits(this.configDir);
|
|
1758
1937
|
}
|
|
1759
|
-
|
|
1760
|
-
this.
|
|
1761
|
-
if (this.config.locked) {
|
|
1762
|
-
throw new SafeguardError("locked", {});
|
|
1763
|
-
}
|
|
1764
|
-
const amount = metadata.amount ?? 0;
|
|
1765
|
-
if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
|
|
1766
|
-
throw new SafeguardError("maxPerTx", {
|
|
1767
|
-
attempted: amount,
|
|
1768
|
-
limit: this.config.maxPerTx
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
this.resetDailyIfNewDay();
|
|
1772
|
-
if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
|
|
1773
|
-
throw new SafeguardError("maxDailySend", {
|
|
1774
|
-
attempted: amount,
|
|
1775
|
-
limit: this.config.maxDailySend,
|
|
1776
|
-
current: this.config.dailyUsed
|
|
1777
|
-
});
|
|
1778
|
-
}
|
|
1938
|
+
hasLimits() {
|
|
1939
|
+
return hasLimits(this.configDir);
|
|
1779
1940
|
}
|
|
1780
|
-
|
|
1781
|
-
this.
|
|
1782
|
-
this.config.dailyUsed += amount;
|
|
1783
|
-
this.save();
|
|
1784
|
-
}
|
|
1785
|
-
lock() {
|
|
1786
|
-
this.config.locked = true;
|
|
1787
|
-
this.save();
|
|
1788
|
-
}
|
|
1789
|
-
unlock() {
|
|
1790
|
-
this.config.locked = false;
|
|
1791
|
-
this.save();
|
|
1792
|
-
}
|
|
1793
|
-
set(key, value) {
|
|
1794
|
-
if (key === "locked" && typeof value === "boolean") {
|
|
1795
|
-
this.config.locked = value;
|
|
1796
|
-
} else if (key === "maxPerTx" && typeof value === "number") {
|
|
1797
|
-
this.config.maxPerTx = value;
|
|
1798
|
-
} else if (key === "maxDailySend" && typeof value === "number") {
|
|
1799
|
-
this.config.maxDailySend = value;
|
|
1800
|
-
}
|
|
1801
|
-
this.save();
|
|
1802
|
-
}
|
|
1803
|
-
getConfig() {
|
|
1804
|
-
this.load();
|
|
1805
|
-
this.resetDailyIfNewDay();
|
|
1806
|
-
return { ...this.config };
|
|
1807
|
-
}
|
|
1808
|
-
isConfigured() {
|
|
1809
|
-
return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
|
|
1810
|
-
}
|
|
1811
|
-
resetDailyIfNewDay() {
|
|
1812
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1813
|
-
if (this.config.dailyResetDate !== today) {
|
|
1814
|
-
this.config.dailyUsed = 0;
|
|
1815
|
-
this.config.dailyResetDate = today;
|
|
1816
|
-
this.save();
|
|
1817
|
-
}
|
|
1941
|
+
setLimits(limits) {
|
|
1942
|
+
setLimits(limits, this.configDir);
|
|
1818
1943
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
existing = JSON.parse(fs.readFileSync(this.configPath, "utf-8"));
|
|
1825
|
-
} catch {
|
|
1826
|
-
}
|
|
1827
|
-
const merged = {
|
|
1828
|
-
...existing,
|
|
1829
|
-
locked: this.config.locked,
|
|
1830
|
-
maxPerTx: this.config.maxPerTx,
|
|
1831
|
-
maxDailySend: this.config.maxDailySend,
|
|
1832
|
-
dailyUsed: this.config.dailyUsed,
|
|
1833
|
-
dailyResetDate: this.config.dailyResetDate
|
|
1834
|
-
};
|
|
1835
|
-
const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
|
|
1836
|
-
if (!fs.existsSync(dir)) {
|
|
1837
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1838
|
-
}
|
|
1839
|
-
fs.writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
1840
|
-
} catch {
|
|
1841
|
-
}
|
|
1944
|
+
clearLimits() {
|
|
1945
|
+
clearLimits(this.configDir);
|
|
1946
|
+
}
|
|
1947
|
+
dailySpentToday() {
|
|
1948
|
+
return dailySpentToday(this.configDir);
|
|
1842
1949
|
}
|
|
1843
1950
|
};
|
|
1844
|
-
var
|
|
1951
|
+
var DEFAULT_CONFIG_DIR2 = path.join(os.homedir(), ".t2000");
|
|
1845
1952
|
var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
1846
1953
|
_signer;
|
|
1847
1954
|
_keypair;
|
|
1848
1955
|
client;
|
|
1849
1956
|
_address;
|
|
1850
|
-
|
|
1957
|
+
/** Unified spending-limit gate (per-tx + cumulative daily, USD). Shared by
|
|
1958
|
+
* CLI + MCP + programmatic writes — one gate, no bypass (R-0 Finding 1). */
|
|
1959
|
+
limits;
|
|
1851
1960
|
constructor(keypairOrSigner, client, configDir, isSignerMode) {
|
|
1852
1961
|
super();
|
|
1853
1962
|
if (isSignerMode) {
|
|
@@ -1861,8 +1970,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1861
1970
|
this._address = getAddress(kp);
|
|
1862
1971
|
}
|
|
1863
1972
|
this.client = client;
|
|
1864
|
-
this.
|
|
1865
|
-
this.enforcer.load();
|
|
1973
|
+
this.limits = new LimitEnforcer(configDir);
|
|
1866
1974
|
}
|
|
1867
1975
|
static async create(options = {}) {
|
|
1868
1976
|
const { keyPath, rpcUrl } = options;
|
|
@@ -1875,7 +1983,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1875
1983
|
);
|
|
1876
1984
|
}
|
|
1877
1985
|
const keypair = await loadKey(void 0, keyPath);
|
|
1878
|
-
return new _T2000(keypair, client,
|
|
1986
|
+
return new _T2000(keypair, client, DEFAULT_CONFIG_DIR2);
|
|
1879
1987
|
}
|
|
1880
1988
|
static fromPrivateKey(privateKey, options = {}) {
|
|
1881
1989
|
const keypair = keypairFromPrivateKey(privateKey);
|
|
@@ -1886,7 +1994,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1886
1994
|
const keypair = generateKeypair();
|
|
1887
1995
|
await saveKey(keypair, void 0, options.keyPath);
|
|
1888
1996
|
const client = getSuiClient();
|
|
1889
|
-
const agent = new _T2000(keypair, client,
|
|
1997
|
+
const agent = new _T2000(keypair, client, DEFAULT_CONFIG_DIR2);
|
|
1890
1998
|
const address = agent.address();
|
|
1891
1999
|
return { agent, address };
|
|
1892
2000
|
}
|
|
@@ -1908,11 +2016,10 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1908
2016
|
}
|
|
1909
2017
|
// -- MPP Payments --
|
|
1910
2018
|
async pay(options) {
|
|
1911
|
-
this.
|
|
1912
|
-
this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
|
|
2019
|
+
this.limits.assert({ operation: "pay", amountUsd: options.maxPrice ?? 0, force: options.force });
|
|
1913
2020
|
const result = await payWithMpp({ signer: this._signer, client: this.client, options });
|
|
1914
2021
|
if (result.paid) {
|
|
1915
|
-
this.
|
|
2022
|
+
this.limits.record(result.cost ?? options.maxPrice ?? 0);
|
|
1916
2023
|
}
|
|
1917
2024
|
return result;
|
|
1918
2025
|
}
|
|
@@ -1926,7 +2033,11 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1926
2033
|
// and the S.323 build-tracker entry.
|
|
1927
2034
|
// -- Swap --
|
|
1928
2035
|
async swap(params) {
|
|
1929
|
-
this.
|
|
2036
|
+
this.limits.assert({
|
|
2037
|
+
operation: "swap",
|
|
2038
|
+
amountUsd: approxUsdValue(params.from, params.amount) ?? 0,
|
|
2039
|
+
force: params.force
|
|
2040
|
+
});
|
|
1930
2041
|
const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
|
|
1931
2042
|
const fromType = resolveTokenType2(params.from);
|
|
1932
2043
|
const toType = resolveTokenType2(params.to);
|
|
@@ -2018,6 +2129,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
2018
2129
|
const fromName = resolveSymbol(fromType);
|
|
2019
2130
|
const toName = resolveSymbol(toType);
|
|
2020
2131
|
const routeDesc = route.routerData.paths?.map((p) => p.provider).filter(Boolean).slice(0, 3).join(" + ") ?? "Cetus Aggregator";
|
|
2132
|
+
this.limits.record(approxUsdValue(params.from, params.amount) ?? 0);
|
|
2021
2133
|
return {
|
|
2022
2134
|
success: true,
|
|
2023
2135
|
tx: gasResult.digest,
|
|
@@ -2074,7 +2186,6 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
2074
2186
|
* the "build via gRPC, execute via JSON-RPC" hybrid).
|
|
2075
2187
|
*/
|
|
2076
2188
|
async send(params) {
|
|
2077
|
-
this.enforcer.assertNotLocked();
|
|
2078
2189
|
const asset = params.asset;
|
|
2079
2190
|
if (!asset) {
|
|
2080
2191
|
throw new exports.T2000Error(
|
|
@@ -2084,6 +2195,11 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
2084
2195
|
}
|
|
2085
2196
|
assertAllowedAsset("send", asset);
|
|
2086
2197
|
const sendableAsset = asset;
|
|
2198
|
+
this.limits.assert({
|
|
2199
|
+
operation: "send",
|
|
2200
|
+
amountUsd: approxUsdValue(sendableAsset, params.amount) ?? 0,
|
|
2201
|
+
force: params.force
|
|
2202
|
+
});
|
|
2087
2203
|
const resolved = await this.resolveRecipient(params.to);
|
|
2088
2204
|
const sendAmount = params.amount;
|
|
2089
2205
|
const sendTo = resolved.address;
|
|
@@ -2095,7 +2211,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
2095
2211
|
() => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset: sendableAsset }),
|
|
2096
2212
|
{ buildClient }
|
|
2097
2213
|
);
|
|
2098
|
-
this.
|
|
2214
|
+
this.limits.record(approxUsdValue(sendableAsset, sendAmount) ?? 0);
|
|
2099
2215
|
const balance = await this.balance();
|
|
2100
2216
|
this.emitBalanceChange(sendableAsset, sendAmount, "send", gasResult.digest);
|
|
2101
2217
|
return {
|
|
@@ -2653,7 +2769,6 @@ exports.CETUS_USDC_SUI_POOL = CETUS_USDC_SUI_POOL;
|
|
|
2653
2769
|
exports.CLOCK_ID = CLOCK_ID;
|
|
2654
2770
|
exports.DEFAULT_GRPC_URL = DEFAULT_GRPC_URL;
|
|
2655
2771
|
exports.DEFAULT_NETWORK = DEFAULT_NETWORK;
|
|
2656
|
-
exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
|
|
2657
2772
|
exports.GASLESS_MIN_STABLE_AMOUNT = GASLESS_MIN_STABLE_AMOUNT;
|
|
2658
2773
|
exports.GASLESS_STABLE_TYPES = GASLESS_STABLE_TYPES;
|
|
2659
2774
|
exports.GAS_RESERVE_MIN = GAS_RESERVE_MIN;
|
|
@@ -2661,6 +2776,8 @@ exports.InvalidAddressError = InvalidAddressError;
|
|
|
2661
2776
|
exports.KNOWN_TARGETS = KNOWN_TARGETS;
|
|
2662
2777
|
exports.KeypairSigner = KeypairSigner;
|
|
2663
2778
|
exports.LABEL_PATTERNS = LABEL_PATTERNS;
|
|
2779
|
+
exports.LimitEnforcer = LimitEnforcer;
|
|
2780
|
+
exports.LimitExceededError = LimitExceededError;
|
|
2664
2781
|
exports.MIST_PER_SUI = MIST_PER_SUI;
|
|
2665
2782
|
exports.OPERATION_ASSETS = OPERATION_ASSETS;
|
|
2666
2783
|
exports.SENDABLE_ASSETS = SENDABLE_ASSETS;
|
|
@@ -2671,8 +2788,6 @@ exports.SUI_ADDRESS_REGEX = SUI_ADDRESS_REGEX;
|
|
|
2671
2788
|
exports.SUI_ADDRESS_STRICT_REGEX = SUI_ADDRESS_STRICT_REGEX;
|
|
2672
2789
|
exports.SUI_DECIMALS = SUI_DECIMALS;
|
|
2673
2790
|
exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
|
|
2674
|
-
exports.SafeguardEnforcer = SafeguardEnforcer;
|
|
2675
|
-
exports.SafeguardError = SafeguardError;
|
|
2676
2791
|
exports.SuinsNotRegisteredError = SuinsNotRegisteredError;
|
|
2677
2792
|
exports.SuinsRpcError = SuinsRpcError;
|
|
2678
2793
|
exports.T2000 = T2000;
|
|
@@ -2682,7 +2797,9 @@ exports.WRITE_APPENDER_REGISTRY = WRITE_APPENDER_REGISTRY;
|
|
|
2682
2797
|
exports.ZkLoginSigner = ZkLoginSigner;
|
|
2683
2798
|
exports.addSendToTx = addSendToTx;
|
|
2684
2799
|
exports.addSwapToTx = addSwapToTx;
|
|
2800
|
+
exports.approxUsdValue = approxUsdValue;
|
|
2685
2801
|
exports.assertAllowedAsset = assertAllowedAsset;
|
|
2802
|
+
exports.assertLimitConfig = assertLimitConfig;
|
|
2686
2803
|
exports.buildAddLeafTx = buildAddLeafTx;
|
|
2687
2804
|
exports.buildRevokeLeafTx = buildRevokeLeafTx;
|
|
2688
2805
|
exports.buildSendTx = buildSendTx;
|
|
@@ -2690,7 +2807,9 @@ exports.buildSwapTx = buildSwapTx;
|
|
|
2690
2807
|
exports.classifyAction = classifyAction;
|
|
2691
2808
|
exports.classifyLabel = classifyLabel;
|
|
2692
2809
|
exports.classifyTransaction = classifyTransaction;
|
|
2810
|
+
exports.clearLimits = clearLimits;
|
|
2693
2811
|
exports.composeTx = composeTx;
|
|
2812
|
+
exports.dailySpentToday = dailySpentToday;
|
|
2694
2813
|
exports.deriveAllowedAddressesFromPtb = deriveAllowedAddressesFromPtb;
|
|
2695
2814
|
exports.deserializeCetusRoute = deserializeCetusRoute;
|
|
2696
2815
|
exports.displayHandle = displayHandle;
|
|
@@ -2712,10 +2831,12 @@ exports.getAddress = getAddress;
|
|
|
2712
2831
|
exports.getCoinMeta = getCoinMeta;
|
|
2713
2832
|
exports.getDecimals = getDecimals;
|
|
2714
2833
|
exports.getDecimalsForCoinType = getDecimalsForCoinType;
|
|
2834
|
+
exports.getLimits = getLimits;
|
|
2715
2835
|
exports.getSponsoredSwapProviders = getSponsoredSwapProviders;
|
|
2716
2836
|
exports.getSuiClient = getSuiClient;
|
|
2717
2837
|
exports.getSuiGrpcClient = getSuiGrpcClient;
|
|
2718
2838
|
exports.getSwapQuote = getSwapQuote;
|
|
2839
|
+
exports.hasLimits = hasLimits;
|
|
2719
2840
|
exports.isAllowedAsset = isAllowedAsset;
|
|
2720
2841
|
exports.isCetusRouteFresh = isCetusRouteFresh;
|
|
2721
2842
|
exports.isInRegistry = isInRegistry;
|
|
@@ -2734,6 +2855,8 @@ exports.queryHistory = queryHistory;
|
|
|
2734
2855
|
exports.queryTransaction = queryTransaction;
|
|
2735
2856
|
exports.rawToStable = rawToStable;
|
|
2736
2857
|
exports.rawToUsdc = rawToUsdc;
|
|
2858
|
+
exports.readLimitsFile = readLimitsFile;
|
|
2859
|
+
exports.recordDailySpend = recordDailySpend;
|
|
2737
2860
|
exports.refineLendingLabel = refineLendingLabel;
|
|
2738
2861
|
exports.resolveAddressToSuinsViaRpc = resolveAddressToSuinsViaRpc;
|
|
2739
2862
|
exports.resolveSuinsViaRpc = resolveSuinsViaRpc;
|
|
@@ -2744,6 +2867,7 @@ exports.saveKey = saveKey;
|
|
|
2744
2867
|
exports.selectAndSplitCoin = selectAndSplitCoin;
|
|
2745
2868
|
exports.selectSuiCoin = selectSuiCoin;
|
|
2746
2869
|
exports.serializeCetusRoute = serializeCetusRoute;
|
|
2870
|
+
exports.setLimits = setLimits;
|
|
2747
2871
|
exports.simulateTransaction = simulateTransaction;
|
|
2748
2872
|
exports.stableToRaw = stableToRaw;
|
|
2749
2873
|
exports.suiToMist = suiToMist;
|
|
@@ -2754,5 +2878,6 @@ exports.validateAddress = validateAddress;
|
|
|
2754
2878
|
exports.validateLabel = validateLabel;
|
|
2755
2879
|
exports.verifyCetusRouteCoinMatch = verifyCetusRouteCoinMatch;
|
|
2756
2880
|
exports.walletExists = walletExists;
|
|
2881
|
+
exports.writeLimitsFile = writeLimitsFile;
|
|
2757
2882
|
//# sourceMappingURL=index.cjs.map
|
|
2758
2883
|
//# sourceMappingURL=index.cjs.map
|