@agentlayer.tech/wallet 0.1.92 → 0.1.94

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.
Files changed (33) hide show
  1. package/.openclaw/extensions/agent-wallet/dist/index.js +41 -0
  2. package/.openclaw/extensions/agent-wallet/index.ts +41 -0
  3. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +4 -1
  4. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  5. package/CHANGELOG.md +35 -0
  6. package/VERSION +1 -1
  7. package/agent-wallet/agent_wallet/__init__.py +1 -1
  8. package/agent-wallet/agent_wallet/autonomous_permissions.py +1 -0
  9. package/agent-wallet/agent_wallet/openclaw_adapter.py +136 -0
  10. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +2 -0
  11. package/agent-wallet/agent_wallet/providers/x402.py +9 -1
  12. package/agent-wallet/agent_wallet/user_wallets.py +2 -0
  13. package/agent-wallet/agent_wallet/wallet_layer/base.py +18 -0
  14. package/agent-wallet/agent_wallet/wallet_layer/solana.py +2 -0
  15. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +97 -0
  16. package/agent-wallet/openclaw.plugin.json +1 -1
  17. package/agent-wallet/pyproject.toml +1 -1
  18. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  19. package/claude-code/plugins/agent-wallet/README.md +2 -0
  20. package/claude-code/plugins/agent-wallet/commands/cards.md +129 -0
  21. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  22. package/codex/plugins/agent-wallet/README.md +6 -2
  23. package/codex/plugins/agent-wallet/server.py +1 -0
  24. package/codex/plugins/agent-wallet/skills/cards/SKILL.md +119 -0
  25. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  26. package/package.json +1 -1
  27. package/wdk-btc-wallet/package.json +1 -1
  28. package/wdk-evm-wallet/.env.example +3 -0
  29. package/wdk-evm-wallet/README.md +6 -0
  30. package/wdk-evm-wallet/package.json +1 -1
  31. package/wdk-evm-wallet/src/config.js +9 -0
  32. package/wdk-evm-wallet/src/server.js +12 -0
  33. package/wdk-evm-wallet/src/wdk_evm_wallet.js +345 -0
@@ -23,6 +23,28 @@ const DEFAULT_LIFI_SLIPPAGE = 0.005;
23
23
  const ALWAYS_DENIED_LIFI_BRIDGES = ["mayan"];
24
24
  const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
25
25
  const UNISWAP_SUPPORTED_CHAIN_IDS = { ethereum: 1, base: 8453, robinhood: 4663 };
26
+ const UNISWAP_LIQUIDITY_ACTIONS = new Set(["create", "increase", "decrease", "claim_fees"]);
27
+ const UNISWAP_LIQUIDITY_PROTOCOLS = new Set(["V3", "V4"]);
28
+ // LP API transactions are accepted only for these official per-chain position
29
+ // managers. Keeping the list here makes the local signer a narrow protocol
30
+ // integration rather than a generic calldata relay.
31
+ const UNISWAP_LIQUIDITY_POSITION_MANAGERS = {
32
+ ethereum: {
33
+ V3: "0xc36442b4a4522e871399cd717abdd847ab11fe88",
34
+ V4: "0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e",
35
+ },
36
+ base: {
37
+ V3: "0x03a520b32c04bf3beef7beb72e919cf822ed34f1",
38
+ V4: "0x7c5f5a4bbd8fd63184577525326123b519429bdc",
39
+ },
40
+ robinhood: {
41
+ V3: "0x73991a25c818bf1f1128deaab1492d45638de0d3",
42
+ V4: "0x58daec3116aae6d93017baaea7749052e8a04fa7",
43
+ },
44
+ };
45
+ const ERC20_APPROVE_INTERFACE = new Interface([
46
+ "function approve(address spender,uint256 amount) returns (bool)",
47
+ ]);
26
48
  // Every executable path is declared here rather than inferred from a Trading
27
49
  // API response. A new chain/router version therefore requires an explicit,
28
50
  // reviewed allow-list entry before it can receive a signed transaction.
