@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.js
CHANGED
|
@@ -5,7 +5,7 @@ import { EventEmitter } from 'eventemitter3';
|
|
|
5
5
|
import { createPaymentTransactionUri } from '@mysten/payment-kit';
|
|
6
6
|
import { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
7
7
|
import { SuiGraphQLClient } from '@mysten/sui/graphql';
|
|
8
|
-
import { normalizeSuiAddress, isValidSuiAddress } from '@mysten/sui/utils';
|
|
8
|
+
import { normalizeSuiAddress, isValidSuiAddress, fromBase64 } from '@mysten/sui/utils';
|
|
9
9
|
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
10
10
|
import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
|
|
11
11
|
import { access, mkdir, writeFile, readFile } from 'fs/promises';
|
|
@@ -570,8 +570,8 @@ async function addSwapToTx(tx, client, address, input) {
|
|
|
570
570
|
inputCoin = result.coin;
|
|
571
571
|
effectiveRaw = result.effectiveAmount;
|
|
572
572
|
} else {
|
|
573
|
-
const { selectAndSplitCoin:
|
|
574
|
-
const result = await
|
|
573
|
+
const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
|
|
574
|
+
const result = await selectAndSplitCoin2(tx, client, address, fromType, requestedRaw, {
|
|
575
575
|
sponsoredContext: input.sponsoredContext ?? false,
|
|
576
576
|
mergeCache: input.coinMergeCache
|
|
577
577
|
});
|
|
@@ -991,55 +991,126 @@ async function executeTx(client, signer, buildTx, options = {}) {
|
|
|
991
991
|
return { digest: txn.digest, gasCostSui, effects };
|
|
992
992
|
}
|
|
993
993
|
|
|
994
|
-
// src/mpp-cost.ts
|
|
995
|
-
function parseChallengeAmount(challenge) {
|
|
996
|
-
const raw = challenge?.request?.amount;
|
|
997
|
-
const n = typeof raw === "string" ? Number(raw) : typeof raw === "number" ? raw : Number.NaN;
|
|
998
|
-
return Number.isFinite(n) ? n : void 0;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
994
|
// src/wallet/pay.ts
|
|
995
|
+
init_errors();
|
|
1002
996
|
async function payWithMpp(args) {
|
|
1003
997
|
const { signer, client, options } = args;
|
|
1004
|
-
const { Mppx } = await import('mppx/client');
|
|
1005
|
-
const { sui, USDC } = await import('@suimpp/mpp/client');
|
|
1006
|
-
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
1007
|
-
const signerAddress = signer.getAddress();
|
|
1008
|
-
let paymentDigest;
|
|
1009
|
-
let gasCostSui = 0;
|
|
1010
|
-
let chargedAmount;
|
|
1011
|
-
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
1012
|
-
const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
1013
|
-
const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
|
|
1014
|
-
const mppx = Mppx.create({
|
|
1015
|
-
polyfill: false,
|
|
1016
|
-
onChallenge: async (challenge) => {
|
|
1017
|
-
const parsed = parseChallengeAmount(challenge);
|
|
1018
|
-
if (parsed !== void 0) chargedAmount = parsed;
|
|
1019
|
-
return void 0;
|
|
1020
|
-
},
|
|
1021
|
-
methods: [sui({
|
|
1022
|
-
client,
|
|
1023
|
-
currency: USDC,
|
|
1024
|
-
signer: {
|
|
1025
|
-
toSuiAddress: () => signerAddress,
|
|
1026
|
-
signPersonalMessage: (bytes) => signer.signPersonalMessage(bytes)
|
|
1027
|
-
},
|
|
1028
|
-
execute: async (tx) => {
|
|
1029
|
-
const result = await executeTx(client, signer, () => tx, { buildClient: grpcClient });
|
|
1030
|
-
paymentDigest = result.digest;
|
|
1031
|
-
gasCostSui = result.gasCostSui;
|
|
1032
|
-
return { digest: result.digest };
|
|
1033
|
-
}
|
|
1034
|
-
})]
|
|
1035
|
-
});
|
|
1036
998
|
const method = (options.method ?? "GET").toUpperCase();
|
|
1037
999
|
const canHaveBody = method !== "GET" && method !== "HEAD";
|
|
1038
|
-
const
|
|
1000
|
+
const reqInit = {
|
|
1039
1001
|
method,
|
|
1040
1002
|
headers: options.headers,
|
|
1041
1003
|
body: canHaveBody ? options.body : void 0
|
|
1004
|
+
};
|
|
1005
|
+
const probe = await fetch(options.url, reqInit);
|
|
1006
|
+
if (probe.status !== 402) {
|
|
1007
|
+
return finalize(probe, { paid: false });
|
|
1008
|
+
}
|
|
1009
|
+
const requirements = await pickSuiExactRequirements(probe, client.network);
|
|
1010
|
+
if (!requirements) {
|
|
1011
|
+
throw new T2000Error(
|
|
1012
|
+
"FACILITATOR_REJECTION",
|
|
1013
|
+
`Endpoint returned 402 without an x402 'exact' / sui:${client.network} payment requirement. This SDK only speaks the x402 dialect.`
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
return payViaX402({ signer, client, options, reqInit, requirements });
|
|
1017
|
+
}
|
|
1018
|
+
async function pickSuiExactRequirements(response, network) {
|
|
1019
|
+
try {
|
|
1020
|
+
const body = await response.clone().json();
|
|
1021
|
+
const want = `sui:${network === "testnet" ? "testnet" : "mainnet"}`;
|
|
1022
|
+
return body.accepts?.find((a) => a.scheme === "exact" && a.network === want);
|
|
1023
|
+
} catch {
|
|
1024
|
+
return void 0;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
async function payViaX402(args) {
|
|
1028
|
+
const { signer, client, options, reqInit, requirements } = args;
|
|
1029
|
+
const { buildX402SignedPayment, X402_PAYMENT_HEADER, X402_PAYMENT_RESPONSE_HEADER } = await import('@suimpp/mpp/x402');
|
|
1030
|
+
const amountRaw = BigInt(requirements.maxAmountRequired);
|
|
1031
|
+
const migrationGasSui = await ensureAddressBalanceCovers({
|
|
1032
|
+
signer,
|
|
1033
|
+
client,
|
|
1034
|
+
asset: requirements.asset,
|
|
1035
|
+
amountRaw
|
|
1036
|
+
});
|
|
1037
|
+
const signerAdapter = {
|
|
1038
|
+
toSuiAddress: () => signer.getAddress(),
|
|
1039
|
+
signTransaction: (bytes) => signer.signTransaction(bytes)
|
|
1040
|
+
};
|
|
1041
|
+
const { header } = await buildX402SignedPayment({ requirements, signer: signerAdapter });
|
|
1042
|
+
const res = await fetch(options.url, {
|
|
1043
|
+
...reqInit,
|
|
1044
|
+
headers: { ...options.headers ?? {}, [X402_PAYMENT_HEADER]: header }
|
|
1042
1045
|
});
|
|
1046
|
+
const settleHeader = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);
|
|
1047
|
+
const paid = !!settleHeader;
|
|
1048
|
+
let digest;
|
|
1049
|
+
if (settleHeader) {
|
|
1050
|
+
try {
|
|
1051
|
+
digest = JSON.parse(new TextDecoder().decode(fromBase64(settleHeader))).transaction;
|
|
1052
|
+
} catch {
|
|
1053
|
+
digest = void 0;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
const result = await finalize(res, { paid });
|
|
1057
|
+
if (!paid) return { ...result, dialect: "x402" };
|
|
1058
|
+
return {
|
|
1059
|
+
...result,
|
|
1060
|
+
dialect: "x402",
|
|
1061
|
+
cost: atomicToHuman(amountRaw, await assetDecimals(requirements.asset)),
|
|
1062
|
+
gasCostSui: migrationGasSui,
|
|
1063
|
+
receipt: digest ? { reference: digest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : result.receipt
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
async function ensureAddressBalanceCovers(args) {
|
|
1067
|
+
const { signer, client, asset, amountRaw } = args;
|
|
1068
|
+
const owner = signer.getAddress();
|
|
1069
|
+
const balanceResp = await client.core.getBalance({ owner, coinType: asset });
|
|
1070
|
+
const total = BigInt(balanceResp.balance.balance);
|
|
1071
|
+
if (total < amountRaw) {
|
|
1072
|
+
throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} to pay`, {
|
|
1073
|
+
available: total.toString(),
|
|
1074
|
+
required: amountRaw.toString()
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
let coinSum = 0n;
|
|
1078
|
+
let cursor;
|
|
1079
|
+
let hasNext = true;
|
|
1080
|
+
while (hasNext) {
|
|
1081
|
+
const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? void 0 });
|
|
1082
|
+
for (const c of page.objects) coinSum += BigInt(c.balance);
|
|
1083
|
+
cursor = page.cursor;
|
|
1084
|
+
hasNext = page.hasNextPage;
|
|
1085
|
+
}
|
|
1086
|
+
const addressBalance = total - coinSum;
|
|
1087
|
+
if (addressBalance >= amountRaw) return 0;
|
|
1088
|
+
const shortfall = amountRaw - addressBalance;
|
|
1089
|
+
const { Transaction: Transaction6 } = await import('@mysten/sui/transactions');
|
|
1090
|
+
const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
|
|
1091
|
+
const grpcClient = await makeGrpcBuildClient(client);
|
|
1092
|
+
const migration = await executeTx(
|
|
1093
|
+
client,
|
|
1094
|
+
signer,
|
|
1095
|
+
async () => {
|
|
1096
|
+
const tx = new Transaction6();
|
|
1097
|
+
const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
|
|
1098
|
+
sponsoredContext: true,
|
|
1099
|
+
// coin-objects-only — never the address balance
|
|
1100
|
+
allowSwapAll: false
|
|
1101
|
+
});
|
|
1102
|
+
tx.moveCall({
|
|
1103
|
+
target: "0x2::coin::send_funds",
|
|
1104
|
+
typeArguments: [asset],
|
|
1105
|
+
arguments: [coin, tx.pure.address(owner)]
|
|
1106
|
+
});
|
|
1107
|
+
return tx;
|
|
1108
|
+
},
|
|
1109
|
+
{ buildClient: grpcClient }
|
|
1110
|
+
);
|
|
1111
|
+
return migration.gasCostSui;
|
|
1112
|
+
}
|
|
1113
|
+
async function finalize(response, opts) {
|
|
1043
1114
|
const contentType = response.headers.get("content-type") ?? "";
|
|
1044
1115
|
let body;
|
|
1045
1116
|
try {
|
|
@@ -1047,15 +1118,25 @@ async function payWithMpp(args) {
|
|
|
1047
1118
|
} catch {
|
|
1048
1119
|
body = null;
|
|
1049
1120
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1121
|
+
return { status: response.status, body, paid: opts.paid };
|
|
1122
|
+
}
|
|
1123
|
+
async function makeGrpcBuildClient(client) {
|
|
1124
|
+
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
1125
|
+
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
1126
|
+
const baseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
1127
|
+
return new SuiGrpcClient2({ baseUrl, network });
|
|
1128
|
+
}
|
|
1129
|
+
function atomicToHuman(raw, decimals) {
|
|
1130
|
+
return Number(raw) / 10 ** decimals;
|
|
1131
|
+
}
|
|
1132
|
+
async function assetDecimals(coinType) {
|
|
1133
|
+
try {
|
|
1134
|
+
const { getDecimalsForCoinType: getDecimalsForCoinType2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
|
|
1135
|
+
const d = getDecimalsForCoinType2(coinType);
|
|
1136
|
+
return typeof d === "number" ? d : 6;
|
|
1137
|
+
} catch {
|
|
1138
|
+
return 6;
|
|
1139
|
+
}
|
|
1059
1140
|
}
|
|
1060
1141
|
|
|
1061
1142
|
// src/wallet/zkLoginSigner.ts
|
|
@@ -1678,170 +1759,198 @@ async function normalizeAddressInput(value, ctx = {}) {
|
|
|
1678
1759
|
|
|
1679
1760
|
// src/t2000.ts
|
|
1680
1761
|
init_errors();
|
|
1762
|
+
var DEFAULT_CONFIG_DIR = join(homedir(), ".t2000");
|
|
1763
|
+
function resolveConfigPath(configDir) {
|
|
1764
|
+
return join(configDir ?? DEFAULT_CONFIG_DIR, "config.json");
|
|
1765
|
+
}
|
|
1766
|
+
function todayUtc() {
|
|
1767
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1768
|
+
}
|
|
1769
|
+
function readLimitsFile(configDir) {
|
|
1770
|
+
const path = resolveConfigPath(configDir);
|
|
1771
|
+
try {
|
|
1772
|
+
return sanitize(JSON.parse(readFileSync(path, "utf-8")));
|
|
1773
|
+
} catch {
|
|
1774
|
+
return {};
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
function writeLimitsFile(file, configDir) {
|
|
1778
|
+
const path = resolveConfigPath(configDir);
|
|
1779
|
+
let existing = {};
|
|
1780
|
+
try {
|
|
1781
|
+
existing = JSON.parse(readFileSync(path, "utf-8"));
|
|
1782
|
+
} catch {
|
|
1783
|
+
existing = {};
|
|
1784
|
+
}
|
|
1785
|
+
for (const dead of ["maxPerTx", "maxDailySend", "dailyUsed", "dailyResetDate", "locked"]) {
|
|
1786
|
+
delete existing[dead];
|
|
1787
|
+
}
|
|
1788
|
+
const merged = { ...existing };
|
|
1789
|
+
if (file.limits && (file.limits.perTxUsd !== void 0 || file.limits.dailyUsd !== void 0)) {
|
|
1790
|
+
merged.limits = file.limits;
|
|
1791
|
+
} else {
|
|
1792
|
+
delete merged.limits;
|
|
1793
|
+
}
|
|
1794
|
+
if (file.dailySpend) merged.dailySpend = file.dailySpend;
|
|
1795
|
+
else delete merged.dailySpend;
|
|
1796
|
+
const dir = dirname(path);
|
|
1797
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
1798
|
+
writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", { mode: 384 });
|
|
1799
|
+
return path;
|
|
1800
|
+
}
|
|
1801
|
+
function getLimits(configDir) {
|
|
1802
|
+
return readLimitsFile(configDir).limits;
|
|
1803
|
+
}
|
|
1804
|
+
function hasLimits(configDir) {
|
|
1805
|
+
const l = readLimitsFile(configDir).limits;
|
|
1806
|
+
return !!l && (l.perTxUsd !== void 0 || l.dailyUsd !== void 0);
|
|
1807
|
+
}
|
|
1808
|
+
function setLimits(limits, configDir) {
|
|
1809
|
+
const file = readLimitsFile(configDir);
|
|
1810
|
+
const merged = { ...file.limits };
|
|
1811
|
+
if (limits.perTxUsd !== void 0) merged.perTxUsd = limits.perTxUsd;
|
|
1812
|
+
if (limits.dailyUsd !== void 0) merged.dailyUsd = limits.dailyUsd;
|
|
1813
|
+
return writeLimitsFile({ ...file, limits: merged }, configDir);
|
|
1814
|
+
}
|
|
1815
|
+
function clearLimits(configDir) {
|
|
1816
|
+
const file = readLimitsFile(configDir);
|
|
1817
|
+
return writeLimitsFile({ ...file, limits: void 0 }, configDir);
|
|
1818
|
+
}
|
|
1819
|
+
function dailySpentToday(configDir) {
|
|
1820
|
+
const { dailySpend } = readLimitsFile(configDir);
|
|
1821
|
+
if (!dailySpend || dailySpend.date !== todayUtc()) return 0;
|
|
1822
|
+
return dailySpend.usd;
|
|
1823
|
+
}
|
|
1824
|
+
function recordDailySpend(usd, configDir) {
|
|
1825
|
+
if (!Number.isFinite(usd) || usd <= 0) return;
|
|
1826
|
+
const file = readLimitsFile(configDir);
|
|
1827
|
+
const today = todayUtc();
|
|
1828
|
+
const prior = file.dailySpend && file.dailySpend.date === today ? file.dailySpend.usd : 0;
|
|
1829
|
+
writeLimitsFile({ ...file, dailySpend: { date: today, usd: prior + usd } }, configDir);
|
|
1830
|
+
}
|
|
1831
|
+
function sanitize(raw) {
|
|
1832
|
+
if (typeof raw !== "object" || raw === null) return {};
|
|
1833
|
+
const r = raw;
|
|
1834
|
+
const out = {};
|
|
1835
|
+
if (typeof r.limits === "object" && r.limits !== null) {
|
|
1836
|
+
const l = r.limits;
|
|
1837
|
+
const limits = {};
|
|
1838
|
+
if (typeof l.perTxUsd === "number" && l.perTxUsd > 0) limits.perTxUsd = l.perTxUsd;
|
|
1839
|
+
if (typeof l.dailyUsd === "number" && l.dailyUsd > 0) limits.dailyUsd = l.dailyUsd;
|
|
1840
|
+
else if (typeof l.dailySendUsd === "number" && l.dailySendUsd > 0) limits.dailyUsd = l.dailySendUsd;
|
|
1841
|
+
if (limits.perTxUsd !== void 0 || limits.dailyUsd !== void 0) out.limits = limits;
|
|
1842
|
+
}
|
|
1843
|
+
if (typeof r.dailySpend === "object" && r.dailySpend !== null) {
|
|
1844
|
+
const d = r.dailySpend;
|
|
1845
|
+
if (typeof d.date === "string" && typeof d.usd === "number" && d.usd >= 0) {
|
|
1846
|
+
out.dailySpend = { date: d.date, usd: d.usd };
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
return out;
|
|
1850
|
+
}
|
|
1681
1851
|
|
|
1682
|
-
// src/
|
|
1683
|
-
var
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
this.name = "SafeguardError";
|
|
1700
|
-
this.rule = rule;
|
|
1701
|
-
this.details = details;
|
|
1852
|
+
// src/limits/errors.ts
|
|
1853
|
+
var LimitExceededError = class extends Error {
|
|
1854
|
+
code = "LIMIT_EXCEEDED";
|
|
1855
|
+
operation;
|
|
1856
|
+
limitKind;
|
|
1857
|
+
limit;
|
|
1858
|
+
attempted;
|
|
1859
|
+
constructor(params) {
|
|
1860
|
+
const label = params.limitKind === "perTxUsd" ? "per-transaction limit" : "daily spend limit";
|
|
1861
|
+
super(
|
|
1862
|
+
`Exceeds ${label} ($${params.limit}). Attempted $${params.attempted.toFixed(2)}. Use --force / force:true to override.`
|
|
1863
|
+
);
|
|
1864
|
+
this.name = "LimitExceededError";
|
|
1865
|
+
this.operation = params.operation;
|
|
1866
|
+
this.limitKind = params.limitKind;
|
|
1867
|
+
this.limit = params.limit;
|
|
1868
|
+
this.attempted = params.attempted;
|
|
1702
1869
|
}
|
|
1703
1870
|
toJSON() {
|
|
1704
1871
|
return {
|
|
1705
|
-
error:
|
|
1872
|
+
error: this.code,
|
|
1706
1873
|
message: this.message,
|
|
1707
|
-
|
|
1708
|
-
|
|
1874
|
+
operation: this.operation,
|
|
1875
|
+
limitKind: this.limitKind,
|
|
1876
|
+
limit: this.limit,
|
|
1877
|
+
attempted: this.attempted
|
|
1709
1878
|
};
|
|
1710
1879
|
}
|
|
1711
1880
|
};
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1881
|
+
|
|
1882
|
+
// src/limits/enforce.ts
|
|
1883
|
+
function approxUsdValue(asset, amount) {
|
|
1884
|
+
const symbol = asset.toUpperCase();
|
|
1885
|
+
if (symbol === "USDC" || symbol === "USDSUI") return amount;
|
|
1886
|
+
return null;
|
|
1887
|
+
}
|
|
1888
|
+
function assertLimitConfig(input) {
|
|
1889
|
+
if (input.force) return;
|
|
1890
|
+
if (!Number.isFinite(input.amountUsd) || input.amountUsd <= 0) return;
|
|
1891
|
+
const { limits } = input;
|
|
1892
|
+
if (!limits) return;
|
|
1893
|
+
if (limits.perTxUsd !== void 0 && input.amountUsd > limits.perTxUsd) {
|
|
1894
|
+
throw new LimitExceededError({
|
|
1895
|
+
operation: input.operation,
|
|
1896
|
+
limitKind: "perTxUsd",
|
|
1897
|
+
limit: limits.perTxUsd,
|
|
1898
|
+
attempted: input.amountUsd
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
if (limits.dailyUsd !== void 0 && input.spentTodayUsd + input.amountUsd > limits.dailyUsd) {
|
|
1902
|
+
throw new LimitExceededError({
|
|
1903
|
+
operation: input.operation,
|
|
1904
|
+
limitKind: "dailyUsd",
|
|
1905
|
+
// The cap is total/day; report the cap (attempted is THIS write's USD).
|
|
1906
|
+
limit: limits.dailyUsd,
|
|
1907
|
+
attempted: input.spentTodayUsd + input.amountUsd
|
|
1908
|
+
});
|
|
1720
1909
|
}
|
|
1721
1910
|
}
|
|
1722
|
-
|
|
1723
|
-
// src/safeguards/enforcer.ts
|
|
1724
|
-
var SafeguardEnforcer = class {
|
|
1725
|
-
config;
|
|
1726
|
-
configPath;
|
|
1911
|
+
var LimitEnforcer = class {
|
|
1727
1912
|
constructor(configDir) {
|
|
1728
|
-
this.
|
|
1729
|
-
|
|
1913
|
+
this.configDir = configDir;
|
|
1914
|
+
}
|
|
1915
|
+
/** Throws `LimitExceededError` when the write exceeds an opted-in cap. */
|
|
1916
|
+
assert(input) {
|
|
1917
|
+
assertLimitConfig({
|
|
1918
|
+
limits: getLimits(this.configDir),
|
|
1919
|
+
spentTodayUsd: dailySpentToday(this.configDir),
|
|
1920
|
+
operation: input.operation,
|
|
1921
|
+
amountUsd: input.amountUsd,
|
|
1922
|
+
force: input.force
|
|
1923
|
+
});
|
|
1730
1924
|
}
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
const raw = JSON.parse(readFileSync(this.configPath, "utf-8"));
|
|
1735
|
-
this.config = {
|
|
1736
|
-
...DEFAULT_SAFEGUARD_CONFIG,
|
|
1737
|
-
locked: raw.locked ?? false,
|
|
1738
|
-
maxPerTx: raw.maxPerTx ?? 0,
|
|
1739
|
-
maxDailySend: raw.maxDailySend ?? 0,
|
|
1740
|
-
dailyUsed: raw.dailyUsed ?? 0,
|
|
1741
|
-
dailyResetDate: raw.dailyResetDate ?? ""
|
|
1742
|
-
};
|
|
1743
|
-
} catch {
|
|
1744
|
-
this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
|
|
1745
|
-
}
|
|
1925
|
+
/** Add a settled write's USD value to today's cumulative total. */
|
|
1926
|
+
record(amountUsd) {
|
|
1927
|
+
recordDailySpend(amountUsd, this.configDir);
|
|
1746
1928
|
}
|
|
1747
|
-
|
|
1748
|
-
this.
|
|
1749
|
-
if (this.config.locked) {
|
|
1750
|
-
throw new SafeguardError("locked", {});
|
|
1751
|
-
}
|
|
1929
|
+
getLimits() {
|
|
1930
|
+
return getLimits(this.configDir);
|
|
1752
1931
|
}
|
|
1753
|
-
|
|
1754
|
-
this.
|
|
1755
|
-
if (this.config.locked) {
|
|
1756
|
-
throw new SafeguardError("locked", {});
|
|
1757
|
-
}
|
|
1758
|
-
const amount = metadata.amount ?? 0;
|
|
1759
|
-
if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
|
|
1760
|
-
throw new SafeguardError("maxPerTx", {
|
|
1761
|
-
attempted: amount,
|
|
1762
|
-
limit: this.config.maxPerTx
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
this.resetDailyIfNewDay();
|
|
1766
|
-
if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
|
|
1767
|
-
throw new SafeguardError("maxDailySend", {
|
|
1768
|
-
attempted: amount,
|
|
1769
|
-
limit: this.config.maxDailySend,
|
|
1770
|
-
current: this.config.dailyUsed
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1932
|
+
hasLimits() {
|
|
1933
|
+
return hasLimits(this.configDir);
|
|
1773
1934
|
}
|
|
1774
|
-
|
|
1775
|
-
this.
|
|
1776
|
-
this.config.dailyUsed += amount;
|
|
1777
|
-
this.save();
|
|
1778
|
-
}
|
|
1779
|
-
lock() {
|
|
1780
|
-
this.config.locked = true;
|
|
1781
|
-
this.save();
|
|
1782
|
-
}
|
|
1783
|
-
unlock() {
|
|
1784
|
-
this.config.locked = false;
|
|
1785
|
-
this.save();
|
|
1786
|
-
}
|
|
1787
|
-
set(key, value) {
|
|
1788
|
-
if (key === "locked" && typeof value === "boolean") {
|
|
1789
|
-
this.config.locked = value;
|
|
1790
|
-
} else if (key === "maxPerTx" && typeof value === "number") {
|
|
1791
|
-
this.config.maxPerTx = value;
|
|
1792
|
-
} else if (key === "maxDailySend" && typeof value === "number") {
|
|
1793
|
-
this.config.maxDailySend = value;
|
|
1794
|
-
}
|
|
1795
|
-
this.save();
|
|
1796
|
-
}
|
|
1797
|
-
getConfig() {
|
|
1798
|
-
this.load();
|
|
1799
|
-
this.resetDailyIfNewDay();
|
|
1800
|
-
return { ...this.config };
|
|
1801
|
-
}
|
|
1802
|
-
isConfigured() {
|
|
1803
|
-
return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
|
|
1804
|
-
}
|
|
1805
|
-
resetDailyIfNewDay() {
|
|
1806
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1807
|
-
if (this.config.dailyResetDate !== today) {
|
|
1808
|
-
this.config.dailyUsed = 0;
|
|
1809
|
-
this.config.dailyResetDate = today;
|
|
1810
|
-
this.save();
|
|
1811
|
-
}
|
|
1935
|
+
setLimits(limits) {
|
|
1936
|
+
setLimits(limits, this.configDir);
|
|
1812
1937
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
existing = JSON.parse(readFileSync(this.configPath, "utf-8"));
|
|
1819
|
-
} catch {
|
|
1820
|
-
}
|
|
1821
|
-
const merged = {
|
|
1822
|
-
...existing,
|
|
1823
|
-
locked: this.config.locked,
|
|
1824
|
-
maxPerTx: this.config.maxPerTx,
|
|
1825
|
-
maxDailySend: this.config.maxDailySend,
|
|
1826
|
-
dailyUsed: this.config.dailyUsed,
|
|
1827
|
-
dailyResetDate: this.config.dailyResetDate
|
|
1828
|
-
};
|
|
1829
|
-
const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
|
|
1830
|
-
if (!existsSync(dir)) {
|
|
1831
|
-
mkdirSync(dir, { recursive: true });
|
|
1832
|
-
}
|
|
1833
|
-
writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
1834
|
-
} catch {
|
|
1835
|
-
}
|
|
1938
|
+
clearLimits() {
|
|
1939
|
+
clearLimits(this.configDir);
|
|
1940
|
+
}
|
|
1941
|
+
dailySpentToday() {
|
|
1942
|
+
return dailySpentToday(this.configDir);
|
|
1836
1943
|
}
|
|
1837
1944
|
};
|
|
1838
|
-
var
|
|
1945
|
+
var DEFAULT_CONFIG_DIR2 = join(homedir(), ".t2000");
|
|
1839
1946
|
var T2000 = class _T2000 extends EventEmitter {
|
|
1840
1947
|
_signer;
|
|
1841
1948
|
_keypair;
|
|
1842
1949
|
client;
|
|
1843
1950
|
_address;
|
|
1844
|
-
|
|
1951
|
+
/** Unified spending-limit gate (per-tx + cumulative daily, USD). Shared by
|
|
1952
|
+
* CLI + MCP + programmatic writes — one gate, no bypass (R-0 Finding 1). */
|
|
1953
|
+
limits;
|
|
1845
1954
|
constructor(keypairOrSigner, client, configDir, isSignerMode) {
|
|
1846
1955
|
super();
|
|
1847
1956
|
if (isSignerMode) {
|
|
@@ -1855,8 +1964,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1855
1964
|
this._address = getAddress(kp);
|
|
1856
1965
|
}
|
|
1857
1966
|
this.client = client;
|
|
1858
|
-
this.
|
|
1859
|
-
this.enforcer.load();
|
|
1967
|
+
this.limits = new LimitEnforcer(configDir);
|
|
1860
1968
|
}
|
|
1861
1969
|
static async create(options = {}) {
|
|
1862
1970
|
const { keyPath, rpcUrl } = options;
|
|
@@ -1869,7 +1977,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1869
1977
|
);
|
|
1870
1978
|
}
|
|
1871
1979
|
const keypair = await loadKey(void 0, keyPath);
|
|
1872
|
-
return new _T2000(keypair, client,
|
|
1980
|
+
return new _T2000(keypair, client, DEFAULT_CONFIG_DIR2);
|
|
1873
1981
|
}
|
|
1874
1982
|
static fromPrivateKey(privateKey, options = {}) {
|
|
1875
1983
|
const keypair = keypairFromPrivateKey(privateKey);
|
|
@@ -1880,7 +1988,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1880
1988
|
const keypair = generateKeypair();
|
|
1881
1989
|
await saveKey(keypair, void 0, options.keyPath);
|
|
1882
1990
|
const client = getSuiClient();
|
|
1883
|
-
const agent = new _T2000(keypair, client,
|
|
1991
|
+
const agent = new _T2000(keypair, client, DEFAULT_CONFIG_DIR2);
|
|
1884
1992
|
const address = agent.address();
|
|
1885
1993
|
return { agent, address };
|
|
1886
1994
|
}
|
|
@@ -1902,11 +2010,10 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1902
2010
|
}
|
|
1903
2011
|
// -- MPP Payments --
|
|
1904
2012
|
async pay(options) {
|
|
1905
|
-
this.
|
|
1906
|
-
this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
|
|
2013
|
+
this.limits.assert({ operation: "pay", amountUsd: options.maxPrice ?? 0, force: options.force });
|
|
1907
2014
|
const result = await payWithMpp({ signer: this._signer, client: this.client, options });
|
|
1908
2015
|
if (result.paid) {
|
|
1909
|
-
this.
|
|
2016
|
+
this.limits.record(result.cost ?? options.maxPrice ?? 0);
|
|
1910
2017
|
}
|
|
1911
2018
|
return result;
|
|
1912
2019
|
}
|
|
@@ -1920,7 +2027,11 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1920
2027
|
// and the S.323 build-tracker entry.
|
|
1921
2028
|
// -- Swap --
|
|
1922
2029
|
async swap(params) {
|
|
1923
|
-
this.
|
|
2030
|
+
this.limits.assert({
|
|
2031
|
+
operation: "swap",
|
|
2032
|
+
amountUsd: approxUsdValue(params.from, params.amount) ?? 0,
|
|
2033
|
+
force: params.force
|
|
2034
|
+
});
|
|
1924
2035
|
const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
|
|
1925
2036
|
const fromType = resolveTokenType2(params.from);
|
|
1926
2037
|
const toType = resolveTokenType2(params.to);
|
|
@@ -2012,6 +2123,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
2012
2123
|
const fromName = resolveSymbol(fromType);
|
|
2013
2124
|
const toName = resolveSymbol(toType);
|
|
2014
2125
|
const routeDesc = route.routerData.paths?.map((p) => p.provider).filter(Boolean).slice(0, 3).join(" + ") ?? "Cetus Aggregator";
|
|
2126
|
+
this.limits.record(approxUsdValue(params.from, params.amount) ?? 0);
|
|
2015
2127
|
return {
|
|
2016
2128
|
success: true,
|
|
2017
2129
|
tx: gasResult.digest,
|
|
@@ -2068,7 +2180,6 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
2068
2180
|
* the "build via gRPC, execute via JSON-RPC" hybrid).
|
|
2069
2181
|
*/
|
|
2070
2182
|
async send(params) {
|
|
2071
|
-
this.enforcer.assertNotLocked();
|
|
2072
2183
|
const asset = params.asset;
|
|
2073
2184
|
if (!asset) {
|
|
2074
2185
|
throw new T2000Error(
|
|
@@ -2078,6 +2189,11 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
2078
2189
|
}
|
|
2079
2190
|
assertAllowedAsset("send", asset);
|
|
2080
2191
|
const sendableAsset = asset;
|
|
2192
|
+
this.limits.assert({
|
|
2193
|
+
operation: "send",
|
|
2194
|
+
amountUsd: approxUsdValue(sendableAsset, params.amount) ?? 0,
|
|
2195
|
+
force: params.force
|
|
2196
|
+
});
|
|
2081
2197
|
const resolved = await this.resolveRecipient(params.to);
|
|
2082
2198
|
const sendAmount = params.amount;
|
|
2083
2199
|
const sendTo = resolved.address;
|
|
@@ -2089,7 +2205,7 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
2089
2205
|
() => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset: sendableAsset }),
|
|
2090
2206
|
{ buildClient }
|
|
2091
2207
|
);
|
|
2092
|
-
this.
|
|
2208
|
+
this.limits.record(approxUsdValue(sendableAsset, sendAmount) ?? 0);
|
|
2093
2209
|
const balance = await this.balance();
|
|
2094
2210
|
this.emitBalanceChange(sendableAsset, sendAmount, "send", gasResult.digest);
|
|
2095
2211
|
return {
|
|
@@ -2641,6 +2757,6 @@ function displayHandle(label) {
|
|
|
2641
2757
|
return `${label}@${parentDisplay}`;
|
|
2642
2758
|
}
|
|
2643
2759
|
|
|
2644
|
-
export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, DEFAULT_GRPC_URL, DEFAULT_NETWORK,
|
|
2760
|
+
export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, DEFAULT_GRPC_URL, DEFAULT_NETWORK, ETH_TYPE, GASLESS_MIN_STABLE_AMOUNT, GASLESS_STABLE_TYPES, GAS_RESERVE_MIN, IKA_TYPE, InvalidAddressError, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, LimitEnforcer, LimitExceededError, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, OPERATION_ASSETS, OVERLAY_FEE_RATE, SENDABLE_ASSETS, SPONSORED_PYTH_DEPENDENT_PROVIDERS, STABLE_ASSETS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SuinsNotRegisteredError, SuinsRpcError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE, WAL_TYPE, WBTC_TYPE, WRITE_APPENDER_REGISTRY, ZkLoginSigner, addSendToTx, addSwapToTx, approxUsdValue, assertAllowedAsset, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, buildSendTx, buildSwapTx, classifyAction, classifyLabel, classifyTransaction, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, deserializeCetusRoute, displayHandle, executeTx, exportPrivateKey, extractAllUserLegs, extractTransferDetails, extractTxCommands, extractTxSender, fallbackLabel, fetchAllCoins, findSwapRoute, formatAssetAmount, formatSui, formatUsd, fullHandle, generateKeypair, getAddress, getCoinMeta, getDecimals, getDecimalsForCoinType, getLimits, getSponsoredSwapProviders, getSuiClient, getSuiGrpcClient, getSwapQuote, hasLimits, isAllowedAsset, isCetusRouteFresh, isInRegistry, keypairFromPrivateKey, loadKey, looksLikeSuiNs, mapMoveAbortCode, mapWalletError, mistToSui, normalizeAddressInput, normalizeAsset, normalizeCoinType, parseSuiRpcTx, payWithMpp, queryHistory, queryTransaction, rawToStable, rawToUsdc, readLimitsFile, recordDailySpend, refineLendingLabel, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, resolveSymbol, resolveTokenType, saveBech32, saveKey, selectAndSplitCoin, selectSuiCoin, serializeCetusRoute, setLimits, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, validateLabel, verifyCetusRouteCoinMatch, walletExists, writeLimitsFile };
|
|
2645
2761
|
//# sourceMappingURL=index.js.map
|
|
2646
2762
|
//# sourceMappingURL=index.js.map
|