@4mica/sdk 1.2.13 → 1.2.16

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 CHANGED
@@ -316,6 +316,13 @@ Notes:
316
316
  - `cancelWithdrawal(erc20Token?)`
317
317
  - `finalizeWithdrawal(erc20Token?)`
318
318
 
319
+ ERC20 approval behavior:
320
+
321
+ - `deposit(amount, erc20Token)` requires a prior `approveErc20(token, amount)` call.
322
+ - `payTab(...)` auto-approves the Core4Mica contract when paying an ERC20 tab and the current
323
+ allowance is below the payment amount.
324
+ - `approveErc20` returns `undefined` when the existing allowance is already sufficient.
325
+
319
326
  #### RecipientClient Methods
320
327
 
321
328
  - `createTab(userAddress, recipientAddress, erc20Token?, ttl?, guaranteeVersion?)`
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4mica/sdk",
3
- "version": "1.2.13",
3
+ "version": "1.2.15",
4
4
  "description": "TypeScript SDK for interacting with the 4Mica payment network",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -56,8 +56,12 @@
56
56
  "@quillai-network/wachai-validation-sdk": "^0.1.0",
57
57
  "viem": "^2.45.1"
58
58
  },
59
+ "optionalDependencies": {
60
+ "@coinbase/cdp-sdk": "^1.49.2"
61
+ },
59
62
  "devDependencies": {
60
- "@4mica/x402": "^1.2.0",
63
+ "@4mica/x402": "^1.2.5",
64
+ "@coinbase/cdp-sdk": "^1.49.2",
61
65
  "@eslint/js": "^9.39.2",
62
66
  "@types/node": "^25.2.0",
63
67
  "@typescript-eslint/eslint-plugin": "^8.54.0",
@@ -1,4 +1,4 @@
1
- import { PaymentGuaranteeRequestClaims, PaymentGuaranteeRequestClaimsV2, PaymentSignature, SigningScheme, TabPaymentStatus, UserInfo } from '../models';
1
+ import { PaymentGuaranteeRequestClaims, PaymentGuaranteeRequestClaimsV2, PaymentSignature, SigningScheme, TabInfo, TabPaymentStatus, UserInfo } from '../models';
2
2
  import type { TxReceiptWaitOptions } from '../contract';
3
3
  import type { Client } from './index';
4
4
  /** Payer-side operations: collateral management, payment signing, withdrawals. */
@@ -57,9 +57,23 @@ export declare class UserClient {
57
57
  */
58
58
  signPayment(claims: PaymentGuaranteeRequestClaims | PaymentGuaranteeRequestClaimsV2, scheme?: SigningScheme): Promise<PaymentSignature>;
59
59
  /**
60
- * Pay a tab by transferring collateral to the recipient.
60
+ * List all tabs where the current signer is the payer.
61
61
  *
62
- * Routes to `payTabErc20` when `erc20Token` is provided, otherwise `payTabEth`.
62
+ * @param settlementStatuses - Optional filter on settlement status (e.g. `['pending']`).
63
+ * @returns Array of tabs belonging to the current signer.
64
+ */
65
+ listTabs(settlementStatuses?: string[]): Promise<TabInfo[]>;
66
+ /**
67
+ * Pay a tab on-chain. Automatically resolves the recipient, asset, and amount
68
+ * from the tab and its latest guarantee.
69
+ *
70
+ * @param tabId - Tab identifier.
71
+ * @param waitOptions - Optional timeout/polling overrides.
72
+ * @throws if the tab is not found or has no guarantee.
73
+ */
74
+ payTab(tabId: number | bigint, waitOptions?: TxReceiptWaitOptions): Promise<unknown>;
75
+ /**
76
+ * Pay a tab on-chain with explicit parameters.
63
77
  *
64
78
  * @param tabId - Tab identifier.
65
79
  * @param reqId - Request ID from the latest guarantee (used for ETH payment memo).
@@ -68,7 +82,7 @@ export declare class UserClient {
68
82
  * @param erc20Token - ERC20 token address. Omit to pay in ETH.
69
83
  * @param waitOptions - Optional timeout/polling overrides.
70
84
  */
71
- payTab(tabId: number | bigint, reqId: number | bigint, amount: number | bigint | string, recipientAddress: string, erc20Token?: string, waitOptions?: TxReceiptWaitOptions): Promise<import("viem").TransactionReceipt>;
85
+ payTab(tabId: number | bigint, reqId: number | bigint, amount: number | bigint | string, recipientAddress: string, erc20Token?: string, waitOptions?: TxReceiptWaitOptions): Promise<unknown>;
72
86
  /**
73
87
  * Initiate a collateral withdrawal request. The withdrawal is subject to an
74
88
  * on-chain timelock before it can be finalised.
@@ -79,18 +79,36 @@ class UserClient {
79
79
  return this.client.signer.signRequest(this.client.params, claims, scheme);
80
80
  }
81
81
  /**
82
- * Pay a tab by transferring collateral to the recipient.
82
+ * List all tabs where the current signer is the payer.
83
83
  *
84
- * Routes to `payTabErc20` when `erc20Token` is provided, otherwise `payTabEth`.
85
- *
86
- * @param tabId - Tab identifier.
87
- * @param reqId - Request ID from the latest guarantee (used for ETH payment memo).
88
- * @param amount - Amount to pay (in token base units / wei).
89
- * @param recipientAddress - Address of the recipient.
90
- * @param erc20Token - ERC20 token address. Omit to pay in ETH.
91
- * @param waitOptions - Optional timeout/polling overrides.
84
+ * @param settlementStatuses - Optional filter on settlement status (e.g. `['pending']`).
85
+ * @returns Array of tabs belonging to the current signer.
92
86
  */
93
- async payTab(tabId, reqId, amount, recipientAddress, erc20Token, waitOptions) {
87
+ async listTabs(settlementStatuses) {
88
+ const myAddress = (0, utils_1.normalizeAddress)(this.client.signer.signer.address);
89
+ const raw = await this.client.rpc.listUserTabs(myAddress, settlementStatuses);
90
+ return raw.map((t) => models_1.TabInfo.fromRpc(t));
91
+ }
92
+ async payTab(tabId, reqIdOrWaitOptions, amount, recipientAddress, erc20Token, waitOptions) {
93
+ // Simple overload: auto-resolve from tab + latest guarantee
94
+ if (reqIdOrWaitOptions === undefined ||
95
+ (typeof reqIdOrWaitOptions === 'object' &&
96
+ reqIdOrWaitOptions !== null &&
97
+ !('valueOf' in reqIdOrWaitOptions && typeof reqIdOrWaitOptions.valueOf() === 'bigint'))) {
98
+ const opts = reqIdOrWaitOptions;
99
+ const tab = await this.client.recipient.getTab(tabId);
100
+ if (!tab)
101
+ throw new Error(`Tab ${tabId} not found`);
102
+ const guarantee = await this.client.recipient.getLatestGuarantee(tabId);
103
+ if (!guarantee)
104
+ throw new Error(`Tab ${tabId} has no guarantee`);
105
+ const isEth = tab.assetAddress === '0x0000000000000000000000000000000000000000';
106
+ return isEth
107
+ ? this.client.gateway.payTabEth(tabId, guarantee.reqId, guarantee.amount, tab.recipientAddress, opts)
108
+ : this.client.gateway.payTabErc20(tabId, guarantee.amount, tab.assetAddress, tab.recipientAddress, opts);
109
+ }
110
+ // Explicit overload
111
+ const reqId = reqIdOrWaitOptions;
94
112
  if (erc20Token) {
95
113
  return this.client.gateway.payTabErc20(tabId, amount, erc20Token, recipientAddress, waitOptions);
96
114
  }
@@ -246,7 +246,11 @@ class ContractGateway {
246
246
  }
247
247
  async payTabErc20(tabId, amount, erc20Token, recipient, waitOptions) {
248
248
  const { gas, receipt } = this.splitWaitOptions(waitOptions);
249
- const hash = await this.enqueueTx(() => this.contract.write.payTabInERC20Token([(0, utils_1.parseU256)(tabId), erc20Token, (0, utils_1.parseU256)(amount), recipient], {
249
+ const parsedAmount = (0, utils_1.parseU256)(amount);
250
+ // payTabInERC20Token uses safeTransferFrom, so the contract must be an
251
+ // approved spender. Auto-approve if the current allowance is insufficient.
252
+ await this.approveErc20(erc20Token, parsedAmount, waitOptions);
253
+ const hash = await this.enqueueTx(() => this.contract.write.payTabInERC20Token([(0, utils_1.parseU256)(tabId), erc20Token, parsedAmount, recipient], {
250
254
  gas: gas ?? DEFAULT_PAY_TAB_ERC20_GAS_LIMIT,
251
255
  ...this.defaultFeeParams(),
252
256
  }));
@@ -14,3 +14,4 @@ export * from './guarantee';
14
14
  export * from './bls';
15
15
  export * from './x402/index';
16
16
  export * from './client';
17
+ export * from './wallet';
package/dist/src/index.js CHANGED
@@ -30,3 +30,4 @@ __exportStar(require("./guarantee"), exports);
30
30
  __exportStar(require("./bls"), exports);
31
31
  __exportStar(require("./x402/index"), exports);
32
32
  __exportStar(require("./client"), exports);
33
+ __exportStar(require("./wallet"), exports);
package/dist/src/rpc.d.ts CHANGED
@@ -24,6 +24,7 @@ export declare class RpcProxy {
24
24
  listPendingRemunerations(recipientAddress: string): Promise<Record<string, unknown>[]>;
25
25
  getTab(tabId: number | bigint): Promise<Record<string, unknown> | null>;
26
26
  listRecipientTabs(recipientAddress: string, settlementStatuses?: string[]): Promise<Record<string, unknown>[]>;
27
+ listUserTabs(userAddress: string, settlementStatuses?: string[]): Promise<Record<string, unknown>[]>;
27
28
  getTabGuarantees(tabId: number | bigint): Promise<Record<string, unknown>[]>;
28
29
  getLatestGuarantee(tabId: number | bigint): Promise<Record<string, unknown> | null>;
29
30
  getGuarantee(tabId: number | bigint, reqId: number | bigint): Promise<Record<string, unknown> | null>;
package/dist/src/rpc.js CHANGED
@@ -113,6 +113,14 @@ class RpcProxy {
113
113
  }
114
114
  return this.get(`/core/recipients/${recipientAddress}/tabs${query}`);
115
115
  }
116
+ async listUserTabs(userAddress, settlementStatuses) {
117
+ let query = '';
118
+ if (settlementStatuses?.length) {
119
+ query =
120
+ '?' + settlementStatuses.map((s) => `settlement_status=${encodeURIComponent(s)}`).join('&');
121
+ }
122
+ return this.get(`/core/users/${userAddress}/tabs${query}`);
123
+ }
116
124
  async getTabGuarantees(tabId) {
117
125
  return this.get(`/core/tabs/${serializeTabId(tabId)}/guarantees`);
118
126
  }
@@ -0,0 +1,13 @@
1
+ import { type Account } from 'viem';
2
+ export interface CdpAccountConfig {
3
+ /** CDP API key ID from the Coinbase Developer Platform dashboard. */
4
+ apiKeyId: string;
5
+ /** CDP API key secret from the Coinbase Developer Platform dashboard. */
6
+ apiKeySecret: string;
7
+ /** CDP wallet secret — required for account creation/signing operations. */
8
+ walletSecret: string;
9
+ /** Idempotency name — getOrCreateAccount always returns the same wallet for a given name. */
10
+ name: string;
11
+ }
12
+ /** Creates a viem Account backed by a Coinbase CDP MPC wallet (private key never leaves CDP). */
13
+ export declare function createCdpAccount(config: CdpAccountConfig): Promise<Account>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createCdpAccount = createCdpAccount;
37
+ const viem_1 = require("viem");
38
+ const accounts_1 = require("viem/accounts");
39
+ /** Creates a viem Account backed by a Coinbase CDP MPC wallet (private key never leaves CDP). */
40
+ async function createCdpAccount(config) {
41
+ const { CdpClient } = await Promise.resolve().then(() => __importStar(require('@coinbase/cdp-sdk')));
42
+ const cdp = new CdpClient({
43
+ apiKeyId: config.apiKeyId,
44
+ apiKeySecret: config.apiKeySecret,
45
+ walletSecret: config.walletSecret,
46
+ });
47
+ const evmAccount = await cdp.evm.getOrCreateAccount({ name: config.name });
48
+ const address = evmAccount.address;
49
+ async function signMessage({ message }) {
50
+ if (typeof message === 'string') {
51
+ // CDP's signMessage applies the EIP-191 prefix internally.
52
+ const result = await cdp.evm.signMessage({ address, message });
53
+ return result.signature;
54
+ }
55
+ const hash = (0, viem_1.hashMessage)(message);
56
+ const result = await cdp.evm.signHash({ address, hash });
57
+ return result.signature;
58
+ }
59
+ async function signTypedData(parameters) {
60
+ // Always hash locally with viem (guaranteed correct EIP-712 encoding including
61
+ // non-standard types like uint64) and sign only the hash via CDP.
62
+ // CDP's native signTypedData can silently produce a wrong hash for custom types,
63
+ // which would pass without error but fail signature verification on the core side.
64
+ const hash = (0, viem_1.hashTypedData)(parameters);
65
+ const result = await cdp.evm.signHash({ address, hash });
66
+ return result.signature;
67
+ }
68
+ async function signTransaction(transaction, options) {
69
+ const serializer = options?.serializer ?? viem_1.serializeTransaction;
70
+ const hash = (0, viem_1.keccak256)(serializer(transaction));
71
+ const result = await cdp.evm.signHash({ address, hash });
72
+ const { r, s, yParity } = (0, viem_1.hexToSignature)(result.signature);
73
+ return serializer(transaction, { r, s, yParity });
74
+ }
75
+ // viem's signTypedData generic cannot be satisfied by a hand-rolled adapter without casting — same pattern as hdKeyToAccount.
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ return (0, accounts_1.toAccount)({ address, signMessage, signTypedData, signTransaction });
78
+ }
@@ -0,0 +1,2 @@
1
+ export { createCdpAccount } from './cdp';
2
+ export type { CdpAccountConfig } from './cdp';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCdpAccount = void 0;
4
+ var cdp_1 = require("./cdp");
5
+ Object.defineProperty(exports, "createCdpAccount", { enumerable: true, get: function () { return cdp_1.createCdpAccount; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4mica/sdk",
3
- "version": "1.2.13",
3
+ "version": "1.2.16",
4
4
  "description": "TypeScript SDK for interacting with the 4Mica payment network",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -56,8 +56,12 @@
56
56
  "@quillai-network/wachai-validation-sdk": "^0.1.0",
57
57
  "viem": "^2.45.1"
58
58
  },
59
+ "optionalDependencies": {
60
+ "@coinbase/cdp-sdk": "^1.49.2"
61
+ },
59
62
  "devDependencies": {
60
- "@4mica/x402": "^1.2.0",
63
+ "@4mica/x402": "^1.2.5",
64
+ "@coinbase/cdp-sdk": "^1.49.2",
61
65
  "@eslint/js": "^9.39.2",
62
66
  "@types/node": "^25.2.0",
63
67
  "@typescript-eslint/eslint-plugin": "^8.54.0",