@@ -925,6 +947,31 @@ function assertUniswapSupportedNetwork(network) {
925
947
  return chainId;
926
948
  }
927
949
 
950
+ function normalizeUniswapLiquidityAction(value) {
951
+ const action = String(value || "").trim().toLowerCase();
952
+ if (!UNISWAP_LIQUIDITY_ACTIONS.has(action)) {
953
+ throw new Error("Uniswap liquidity action must be create, increase, decrease, or claim_fees.");
954
+ }
955
+ return action;
956
+ }
957
+
958
+ function normalizeUniswapLiquidityProtocol(value) {
959
+ const protocol = String(value || "").trim().toUpperCase();
960
+ if (!UNISWAP_LIQUIDITY_PROTOCOLS.has(protocol)) {
961
+ throw new Error("Uniswap liquidity protocol must be V3 or V4.");
962
+ }
963
+ return protocol;
964
+ }
965
+
966
+ function getUniswapLiquidityPositionManager(network, protocol) {
967
+ assertUniswapSupportedNetwork(network);
968
+ const manager = UNISWAP_LIQUIDITY_POSITION_MANAGERS[network]?.[protocol];
969
+ if (!manager) {
970
+ throw new Error(`Uniswap ${protocol} liquidity is not configured for ${network}.`);
971
+ }
972
+ return manager;
973
+ }
974
+
928
975
  function getUniswapNetworkExecutionProfile(network) {
929
976
  const profile = UNISWAP_EXECUTION_PROFILES[network];
930
977
  if (!profile) {
@@ -5743,6 +5790,304 @@ export class WdkEvmWalletService {
5743
5790
  return payload;
5744
5791
  }
5745
5792
 
5793
+ async #uniswapLiquidityApiRequest(action, body) {
5794
+ const normalizedAction = String(action || "").trim().toLowerCase();
5795
+ const headers = { "Content-Type": "application/json", Accept: "application/json" };
5796
+ if (this.config.uniswapLiquidityViaGateway) {
5797
+ const token = String(this.config.providerGatewayToken || "").trim();
5798
+ if (token) {
5799
+ headers.Authorization = `Bearer ${token}`;
5800
+ }
5801
+ } else {
5802
+ if (!this.config.uniswapApiKey) {
5803
+ throw createTaggedError(
5804
+ "UNISWAP_API_KEY is not configured. Set it, or route Uniswap through the provider gateway, to use liquidity operations.",
5805
+ "uniswap_api_key_missing",
5806
+ { provider: "uniswap" }
5807
+ );
5808
+ }
5809
+ headers["x-api-key"] = this.config.uniswapApiKey;
5810
+ }
5811
+ const base = String(this.config.uniswapLiquidityApiBaseUrl).replace(/\/+$/, "");
5812
+ const suffix = this.config.uniswapLiquidityViaGateway ? `/${normalizedAction}` : `/lp/${normalizedAction}`;
5813
+ let response;
5814
+ try {
5815
+ response = await fetch(`${base}${suffix}`, { method: "POST", headers, body: JSON.stringify(body) });
5816
+ } catch (error) {
5817
+ const message = error instanceof Error ? error.message : String(error);
5818
+ throw createTaggedError(`Uniswap liquidity API unavailable: ${message}`, "network_unavailable", {
5819
+ provider: "uniswap",
5820
+ action: normalizedAction,
5821
+ });
5822
+ }
5823
+ let payload;
5824
+ try {
5825
+ payload = await response.json();
5826
+ } catch {
5827
+ payload = null;
5828
+ }
5829
+ if (!response.ok || !payload || typeof payload !== "object") {
5830
+ throw createTaggedError(
5831
+ String(payload?.message || payload?.error || `Uniswap liquidity ${normalizedAction} failed with HTTP ${response.status}.`),
5832
+ "network_unavailable",
5833
+ { provider: "uniswap", action: normalizedAction, httpStatus: response.status }
5834
+ );
5835
+ }
5836
+ return payload;
5837
+ }
5838
+
5839
+ #buildUniswapLiquidityRequest({ action, protocol, address, request }) {
5840
+ const body = { ...(request && typeof request === "object" ? request : {}) };
5841
+ // Caller cannot select identity, chain or simulator behavior. These are set
5842
+ // locally to make every preview/send reproducible through the active wallet.
5843
+ body.walletAddress = address;
5844
+ body.protocol = protocol;
5845
+ body.simulateTransaction = true;
5846
+ delete body.signature;
5847
+ delete body.batchPermitData;
5848
+ delete body.v4BatchPermitData;
5849
+ delete body.v3NftPermitData;
5850
+ if (action === "claim_fees") {
5851
+ delete body.independentToken;
5852
+ }
5853
+ return body;
5854
+ }
5855
+
5856
+ #extractUniswapLiquidityTransaction(payload, action) {
5857
+ const field = action === "create" ? "create" : action === "increase" ? "increase" : action === "decrease" ? "decrease" : "claim";
5858
+ const tx = payload?.[field] || payload?.transaction;
5859
+ if (!tx || typeof tx !== "object") {
5860
+ throw createTaggedError("Uniswap liquidity API returned no executable transaction.", "network_unavailable", {
5861
+ provider: "uniswap",
5862
+ action,
5863
+ });
5864
+ }
5865
+ return tx;
5866
+ }
5867
+
5868
+ async #getUniswapLiquidityApprovals({ runtimeConfig, protocol, action, address, payload }) {
5869
+ if (action !== "create" && action !== "increase") {
5870
+ return [];
5871
+ }
5872
+ const lpTokens = [payload?.token0, payload?.token1]
5873
+ .filter((token) => token && typeof token === "object")
5874
+ // The LP API represents native ETH with the zero-address sentinel. It
5875
+ // cannot have an ERC-20 allowance, so exclude it from check_approval.
5876
+ .filter((token) => !isZeroAddress(String(token.tokenAddress || "")))
5877
+ .map((token) => {
5878
+ const amount = BigInt(assertNonNegativeBigIntString(token.amount, "lp token amount"));
5879
+ return {
5880
+ tokenAddress: normalizeAddress(String(token.tokenAddress || ""), "lp token address"),
5881
+ amount,
5882
+ };
5883
+ })
5884
+ // Concentrated positions can legitimately be single-sided while the
5885
+ // current price lies outside their selected range. A zero contribution
5886
+ // has no ERC-20 allowance requirement and must not block the LP action.
5887
+ .filter((token) => token.amount > 0n)
5888
+ .map((token) => ({ ...token, amount: token.amount.toString() }));
5889
+ if (!lpTokens.length) {
5890
+ return [];
5891
+ }
5892
+ const expectedTokens = new Set(
5893
+ lpTokens.map((token) => String(token.tokenAddress).toLowerCase())
5894
+ );
5895
+ const approvalPayload = await this.#uniswapLiquidityApiRequest("check_approval", {
5896
+ walletAddress: address,
5897
+ chainId: runtimeConfig.chainId,
5898
+ protocol,
5899
+ lpTokens,
5900
+ action: action.toUpperCase(),
5901
+ // The initial version uses ordinary, bounded approval transactions. This
5902
+ // keeps the send path observable and avoids requiring an extra signature
5903
+ // protocol before LP workflows have seen live use.
5904
+ generatePermitAsTransaction: true,
5905
+ });
5906
+ const transactions = Array.isArray(approvalPayload.transactions) ? approvalPayload.transactions : [];
5907
+ return transactions.map((item, index) => {
5908
+ const raw = item?.transaction;
5909
+ if (!raw || typeof raw !== "object") {
5910
+ throw createTaggedError("Uniswap liquidity approval response is malformed.", "uniswap_liquidity_invalid_approval", { index });
5911
+ }
5912
+ const to = normalizeAddress(String(raw.to || ""), "liquidity approval.to");
5913
+ if (!expectedTokens.has(to.toLowerCase())) {
5914
+ throw createTaggedError("Uniswap liquidity approval is for a token outside this LP request.", "uniswap_liquidity_unexpected_token", { to });
5915
+ }
5916
+ const data = assertNonEmptyString(String(raw.data || ""), "liquidity approval.data");
5917
+ if (parseHexOrDecimalBigInt(raw.value || "0", "liquidity approval.value") !== 0n) {
5918
+ throw createTaggedError("Uniswap liquidity approval must not transfer native value.", "uniswap_liquidity_invalid_approval", { to });
5919
+ }
5920
+ if (raw.chainId !== undefined && Number(raw.chainId) !== runtimeConfig.chainId) {
5921
+ throw createTaggedError("Uniswap liquidity approval has the wrong chain id.", "uniswap_liquidity_chain_mismatch");
5922
+ }
5923
+ if (raw.from && normalizeAddress(String(raw.from), "liquidity approval.from").toLowerCase() !== address.toLowerCase()) {
5924
+ throw createTaggedError("Uniswap liquidity approval sender does not match the active wallet.", "uniswap_liquidity_sender_mismatch");
5925
+ }
5926
+ let decoded;
5927
+ try {
5928
+ decoded = ERC20_APPROVE_INTERFACE.parseTransaction({ data });
5929
+ } catch {
5930
+ decoded = null;
5931
+ }
5932
+ if (!decoded || decoded.name !== "approve") {
5933
+ throw createTaggedError("Uniswap liquidity approval is not a bounded ERC-20 approve call.", "uniswap_liquidity_invalid_approval", { to });
5934
+ }
5935
+ const spender = normalizeAddress(String(decoded.args[0]), "liquidity approval spender").toLowerCase();
5936
+ const amount = BigInt(decoded.args[1]);
5937
+ const positionManager = getUniswapLiquidityPositionManager(runtimeConfig.network, protocol);
5938
+ if (spender !== positionManager && spender !== PERMIT2_ADDRESS.toLowerCase()) {
5939
+ throw createTaggedError("Uniswap liquidity approval has an unexpected spender.", "uniswap_liquidity_unexpected_spender", { spender, positionManager });
5940
+ }
5941
+ if (amount <= 0n || amount >= (2n ** 255n)) {
5942
+ throw createTaggedError("Uniswap liquidity approval must use a bounded amount.", "uniswap_liquidity_unbounded_approval", { spender });
5943
+ }
5944
+ return {
5945
+ tx: { to, data, value: 0n },
5946
+ token: to,
5947
+ spender,
5948
+ amount,
5949
+ action: String(item?.action || action).toUpperCase(),
5950
+ };
5951
+ });
5952
+ }
5953
+
5954
+ #validateUniswapLiquidityTransaction({ runtimeConfig, protocol, address, transaction }) {
5955
+ const to = normalizeAddress(String(transaction.to || ""), "liquidity transaction.to");
5956
+ const expectedManager = getUniswapLiquidityPositionManager(runtimeConfig.network, protocol);
5957
+ if (to.toLowerCase() !== expectedManager) {
5958
+ throw createTaggedError("Uniswap liquidity API returned an unexpected PositionManager.", "uniswap_unexpected_position_manager", {
5959
+ expected: expectedManager,
5960
+ actual: to.toLowerCase(),
5961
+ network: runtimeConfig.network,
5962
+ protocol,
5963
+ });
5964
+ }
5965
+ if (transaction.chainId !== undefined && Number(transaction.chainId) !== runtimeConfig.chainId) {
5966
+ throw createTaggedError("Uniswap liquidity transaction has the wrong chain id.", "uniswap_liquidity_chain_mismatch", {
5967
+ expected: runtimeConfig.chainId,
5968
+ actual: transaction.chainId,
5969
+ });
5970
+ }
5971
+ if (transaction.from && normalizeAddress(String(transaction.from), "liquidity transaction.from").toLowerCase() !== address.toLowerCase()) {
5972
+ throw createTaggedError("Uniswap liquidity transaction sender does not match the active wallet.", "uniswap_liquidity_sender_mismatch");
5973
+ }
5974
+ const data = assertNonEmptyString(String(transaction.data || ""), "liquidity transaction.data");
5975
+ if (!/^0x[0-9a-fA-F]+$/.test(data) || data.length < 10) {
5976
+ throw createTaggedError("Uniswap liquidity transaction calldata is invalid.", "uniswap_liquidity_invalid_calldata");
5977
+ }
5978
+ return { to, data, value: parseHexOrDecimalBigInt(transaction.value || "0", "liquidity transaction.value") };
5979
+ }
5980
+
5981
+ #formatUniswapLiquidityResponse({ runtimeConfig, accountIndex, address, action, protocol, request, payload, transaction, approvals = [], simulation = null }) {
5982
+ const tx = transaction ? {
5983
+ to: transaction.to,
5984
+ value: transaction.value.toString(),
5985
+ dataHash: sha256Hex(transaction.data),
5986
+ } : null;
5987
+ return {
5988
+ network: runtimeConfig.network,
5989
+ chainId: runtimeConfig.chainId,
5990
+ accountIndex,
5991
+ address,
5992
+ protocol: "uniswap",
5993
+ liquidityAction: action,
5994
+ liquidityProtocol: protocol,
5995
+ request,
5996
+ requestId: String(payload?.requestId || "").trim() || null,
5997
+ token0: payload?.token0 || null,
5998
+ token1: payload?.token1 || null,
5999
+ tickLower: payload?.tickLower ?? null,
6000
+ tickUpper: payload?.tickUpper ?? null,
6001
+ adjustedMinPrice: payload?.adjustedMinPrice ?? null,
6002
+ adjustedMaxPrice: payload?.adjustedMaxPrice ?? null,
6003
+ gasFee: payload?.gasFee ?? null,
6004
+ positionTokenId: String(request?.nftTokenId || request?.tokenId || "").trim() || null,
6005
+ positionManager: getUniswapLiquidityPositionManager(runtimeConfig.network, protocol),
6006
+ approvals: approvals.map((approval) => ({
6007
+ token: approval.token,
6008
+ spender: approval.spender,
6009
+ amount: approval.amount.toString(),
6010
+ action: approval.action,
6011
+ })),
6012
+ transaction: tx,
6013
+ simulation,
6014
+ source: "uniswap-liquidity-api",
6015
+ };
6016
+ }
6017
+
6018
+ async quoteUniswapLiquidity({ seedPhrase, address, action, protocol, request, accountIndex = 0, network }) {
6019
+ return this.#withReadableAccount({ seedPhrase, address, accountIndex, network }, async (account, runtimeConfig) => {
6020
+ assertUniswapSupportedNetwork(runtimeConfig.network);
6021
+ const normalizedAction = normalizeUniswapLiquidityAction(action);
6022
+ const normalizedProtocol = normalizeUniswapLiquidityProtocol(protocol);
6023
+ const walletAddress = await account.getAddress();
6024
+ const body = this.#buildUniswapLiquidityRequest({ action: normalizedAction, protocol: normalizedProtocol, address: walletAddress, request });
6025
+ body.chainId = runtimeConfig.chainId;
6026
+ const payload = await this.#uniswapLiquidityApiRequest(normalizedAction, body);
6027
+ const transaction = this.#validateUniswapLiquidityTransaction({
6028
+ runtimeConfig,
6029
+ protocol: normalizedProtocol,
6030
+ address: walletAddress,
6031
+ transaction: this.#extractUniswapLiquidityTransaction(payload, normalizedAction),
6032
+ });
6033
+ const approvals = await this.#getUniswapLiquidityApprovals({ runtimeConfig, protocol: normalizedProtocol, action: normalizedAction, address: walletAddress, payload });
6034
+ const simulation = await this.#simulatePreparedTransaction({ runtimeConfig, from: walletAddress, tx: transaction, operationLabel: "Uniswap liquidity" });
6035
+ return this.#formatUniswapLiquidityResponse({ runtimeConfig, accountIndex, address: walletAddress, action: normalizedAction, protocol: normalizedProtocol, request: body, payload, transaction, approvals, simulation });
6036
+ });
6037
+ }
6038
+
6039
+ async sendUniswapLiquidity({ seedPhrase, action, protocol, request, accountIndex = 0, network }) {
6040
+ return this.#withAccount({ seedPhrase, accountIndex, network }, async (account, runtimeConfig) => {
6041
+ assertUniswapSupportedNetwork(runtimeConfig.network);
6042
+ const normalizedAction = normalizeUniswapLiquidityAction(action);
6043
+ const normalizedProtocol = normalizeUniswapLiquidityProtocol(protocol);
6044
+ const address = await account.getAddress();
6045
+ // Rebuild immediately before signing: price/ticks and calldata can change
6046
+ // while an intent approval is being reviewed.
6047
+ const body = this.#buildUniswapLiquidityRequest({ action: normalizedAction, protocol: normalizedProtocol, address, request });
6048
+ body.chainId = runtimeConfig.chainId;
6049
+ let payload = await this.#uniswapLiquidityApiRequest(normalizedAction, body);
6050
+ let transaction = this.#validateUniswapLiquidityTransaction({
6051
+ runtimeConfig,
6052
+ protocol: normalizedProtocol,
6053
+ address,
6054
+ transaction: this.#extractUniswapLiquidityTransaction(payload, normalizedAction),
6055
+ });
6056
+ const approvals = await this.#getUniswapLiquidityApprovals({ runtimeConfig, protocol: normalizedProtocol, action: normalizedAction, address, payload });
6057
+ const approvalResults = [];
6058
+ for (const approval of approvals) {
6059
+ const approvalSimulation = await this.#simulatePreparedTransaction({ runtimeConfig, from: address, tx: approval.tx, operationLabel: "Uniswap liquidity approval" });
6060
+ this.#assertSimulationSucceeded(approvalSimulation);
6061
+ const approvalResult = await this.#sendBufferedDefiTransaction({ account, runtimeConfig, from: address, tx: approval.tx, operationLabel: "Uniswap liquidity approval" });
6062
+ await this.#waitForTransactionReceipt(runtimeConfig, approvalResult.hash, { operationLabel: "Uniswap liquidity approval", failureCode: "uniswap_liquidity_approval_reverted", timeoutCode: "uniswap_liquidity_approval_timeout" });
6063
+ approvalResults.push({ token: approval.token, spender: approval.spender, amount: approval.amount.toString(), hash: approvalResult.hash });
6064
+ }
6065
+ if (approvals.length) {
6066
+ payload = await this.#uniswapLiquidityApiRequest(normalizedAction, body);
6067
+ transaction = this.#validateUniswapLiquidityTransaction({
6068
+ runtimeConfig,
6069
+ protocol: normalizedProtocol,
6070
+ address,
6071
+ transaction: this.#extractUniswapLiquidityTransaction(payload, normalizedAction),
6072
+ });
6073
+ }
6074
+ const simulation = await this.#simulatePreparedTransaction({ runtimeConfig, from: address, tx: transaction, operationLabel: "Uniswap liquidity" });
6075
+ this.#assertSimulationSucceeded(simulation);
6076
+ const result = await this.#sendBufferedDefiTransaction({ account, runtimeConfig, from: address, tx: transaction, operationLabel: "Uniswap liquidity" });
6077
+ await this.#waitForTransactionReceipt(runtimeConfig, result.hash, {
6078
+ operationLabel: "Uniswap liquidity",
6079
+ failureCode: "uniswap_liquidity_reverted",
6080
+ timeoutCode: "uniswap_liquidity_confirmation_timeout",
6081
+ });
6082
+ return {
6083
+ ...this.#formatUniswapLiquidityResponse({ runtimeConfig, accountIndex, address, action: normalizedAction, protocol: normalizedProtocol, request: body, payload, transaction, approvals, simulation }),
6084
+ result,
6085
+ approvalResults,
6086
+ confirmed: true,
6087
+ };
6088
+ });
6089
+ }
6090
+
5746
6091
  async #fetchUniswapQuote({ runtimeConfig, routerProfile, address, swapRequest }) {
5747
6092
  const chainId = UNISWAP_SUPPORTED_CHAIN_IDS[runtimeConfig.network];
5748
6093
  const quoteRequest = {