@4mica/sdk 1.2.13 → 1.2.14

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.14",
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 recipientAddress - Address of the recipient to query tabs from.
63
+ * @returns Array of tabs belonging to the current signer.
64
+ */
65
+ listTabs(recipientAddress: 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,38 @@ 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 recipientAddress - Address of the recipient to query tabs from.
85
+ * @returns Array of tabs belonging to the current signer.
92
86
  */
93
- async payTab(tabId, reqId, amount, recipientAddress, erc20Token, waitOptions) {
87
+ async listTabs(recipientAddress) {
88
+ const myAddress = (0, utils_1.normalizeAddress)(this.client.signer.signer.address);
89
+ const raw = await this.client.rpc.listRecipientTabs(recipientAddress);
90
+ return raw
91
+ .map((t) => models_1.TabInfo.fromRpc(t))
92
+ .filter((t) => (0, utils_1.normalizeAddress)(t.userAddress) === myAddress);
93
+ }
94
+ async payTab(tabId, reqIdOrWaitOptions, amount, recipientAddress, erc20Token, waitOptions) {
95
+ // Simple overload: auto-resolve from tab + latest guarantee
96
+ if (reqIdOrWaitOptions === undefined ||
97
+ (typeof reqIdOrWaitOptions === 'object' &&
98
+ reqIdOrWaitOptions !== null &&
99
+ !('valueOf' in reqIdOrWaitOptions && typeof reqIdOrWaitOptions.valueOf() === 'bigint'))) {
100
+ const opts = reqIdOrWaitOptions;
101
+ const tab = await this.client.recipient.getTab(tabId);
102
+ if (!tab)
103
+ throw new Error(`Tab ${tabId} not found`);
104
+ const guarantee = await this.client.recipient.getLatestGuarantee(tabId);
105
+ if (!guarantee)
106
+ throw new Error(`Tab ${tabId} has no guarantee`);
107
+ const isEth = tab.assetAddress === '0x0000000000000000000000000000000000000000';
108
+ return isEth
109
+ ? this.client.gateway.payTabEth(tabId, guarantee.reqId, guarantee.amount, tab.recipientAddress, opts)
110
+ : this.client.gateway.payTabErc20(tabId, guarantee.amount, tab.assetAddress, tab.recipientAddress, opts);
111
+ }
112
+ // Explicit overload
113
+ const reqId = reqIdOrWaitOptions;
94
114
  if (erc20Token) {
95
115
  return this.client.gateway.payTabErc20(tabId, amount, erc20Token, recipientAddress, waitOptions);
96
116
  }
@@ -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);
@@ -0,0 +1,11 @@
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
+ /** Idempotency name — getOrCreateAccount always returns the same wallet for a given name. */
8
+ name: string;
9
+ }
10
+ /** Creates a viem Account backed by a Coinbase CDP MPC wallet (private key never leaves CDP). */
11
+ export declare function createCdpAccount(config: CdpAccountConfig): Promise<Account>;
@@ -0,0 +1,85 @@
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
+ });
46
+ const evmAccount = await cdp.evm.getOrCreateAccount({ name: config.name });
47
+ const address = evmAccount.address;
48
+ async function signMessage({ message }) {
49
+ if (typeof message === 'string') {
50
+ // CDP's signMessage applies the EIP-191 prefix internally.
51
+ const result = await cdp.evm.signMessage({ address, message });
52
+ return result.signature;
53
+ }
54
+ const hash = (0, viem_1.hashMessage)(message);
55
+ const result = await cdp.evm.signHash({ address, hash });
56
+ return result.signature;
57
+ }
58
+ async function signTypedData(parameters) {
59
+ const { domain, types, primaryType, message } = parameters;
60
+ if (domain && types && primaryType && message) {
61
+ try {
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ const payload = { address, domain, types, primaryType, message };
64
+ const result = await cdp.evm.signTypedData(payload);
65
+ return result.signature;
66
+ }
67
+ catch {
68
+ // Fall back to hash-then-sign if CDP rejects the typed-data structure.
69
+ }
70
+ }
71
+ const hash = (0, viem_1.hashTypedData)(parameters);
72
+ const result = await cdp.evm.signHash({ address, hash });
73
+ return result.signature;
74
+ }
75
+ async function signTransaction(transaction, options) {
76
+ const serializer = options?.serializer ?? viem_1.serializeTransaction;
77
+ const hash = (0, viem_1.keccak256)(serializer(transaction));
78
+ const result = await cdp.evm.signHash({ address, hash });
79
+ const { r, s, yParity } = (0, viem_1.hexToSignature)(result.signature);
80
+ return serializer(transaction, { r, s, yParity });
81
+ }
82
+ // viem's signTypedData generic cannot be satisfied by a hand-rolled adapter without casting — same pattern as hdKeyToAccount.
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ return (0, accounts_1.toAccount)({ address, signMessage, signTypedData, signTransaction });
85
+ }
@@ -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.14",
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",