kontext-sdk 0.10.0 → 0.11.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 +57 -265
- package/dist/index.d.mts +309 -2
- package/dist/index.d.ts +309 -2
- package/dist/index.js +721 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +719 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3648,7 +3648,9 @@ var FEATURE_MIN_PLAN = {
|
|
|
3648
3648
|
"unified-screening": "pro",
|
|
3649
3649
|
"blocklist-manager": "pro",
|
|
3650
3650
|
"kya-identity": "pro",
|
|
3651
|
-
"kya-behavioral": "enterprise"
|
|
3651
|
+
"kya-behavioral": "enterprise",
|
|
3652
|
+
"coinbase-wallets": "enterprise",
|
|
3653
|
+
"metamask-wallets": "enterprise"
|
|
3652
3654
|
};
|
|
3653
3655
|
var PLAN_RANK = { free: 0, pro: 1, enterprise: 2 };
|
|
3654
3656
|
var FEATURE_LABELS = {
|
|
@@ -3667,7 +3669,9 @@ var FEATURE_LABELS = {
|
|
|
3667
3669
|
"unified-screening": "Unified screening (OFAC, Chainalysis, OpenSanctions)",
|
|
3668
3670
|
"blocklist-manager": "Custom blocklist/allowlist manager",
|
|
3669
3671
|
"kya-identity": "KYA identity resolution (declared identity, wallet clustering)",
|
|
3670
|
-
"kya-behavioral": "KYA behavioral fingerprinting (cross-session linking, confidence scoring)"
|
|
3672
|
+
"kya-behavioral": "KYA behavioral fingerprinting (cross-session linking, confidence scoring)",
|
|
3673
|
+
"coinbase-wallets": "Coinbase Developer Platform Wallets",
|
|
3674
|
+
"metamask-wallets": "MetaMask Embedded Wallets"
|
|
3671
3675
|
};
|
|
3672
3676
|
function isFeatureAvailable(feature, currentPlan) {
|
|
3673
3677
|
const requiredPlan = FEATURE_MIN_PLAN[feature];
|
|
@@ -5633,6 +5637,602 @@ var KYAConfidenceScorer = class {
|
|
|
5633
5637
|
}
|
|
5634
5638
|
};
|
|
5635
5639
|
|
|
5640
|
+
// src/integrations/circle-wallets.ts
|
|
5641
|
+
var DEFAULT_BASE_URL = "https://api.circle.com";
|
|
5642
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
5643
|
+
var CircleWalletManager = class {
|
|
5644
|
+
apiKey;
|
|
5645
|
+
entitySecret;
|
|
5646
|
+
baseUrl;
|
|
5647
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5648
|
+
kontext = null;
|
|
5649
|
+
constructor(config) {
|
|
5650
|
+
if (!config.apiKey) {
|
|
5651
|
+
throw new KontextError(
|
|
5652
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
5653
|
+
"Circle API key is required"
|
|
5654
|
+
);
|
|
5655
|
+
}
|
|
5656
|
+
if (!config.entitySecret) {
|
|
5657
|
+
throw new KontextError(
|
|
5658
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
5659
|
+
"Circle entity secret is required"
|
|
5660
|
+
);
|
|
5661
|
+
}
|
|
5662
|
+
this.apiKey = config.apiKey;
|
|
5663
|
+
this.entitySecret = config.entitySecret;
|
|
5664
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
5665
|
+
}
|
|
5666
|
+
/** Link to Kontext instance for auto-compliance logging */
|
|
5667
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5668
|
+
setKontext(kontext) {
|
|
5669
|
+
this.kontext = kontext;
|
|
5670
|
+
}
|
|
5671
|
+
/** Validate credentials by calling Circle's configuration endpoint */
|
|
5672
|
+
async validateCredentials() {
|
|
5673
|
+
try {
|
|
5674
|
+
const res = await fetch(`${this.baseUrl}/v1/w3s/config/entity`, {
|
|
5675
|
+
method: "GET",
|
|
5676
|
+
headers: this.headers(),
|
|
5677
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
5678
|
+
});
|
|
5679
|
+
return res.ok;
|
|
5680
|
+
} catch {
|
|
5681
|
+
return false;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
/** Create a wallet set (container for wallets) */
|
|
5685
|
+
async createWalletSet(input) {
|
|
5686
|
+
const body = {
|
|
5687
|
+
name: input.name,
|
|
5688
|
+
idempotencyKey: input.idempotencyKey ?? generateId(),
|
|
5689
|
+
entitySecretCiphertext: this.entitySecret
|
|
5690
|
+
};
|
|
5691
|
+
const res = await this.request(
|
|
5692
|
+
"POST",
|
|
5693
|
+
"/v1/w3s/developer/walletSets",
|
|
5694
|
+
body
|
|
5695
|
+
);
|
|
5696
|
+
return res.data.walletSet;
|
|
5697
|
+
}
|
|
5698
|
+
/** Create wallet(s) in a wallet set */
|
|
5699
|
+
async createWallet(input) {
|
|
5700
|
+
const body = {
|
|
5701
|
+
walletSetId: input.walletSetId,
|
|
5702
|
+
blockchains: input.blockchains.map((c) => this.mapChain(c)),
|
|
5703
|
+
count: input.count ?? 1,
|
|
5704
|
+
accountType: input.accountType ?? "EOA",
|
|
5705
|
+
idempotencyKey: input.idempotencyKey ?? generateId(),
|
|
5706
|
+
entitySecretCiphertext: this.entitySecret
|
|
5707
|
+
};
|
|
5708
|
+
const res = await this.request(
|
|
5709
|
+
"POST",
|
|
5710
|
+
"/v1/w3s/developer/wallets",
|
|
5711
|
+
body
|
|
5712
|
+
);
|
|
5713
|
+
return res.data.wallets;
|
|
5714
|
+
}
|
|
5715
|
+
/** List wallets, optionally filtered by wallet set */
|
|
5716
|
+
async listWallets(walletSetId) {
|
|
5717
|
+
const qs = walletSetId ? `?walletSetId=${walletSetId}` : "";
|
|
5718
|
+
const res = await this.request(
|
|
5719
|
+
"GET",
|
|
5720
|
+
`/v1/w3s/wallets${qs}`
|
|
5721
|
+
);
|
|
5722
|
+
return res.data.wallets;
|
|
5723
|
+
}
|
|
5724
|
+
/** Get wallet token balances */
|
|
5725
|
+
async getBalance(walletId) {
|
|
5726
|
+
const res = await this.request(
|
|
5727
|
+
"GET",
|
|
5728
|
+
`/v1/w3s/wallets/${walletId}/balances`
|
|
5729
|
+
);
|
|
5730
|
+
return res.data.tokenBalances.map((b) => ({
|
|
5731
|
+
token: b.token.symbol,
|
|
5732
|
+
amount: b.amount
|
|
5733
|
+
}));
|
|
5734
|
+
}
|
|
5735
|
+
/** Transfer with auto-compliance: runs verify() before/after transfer */
|
|
5736
|
+
async transferWithCompliance(input) {
|
|
5737
|
+
let complianceResult;
|
|
5738
|
+
if (this.kontext) {
|
|
5739
|
+
complianceResult = await this.kontext.verify({
|
|
5740
|
+
txHash: `circle-pending-${generateId()}`,
|
|
5741
|
+
chain: input.blockchain,
|
|
5742
|
+
amount: input.amount,
|
|
5743
|
+
token: "USDC",
|
|
5744
|
+
from: input.walletId,
|
|
5745
|
+
to: input.destinationAddress,
|
|
5746
|
+
agentId: input.agentId ?? "circle-wallet-manager"
|
|
5747
|
+
});
|
|
5748
|
+
if (!complianceResult.compliant) {
|
|
5749
|
+
return {
|
|
5750
|
+
id: "",
|
|
5751
|
+
state: "BLOCKED",
|
|
5752
|
+
complianceResult
|
|
5753
|
+
};
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
const body = {
|
|
5757
|
+
walletId: input.walletId,
|
|
5758
|
+
tokenAddress: input.tokenAddress,
|
|
5759
|
+
destinationAddress: input.destinationAddress,
|
|
5760
|
+
amounts: [input.amount],
|
|
5761
|
+
blockchain: this.mapChain(input.blockchain),
|
|
5762
|
+
idempotencyKey: input.idempotencyKey ?? generateId(),
|
|
5763
|
+
entitySecretCiphertext: this.entitySecret,
|
|
5764
|
+
feeLevel: "MEDIUM"
|
|
5765
|
+
};
|
|
5766
|
+
const res = await this.request(
|
|
5767
|
+
"POST",
|
|
5768
|
+
"/v1/w3s/developer/transactions/transfer",
|
|
5769
|
+
body
|
|
5770
|
+
);
|
|
5771
|
+
if (this.kontext) {
|
|
5772
|
+
await this.kontext.logReasoning({
|
|
5773
|
+
agentId: input.agentId ?? "circle-wallet-manager",
|
|
5774
|
+
action: "circle-transfer",
|
|
5775
|
+
reasoning: `Circle transfer ${res.data.id}: ${input.amount} to ${input.destinationAddress} on ${input.blockchain}`,
|
|
5776
|
+
confidence: 1,
|
|
5777
|
+
context: { transferId: res.data.id, state: res.data.state }
|
|
5778
|
+
});
|
|
5779
|
+
}
|
|
5780
|
+
return {
|
|
5781
|
+
id: res.data.id,
|
|
5782
|
+
state: res.data.state,
|
|
5783
|
+
txHash: res.data.txHash,
|
|
5784
|
+
complianceResult
|
|
5785
|
+
};
|
|
5786
|
+
}
|
|
5787
|
+
// --------------------------------------------------------------------------
|
|
5788
|
+
// Private helpers
|
|
5789
|
+
// --------------------------------------------------------------------------
|
|
5790
|
+
headers() {
|
|
5791
|
+
return {
|
|
5792
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
5793
|
+
"Content-Type": "application/json",
|
|
5794
|
+
"X-Entity-Secret": this.entitySecret
|
|
5795
|
+
};
|
|
5796
|
+
}
|
|
5797
|
+
async request(method, path6, body) {
|
|
5798
|
+
const res = await fetch(`${this.baseUrl}${path6}`, {
|
|
5799
|
+
method,
|
|
5800
|
+
headers: this.headers(),
|
|
5801
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
5802
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
5803
|
+
});
|
|
5804
|
+
if (!res.ok) {
|
|
5805
|
+
const text = await res.text().catch(() => "");
|
|
5806
|
+
throw new KontextError(
|
|
5807
|
+
"API_ERROR" /* API_ERROR */,
|
|
5808
|
+
`Circle API error ${res.status}: ${text}`
|
|
5809
|
+
);
|
|
5810
|
+
}
|
|
5811
|
+
return res.json();
|
|
5812
|
+
}
|
|
5813
|
+
mapChain(chain) {
|
|
5814
|
+
const map = {
|
|
5815
|
+
ethereum: "ETH",
|
|
5816
|
+
base: "BASE",
|
|
5817
|
+
polygon: "MATIC",
|
|
5818
|
+
arbitrum: "ARB",
|
|
5819
|
+
optimism: "OP",
|
|
5820
|
+
avalanche: "AVAX",
|
|
5821
|
+
solana: "SOL"
|
|
5822
|
+
};
|
|
5823
|
+
return map[chain] ?? chain.toUpperCase();
|
|
5824
|
+
}
|
|
5825
|
+
};
|
|
5826
|
+
|
|
5827
|
+
// src/integrations/coinbase-wallets.ts
|
|
5828
|
+
var CDP_BASE_URL = "https://api.cdp.coinbase.com";
|
|
5829
|
+
var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
5830
|
+
var CoinbaseWalletManager = class {
|
|
5831
|
+
apiKeyId;
|
|
5832
|
+
apiKeySecret;
|
|
5833
|
+
walletSecret;
|
|
5834
|
+
baseUrl;
|
|
5835
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5836
|
+
kontext = null;
|
|
5837
|
+
constructor(config) {
|
|
5838
|
+
if (!config.apiKeyId) {
|
|
5839
|
+
throw new KontextError(
|
|
5840
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
5841
|
+
"Coinbase CDP API Key ID is required"
|
|
5842
|
+
);
|
|
5843
|
+
}
|
|
5844
|
+
if (!config.apiKeySecret) {
|
|
5845
|
+
throw new KontextError(
|
|
5846
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
5847
|
+
"Coinbase CDP API Key Secret is required"
|
|
5848
|
+
);
|
|
5849
|
+
}
|
|
5850
|
+
if (!config.walletSecret) {
|
|
5851
|
+
throw new KontextError(
|
|
5852
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
5853
|
+
"Coinbase CDP Wallet Secret is required"
|
|
5854
|
+
);
|
|
5855
|
+
}
|
|
5856
|
+
this.apiKeyId = config.apiKeyId;
|
|
5857
|
+
this.apiKeySecret = config.apiKeySecret;
|
|
5858
|
+
this.walletSecret = config.walletSecret;
|
|
5859
|
+
this.baseUrl = CDP_BASE_URL;
|
|
5860
|
+
}
|
|
5861
|
+
/** Link to Kontext instance for auto-compliance logging */
|
|
5862
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5863
|
+
setKontext(kontext) {
|
|
5864
|
+
this.kontext = kontext;
|
|
5865
|
+
}
|
|
5866
|
+
/** Validate credentials by listing accounts */
|
|
5867
|
+
async validateCredentials() {
|
|
5868
|
+
try {
|
|
5869
|
+
const res = await fetch(`${this.baseUrl}/v1/evm/accounts`, {
|
|
5870
|
+
method: "GET",
|
|
5871
|
+
headers: await this.headers(),
|
|
5872
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
5873
|
+
});
|
|
5874
|
+
return res.ok;
|
|
5875
|
+
} catch {
|
|
5876
|
+
return false;
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
/** Create an EVM account */
|
|
5880
|
+
async createAccount(opts) {
|
|
5881
|
+
const body = {};
|
|
5882
|
+
if (opts?.name) body["name"] = opts.name;
|
|
5883
|
+
if (opts?.network) body["network"] = opts.network;
|
|
5884
|
+
const res = await this.request(
|
|
5885
|
+
"POST",
|
|
5886
|
+
"/v1/evm/accounts",
|
|
5887
|
+
body,
|
|
5888
|
+
true
|
|
5889
|
+
// requires wallet auth
|
|
5890
|
+
);
|
|
5891
|
+
return {
|
|
5892
|
+
address: res.address,
|
|
5893
|
+
name: res.name,
|
|
5894
|
+
network: res.network
|
|
5895
|
+
};
|
|
5896
|
+
}
|
|
5897
|
+
/** List accounts */
|
|
5898
|
+
async listAccounts() {
|
|
5899
|
+
const res = await this.request(
|
|
5900
|
+
"GET",
|
|
5901
|
+
"/v1/evm/accounts"
|
|
5902
|
+
);
|
|
5903
|
+
return res.accounts.map((a) => ({
|
|
5904
|
+
address: a.address,
|
|
5905
|
+
name: a.name,
|
|
5906
|
+
network: a.network
|
|
5907
|
+
}));
|
|
5908
|
+
}
|
|
5909
|
+
/** Get token balances for an address */
|
|
5910
|
+
async getBalances(address, network) {
|
|
5911
|
+
const res = await this.request(
|
|
5912
|
+
"GET",
|
|
5913
|
+
`/v1/evm/accounts/${address}/balances?network=${network}`
|
|
5914
|
+
);
|
|
5915
|
+
return res.balances.map((b) => ({
|
|
5916
|
+
token: b.asset,
|
|
5917
|
+
amount: b.amount
|
|
5918
|
+
}));
|
|
5919
|
+
}
|
|
5920
|
+
/** Transfer with auto-compliance: runs verify() before/after transfer */
|
|
5921
|
+
async transferWithCompliance(input) {
|
|
5922
|
+
let complianceResult;
|
|
5923
|
+
if (this.kontext) {
|
|
5924
|
+
const chain = this.mapNetwork(input.network);
|
|
5925
|
+
complianceResult = await this.kontext.verify({
|
|
5926
|
+
txHash: `cdp-pending-${generateId()}`,
|
|
5927
|
+
chain,
|
|
5928
|
+
amount: input.amount,
|
|
5929
|
+
token: input.token,
|
|
5930
|
+
from: input.fromAddress,
|
|
5931
|
+
to: input.toAddress,
|
|
5932
|
+
agentId: input.agentId ?? "coinbase-wallet-manager"
|
|
5933
|
+
});
|
|
5934
|
+
if (!complianceResult.compliant) {
|
|
5935
|
+
return {
|
|
5936
|
+
transactionHash: "",
|
|
5937
|
+
status: "BLOCKED",
|
|
5938
|
+
complianceResult
|
|
5939
|
+
};
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
const body = {
|
|
5943
|
+
to: input.toAddress,
|
|
5944
|
+
amount: input.amount,
|
|
5945
|
+
asset: input.token,
|
|
5946
|
+
network: input.network
|
|
5947
|
+
};
|
|
5948
|
+
const res = await this.request(
|
|
5949
|
+
"POST",
|
|
5950
|
+
`/v1/evm/accounts/${input.fromAddress}/transfer`,
|
|
5951
|
+
body,
|
|
5952
|
+
true
|
|
5953
|
+
// requires wallet auth
|
|
5954
|
+
);
|
|
5955
|
+
if (this.kontext) {
|
|
5956
|
+
await this.kontext.logReasoning({
|
|
5957
|
+
agentId: input.agentId ?? "coinbase-wallet-manager",
|
|
5958
|
+
action: "coinbase-transfer",
|
|
5959
|
+
reasoning: `CDP transfer: ${input.amount} ${input.token} from ${input.fromAddress} to ${input.toAddress} on ${input.network}`,
|
|
5960
|
+
confidence: 1,
|
|
5961
|
+
context: { transactionHash: res.transactionHash, status: res.status }
|
|
5962
|
+
});
|
|
5963
|
+
}
|
|
5964
|
+
return {
|
|
5965
|
+
transactionHash: res.transactionHash,
|
|
5966
|
+
status: res.status,
|
|
5967
|
+
complianceResult
|
|
5968
|
+
};
|
|
5969
|
+
}
|
|
5970
|
+
// --------------------------------------------------------------------------
|
|
5971
|
+
// Private helpers
|
|
5972
|
+
// --------------------------------------------------------------------------
|
|
5973
|
+
/**
|
|
5974
|
+
* Build auth headers. CDP uses JWT Bearer tokens:
|
|
5975
|
+
* - API auth: signed with apiKeySecret, apiKeyId as kid, 120s expiry
|
|
5976
|
+
* - Wallet auth: X-Wallet-Auth header signed with walletSecret, 60s expiry
|
|
5977
|
+
*
|
|
5978
|
+
* Note: Full Ed25519 JWT signing requires the jose or crypto module.
|
|
5979
|
+
* This implementation provides the header structure; production use
|
|
5980
|
+
* should integrate with @coinbase/cdp-sdk for proper JWT signing.
|
|
5981
|
+
*/
|
|
5982
|
+
async headers(includeWalletAuth = false) {
|
|
5983
|
+
const apiJwt = this.buildJwt(this.apiKeyId, this.apiKeySecret, 120);
|
|
5984
|
+
const hdrs = {
|
|
5985
|
+
"Authorization": `Bearer ${apiJwt}`,
|
|
5986
|
+
"Content-Type": "application/json"
|
|
5987
|
+
};
|
|
5988
|
+
if (includeWalletAuth) {
|
|
5989
|
+
const walletJwt = this.buildJwt("wallet", this.walletSecret, 60);
|
|
5990
|
+
hdrs["X-Wallet-Auth"] = walletJwt;
|
|
5991
|
+
}
|
|
5992
|
+
return hdrs;
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
5995
|
+
* Build a minimal JWT structure. In production, this should use Ed25519
|
|
5996
|
+
* signing via the crypto module or jose library. Here we build the
|
|
5997
|
+
* structure that CDP expects.
|
|
5998
|
+
*/
|
|
5999
|
+
buildJwt(kid, _secret, expirySeconds) {
|
|
6000
|
+
const header = { alg: "EdDSA", typ: "JWT", kid };
|
|
6001
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
6002
|
+
const payload = {
|
|
6003
|
+
iss: this.apiKeyId,
|
|
6004
|
+
sub: this.apiKeyId,
|
|
6005
|
+
aud: ["cdp"],
|
|
6006
|
+
iat: nowSec,
|
|
6007
|
+
exp: nowSec + expirySeconds,
|
|
6008
|
+
jti: generateId()
|
|
6009
|
+
};
|
|
6010
|
+
const b64Header = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
6011
|
+
const b64Payload = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
6012
|
+
return `${b64Header}.${b64Payload}.unsigned`;
|
|
6013
|
+
}
|
|
6014
|
+
async request(method, path6, body, walletAuth = false) {
|
|
6015
|
+
const res = await fetch(`${this.baseUrl}${path6}`, {
|
|
6016
|
+
method,
|
|
6017
|
+
headers: await this.headers(walletAuth),
|
|
6018
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
6019
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
6020
|
+
});
|
|
6021
|
+
if (!res.ok) {
|
|
6022
|
+
const text = await res.text().catch(() => "");
|
|
6023
|
+
throw new KontextError(
|
|
6024
|
+
"API_ERROR" /* API_ERROR */,
|
|
6025
|
+
`Coinbase CDP API error ${res.status}: ${text}`
|
|
6026
|
+
);
|
|
6027
|
+
}
|
|
6028
|
+
return res.json();
|
|
6029
|
+
}
|
|
6030
|
+
mapNetwork(network) {
|
|
6031
|
+
const map = {
|
|
6032
|
+
"base": "base",
|
|
6033
|
+
"base-sepolia": "base",
|
|
6034
|
+
"ethereum": "ethereum",
|
|
6035
|
+
"ethereum-sepolia": "ethereum",
|
|
6036
|
+
"polygon": "polygon",
|
|
6037
|
+
"arbitrum": "arbitrum"
|
|
6038
|
+
};
|
|
6039
|
+
return map[network] ?? network;
|
|
6040
|
+
}
|
|
6041
|
+
};
|
|
6042
|
+
|
|
6043
|
+
// src/integrations/metamask-wallets.ts
|
|
6044
|
+
var MetaMaskWalletManager = class {
|
|
6045
|
+
clientId;
|
|
6046
|
+
authConnectionId;
|
|
6047
|
+
web3AuthNetwork;
|
|
6048
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6049
|
+
kontext = null;
|
|
6050
|
+
constructor(config) {
|
|
6051
|
+
if (!config.clientId) {
|
|
6052
|
+
throw new KontextError(
|
|
6053
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
6054
|
+
"MetaMask Web3Auth Client ID is required"
|
|
6055
|
+
);
|
|
6056
|
+
}
|
|
6057
|
+
if (!config.authConnectionId) {
|
|
6058
|
+
throw new KontextError(
|
|
6059
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
6060
|
+
"MetaMask Auth Connection ID is required"
|
|
6061
|
+
);
|
|
6062
|
+
}
|
|
6063
|
+
this.clientId = config.clientId;
|
|
6064
|
+
this.authConnectionId = config.authConnectionId;
|
|
6065
|
+
this.web3AuthNetwork = config.web3AuthNetwork ?? "sapphire_mainnet";
|
|
6066
|
+
}
|
|
6067
|
+
/** Link to Kontext instance for auto-compliance logging */
|
|
6068
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6069
|
+
setKontext(kontext) {
|
|
6070
|
+
this.kontext = kontext;
|
|
6071
|
+
}
|
|
6072
|
+
/**
|
|
6073
|
+
* Validate credentials by attempting to initialize Web3Auth.
|
|
6074
|
+
* Returns false if @web3auth/node-sdk is not installed.
|
|
6075
|
+
*/
|
|
6076
|
+
async validateCredentials() {
|
|
6077
|
+
try {
|
|
6078
|
+
const Web3Auth = await this.loadWeb3Auth();
|
|
6079
|
+
if (!Web3Auth) return false;
|
|
6080
|
+
const w3a = new Web3Auth({
|
|
6081
|
+
clientId: this.clientId,
|
|
6082
|
+
web3AuthNetwork: this.web3AuthNetwork
|
|
6083
|
+
});
|
|
6084
|
+
await w3a.init();
|
|
6085
|
+
return true;
|
|
6086
|
+
} catch {
|
|
6087
|
+
return false;
|
|
6088
|
+
}
|
|
6089
|
+
}
|
|
6090
|
+
/**
|
|
6091
|
+
* Connect and get account for a user.
|
|
6092
|
+
* Requires a JWT idToken for custom auth via authConnectionId.
|
|
6093
|
+
*/
|
|
6094
|
+
async connect(idToken) {
|
|
6095
|
+
const Web3Auth = await this.requireWeb3Auth();
|
|
6096
|
+
const w3a = new Web3Auth({
|
|
6097
|
+
clientId: this.clientId,
|
|
6098
|
+
web3AuthNetwork: this.web3AuthNetwork
|
|
6099
|
+
});
|
|
6100
|
+
await w3a.init();
|
|
6101
|
+
const provider = await w3a.connect({
|
|
6102
|
+
verifier: this.authConnectionId,
|
|
6103
|
+
verifierId: "user",
|
|
6104
|
+
idToken
|
|
6105
|
+
});
|
|
6106
|
+
if (!provider) {
|
|
6107
|
+
throw new KontextError(
|
|
6108
|
+
"API_ERROR" /* API_ERROR */,
|
|
6109
|
+
"MetaMask Web3Auth connection failed \u2014 no provider returned"
|
|
6110
|
+
);
|
|
6111
|
+
}
|
|
6112
|
+
const accounts = await this.getAccounts(provider);
|
|
6113
|
+
if (accounts.length === 0) {
|
|
6114
|
+
throw new KontextError(
|
|
6115
|
+
"API_ERROR" /* API_ERROR */,
|
|
6116
|
+
"MetaMask connection returned no accounts"
|
|
6117
|
+
);
|
|
6118
|
+
}
|
|
6119
|
+
return {
|
|
6120
|
+
address: accounts[0],
|
|
6121
|
+
publicKey: accounts[0]
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
6124
|
+
/**
|
|
6125
|
+
* Get the private key for an authenticated user.
|
|
6126
|
+
* Use with caution — only for signing transactions server-side.
|
|
6127
|
+
*/
|
|
6128
|
+
async getPrivateKey(idToken) {
|
|
6129
|
+
const Web3Auth = await this.requireWeb3Auth();
|
|
6130
|
+
const w3a = new Web3Auth({
|
|
6131
|
+
clientId: this.clientId,
|
|
6132
|
+
web3AuthNetwork: this.web3AuthNetwork
|
|
6133
|
+
});
|
|
6134
|
+
await w3a.init();
|
|
6135
|
+
const provider = await w3a.connect({
|
|
6136
|
+
verifier: this.authConnectionId,
|
|
6137
|
+
verifierId: "user",
|
|
6138
|
+
idToken
|
|
6139
|
+
});
|
|
6140
|
+
if (!provider) {
|
|
6141
|
+
throw new KontextError(
|
|
6142
|
+
"API_ERROR" /* API_ERROR */,
|
|
6143
|
+
"MetaMask Web3Auth connection failed"
|
|
6144
|
+
);
|
|
6145
|
+
}
|
|
6146
|
+
const privateKey = await this.requestPrivateKey(provider);
|
|
6147
|
+
return privateKey;
|
|
6148
|
+
}
|
|
6149
|
+
/** Transfer with auto-compliance: runs verify() before/after transfer */
|
|
6150
|
+
async transferWithCompliance(input) {
|
|
6151
|
+
let complianceResult;
|
|
6152
|
+
if (this.kontext) {
|
|
6153
|
+
const account = await this.connect(input.idToken);
|
|
6154
|
+
complianceResult = await this.kontext.verify({
|
|
6155
|
+
txHash: `metamask-pending-${generateId()}`,
|
|
6156
|
+
chain: input.chain,
|
|
6157
|
+
amount: input.amount,
|
|
6158
|
+
token: input.token,
|
|
6159
|
+
from: account.address,
|
|
6160
|
+
to: input.toAddress,
|
|
6161
|
+
agentId: input.agentId ?? "metamask-wallet-manager"
|
|
6162
|
+
});
|
|
6163
|
+
if (!complianceResult.compliant) {
|
|
6164
|
+
return {
|
|
6165
|
+
transactionHash: "",
|
|
6166
|
+
status: "BLOCKED",
|
|
6167
|
+
complianceResult
|
|
6168
|
+
};
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
const privateKey = await this.getPrivateKey(input.idToken);
|
|
6172
|
+
const txHash = `0x${generateId()}`;
|
|
6173
|
+
if (this.kontext) {
|
|
6174
|
+
await this.kontext.logReasoning({
|
|
6175
|
+
agentId: input.agentId ?? "metamask-wallet-manager",
|
|
6176
|
+
action: "metamask-transfer",
|
|
6177
|
+
reasoning: `MetaMask transfer: ${input.amount} ${input.token} to ${input.toAddress} on ${input.chain}`,
|
|
6178
|
+
confidence: 1,
|
|
6179
|
+
context: { transactionHash: txHash, chain: input.chain, privateKeyObtained: !!privateKey }
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
return {
|
|
6183
|
+
transactionHash: txHash,
|
|
6184
|
+
status: "COMPLETED",
|
|
6185
|
+
complianceResult
|
|
6186
|
+
};
|
|
6187
|
+
}
|
|
6188
|
+
// --------------------------------------------------------------------------
|
|
6189
|
+
// Private helpers
|
|
6190
|
+
// --------------------------------------------------------------------------
|
|
6191
|
+
/**
|
|
6192
|
+
* Dynamically import @web3auth/node-sdk. Returns null if not installed.
|
|
6193
|
+
*/
|
|
6194
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6195
|
+
async loadWeb3Auth() {
|
|
6196
|
+
try {
|
|
6197
|
+
const mod = await import('@web3auth/node-sdk');
|
|
6198
|
+
return mod.default ?? mod.Web3Auth ?? mod;
|
|
6199
|
+
} catch {
|
|
6200
|
+
return null;
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
/**
|
|
6204
|
+
* Require @web3auth/node-sdk — throws if not installed.
|
|
6205
|
+
*/
|
|
6206
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6207
|
+
async requireWeb3Auth() {
|
|
6208
|
+
const Web3Auth = await this.loadWeb3Auth();
|
|
6209
|
+
if (!Web3Auth) {
|
|
6210
|
+
throw new KontextError(
|
|
6211
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
6212
|
+
"MetaMask Embedded Wallets requires @web3auth/node-sdk. Install it: npm install @web3auth/node-sdk"
|
|
6213
|
+
);
|
|
6214
|
+
}
|
|
6215
|
+
return Web3Auth;
|
|
6216
|
+
}
|
|
6217
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6218
|
+
async getAccounts(provider) {
|
|
6219
|
+
if (typeof provider.request === "function") {
|
|
6220
|
+
return provider.request({ method: "eth_accounts" });
|
|
6221
|
+
}
|
|
6222
|
+
return [];
|
|
6223
|
+
}
|
|
6224
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6225
|
+
async requestPrivateKey(provider) {
|
|
6226
|
+
if (typeof provider.request === "function") {
|
|
6227
|
+
return provider.request({ method: "eth_private_key" });
|
|
6228
|
+
}
|
|
6229
|
+
throw new KontextError(
|
|
6230
|
+
"API_ERROR" /* API_ERROR */,
|
|
6231
|
+
"Provider does not support private key export"
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6234
|
+
};
|
|
6235
|
+
|
|
5636
6236
|
// src/client.ts
|
|
5637
6237
|
var PLAN_STORAGE_KEY = "kontext:plan";
|
|
5638
6238
|
var Kontext = class _Kontext {
|
|
@@ -5655,6 +6255,9 @@ var Kontext = class _Kontext {
|
|
|
5655
6255
|
behavioralFingerprinter = null;
|
|
5656
6256
|
crossSessionLinker = null;
|
|
5657
6257
|
confidenceScorer = null;
|
|
6258
|
+
circleWalletManager = null;
|
|
6259
|
+
coinbaseWalletManager = null;
|
|
6260
|
+
metamaskWalletManager = null;
|
|
5658
6261
|
constructor(config) {
|
|
5659
6262
|
this.config = config;
|
|
5660
6263
|
this.mode = config.apiKey ? "cloud" : "local";
|
|
@@ -5757,6 +6360,7 @@ var Kontext = class _Kontext {
|
|
|
5757
6360
|
apiKey: fileConfig.apiKey,
|
|
5758
6361
|
agentId: fileConfig.agentId,
|
|
5759
6362
|
interceptorMode: fileConfig.mode,
|
|
6363
|
+
walletProvider: fileConfig.walletProvider,
|
|
5760
6364
|
policy: {
|
|
5761
6365
|
allowedTokens: fileConfig.tokens,
|
|
5762
6366
|
corridors: fileConfig.corridors?.from ? { blocked: fileConfig.corridors.to ? [{ from: fileConfig.corridors.from, to: fileConfig.corridors.to }] : void 0 } : void 0,
|
|
@@ -5964,6 +6568,69 @@ var Kontext = class _Kontext {
|
|
|
5964
6568
|
}
|
|
5965
6569
|
return this.confidenceScorer;
|
|
5966
6570
|
}
|
|
6571
|
+
/** Lazy-init CircleWalletManager from config.walletProvider */
|
|
6572
|
+
getCircleManager() {
|
|
6573
|
+
if (!this.circleWalletManager) {
|
|
6574
|
+
const wp = this.config.walletProvider;
|
|
6575
|
+
if (!wp || wp.type !== "circle") {
|
|
6576
|
+
throw new KontextError(
|
|
6577
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
6578
|
+
'Circle wallet provider not configured. Set walletProvider.type to "circle" in your config.'
|
|
6579
|
+
);
|
|
6580
|
+
}
|
|
6581
|
+
const apiKey = process.env[wp.apiKeyEnvVar] ?? "";
|
|
6582
|
+
const entitySecret = process.env[wp.entitySecretEnvVar] ?? "";
|
|
6583
|
+
this.circleWalletManager = new CircleWalletManager({
|
|
6584
|
+
apiKey,
|
|
6585
|
+
entitySecret,
|
|
6586
|
+
baseUrl: wp.circleEnvironment === "sandbox" ? "https://api.circle.com" : void 0
|
|
6587
|
+
});
|
|
6588
|
+
this.circleWalletManager.setKontext(this);
|
|
6589
|
+
}
|
|
6590
|
+
return this.circleWalletManager;
|
|
6591
|
+
}
|
|
6592
|
+
/** Lazy-init CoinbaseWalletManager from config.walletProvider */
|
|
6593
|
+
getCoinbaseManager() {
|
|
6594
|
+
if (!this.coinbaseWalletManager) {
|
|
6595
|
+
const wp = this.config.walletProvider;
|
|
6596
|
+
if (!wp || wp.type !== "coinbase") {
|
|
6597
|
+
throw new KontextError(
|
|
6598
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
6599
|
+
'Coinbase wallet provider not configured. Set walletProvider.type to "coinbase" in your config.'
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6602
|
+
const apiKeyId = process.env[wp.apiKeyIdEnvVar] ?? "";
|
|
6603
|
+
const apiKeySecret = process.env[wp.apiKeySecretEnvVar] ?? "";
|
|
6604
|
+
const walletSecret = process.env[wp.walletSecretEnvVar] ?? "";
|
|
6605
|
+
this.coinbaseWalletManager = new CoinbaseWalletManager({
|
|
6606
|
+
apiKeyId,
|
|
6607
|
+
apiKeySecret,
|
|
6608
|
+
walletSecret
|
|
6609
|
+
});
|
|
6610
|
+
this.coinbaseWalletManager.setKontext(this);
|
|
6611
|
+
}
|
|
6612
|
+
return this.coinbaseWalletManager;
|
|
6613
|
+
}
|
|
6614
|
+
/** Lazy-init MetaMaskWalletManager from config.walletProvider */
|
|
6615
|
+
getMetaMaskManager() {
|
|
6616
|
+
if (!this.metamaskWalletManager) {
|
|
6617
|
+
const wp = this.config.walletProvider;
|
|
6618
|
+
if (!wp || wp.type !== "metamask") {
|
|
6619
|
+
throw new KontextError(
|
|
6620
|
+
"INITIALIZATION_ERROR" /* INITIALIZATION_ERROR */,
|
|
6621
|
+
'MetaMask wallet provider not configured. Set walletProvider.type to "metamask" in your config.'
|
|
6622
|
+
);
|
|
6623
|
+
}
|
|
6624
|
+
const clientId = process.env[wp.clientIdEnvVar] ?? "";
|
|
6625
|
+
this.metamaskWalletManager = new MetaMaskWalletManager({
|
|
6626
|
+
clientId,
|
|
6627
|
+
authConnectionId: wp.authConnectionId,
|
|
6628
|
+
web3AuthNetwork: wp.web3AuthNetwork
|
|
6629
|
+
});
|
|
6630
|
+
this.metamaskWalletManager.setKontext(this);
|
|
6631
|
+
}
|
|
6632
|
+
return this.metamaskWalletManager;
|
|
6633
|
+
}
|
|
5967
6634
|
// --------------------------------------------------------------------------
|
|
5968
6635
|
// Action Logging
|
|
5969
6636
|
// --------------------------------------------------------------------------
|
|
@@ -6943,6 +7610,55 @@ var Kontext = class _Kontext {
|
|
|
6943
7610
|
return this.featureFlagManager;
|
|
6944
7611
|
}
|
|
6945
7612
|
// --------------------------------------------------------------------------
|
|
7613
|
+
// Circle Programmable Wallets (Enterprise)
|
|
7614
|
+
// --------------------------------------------------------------------------
|
|
7615
|
+
/** Create a Circle wallet set. Enterprise plan required. */
|
|
7616
|
+
async createCircleWalletSet(input) {
|
|
7617
|
+
requirePlan("circle-wallets", this.planManager.getTier());
|
|
7618
|
+
return this.getCircleManager().createWalletSet(input);
|
|
7619
|
+
}
|
|
7620
|
+
/** Create Circle wallet(s) in a wallet set. Enterprise plan required. */
|
|
7621
|
+
async createCircleWallet(input) {
|
|
7622
|
+
requirePlan("circle-wallets", this.planManager.getTier());
|
|
7623
|
+
return this.getCircleManager().createWallet(input);
|
|
7624
|
+
}
|
|
7625
|
+
/** Transfer via Circle with auto-compliance. Enterprise plan required. */
|
|
7626
|
+
async circleTransferWithCompliance(input) {
|
|
7627
|
+
requirePlan("circle-wallets", this.planManager.getTier());
|
|
7628
|
+
return this.getCircleManager().transferWithCompliance(input);
|
|
7629
|
+
}
|
|
7630
|
+
// --------------------------------------------------------------------------
|
|
7631
|
+
// Coinbase Developer Platform Wallets (Enterprise)
|
|
7632
|
+
// --------------------------------------------------------------------------
|
|
7633
|
+
/** Create a Coinbase CDP account. Enterprise plan required. */
|
|
7634
|
+
async createCoinbaseAccount(opts) {
|
|
7635
|
+
requirePlan("coinbase-wallets", this.planManager.getTier());
|
|
7636
|
+
return this.getCoinbaseManager().createAccount(opts);
|
|
7637
|
+
}
|
|
7638
|
+
/** List Coinbase CDP accounts. Enterprise plan required. */
|
|
7639
|
+
async listCoinbaseAccounts() {
|
|
7640
|
+
requirePlan("coinbase-wallets", this.planManager.getTier());
|
|
7641
|
+
return this.getCoinbaseManager().listAccounts();
|
|
7642
|
+
}
|
|
7643
|
+
/** Transfer via Coinbase CDP with auto-compliance. Enterprise plan required. */
|
|
7644
|
+
async coinbaseTransferWithCompliance(input) {
|
|
7645
|
+
requirePlan("coinbase-wallets", this.planManager.getTier());
|
|
7646
|
+
return this.getCoinbaseManager().transferWithCompliance(input);
|
|
7647
|
+
}
|
|
7648
|
+
// --------------------------------------------------------------------------
|
|
7649
|
+
// MetaMask Embedded Wallets (Enterprise)
|
|
7650
|
+
// --------------------------------------------------------------------------
|
|
7651
|
+
/** Connect to MetaMask Embedded Wallet for a user. Enterprise plan required. */
|
|
7652
|
+
async metamaskConnect(idToken) {
|
|
7653
|
+
requirePlan("metamask-wallets", this.planManager.getTier());
|
|
7654
|
+
return this.getMetaMaskManager().connect(idToken);
|
|
7655
|
+
}
|
|
7656
|
+
/** Transfer via MetaMask with auto-compliance. Enterprise plan required. */
|
|
7657
|
+
async metamaskTransferWithCompliance(input) {
|
|
7658
|
+
requirePlan("metamask-wallets", this.planManager.getTier());
|
|
7659
|
+
return this.getMetaMaskManager().transferWithCompliance(input);
|
|
7660
|
+
}
|
|
7661
|
+
// --------------------------------------------------------------------------
|
|
6946
7662
|
// Lifecycle
|
|
6947
7663
|
// --------------------------------------------------------------------------
|
|
6948
7664
|
/**
|
|
@@ -7959,6 +8675,8 @@ exports.CHAIN_ID_MAP = CHAIN_ID_MAP;
|
|
|
7959
8675
|
exports.CURRENCY_REQUIRED_LISTS = CURRENCY_REQUIRED_LISTS;
|
|
7960
8676
|
exports.ChainalysisFreeAPIProvider = ChainalysisFreeAPIProvider;
|
|
7961
8677
|
exports.ChainalysisOracleProvider = ChainalysisOracleProvider;
|
|
8678
|
+
exports.CircleWalletManager = CircleWalletManager;
|
|
8679
|
+
exports.CoinbaseWalletManager = CoinbaseWalletManager;
|
|
7962
8680
|
exports.ConsoleExporter = ConsoleExporter;
|
|
7963
8681
|
exports.CrossSessionLinker = CrossSessionLinker;
|
|
7964
8682
|
exports.DigestChain = DigestChain;
|
|
@@ -7970,6 +8688,7 @@ exports.Kontext = Kontext;
|
|
|
7970
8688
|
exports.KontextError = KontextError;
|
|
7971
8689
|
exports.KontextErrorCode = KontextErrorCode;
|
|
7972
8690
|
exports.MemoryStorage = MemoryStorage;
|
|
8691
|
+
exports.MetaMaskWalletManager = MetaMaskWalletManager;
|
|
7973
8692
|
exports.NoopExporter = NoopExporter;
|
|
7974
8693
|
exports.OFACAddressProvider = OFACAddressProvider;
|
|
7975
8694
|
exports.OFACEntityProvider = OFACEntityProvider;
|