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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var crypto$1 = require('crypto');
4
- var fs4 = require('fs');
5
- var path4 = require('path');
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 fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
26
- var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
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 = path4__namespace.join(outputDir, "logs");
1977
+ const logDir = path5__namespace.join(outputDir, "logs");
1978
1978
  try {
1979
- fs4__namespace.mkdirSync(logDir, { recursive: true });
1979
+ fs5__namespace.mkdirSync(logDir, { recursive: true });
1980
1980
  const filename = `actions-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.jsonl`;
1981
- const filePath = path4__namespace.join(logDir, filename);
1981
+ const filePath = path5__namespace.join(logDir, filename);
1982
1982
  const lines = actions.map((a) => JSON.stringify(a)).join("\n") + "\n";
1983
- fs4__namespace.appendFileSync(filePath, lines, "utf-8");
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 = path4__namespace.join(dataDir, "ofac-sdn-cache.json");
2594
- if (fs4__namespace.existsSync(cachePath)) {
2595
- const cache = JSON.parse(fs4__namespace.readFileSync(cachePath, "utf-8"));
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 = path4__namespace.resolve(options?.outputDir ?? ".kontext/exports");
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
- fs4__namespace.mkdirSync(this.outputDir, { recursive: true });
3731
+ fs5__namespace.mkdirSync(this.outputDir, { recursive: true });
3732
3732
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3733
- const filePath = path4__namespace.join(this.outputDir, `events-${date}.jsonl`);
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
- fs4__namespace.appendFileSync(filePath, lines, "utf-8");
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
- * Gracefully shut down the SDK, flushing any pending data.
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 = path4__namespace.resolve(baseDir);
6992
+ this.baseDir = path5__namespace.resolve(baseDir);
6733
6993
  }
6734
6994
  async save(key, data) {
6735
- fs4__namespace.mkdirSync(this.baseDir, { recursive: true });
6995
+ fs5__namespace.mkdirSync(this.baseDir, { recursive: true });
6736
6996
  const filePath = this.keyToPath(key);
6737
- const dir = path4__namespace.dirname(filePath);
6738
- fs4__namespace.mkdirSync(dir, { recursive: true });
6739
- fs4__namespace.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
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 (!fs4__namespace.existsSync(filePath)) return null;
7003
+ if (!fs5__namespace.existsSync(filePath)) return null;
6744
7004
  try {
6745
- const raw = fs4__namespace.readFileSync(filePath, "utf-8");
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 (fs4__namespace.existsSync(filePath)) {
6754
- fs4__namespace.unlinkSync(filePath);
7013
+ if (fs5__namespace.existsSync(filePath)) {
7014
+ fs5__namespace.unlinkSync(filePath);
6755
7015
  }
6756
7016
  }
6757
7017
  async list(prefix) {
6758
- if (!fs4__namespace.existsSync(this.baseDir)) return [];
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 path4__namespace.join(this.baseDir, `${safeName}.json`);
7030
+ return path5__namespace.join(this.baseDir, `${safeName}.json`);
6771
7031
  }
6772
7032
  pathToKey(filePath) {
6773
- const relative2 = path4__namespace.relative(this.baseDir, filePath);
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 (!fs4__namespace.existsSync(dir)) return keys;
6779
- const entries = fs4__namespace.readdirSync(dir, { withFileTypes: true });
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 = path4__namespace.join(dir, entry.name);
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 fs5 = __require("fs");
7403
+ const fs6 = __require("fs");
7144
7404
  const pathMod = __require("path");
7145
7405
  const dataPath = pathMod.resolve(this.dataDir);
7146
- if (!fs5.existsSync(dataPath)) return;
7147
- const files = fs5.readdirSync(dataPath).filter(
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 = fs5.readFileSync(filePath, "utf-8");
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