@chipi-stack/backend 14.1.1 → 14.2.1
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.d.mts +84 -1
- package/dist/index.d.ts +84 -1
- package/dist/index.js +318 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +318 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1419,6 +1419,324 @@ var ChipiSessions = class {
|
|
|
1419
1419
|
client: this.client
|
|
1420
1420
|
});
|
|
1421
1421
|
}
|
|
1422
|
+
// --- Spending Policy Methods ---
|
|
1423
|
+
// Wraps ISessionSpendingPolicy contract interface.
|
|
1424
|
+
// Spending policies enforce per-token limits on ERC-20 operations
|
|
1425
|
+
// (transfer, approve, increase_allowance) executed by session keys.
|
|
1426
|
+
/**
|
|
1427
|
+
* Split a bigint into Cairo u256 representation (low u128, high u128).
|
|
1428
|
+
*/
|
|
1429
|
+
splitU256(value) {
|
|
1430
|
+
const mask = (1n << 128n) - 1n;
|
|
1431
|
+
return [num.toHex(value & mask), num.toHex(value >> 128n)];
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Reconstruct a bigint from Cairo u256 (low, high) hex strings.
|
|
1435
|
+
*/
|
|
1436
|
+
joinU256(low, high) {
|
|
1437
|
+
return BigInt(low) + (BigInt(high) << 128n);
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Set a spending policy for a session key + token pair.
|
|
1441
|
+
*
|
|
1442
|
+
* This executes a sponsored transaction (using owner signature) to call
|
|
1443
|
+
* `set_spending_policy` on the CHIPI wallet contract.
|
|
1444
|
+
*
|
|
1445
|
+
* The contract enforces these limits automatically during `__execute__()` on
|
|
1446
|
+
* ERC-20 operations (transfer, approve, increase_allowance) made by the session.
|
|
1447
|
+
*
|
|
1448
|
+
* @param params - Spending policy parameters
|
|
1449
|
+
* @param bearerToken - Authentication token
|
|
1450
|
+
* @returns Transaction hash
|
|
1451
|
+
*
|
|
1452
|
+
* @example
|
|
1453
|
+
* ```typescript
|
|
1454
|
+
* // Set a spending limit: max 1 USDC per call, 50 USDC per day
|
|
1455
|
+
* const txHash = await sdk.sessions.setSpendingPolicy({
|
|
1456
|
+
* encryptKey: userEncryptKey,
|
|
1457
|
+
* wallet: userWallet,
|
|
1458
|
+
* spendingPolicyConfig: {
|
|
1459
|
+
* sessionPublicKey: session.publicKey,
|
|
1460
|
+
* token: USDC_ADDRESS,
|
|
1461
|
+
* maxPerCall: 1_000_000n, // 1 USDC (6 decimals)
|
|
1462
|
+
* maxPerWindow: 50_000_000n, // 50 USDC
|
|
1463
|
+
* windowSeconds: 86400, // 24 hours
|
|
1464
|
+
* }
|
|
1465
|
+
* }, bearerToken);
|
|
1466
|
+
* ```
|
|
1467
|
+
*/
|
|
1468
|
+
async setSpendingPolicy(params, bearerToken) {
|
|
1469
|
+
const { encryptKey, wallet, spendingPolicyConfig } = params;
|
|
1470
|
+
const { sessionPublicKey, token, maxPerCall, maxPerWindow, windowSeconds } = spendingPolicyConfig;
|
|
1471
|
+
if (wallet.walletType && wallet.walletType !== "CHIPI") {
|
|
1472
|
+
throw new ChipiSessionError(
|
|
1473
|
+
`Spending policies only support CHIPI wallets. Got: ${wallet.walletType}`,
|
|
1474
|
+
SESSION_ERRORS.INVALID_WALLET_TYPE_FOR_SESSION
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
const U256_MAX = (1n << 256n) - 1n;
|
|
1478
|
+
const U64_MAX = (1n << 64n) - 1n;
|
|
1479
|
+
if (!token || token.trim().length === 0) {
|
|
1480
|
+
throw new ChipiSessionError(
|
|
1481
|
+
`Invalid token address for spending policy: "${token}"`,
|
|
1482
|
+
SESSION_ERRORS.INVALID_SPENDING_POLICY
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
if (typeof windowSeconds !== "number" || !Number.isFinite(windowSeconds) || !Number.isInteger(windowSeconds) || windowSeconds <= 0 || BigInt(windowSeconds) > U64_MAX) {
|
|
1486
|
+
throw new ChipiSessionError(
|
|
1487
|
+
`Invalid window duration: ${windowSeconds}. Must be a positive integer within u64 range.`,
|
|
1488
|
+
SESSION_ERRORS.INVALID_SPENDING_POLICY
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
if (maxPerCall < 0n || maxPerCall > U256_MAX) {
|
|
1492
|
+
throw new ChipiSessionError(
|
|
1493
|
+
`Invalid maxPerCall: must be >= 0 and <= 2^256 - 1`,
|
|
1494
|
+
SESSION_ERRORS.INVALID_SPENDING_POLICY
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
if (maxPerWindow < 0n || maxPerWindow > U256_MAX) {
|
|
1498
|
+
throw new ChipiSessionError(
|
|
1499
|
+
`Invalid maxPerWindow: must be >= 0 and <= 2^256 - 1`,
|
|
1500
|
+
SESSION_ERRORS.INVALID_SPENDING_POLICY
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
if (maxPerWindow > 0n && maxPerCall > maxPerWindow) {
|
|
1504
|
+
throw new ChipiSessionError(
|
|
1505
|
+
`Invalid spending policy: maxPerCall (${maxPerCall}) cannot exceed maxPerWindow (${maxPerWindow})`,
|
|
1506
|
+
SESSION_ERRORS.INVALID_SPENDING_POLICY
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
try {
|
|
1510
|
+
console.log("[ChipiSDK:Session:SetSpendingPolicy] Setting spending policy", {
|
|
1511
|
+
walletAddress: wallet.publicKey.slice(0, 15) + "...",
|
|
1512
|
+
sessionPubKey: sessionPublicKey.slice(0, 15) + "...",
|
|
1513
|
+
token: token.slice(0, 15) + "...",
|
|
1514
|
+
maxPerCall: maxPerCall.toString(),
|
|
1515
|
+
maxPerWindow: maxPerWindow.toString(),
|
|
1516
|
+
windowSeconds
|
|
1517
|
+
});
|
|
1518
|
+
const [maxPerCallLow, maxPerCallHigh] = this.splitU256(maxPerCall);
|
|
1519
|
+
const [maxPerWindowLow, maxPerWindowHigh] = this.splitU256(maxPerWindow);
|
|
1520
|
+
const calldata = [
|
|
1521
|
+
sessionPublicKey,
|
|
1522
|
+
token,
|
|
1523
|
+
maxPerCallLow,
|
|
1524
|
+
maxPerCallHigh,
|
|
1525
|
+
maxPerWindowLow,
|
|
1526
|
+
maxPerWindowHigh,
|
|
1527
|
+
num.toHex(windowSeconds)
|
|
1528
|
+
];
|
|
1529
|
+
const txHash = await executePaymasterTransaction({
|
|
1530
|
+
params: {
|
|
1531
|
+
encryptKey,
|
|
1532
|
+
wallet: { ...wallet, walletType: "CHIPI" },
|
|
1533
|
+
calls: [
|
|
1534
|
+
{
|
|
1535
|
+
contractAddress: wallet.publicKey,
|
|
1536
|
+
entrypoint: SESSION_ENTRYPOINTS.SET_SPENDING_POLICY,
|
|
1537
|
+
calldata
|
|
1538
|
+
}
|
|
1539
|
+
],
|
|
1540
|
+
saveToDatabase: false
|
|
1541
|
+
},
|
|
1542
|
+
bearerToken,
|
|
1543
|
+
client: this.client
|
|
1544
|
+
});
|
|
1545
|
+
console.log("[ChipiSDK:Session:SetSpendingPolicy] Spending policy set successfully", {
|
|
1546
|
+
txHash,
|
|
1547
|
+
sessionPubKey: sessionPublicKey.slice(0, 15) + "...",
|
|
1548
|
+
token: token.slice(0, 15) + "..."
|
|
1549
|
+
});
|
|
1550
|
+
return txHash;
|
|
1551
|
+
} catch (error) {
|
|
1552
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1553
|
+
console.error("[ChipiSDK:Session:SetSpendingPolicy] Failed to set spending policy", {
|
|
1554
|
+
error: err.message,
|
|
1555
|
+
walletAddress: wallet.publicKey?.slice(0, 15) + "...",
|
|
1556
|
+
sessionPubKey: sessionPublicKey?.slice(0, 15) + "...",
|
|
1557
|
+
token: token?.slice(0, 15) + "..."
|
|
1558
|
+
});
|
|
1559
|
+
throw error;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Query a spending policy from the smart contract.
|
|
1564
|
+
*
|
|
1565
|
+
* This is a read-only call that does not require signing or gas.
|
|
1566
|
+
* Returns zeroed data if no policy is set (meaning no enforcement).
|
|
1567
|
+
*
|
|
1568
|
+
* @param params - Query parameters
|
|
1569
|
+
* @returns Spending policy data including limits and current window state
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* ```typescript
|
|
1573
|
+
* const policy = await sdk.sessions.getSpendingPolicy({
|
|
1574
|
+
* walletAddress: userWallet.publicKey,
|
|
1575
|
+
* sessionPublicKey: session.publicKey,
|
|
1576
|
+
* token: USDC_ADDRESS,
|
|
1577
|
+
* });
|
|
1578
|
+
* if (policy.maxPerWindow > 0n) {
|
|
1579
|
+
* console.log(`Spent ${policy.spentInWindow} of ${policy.maxPerWindow} this window`);
|
|
1580
|
+
* }
|
|
1581
|
+
* ```
|
|
1582
|
+
*/
|
|
1583
|
+
async getSpendingPolicy(params) {
|
|
1584
|
+
const { walletAddress, sessionPublicKey, token } = params;
|
|
1585
|
+
try {
|
|
1586
|
+
const provider = new RpcProvider({
|
|
1587
|
+
nodeUrl: WALLET_RPC_ENDPOINTS["CHIPI"]
|
|
1588
|
+
});
|
|
1589
|
+
console.log("[ChipiSDK:Session:GetSpendingPolicy] Querying spending policy", {
|
|
1590
|
+
walletAddress: walletAddress.slice(0, 15) + "...",
|
|
1591
|
+
sessionPubKey: sessionPublicKey.slice(0, 15) + "...",
|
|
1592
|
+
token: token.slice(0, 15) + "..."
|
|
1593
|
+
});
|
|
1594
|
+
const result = await provider.callContract({
|
|
1595
|
+
contractAddress: walletAddress,
|
|
1596
|
+
entrypoint: SESSION_ENTRYPOINTS.GET_SPENDING_POLICY,
|
|
1597
|
+
calldata: [sessionPublicKey, token]
|
|
1598
|
+
});
|
|
1599
|
+
const resultArray = Array.isArray(result) ? result : result.result || [];
|
|
1600
|
+
if (resultArray.length < 8) {
|
|
1601
|
+
console.warn("[ChipiSDK:Session:GetSpendingPolicy] Unexpected response format", {
|
|
1602
|
+
resultLength: resultArray.length,
|
|
1603
|
+
result: resultArray
|
|
1604
|
+
});
|
|
1605
|
+
return {
|
|
1606
|
+
maxPerCall: 0n,
|
|
1607
|
+
maxPerWindow: 0n,
|
|
1608
|
+
windowSeconds: 0,
|
|
1609
|
+
spentInWindow: 0n,
|
|
1610
|
+
windowStart: 0
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
const policyData = {
|
|
1614
|
+
maxPerCall: this.joinU256(resultArray[0], resultArray[1]),
|
|
1615
|
+
maxPerWindow: this.joinU256(resultArray[2], resultArray[3]),
|
|
1616
|
+
windowSeconds: Number(BigInt(resultArray[4])),
|
|
1617
|
+
spentInWindow: this.joinU256(resultArray[5], resultArray[6]),
|
|
1618
|
+
windowStart: Number(BigInt(resultArray[7]))
|
|
1619
|
+
};
|
|
1620
|
+
console.log("[ChipiSDK:Session:GetSpendingPolicy] Spending policy retrieved", {
|
|
1621
|
+
maxPerCall: policyData.maxPerCall.toString(),
|
|
1622
|
+
maxPerWindow: policyData.maxPerWindow.toString(),
|
|
1623
|
+
windowSeconds: policyData.windowSeconds,
|
|
1624
|
+
spentInWindow: policyData.spentInWindow.toString(),
|
|
1625
|
+
windowStart: policyData.windowStart
|
|
1626
|
+
});
|
|
1627
|
+
return policyData;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1630
|
+
const notFoundPatterns = [
|
|
1631
|
+
/entry point.*not found/i,
|
|
1632
|
+
/requested entrypoint was not found/i,
|
|
1633
|
+
/invalid message selector/i,
|
|
1634
|
+
/contract not found/i,
|
|
1635
|
+
/is not deployed/i
|
|
1636
|
+
];
|
|
1637
|
+
const isNotFound = notFoundPatterns.some((p) => p.test(err.message));
|
|
1638
|
+
if (isNotFound) {
|
|
1639
|
+
console.log(
|
|
1640
|
+
"[ChipiSDK:Session:GetSpendingPolicy] No policy found (contract or entrypoint missing)",
|
|
1641
|
+
{
|
|
1642
|
+
walletAddress: walletAddress?.slice(0, 15) + "...",
|
|
1643
|
+
sessionPubKey: sessionPublicKey?.slice(0, 15) + "...",
|
|
1644
|
+
token: token?.slice(0, 15) + "..."
|
|
1645
|
+
}
|
|
1646
|
+
);
|
|
1647
|
+
return {
|
|
1648
|
+
maxPerCall: 0n,
|
|
1649
|
+
maxPerWindow: 0n,
|
|
1650
|
+
windowSeconds: 0,
|
|
1651
|
+
spentInWindow: 0n,
|
|
1652
|
+
windowStart: 0
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
console.error("[ChipiSDK:Session:GetSpendingPolicy] RPC/network error", {
|
|
1656
|
+
error: err.message,
|
|
1657
|
+
walletAddress: walletAddress?.slice(0, 15) + "...",
|
|
1658
|
+
sessionPubKey: sessionPublicKey?.slice(0, 15) + "...",
|
|
1659
|
+
token: token?.slice(0, 15) + "..."
|
|
1660
|
+
});
|
|
1661
|
+
throw err;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Remove a spending policy for a session key + token pair.
|
|
1666
|
+
*
|
|
1667
|
+
* This executes a sponsored transaction (using owner signature) to call
|
|
1668
|
+
* `remove_spending_policy` on the CHIPI wallet contract.
|
|
1669
|
+
*
|
|
1670
|
+
* After removal, the session key has no spending limits for this token.
|
|
1671
|
+
*
|
|
1672
|
+
* @param params - Policy removal parameters
|
|
1673
|
+
* @param bearerToken - Authentication token
|
|
1674
|
+
* @returns Transaction hash
|
|
1675
|
+
*
|
|
1676
|
+
* @example
|
|
1677
|
+
* ```typescript
|
|
1678
|
+
* const txHash = await sdk.sessions.removeSpendingPolicy({
|
|
1679
|
+
* encryptKey: userEncryptKey,
|
|
1680
|
+
* wallet: userWallet,
|
|
1681
|
+
* sessionPublicKey: session.publicKey,
|
|
1682
|
+
* token: USDC_ADDRESS,
|
|
1683
|
+
* }, bearerToken);
|
|
1684
|
+
* ```
|
|
1685
|
+
*/
|
|
1686
|
+
async removeSpendingPolicy(params, bearerToken) {
|
|
1687
|
+
const { encryptKey, wallet, sessionPublicKey, token } = params;
|
|
1688
|
+
if (wallet.walletType && wallet.walletType !== "CHIPI") {
|
|
1689
|
+
throw new ChipiSessionError(
|
|
1690
|
+
`Spending policies only support CHIPI wallets. Got: ${wallet.walletType}`,
|
|
1691
|
+
SESSION_ERRORS.INVALID_WALLET_TYPE_FOR_SESSION
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
if (!token || token.trim().length === 0) {
|
|
1695
|
+
throw new ChipiSessionError(
|
|
1696
|
+
`Invalid token address for spending policy removal: "${token}"`,
|
|
1697
|
+
SESSION_ERRORS.INVALID_SPENDING_POLICY
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
try {
|
|
1701
|
+
console.log("[ChipiSDK:Session:RemoveSpendingPolicy] Removing spending policy", {
|
|
1702
|
+
walletAddress: wallet.publicKey.slice(0, 15) + "...",
|
|
1703
|
+
sessionPubKey: sessionPublicKey.slice(0, 15) + "...",
|
|
1704
|
+
token: token.slice(0, 15) + "..."
|
|
1705
|
+
});
|
|
1706
|
+
const calldata = [sessionPublicKey, token];
|
|
1707
|
+
const txHash = await executePaymasterTransaction({
|
|
1708
|
+
params: {
|
|
1709
|
+
encryptKey,
|
|
1710
|
+
wallet: { ...wallet, walletType: "CHIPI" },
|
|
1711
|
+
calls: [
|
|
1712
|
+
{
|
|
1713
|
+
contractAddress: wallet.publicKey,
|
|
1714
|
+
entrypoint: SESSION_ENTRYPOINTS.REMOVE_SPENDING_POLICY,
|
|
1715
|
+
calldata
|
|
1716
|
+
}
|
|
1717
|
+
],
|
|
1718
|
+
saveToDatabase: false
|
|
1719
|
+
},
|
|
1720
|
+
bearerToken,
|
|
1721
|
+
client: this.client
|
|
1722
|
+
});
|
|
1723
|
+
console.log("[ChipiSDK:Session:RemoveSpendingPolicy] Spending policy removed successfully", {
|
|
1724
|
+
txHash,
|
|
1725
|
+
sessionPubKey: sessionPublicKey.slice(0, 15) + "...",
|
|
1726
|
+
token: token.slice(0, 15) + "..."
|
|
1727
|
+
});
|
|
1728
|
+
return txHash;
|
|
1729
|
+
} catch (error) {
|
|
1730
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1731
|
+
console.error("[ChipiSDK:Session:RemoveSpendingPolicy] Failed to remove spending policy", {
|
|
1732
|
+
error: err.message,
|
|
1733
|
+
walletAddress: wallet.publicKey?.slice(0, 15) + "...",
|
|
1734
|
+
sessionPubKey: sessionPublicKey?.slice(0, 15) + "...",
|
|
1735
|
+
token: token?.slice(0, 15) + "..."
|
|
1736
|
+
});
|
|
1737
|
+
throw error;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1422
1740
|
};
|
|
1423
1741
|
var InMemoryNonceStore = class {
|
|
1424
1742
|
constructor() {
|