kontext-sdk 0.9.0 → 0.10.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/README.md +29 -0
- package/dist/index.d.mts +183 -3
- package/dist/index.d.ts +183 -3
- package/dist/index.js +493 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +486 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var crypto$1 = require('crypto');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var fs5 = require('fs');
|
|
5
|
+
var path5 = require('path');
|
|
6
6
|
|
|
7
7
|
function _interopNamespace(e) {
|
|
8
8
|
if (e && e.__esModule) return e;
|
|
@@ -22,8 +22,8 @@ function _interopNamespace(e) {
|
|
|
22
22
|
return Object.freeze(n);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
var
|
|
26
|
-
var
|
|
25
|
+
var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
|
|
26
|
+
var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
|
|
27
27
|
|
|
28
28
|
var __defProp = Object.defineProperty;
|
|
29
29
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -1974,13 +1974,13 @@ var ActionLogger = class {
|
|
|
1974
1974
|
}
|
|
1975
1975
|
flushToFile(actions) {
|
|
1976
1976
|
const outputDir = this.config.localOutputDir ?? ".kontext";
|
|
1977
|
-
const logDir =
|
|
1977
|
+
const logDir = path5__namespace.join(outputDir, "logs");
|
|
1978
1978
|
try {
|
|
1979
|
-
|
|
1979
|
+
fs5__namespace.mkdirSync(logDir, { recursive: true });
|
|
1980
1980
|
const filename = `actions-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.jsonl`;
|
|
1981
|
-
const filePath =
|
|
1981
|
+
const filePath = path5__namespace.join(logDir, filename);
|
|
1982
1982
|
const lines = actions.map((a) => JSON.stringify(a)).join("\n") + "\n";
|
|
1983
|
-
|
|
1983
|
+
fs5__namespace.appendFileSync(filePath, lines, "utf-8");
|
|
1984
1984
|
} catch (error) {
|
|
1985
1985
|
this.emitLog("warn", "Failed to write log file", { error });
|
|
1986
1986
|
}
|
|
@@ -2590,9 +2590,9 @@ var SANCTIONED_SET = new Set(
|
|
|
2590
2590
|
function loadCachedSDN() {
|
|
2591
2591
|
try {
|
|
2592
2592
|
const dataDir = process.env["KONTEXT_DATA_DIR"] || ".kontext";
|
|
2593
|
-
const cachePath =
|
|
2594
|
-
if (
|
|
2595
|
-
const cache = JSON.parse(
|
|
2593
|
+
const cachePath = path5__namespace.join(dataDir, "ofac-sdn-cache.json");
|
|
2594
|
+
if (fs5__namespace.existsSync(cachePath)) {
|
|
2595
|
+
const cache = JSON.parse(fs5__namespace.readFileSync(cachePath, "utf-8"));
|
|
2596
2596
|
if (Array.isArray(cache.addresses)) {
|
|
2597
2597
|
for (const addr of cache.addresses) {
|
|
2598
2598
|
SANCTIONED_SET.add(String(addr).toLowerCase());
|
|
@@ -3713,7 +3713,7 @@ var JsonFileExporter = class {
|
|
|
3713
3713
|
buffer = [];
|
|
3714
3714
|
bufferSize;
|
|
3715
3715
|
constructor(options) {
|
|
3716
|
-
this.outputDir =
|
|
3716
|
+
this.outputDir = path5__namespace.resolve(options?.outputDir ?? ".kontext/exports");
|
|
3717
3717
|
this.bufferSize = options?.bufferSize ?? 1;
|
|
3718
3718
|
}
|
|
3719
3719
|
async export(events) {
|
|
@@ -3728,11 +3728,11 @@ var JsonFileExporter = class {
|
|
|
3728
3728
|
const toWrite = [...this.buffer];
|
|
3729
3729
|
this.buffer = [];
|
|
3730
3730
|
try {
|
|
3731
|
-
|
|
3731
|
+
fs5__namespace.mkdirSync(this.outputDir, { recursive: true });
|
|
3732
3732
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3733
|
-
const filePath =
|
|
3733
|
+
const filePath = path5__namespace.join(this.outputDir, `events-${date}.jsonl`);
|
|
3734
3734
|
const lines = toWrite.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
3735
|
-
|
|
3735
|
+
fs5__namespace.appendFileSync(filePath, lines, "utf-8");
|
|
3736
3736
|
} catch (error) {
|
|
3737
3737
|
console.warn("[Kontext JsonFileExporter] Failed to write events:", error);
|
|
3738
3738
|
}
|
|
@@ -3902,6 +3902,218 @@ function extractDocumentId(resourceName) {
|
|
|
3902
3902
|
const parts = resourceName.split("/");
|
|
3903
3903
|
return parts[parts.length - 1] ?? resourceName;
|
|
3904
3904
|
}
|
|
3905
|
+
var CONFIG_FILENAME = "kontext.config.json";
|
|
3906
|
+
function loadConfigFile(startDir) {
|
|
3907
|
+
const dir = startDir ?? process.cwd();
|
|
3908
|
+
const filePath = findConfigFile(dir);
|
|
3909
|
+
if (!filePath) return null;
|
|
3910
|
+
try {
|
|
3911
|
+
const raw = fs5__namespace.readFileSync(filePath, "utf-8");
|
|
3912
|
+
const parsed = JSON.parse(raw);
|
|
3913
|
+
if (!parsed.projectId || typeof parsed.projectId !== "string") {
|
|
3914
|
+
return null;
|
|
3915
|
+
}
|
|
3916
|
+
return parsed;
|
|
3917
|
+
} catch {
|
|
3918
|
+
return null;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
function findConfigFile(dir) {
|
|
3922
|
+
let current = path5__namespace.resolve(dir);
|
|
3923
|
+
const root = path5__namespace.parse(current).root;
|
|
3924
|
+
while (true) {
|
|
3925
|
+
const candidate = path5__namespace.join(current, CONFIG_FILENAME);
|
|
3926
|
+
if (fs5__namespace.existsSync(candidate)) {
|
|
3927
|
+
return candidate;
|
|
3928
|
+
}
|
|
3929
|
+
const parent = path5__namespace.dirname(current);
|
|
3930
|
+
if (parent === current || current === root) {
|
|
3931
|
+
return null;
|
|
3932
|
+
}
|
|
3933
|
+
current = parent;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
// src/integrations/data/stablecoin-contracts.ts
|
|
3938
|
+
var STABLECOIN_CONTRACTS = {
|
|
3939
|
+
// USDC (6 decimals)
|
|
3940
|
+
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": { token: "USDC", chain: "ethereum", decimals: 6 },
|
|
3941
|
+
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": { token: "USDC", chain: "base", decimals: 6 },
|
|
3942
|
+
"0x3c499c542cef5e3811e1192ce70d8cc03d5c3359": { token: "USDC", chain: "polygon", decimals: 6 },
|
|
3943
|
+
"0xaf88d065e77c8cc2239327c5edb3a432268e5831": { token: "USDC", chain: "arbitrum", decimals: 6 },
|
|
3944
|
+
"0x0b2c639c533813f4aa9d7837caf62653d097ff85": { token: "USDC", chain: "optimism", decimals: 6 },
|
|
3945
|
+
"0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": { token: "USDC", chain: "avalanche", decimals: 6 },
|
|
3946
|
+
// USDT (6 decimals)
|
|
3947
|
+
"0xdac17f958d2ee523a2206206994597c13d831ec7": { token: "USDT", chain: "ethereum", decimals: 6 },
|
|
3948
|
+
"0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9": { token: "USDT", chain: "arbitrum", decimals: 6 },
|
|
3949
|
+
"0x94b008aa00579c1307b0ef2c499ad98a8ce58e58": { token: "USDT", chain: "optimism", decimals: 6 },
|
|
3950
|
+
"0xc2132d05d31c914a87c6611c10748aeb04b58e8f": { token: "USDT", chain: "polygon", decimals: 6 },
|
|
3951
|
+
// DAI (18 decimals)
|
|
3952
|
+
"0x6b175474e89094c44da98b954eedeac495271d0f": { token: "DAI", chain: "ethereum", decimals: 18 },
|
|
3953
|
+
"0x50c5725949a6f0c72e6c4a641f24049a917db0cb": { token: "DAI", chain: "base", decimals: 18 },
|
|
3954
|
+
// EURC (6 decimals)
|
|
3955
|
+
"0x1abaea1f7c830bd89acc67ec4af516284b1bc33c": { token: "EURC", chain: "ethereum", decimals: 6 },
|
|
3956
|
+
"0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42": { token: "EURC", chain: "base", decimals: 6 }
|
|
3957
|
+
};
|
|
3958
|
+
new Set(Object.keys(STABLECOIN_CONTRACTS));
|
|
3959
|
+
var CHAIN_ID_MAP = {
|
|
3960
|
+
1: "ethereum",
|
|
3961
|
+
8453: "base",
|
|
3962
|
+
137: "polygon",
|
|
3963
|
+
42161: "arbitrum",
|
|
3964
|
+
10: "optimism",
|
|
3965
|
+
43114: "avalanche"
|
|
3966
|
+
};
|
|
3967
|
+
var TRANSFER_SELECTOR = "0xa9059cbb";
|
|
3968
|
+
var TRANSFER_FROM_SELECTOR = "0x23b872dd";
|
|
3969
|
+
var TRANSFER_EVENT_ABI = {
|
|
3970
|
+
type: "event",
|
|
3971
|
+
name: "Transfer",
|
|
3972
|
+
inputs: [
|
|
3973
|
+
{ type: "address", name: "from", indexed: true },
|
|
3974
|
+
{ type: "address", name: "to", indexed: true },
|
|
3975
|
+
{ type: "uint256", name: "value", indexed: false }
|
|
3976
|
+
]
|
|
3977
|
+
};
|
|
3978
|
+
|
|
3979
|
+
// src/integrations/wallet-monitor.ts
|
|
3980
|
+
var WalletMonitor = class {
|
|
3981
|
+
kontext;
|
|
3982
|
+
config;
|
|
3983
|
+
agentId;
|
|
3984
|
+
tokens;
|
|
3985
|
+
unwatchers = [];
|
|
3986
|
+
running = false;
|
|
3987
|
+
/** Shared dedup set — tracks recently verified txHashes (populated by both layers) */
|
|
3988
|
+
verifiedTxHashes = /* @__PURE__ */ new Set();
|
|
3989
|
+
cleanupTimer = null;
|
|
3990
|
+
txTimestamps = /* @__PURE__ */ new Map();
|
|
3991
|
+
constructor(kontext, config, options) {
|
|
3992
|
+
this.kontext = kontext;
|
|
3993
|
+
this.config = config;
|
|
3994
|
+
this.agentId = options?.agentId ?? "wallet-monitor";
|
|
3995
|
+
this.tokens = options?.tokens ? new Set(options.tokens) : null;
|
|
3996
|
+
}
|
|
3997
|
+
/**
|
|
3998
|
+
* Mark a txHash as already verified (called by the viem interceptor layer).
|
|
3999
|
+
* The monitor will skip this tx if it later sees it on-chain.
|
|
4000
|
+
*/
|
|
4001
|
+
markVerified(txHash) {
|
|
4002
|
+
const lower = txHash.toLowerCase();
|
|
4003
|
+
this.verifiedTxHashes.add(lower);
|
|
4004
|
+
this.txTimestamps.set(lower, Date.now());
|
|
4005
|
+
}
|
|
4006
|
+
/**
|
|
4007
|
+
* Start watching all configured chains for stablecoin transfers.
|
|
4008
|
+
* Dynamically imports viem — requires viem as a peer dependency.
|
|
4009
|
+
*/
|
|
4010
|
+
async start() {
|
|
4011
|
+
if (this.running) return;
|
|
4012
|
+
let viem;
|
|
4013
|
+
try {
|
|
4014
|
+
viem = await import('viem');
|
|
4015
|
+
} catch {
|
|
4016
|
+
throw new Error(
|
|
4017
|
+
"Wallet monitoring requires viem. Install it: npm install viem"
|
|
4018
|
+
);
|
|
4019
|
+
}
|
|
4020
|
+
const { createPublicClient, http } = viem;
|
|
4021
|
+
const wallets = this.config.wallets.map((w) => w.toLowerCase());
|
|
4022
|
+
const pollingInterval = this.config.pollingIntervalMs ?? 12e3;
|
|
4023
|
+
const contractsByChain = /* @__PURE__ */ new Map();
|
|
4024
|
+
for (const [address, info] of Object.entries(STABLECOIN_CONTRACTS)) {
|
|
4025
|
+
if (this.tokens && !this.tokens.has(info.token)) continue;
|
|
4026
|
+
const existing = contractsByChain.get(info.chain) ?? [];
|
|
4027
|
+
existing.push({ address, info });
|
|
4028
|
+
contractsByChain.set(info.chain, existing);
|
|
4029
|
+
}
|
|
4030
|
+
for (const [chain, contracts] of contractsByChain) {
|
|
4031
|
+
const rpcUrl = this.config.rpcEndpoints[chain];
|
|
4032
|
+
if (!rpcUrl) continue;
|
|
4033
|
+
const client = createPublicClient({
|
|
4034
|
+
transport: http(rpcUrl)
|
|
4035
|
+
});
|
|
4036
|
+
for (const { address, info } of contracts) {
|
|
4037
|
+
const unwatch = client.watchEvent({
|
|
4038
|
+
address,
|
|
4039
|
+
event: TRANSFER_EVENT_ABI,
|
|
4040
|
+
args: { from: wallets.length === 1 ? wallets[0] : wallets },
|
|
4041
|
+
poll: true,
|
|
4042
|
+
pollingInterval,
|
|
4043
|
+
onLogs: (logs) => {
|
|
4044
|
+
for (const log of logs) {
|
|
4045
|
+
this.handleTransferLog(log, info);
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
});
|
|
4049
|
+
this.unwatchers.push(unwatch);
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
this.cleanupTimer = setInterval(() => {
|
|
4053
|
+
const cutoff = Date.now() - 5 * 60 * 1e3;
|
|
4054
|
+
for (const [hash, ts] of this.txTimestamps) {
|
|
4055
|
+
if (ts < cutoff) {
|
|
4056
|
+
this.verifiedTxHashes.delete(hash);
|
|
4057
|
+
this.txTimestamps.delete(hash);
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
}, 6e4);
|
|
4061
|
+
this.running = true;
|
|
4062
|
+
}
|
|
4063
|
+
/** Stop all watchers and cleanup */
|
|
4064
|
+
stop() {
|
|
4065
|
+
for (const unwatch of this.unwatchers) {
|
|
4066
|
+
try {
|
|
4067
|
+
unwatch();
|
|
4068
|
+
} catch {
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
this.unwatchers.length = 0;
|
|
4072
|
+
if (this.cleanupTimer) {
|
|
4073
|
+
clearInterval(this.cleanupTimer);
|
|
4074
|
+
this.cleanupTimer = null;
|
|
4075
|
+
}
|
|
4076
|
+
this.running = false;
|
|
4077
|
+
}
|
|
4078
|
+
isRunning() {
|
|
4079
|
+
return this.running;
|
|
4080
|
+
}
|
|
4081
|
+
handleTransferLog(log, contractInfo) {
|
|
4082
|
+
const txHash = log.transactionHash;
|
|
4083
|
+
if (!txHash) return;
|
|
4084
|
+
const lowerHash = txHash.toLowerCase();
|
|
4085
|
+
if (this.verifiedTxHashes.has(lowerHash)) return;
|
|
4086
|
+
this.markVerified(txHash);
|
|
4087
|
+
const from = log.args?.from?.toLowerCase() ?? "";
|
|
4088
|
+
const to = log.args?.to?.toLowerCase() ?? "";
|
|
4089
|
+
const value = log.args?.value;
|
|
4090
|
+
if (!from || !to || value === void 0) return;
|
|
4091
|
+
const amount = formatTokenAmount(value, contractInfo.decimals);
|
|
4092
|
+
const verifyInput = {
|
|
4093
|
+
txHash,
|
|
4094
|
+
chain: contractInfo.chain,
|
|
4095
|
+
amount,
|
|
4096
|
+
token: contractInfo.token,
|
|
4097
|
+
from,
|
|
4098
|
+
to,
|
|
4099
|
+
agentId: this.agentId,
|
|
4100
|
+
metadata: {
|
|
4101
|
+
source: "wallet-monitor",
|
|
4102
|
+
contractAddress: log.address
|
|
4103
|
+
}
|
|
4104
|
+
};
|
|
4105
|
+
this.kontext.verify(verifyInput).catch(() => {
|
|
4106
|
+
});
|
|
4107
|
+
}
|
|
4108
|
+
};
|
|
4109
|
+
function formatTokenAmount(amount, decimals) {
|
|
4110
|
+
const divisor = BigInt(10 ** decimals);
|
|
4111
|
+
const whole = amount / divisor;
|
|
4112
|
+
const fraction = amount % divisor;
|
|
4113
|
+
if (fraction === 0n) return whole.toString();
|
|
4114
|
+
const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
4115
|
+
return `${whole}.${fractionStr}`;
|
|
4116
|
+
}
|
|
3905
4117
|
var ProvenanceManager = class {
|
|
3906
4118
|
store;
|
|
3907
4119
|
logger;
|
|
@@ -5436,6 +5648,7 @@ var Kontext = class _Kontext {
|
|
|
5436
5648
|
trustScorer;
|
|
5437
5649
|
anomalyDetector;
|
|
5438
5650
|
screeningAggregator;
|
|
5651
|
+
walletMonitor = null;
|
|
5439
5652
|
provenanceManager = null;
|
|
5440
5653
|
identityRegistry = null;
|
|
5441
5654
|
walletClusterer = null;
|
|
@@ -5486,6 +5699,19 @@ var Kontext = class _Kontext {
|
|
|
5486
5699
|
thresholds: config.anomalyThresholds
|
|
5487
5700
|
});
|
|
5488
5701
|
}
|
|
5702
|
+
if (config.walletMonitoring && config.walletMonitoring.wallets.length > 0) {
|
|
5703
|
+
const tokens = config.policy?.allowedTokens;
|
|
5704
|
+
this.walletMonitor = new WalletMonitor(
|
|
5705
|
+
this,
|
|
5706
|
+
config.walletMonitoring,
|
|
5707
|
+
{ agentId: config.agentId, tokens: tokens ?? void 0 }
|
|
5708
|
+
);
|
|
5709
|
+
this.walletMonitor.start().catch((err) => {
|
|
5710
|
+
if (config.debug) {
|
|
5711
|
+
console.debug(`[Kontext] Wallet monitor failed to start: ${err}`);
|
|
5712
|
+
}
|
|
5713
|
+
});
|
|
5714
|
+
}
|
|
5489
5715
|
}
|
|
5490
5716
|
/**
|
|
5491
5717
|
* Initialize the Kontext SDK.
|
|
@@ -5517,6 +5743,29 @@ var Kontext = class _Kontext {
|
|
|
5517
5743
|
* ```
|
|
5518
5744
|
*/
|
|
5519
5745
|
static init(config) {
|
|
5746
|
+
if (!config) {
|
|
5747
|
+
const fileConfig = loadConfigFile();
|
|
5748
|
+
if (!fileConfig) {
|
|
5749
|
+
throw new KontextError(
|
|
5750
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
5751
|
+
"No config provided and no kontext.config.json found. Run `npx kontext init` to create one, or pass config to Kontext.init()."
|
|
5752
|
+
);
|
|
5753
|
+
}
|
|
5754
|
+
const mapped = {
|
|
5755
|
+
projectId: fileConfig.projectId,
|
|
5756
|
+
environment: fileConfig.environment ?? "production",
|
|
5757
|
+
apiKey: fileConfig.apiKey,
|
|
5758
|
+
agentId: fileConfig.agentId,
|
|
5759
|
+
interceptorMode: fileConfig.mode,
|
|
5760
|
+
policy: {
|
|
5761
|
+
allowedTokens: fileConfig.tokens,
|
|
5762
|
+
corridors: fileConfig.corridors?.from ? { blocked: fileConfig.corridors.to ? [{ from: fileConfig.corridors.from, to: fileConfig.corridors.to }] : void 0 } : void 0,
|
|
5763
|
+
thresholds: fileConfig.thresholds ? { edd: fileConfig.thresholds.alertAmount ? Number(fileConfig.thresholds.alertAmount) : void 0 } : void 0
|
|
5764
|
+
},
|
|
5765
|
+
walletMonitoring: fileConfig.wallets && fileConfig.wallets.length > 0 && fileConfig.rpcEndpoints ? { wallets: fileConfig.wallets, rpcEndpoints: fileConfig.rpcEndpoints } : void 0
|
|
5766
|
+
};
|
|
5767
|
+
return _Kontext.init(mapped);
|
|
5768
|
+
}
|
|
5520
5769
|
if (!config.projectId || config.projectId.trim() === "") {
|
|
5521
5770
|
throw new KontextError(
|
|
5522
5771
|
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
@@ -6697,9 +6946,20 @@ var Kontext = class _Kontext {
|
|
|
6697
6946
|
// Lifecycle
|
|
6698
6947
|
// --------------------------------------------------------------------------
|
|
6699
6948
|
/**
|
|
6700
|
-
*
|
|
6949
|
+
* Get the wallet monitor instance (or null if not configured).
|
|
6950
|
+
* Used by the viem interceptor for dedup registration.
|
|
6951
|
+
*/
|
|
6952
|
+
getWalletMonitor() {
|
|
6953
|
+
return this.walletMonitor;
|
|
6954
|
+
}
|
|
6955
|
+
/**
|
|
6956
|
+
* Gracefully shut down the SDK, flushing any pending data and stopping watchers.
|
|
6701
6957
|
*/
|
|
6702
6958
|
async destroy() {
|
|
6959
|
+
if (this.walletMonitor) {
|
|
6960
|
+
this.walletMonitor.stop();
|
|
6961
|
+
this.walletMonitor = null;
|
|
6962
|
+
}
|
|
6703
6963
|
await this.logger.destroy();
|
|
6704
6964
|
await this.exporter.shutdown();
|
|
6705
6965
|
}
|
|
@@ -6729,20 +6989,20 @@ var MemoryStorage = class {
|
|
|
6729
6989
|
var FileStorage = class {
|
|
6730
6990
|
baseDir;
|
|
6731
6991
|
constructor(baseDir) {
|
|
6732
|
-
this.baseDir =
|
|
6992
|
+
this.baseDir = path5__namespace.resolve(baseDir);
|
|
6733
6993
|
}
|
|
6734
6994
|
async save(key, data) {
|
|
6735
|
-
|
|
6995
|
+
fs5__namespace.mkdirSync(this.baseDir, { recursive: true });
|
|
6736
6996
|
const filePath = this.keyToPath(key);
|
|
6737
|
-
const dir =
|
|
6738
|
-
|
|
6739
|
-
|
|
6997
|
+
const dir = path5__namespace.dirname(filePath);
|
|
6998
|
+
fs5__namespace.mkdirSync(dir, { recursive: true });
|
|
6999
|
+
fs5__namespace.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6740
7000
|
}
|
|
6741
7001
|
async load(key) {
|
|
6742
7002
|
const filePath = this.keyToPath(key);
|
|
6743
|
-
if (!
|
|
7003
|
+
if (!fs5__namespace.existsSync(filePath)) return null;
|
|
6744
7004
|
try {
|
|
6745
|
-
const raw =
|
|
7005
|
+
const raw = fs5__namespace.readFileSync(filePath, "utf-8");
|
|
6746
7006
|
return JSON.parse(raw);
|
|
6747
7007
|
} catch {
|
|
6748
7008
|
return null;
|
|
@@ -6750,12 +7010,12 @@ var FileStorage = class {
|
|
|
6750
7010
|
}
|
|
6751
7011
|
async delete(key) {
|
|
6752
7012
|
const filePath = this.keyToPath(key);
|
|
6753
|
-
if (
|
|
6754
|
-
|
|
7013
|
+
if (fs5__namespace.existsSync(filePath)) {
|
|
7014
|
+
fs5__namespace.unlinkSync(filePath);
|
|
6755
7015
|
}
|
|
6756
7016
|
}
|
|
6757
7017
|
async list(prefix) {
|
|
6758
|
-
if (!
|
|
7018
|
+
if (!fs5__namespace.existsSync(this.baseDir)) return [];
|
|
6759
7019
|
return this.listRecursive(this.baseDir, prefix);
|
|
6760
7020
|
}
|
|
6761
7021
|
/** Get the base directory path. */
|
|
@@ -6767,18 +7027,18 @@ var FileStorage = class {
|
|
|
6767
7027
|
// --------------------------------------------------------------------------
|
|
6768
7028
|
keyToPath(key) {
|
|
6769
7029
|
const safeName = key.replace(/[<>"|?*]/g, "_");
|
|
6770
|
-
return
|
|
7030
|
+
return path5__namespace.join(this.baseDir, `${safeName}.json`);
|
|
6771
7031
|
}
|
|
6772
7032
|
pathToKey(filePath) {
|
|
6773
|
-
const relative2 =
|
|
7033
|
+
const relative2 = path5__namespace.relative(this.baseDir, filePath);
|
|
6774
7034
|
return relative2.replace(/\.json$/, "");
|
|
6775
7035
|
}
|
|
6776
7036
|
listRecursive(dir, prefix) {
|
|
6777
7037
|
const keys = [];
|
|
6778
|
-
if (!
|
|
6779
|
-
const entries =
|
|
7038
|
+
if (!fs5__namespace.existsSync(dir)) return keys;
|
|
7039
|
+
const entries = fs5__namespace.readdirSync(dir, { withFileTypes: true });
|
|
6780
7040
|
for (const entry of entries) {
|
|
6781
|
-
const fullPath =
|
|
7041
|
+
const fullPath = path5__namespace.join(dir, entry.name);
|
|
6782
7042
|
if (entry.isDirectory()) {
|
|
6783
7043
|
keys.push(...this.listRecursive(fullPath, prefix));
|
|
6784
7044
|
} else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
@@ -7140,16 +7400,16 @@ var OpenSanctionsLocalProvider = class {
|
|
|
7140
7400
|
this.addressSet.clear();
|
|
7141
7401
|
this.addressToEntity.clear();
|
|
7142
7402
|
try {
|
|
7143
|
-
const
|
|
7403
|
+
const fs6 = __require("fs");
|
|
7144
7404
|
const pathMod = __require("path");
|
|
7145
7405
|
const dataPath = pathMod.resolve(this.dataDir);
|
|
7146
|
-
if (!
|
|
7147
|
-
const files =
|
|
7406
|
+
if (!fs6.existsSync(dataPath)) return;
|
|
7407
|
+
const files = fs6.readdirSync(dataPath).filter(
|
|
7148
7408
|
(f) => f.endsWith(".json")
|
|
7149
7409
|
);
|
|
7150
7410
|
for (const file of files) {
|
|
7151
7411
|
const filePath = pathMod.join(dataPath, file);
|
|
7152
|
-
const content =
|
|
7412
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
7153
7413
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
7154
7414
|
for (const line of lines) {
|
|
7155
7415
|
try {
|
|
@@ -7500,9 +7760,202 @@ var ChainalysisOracleProvider = class {
|
|
|
7500
7760
|
}
|
|
7501
7761
|
};
|
|
7502
7762
|
|
|
7763
|
+
// src/integrations/viem-interceptor.ts
|
|
7764
|
+
var ViemComplianceError = class extends Error {
|
|
7765
|
+
result;
|
|
7766
|
+
from;
|
|
7767
|
+
to;
|
|
7768
|
+
amount;
|
|
7769
|
+
constructor(message, result, details) {
|
|
7770
|
+
super(message);
|
|
7771
|
+
this.name = "ViemComplianceError";
|
|
7772
|
+
this.result = result;
|
|
7773
|
+
this.from = details.from;
|
|
7774
|
+
this.to = details.to;
|
|
7775
|
+
this.amount = details.amount;
|
|
7776
|
+
}
|
|
7777
|
+
};
|
|
7778
|
+
function withKontextCompliance(client, kontext, options) {
|
|
7779
|
+
const config = kontext.getConfig();
|
|
7780
|
+
const agentId = options?.agentId ?? config.agentId ?? "viem-agent";
|
|
7781
|
+
const mode = options?.mode ?? config.interceptorMode ?? "post-send";
|
|
7782
|
+
const sessionId = options?.sessionId;
|
|
7783
|
+
const metadata = options?.metadata;
|
|
7784
|
+
const onVerify = options?.onVerify;
|
|
7785
|
+
const onError = options?.onError;
|
|
7786
|
+
const allowedTokens = options?.tokens ? new Set(options.tokens) : config.policy?.allowedTokens ? new Set(config.policy.allowedTokens) : null;
|
|
7787
|
+
const allowedChains = options?.chains ? new Set(options.chains) : null;
|
|
7788
|
+
const allowedContracts = /* @__PURE__ */ new Set();
|
|
7789
|
+
for (const [address, info] of Object.entries(STABLECOIN_CONTRACTS)) {
|
|
7790
|
+
if (allowedTokens && !allowedTokens.has(info.token)) continue;
|
|
7791
|
+
if (allowedChains && !allowedChains.has(info.chain)) continue;
|
|
7792
|
+
allowedContracts.add(address);
|
|
7793
|
+
}
|
|
7794
|
+
const monitor = kontext.getWalletMonitor?.() ?? null;
|
|
7795
|
+
return client.extend((baseClient) => ({
|
|
7796
|
+
async sendTransaction(params) {
|
|
7797
|
+
const target = params.to?.toLowerCase();
|
|
7798
|
+
if (!target || !allowedContracts.has(target)) {
|
|
7799
|
+
return baseClient.sendTransaction(params);
|
|
7800
|
+
}
|
|
7801
|
+
const decoded = params.data ? decodeTransferCalldata(params.data) : null;
|
|
7802
|
+
if (!decoded) {
|
|
7803
|
+
return baseClient.sendTransaction(params);
|
|
7804
|
+
}
|
|
7805
|
+
const contractInfo = STABLECOIN_CONTRACTS[target];
|
|
7806
|
+
const verifyInput = buildVerifyInput(
|
|
7807
|
+
decoded,
|
|
7808
|
+
contractInfo,
|
|
7809
|
+
params,
|
|
7810
|
+
baseClient,
|
|
7811
|
+
agentId,
|
|
7812
|
+
sessionId,
|
|
7813
|
+
metadata
|
|
7814
|
+
);
|
|
7815
|
+
if (mode === "pre-send" || mode === "both") {
|
|
7816
|
+
await runPreSendScreen(kontext, verifyInput);
|
|
7817
|
+
}
|
|
7818
|
+
const txHash = await baseClient.sendTransaction(params);
|
|
7819
|
+
if (mode === "post-send" || mode === "both") {
|
|
7820
|
+
monitor?.markVerified(txHash);
|
|
7821
|
+
runPostSendVerify(kontext, { ...verifyInput, txHash }, txHash, onVerify, onError);
|
|
7822
|
+
}
|
|
7823
|
+
return txHash;
|
|
7824
|
+
},
|
|
7825
|
+
async writeContract(params) {
|
|
7826
|
+
if (!baseClient.writeContract) {
|
|
7827
|
+
throw new Error("writeContract not available on this client");
|
|
7828
|
+
}
|
|
7829
|
+
const target = params.address?.toLowerCase();
|
|
7830
|
+
if (!target || !allowedContracts.has(target)) {
|
|
7831
|
+
return baseClient.writeContract(params);
|
|
7832
|
+
}
|
|
7833
|
+
const fn = params.functionName;
|
|
7834
|
+
if (fn !== "transfer" && fn !== "transferFrom") {
|
|
7835
|
+
return baseClient.writeContract(params);
|
|
7836
|
+
}
|
|
7837
|
+
const decoded = decodeWriteContractArgs(fn, params.args);
|
|
7838
|
+
if (!decoded) {
|
|
7839
|
+
return baseClient.writeContract(params);
|
|
7840
|
+
}
|
|
7841
|
+
const contractInfo = STABLECOIN_CONTRACTS[target];
|
|
7842
|
+
const verifyInput = buildVerifyInput(
|
|
7843
|
+
decoded,
|
|
7844
|
+
contractInfo,
|
|
7845
|
+
params,
|
|
7846
|
+
baseClient,
|
|
7847
|
+
agentId,
|
|
7848
|
+
sessionId,
|
|
7849
|
+
metadata
|
|
7850
|
+
);
|
|
7851
|
+
if (mode === "pre-send" || mode === "both") {
|
|
7852
|
+
await runPreSendScreen(kontext, verifyInput);
|
|
7853
|
+
}
|
|
7854
|
+
const txHash = await baseClient.writeContract(params);
|
|
7855
|
+
if (mode === "post-send" || mode === "both") {
|
|
7856
|
+
monitor?.markVerified(txHash);
|
|
7857
|
+
runPostSendVerify(kontext, { ...verifyInput, txHash }, txHash, onVerify, onError);
|
|
7858
|
+
}
|
|
7859
|
+
return txHash;
|
|
7860
|
+
}
|
|
7861
|
+
}));
|
|
7862
|
+
}
|
|
7863
|
+
function buildVerifyInput(decoded, contractInfo, params, client, agentId, sessionId, metadata) {
|
|
7864
|
+
const chain = client.chain?.id ? CHAIN_ID_MAP[client.chain.id] ?? contractInfo.chain : contractInfo.chain;
|
|
7865
|
+
const from = (decoded.from ?? params.account?.address ?? client.account?.address ?? params.from ?? "").toLowerCase();
|
|
7866
|
+
return {
|
|
7867
|
+
txHash: "",
|
|
7868
|
+
chain,
|
|
7869
|
+
amount: formatTokenAmount2(decoded.amount, contractInfo.decimals),
|
|
7870
|
+
token: contractInfo.token,
|
|
7871
|
+
from,
|
|
7872
|
+
to: decoded.to.toLowerCase(),
|
|
7873
|
+
agentId,
|
|
7874
|
+
sessionId,
|
|
7875
|
+
metadata: {
|
|
7876
|
+
...metadata,
|
|
7877
|
+
source: "viem-auto-instrumentation",
|
|
7878
|
+
contractAddress: params.to ?? params.address
|
|
7879
|
+
}
|
|
7880
|
+
};
|
|
7881
|
+
}
|
|
7882
|
+
function runPostSendVerify(kontext, input, txHash, onVerify, onError) {
|
|
7883
|
+
kontext.verify(input).then(
|
|
7884
|
+
(result) => {
|
|
7885
|
+
if (onVerify) {
|
|
7886
|
+
try {
|
|
7887
|
+
const p = onVerify(result, txHash);
|
|
7888
|
+
if (p && typeof p.catch === "function") {
|
|
7889
|
+
p.catch(() => {
|
|
7890
|
+
});
|
|
7891
|
+
}
|
|
7892
|
+
} catch {
|
|
7893
|
+
}
|
|
7894
|
+
}
|
|
7895
|
+
},
|
|
7896
|
+
(error) => {
|
|
7897
|
+
if (onError) {
|
|
7898
|
+
try {
|
|
7899
|
+
const p = onError(error, txHash);
|
|
7900
|
+
if (p && typeof p.catch === "function") {
|
|
7901
|
+
p.catch(() => {
|
|
7902
|
+
});
|
|
7903
|
+
}
|
|
7904
|
+
} catch {
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7907
|
+
}
|
|
7908
|
+
);
|
|
7909
|
+
}
|
|
7910
|
+
async function runPreSendScreen(kontext, input) {
|
|
7911
|
+
const result = await kontext.verify({ ...input, txHash: "pre-screening" });
|
|
7912
|
+
if (!result.compliant) {
|
|
7913
|
+
throw new ViemComplianceError(
|
|
7914
|
+
`Transaction blocked: ${result.recommendations?.[0] ?? "compliance check failed"}`,
|
|
7915
|
+
result,
|
|
7916
|
+
{ from: input.from, to: input.to, amount: input.amount }
|
|
7917
|
+
);
|
|
7918
|
+
}
|
|
7919
|
+
}
|
|
7920
|
+
function decodeTransferCalldata(data) {
|
|
7921
|
+
if (!data || data.length < 10) return null;
|
|
7922
|
+
const selector = data.slice(0, 10).toLowerCase();
|
|
7923
|
+
if (selector === TRANSFER_SELECTOR && data.length >= 138) {
|
|
7924
|
+
const to = "0x" + data.slice(34, 74);
|
|
7925
|
+
const amount = BigInt("0x" + data.slice(74, 138));
|
|
7926
|
+
return { to, amount };
|
|
7927
|
+
}
|
|
7928
|
+
if (selector === TRANSFER_FROM_SELECTOR && data.length >= 202) {
|
|
7929
|
+
const from = "0x" + data.slice(34, 74);
|
|
7930
|
+
const to = "0x" + data.slice(98, 138);
|
|
7931
|
+
const amount = BigInt("0x" + data.slice(138, 202));
|
|
7932
|
+
return { from, to, amount };
|
|
7933
|
+
}
|
|
7934
|
+
return null;
|
|
7935
|
+
}
|
|
7936
|
+
function decodeWriteContractArgs(functionName, args) {
|
|
7937
|
+
if (!args || !Array.isArray(args)) return null;
|
|
7938
|
+
if (functionName === "transfer" && args.length >= 2) {
|
|
7939
|
+
return { to: String(args[0]), amount: BigInt(args[1]) };
|
|
7940
|
+
}
|
|
7941
|
+
if (functionName === "transferFrom" && args.length >= 3) {
|
|
7942
|
+
return { from: String(args[0]), to: String(args[1]), amount: BigInt(args[2]) };
|
|
7943
|
+
}
|
|
7944
|
+
return null;
|
|
7945
|
+
}
|
|
7946
|
+
function formatTokenAmount2(amount, decimals) {
|
|
7947
|
+
const divisor = BigInt(10 ** decimals);
|
|
7948
|
+
const whole = amount / divisor;
|
|
7949
|
+
const fraction = amount % divisor;
|
|
7950
|
+
if (fraction === 0n) return whole.toString();
|
|
7951
|
+
const fractionStr = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
7952
|
+
return `${whole}.${fractionStr}`;
|
|
7953
|
+
}
|
|
7954
|
+
|
|
7503
7955
|
exports.AgentIdentityRegistry = AgentIdentityRegistry;
|
|
7504
7956
|
exports.AnomalyDetector = AnomalyDetector;
|
|
7505
7957
|
exports.BehavioralFingerprinter = BehavioralFingerprinter;
|
|
7958
|
+
exports.CHAIN_ID_MAP = CHAIN_ID_MAP;
|
|
7506
7959
|
exports.CURRENCY_REQUIRED_LISTS = CURRENCY_REQUIRED_LISTS;
|
|
7507
7960
|
exports.ChainalysisFreeAPIProvider = ChainalysisFreeAPIProvider;
|
|
7508
7961
|
exports.ChainalysisOracleProvider = ChainalysisOracleProvider;
|
|
@@ -7526,12 +7979,15 @@ exports.PLAN_LIMITS = PLAN_LIMITS;
|
|
|
7526
7979
|
exports.PaymentCompliance = PaymentCompliance;
|
|
7527
7980
|
exports.PlanManager = PlanManager;
|
|
7528
7981
|
exports.ProvenanceManager = ProvenanceManager;
|
|
7982
|
+
exports.STABLECOIN_CONTRACTS = STABLECOIN_CONTRACTS;
|
|
7529
7983
|
exports.ScreeningAggregator = ScreeningAggregator;
|
|
7530
7984
|
exports.TOKEN_REQUIRED_LISTS = TOKEN_REQUIRED_LISTS;
|
|
7531
7985
|
exports.TrustScorer = TrustScorer;
|
|
7532
7986
|
exports.UKOFSIProvider = UKOFSIProvider;
|
|
7533
7987
|
exports.UsdcCompliance = UsdcCompliance;
|
|
7988
|
+
exports.ViemComplianceError = ViemComplianceError;
|
|
7534
7989
|
exports.WalletClusterer = WalletClusterer;
|
|
7990
|
+
exports.WalletMonitor = WalletMonitor;
|
|
7535
7991
|
exports.anchorDigest = anchorDigest;
|
|
7536
7992
|
exports.encodeERC8021Suffix = encodeERC8021Suffix;
|
|
7537
7993
|
exports.exchangeAttestation = exchangeAttestation;
|
|
@@ -7542,10 +7998,12 @@ exports.getRequiredLists = getRequiredLists;
|
|
|
7542
7998
|
exports.isBlockchainAddress = isBlockchainAddress;
|
|
7543
7999
|
exports.isCryptoTransaction = isCryptoTransaction;
|
|
7544
8000
|
exports.isFeatureAvailable = isFeatureAvailable;
|
|
8001
|
+
exports.loadConfigFile = loadConfigFile;
|
|
7545
8002
|
exports.parseERC8021Suffix = parseERC8021Suffix;
|
|
7546
8003
|
exports.providerSupportsQuery = providerSupportsQuery;
|
|
7547
8004
|
exports.requirePlan = requirePlan;
|
|
7548
8005
|
exports.verifyAnchor = verifyAnchor;
|
|
7549
8006
|
exports.verifyExportedChain = verifyExportedChain;
|
|
8007
|
+
exports.withKontextCompliance = withKontextCompliance;
|
|
7550
8008
|
//# sourceMappingURL=index.js.map
|
|
7551
8009
|
//# sourceMappingURL=index.js.map
|