@framers/agentos-ext-wallet 0.2.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/LICENSE +98 -0
- package/dist/AgentWalletManager.d.ts +68 -0
- package/dist/AgentWalletManager.d.ts.map +1 -0
- package/dist/AgentWalletManager.js +238 -0
- package/dist/AgentWalletManager.js.map +1 -0
- package/dist/SpendingPolicyEnforcer.d.ts +33 -0
- package/dist/SpendingPolicyEnforcer.d.ts.map +1 -0
- package/dist/SpendingPolicyEnforcer.js +128 -0
- package/dist/SpendingPolicyEnforcer.js.map +1 -0
- package/dist/cards/CardManager.d.ts +71 -0
- package/dist/cards/CardManager.d.ts.map +1 -0
- package/dist/cards/CardManager.js +212 -0
- package/dist/cards/CardManager.js.map +1 -0
- package/dist/cards/LithicCardAdapter.d.ts +40 -0
- package/dist/cards/LithicCardAdapter.d.ts.map +1 -0
- package/dist/cards/LithicCardAdapter.js +116 -0
- package/dist/cards/LithicCardAdapter.js.map +1 -0
- package/dist/cards/MccCategoryMap.d.ts +36 -0
- package/dist/cards/MccCategoryMap.d.ts.map +1 -0
- package/dist/cards/MccCategoryMap.js +113 -0
- package/dist/cards/MccCategoryMap.js.map +1 -0
- package/dist/cards/types.d.ts +131 -0
- package/dist/cards/types.d.ts.map +1 -0
- package/dist/cards/types.js +12 -0
- package/dist/cards/types.js.map +1 -0
- package/dist/chains/EvmWalletAdapter.d.ts +30 -0
- package/dist/chains/EvmWalletAdapter.d.ts.map +1 -0
- package/dist/chains/EvmWalletAdapter.js +135 -0
- package/dist/chains/EvmWalletAdapter.js.map +1 -0
- package/dist/chains/SolanaWalletAdapter.d.ts +32 -0
- package/dist/chains/SolanaWalletAdapter.d.ts.map +1 -0
- package/dist/chains/SolanaWalletAdapter.js +128 -0
- package/dist/chains/SolanaWalletAdapter.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +228 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/CardSpendingSummaryTool.d.ts +14 -0
- package/dist/tools/CardSpendingSummaryTool.d.ts.map +1 -0
- package/dist/tools/CardSpendingSummaryTool.js +40 -0
- package/dist/tools/CardSpendingSummaryTool.js.map +1 -0
- package/dist/tools/CardStatusTool.d.ts +21 -0
- package/dist/tools/CardStatusTool.d.ts.map +1 -0
- package/dist/tools/CardStatusTool.js +48 -0
- package/dist/tools/CardStatusTool.js.map +1 -0
- package/dist/tools/CheckBalanceTool.d.ts +23 -0
- package/dist/tools/CheckBalanceTool.d.ts.map +1 -0
- package/dist/tools/CheckBalanceTool.js +116 -0
- package/dist/tools/CheckBalanceTool.js.map +1 -0
- package/dist/tools/FreezeCardTool.d.ts +15 -0
- package/dist/tools/FreezeCardTool.d.ts.map +1 -0
- package/dist/tools/FreezeCardTool.js +39 -0
- package/dist/tools/FreezeCardTool.js.map +1 -0
- package/dist/tools/IssueCardTool.d.ts +22 -0
- package/dist/tools/IssueCardTool.d.ts.map +1 -0
- package/dist/tools/IssueCardTool.js +55 -0
- package/dist/tools/IssueCardTool.js.map +1 -0
- package/dist/tools/PayWithCardTool.d.ts +29 -0
- package/dist/tools/PayWithCardTool.d.ts.map +1 -0
- package/dist/tools/PayWithCardTool.js +79 -0
- package/dist/tools/PayWithCardTool.js.map +1 -0
- package/dist/tools/SendCryptoTool.d.ts +33 -0
- package/dist/tools/SendCryptoTool.d.ts.map +1 -0
- package/dist/tools/SendCryptoTool.js +136 -0
- package/dist/tools/SendCryptoTool.js.map +1 -0
- package/dist/tools/UnfreezeCardTool.d.ts +15 -0
- package/dist/tools/UnfreezeCardTool.d.ts.map +1 -0
- package/dist/tools/UnfreezeCardTool.js +39 -0
- package/dist/tools/UnfreezeCardTool.js.map +1 -0
- package/dist/tools/WalletHistoryTool.d.ts +35 -0
- package/dist/tools/WalletHistoryTool.d.ts.map +1 -0
- package/dist/tools/WalletHistoryTool.js +75 -0
- package/dist/tools/WalletHistoryTool.js.map +1 -0
- package/dist/types.d.ts +146 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/manifest.json +98 -0
- package/package.json +78 -0
- package/src/AgentWalletManager.ts +324 -0
- package/src/SpendingPolicyEnforcer.ts +169 -0
- package/src/cards/CardManager.ts +274 -0
- package/src/cards/LithicCardAdapter.ts +159 -0
- package/src/cards/MccCategoryMap.ts +138 -0
- package/src/cards/types.ts +169 -0
- package/src/chains/EvmWalletAdapter.ts +170 -0
- package/src/chains/SolanaWalletAdapter.ts +184 -0
- package/src/index.ts +315 -0
- package/src/tools/CardSpendingSummaryTool.ts +52 -0
- package/src/tools/CardStatusTool.ts +68 -0
- package/src/tools/CheckBalanceTool.ts +145 -0
- package/src/tools/FreezeCardTool.ts +52 -0
- package/src/tools/IssueCardTool.ts +76 -0
- package/src/tools/PayWithCardTool.ts +109 -0
- package/src/tools/SendCryptoTool.ts +185 -0
- package/src/tools/UnfreezeCardTool.ts +52 -0
- package/src/tools/WalletHistoryTool.ts +113 -0
- package/src/types.ts +230 -0
- package/test/AgentWalletManager.spec.ts +301 -0
- package/test/CardManager.spec.ts +428 -0
- package/test/LithicCardAdapter.spec.ts +224 -0
- package/test/MccCategoryMap.spec.ts +153 -0
- package/test/SpendingPolicyEnforcer.spec.ts +254 -0
- package/test/card-integration.spec.ts +242 -0
- package/test/card-tools.spec.ts +285 -0
- package/test/integration.spec.ts +171 -0
- package/test/wallet-tools.spec.ts +275 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Agent-callable tool for issuing a virtual card.
|
|
4
|
+
* @module wallet/tools/IssueCardTool
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ITool, ToolExecutionContext, ToolExecutionResult } from '@framers/agentos/core/tools/ITool';
|
|
8
|
+
import type { CardManager } from '../cards/CardManager.js';
|
|
9
|
+
|
|
10
|
+
interface IssueCardInput {
|
|
11
|
+
/** Optional label for the card. */
|
|
12
|
+
memo?: string;
|
|
13
|
+
/** Monthly spending limit in USD. Uses default if omitted. */
|
|
14
|
+
spendLimitUsd?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface IssueCardOutput {
|
|
18
|
+
cardId: string;
|
|
19
|
+
last4: string;
|
|
20
|
+
state: string;
|
|
21
|
+
spendLimitUsd: number;
|
|
22
|
+
network: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createIssueCardTool(cardManager: CardManager): ITool<IssueCardInput, IssueCardOutput> {
|
|
26
|
+
return {
|
|
27
|
+
id: 'wallet-issue-card-v1',
|
|
28
|
+
name: 'issue_virtual_card',
|
|
29
|
+
displayName: 'Issue Virtual Card',
|
|
30
|
+
description:
|
|
31
|
+
'Issue a new virtual debit card for the agent. The card can be used for online purchases, '
|
|
32
|
+
+ 'subscriptions, and API payments. Spending limits and category restrictions from the wallet '
|
|
33
|
+
+ 'spending policy are automatically enforced at the card network level.',
|
|
34
|
+
category: 'wallet',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
hasSideEffects: true,
|
|
37
|
+
requiredCapabilities: ['capability:wallet', 'capability:wallet_card'],
|
|
38
|
+
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
memo: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Optional label for the card (e.g. "API subscriptions").',
|
|
45
|
+
},
|
|
46
|
+
spendLimitUsd: {
|
|
47
|
+
type: 'number',
|
|
48
|
+
description: 'Monthly spending limit in USD. Uses the default from config if omitted.',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async execute(args: IssueCardInput, context: ToolExecutionContext): Promise<ToolExecutionResult<IssueCardOutput>> {
|
|
54
|
+
try {
|
|
55
|
+
const agentId = context.gmiId;
|
|
56
|
+
const card = await cardManager.issueCard(agentId, {
|
|
57
|
+
memo: args.memo,
|
|
58
|
+
spendLimitUsd: args.spendLimitUsd,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
output: {
|
|
64
|
+
cardId: card.id,
|
|
65
|
+
last4: card.last4,
|
|
66
|
+
state: card.state,
|
|
67
|
+
spendLimitUsd: card.spendLimitUsd,
|
|
68
|
+
network: card.network,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
} catch (err: any) {
|
|
72
|
+
return { success: false, error: err.message || String(err) };
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Agent-callable tool for retrieving card details for online payments.
|
|
4
|
+
*
|
|
5
|
+
* Returns sensitive card details (PAN, CVV, expiry) so the agent can fill
|
|
6
|
+
* online checkout forms. Enforces spending policy before revealing details.
|
|
7
|
+
*
|
|
8
|
+
* @module wallet/tools/PayWithCardTool
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ITool, ToolExecutionContext, ToolExecutionResult } from '@framers/agentos/core/tools/ITool';
|
|
12
|
+
import type { CardManager } from '../cards/CardManager.js';
|
|
13
|
+
import type { SpendCategory } from '../types.js';
|
|
14
|
+
|
|
15
|
+
interface PayWithCardInput {
|
|
16
|
+
/** Estimated transaction amount in USD. Used for spending policy pre-check. */
|
|
17
|
+
estimatedAmountUsd?: number;
|
|
18
|
+
/** Spending category for budget tracking. Defaults to 'shopping'. */
|
|
19
|
+
category?: SpendCategory;
|
|
20
|
+
/** Description of what the payment is for. */
|
|
21
|
+
description?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface PayWithCardOutput {
|
|
25
|
+
last4: string;
|
|
26
|
+
pan: string;
|
|
27
|
+
cvv: string;
|
|
28
|
+
expMonth: string;
|
|
29
|
+
expYear: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createPayWithCardTool(cardManager: CardManager): ITool<PayWithCardInput, PayWithCardOutput> {
|
|
33
|
+
return {
|
|
34
|
+
id: 'wallet-pay-with-card-v1',
|
|
35
|
+
name: 'pay_with_card',
|
|
36
|
+
displayName: 'Pay With Card',
|
|
37
|
+
description:
|
|
38
|
+
'Retrieve the agent\'s virtual card details (card number, CVV, expiry) to make an online payment. '
|
|
39
|
+
+ 'If an estimated amount is provided, spending policy is checked before revealing card details. '
|
|
40
|
+
+ 'Use this when filling checkout forms on websites or paying for API services.',
|
|
41
|
+
category: 'wallet',
|
|
42
|
+
version: '1.0.0',
|
|
43
|
+
hasSideEffects: true,
|
|
44
|
+
requiredCapabilities: ['capability:wallet', 'capability:wallet_card', 'capability:wallet_card_pay'],
|
|
45
|
+
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
estimatedAmountUsd: {
|
|
50
|
+
type: 'number',
|
|
51
|
+
description: 'Estimated transaction amount in USD for spending policy pre-check.',
|
|
52
|
+
},
|
|
53
|
+
category: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
enum: [
|
|
56
|
+
'api_costs', 'web_services', 'shopping', 'subscriptions',
|
|
57
|
+
'transfers', 'defi', 'dining', 'travel', 'entertainment',
|
|
58
|
+
'utilities', 'other',
|
|
59
|
+
],
|
|
60
|
+
description: 'Spending category for budget tracking. Defaults to "shopping".',
|
|
61
|
+
},
|
|
62
|
+
description: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'What the payment is for (logged for audit trail).',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async execute(args: PayWithCardInput, context: ToolExecutionContext): Promise<ToolExecutionResult<PayWithCardOutput>> {
|
|
70
|
+
try {
|
|
71
|
+
const agentId = context.gmiId;
|
|
72
|
+
const category: SpendCategory = args.category || 'shopping';
|
|
73
|
+
|
|
74
|
+
const details = await cardManager.getCardDetails(
|
|
75
|
+
agentId,
|
|
76
|
+
args.estimatedAmountUsd,
|
|
77
|
+
category,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!details.pan) {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
error: 'Card details not available. The card provider may not support PAN retrieval in this environment.',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
output: {
|
|
90
|
+
last4: details.last4,
|
|
91
|
+
pan: details.pan,
|
|
92
|
+
cvv: details.cvv,
|
|
93
|
+
expMonth: details.expMonth,
|
|
94
|
+
expYear: details.expYear,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
} catch (err: any) {
|
|
98
|
+
if (err.name === 'CardSpendingBlockedError') {
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: err.message,
|
|
102
|
+
details: { policyViolation: true },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { success: false, error: err.message || String(err) };
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Agent-callable tool for sending crypto with spending policy enforcement.
|
|
4
|
+
* @module wunderland/wallet/tools/SendCryptoTool
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ITool, ToolExecutionContext, ToolExecutionResult } from '@framers/agentos/core/tools/ITool';
|
|
8
|
+
import type { AgentWalletManager } from '../AgentWalletManager.js';
|
|
9
|
+
import type { ChainId, SpendCategory, TokenSymbol } from '../types.js';
|
|
10
|
+
|
|
11
|
+
interface SendCryptoInput {
|
|
12
|
+
/** Destination address. */
|
|
13
|
+
to: string;
|
|
14
|
+
/** Amount in human-readable units (e.g. "0.5" SOL, "10" USDC). */
|
|
15
|
+
amount: string;
|
|
16
|
+
/** Chain to send on. Defaults to 'solana'. */
|
|
17
|
+
chain?: ChainId;
|
|
18
|
+
/** Token to send. Defaults to native (SOL/ETH). */
|
|
19
|
+
token?: TokenSymbol;
|
|
20
|
+
/** Spending category for budget tracking. Defaults to 'transfers'. */
|
|
21
|
+
category?: SpendCategory;
|
|
22
|
+
/** Optional description for the transaction log. */
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface SendCryptoOutput {
|
|
27
|
+
txHash: string;
|
|
28
|
+
chain: ChainId;
|
|
29
|
+
fromAddress: string;
|
|
30
|
+
toAddress: string;
|
|
31
|
+
amount: string;
|
|
32
|
+
token: string;
|
|
33
|
+
status: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const DECIMALS: Record<string, number> = {
|
|
37
|
+
SOL: 9,
|
|
38
|
+
ETH: 18,
|
|
39
|
+
USDC: 6,
|
|
40
|
+
USDT: 6,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const DEFAULT_REFERENCE_USD_PRICES: Record<TokenSymbol, number> = {
|
|
44
|
+
SOL: 1,
|
|
45
|
+
ETH: 1,
|
|
46
|
+
USDC: 1,
|
|
47
|
+
USDT: 1,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function getReferenceUsdPrice(token: TokenSymbol): number {
|
|
51
|
+
const envKey = `WALLET_PRICE_${token}_USD`;
|
|
52
|
+
const override = Number.parseFloat(process.env[envKey] || '');
|
|
53
|
+
if (Number.isFinite(override) && override > 0) {
|
|
54
|
+
return override;
|
|
55
|
+
}
|
|
56
|
+
return DEFAULT_REFERENCE_USD_PRICES[token] ?? 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseAmountToRaw(amount: string, decimals: number): bigint {
|
|
60
|
+
const parts = amount.split('.');
|
|
61
|
+
const intPart = parts[0] || '0';
|
|
62
|
+
const fracPart = (parts[1] || '').padEnd(decimals, '0').slice(0, decimals);
|
|
63
|
+
return BigInt(intPart) * 10n ** BigInt(decimals) + BigInt(fracPart);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createSendCryptoTool(walletManager: AgentWalletManager): ITool<SendCryptoInput, SendCryptoOutput> {
|
|
67
|
+
return {
|
|
68
|
+
id: 'wallet-send-crypto-v1',
|
|
69
|
+
name: 'send_crypto',
|
|
70
|
+
displayName: 'Send Crypto',
|
|
71
|
+
description:
|
|
72
|
+
'Send cryptocurrency from the agent\'s personal wallet to a destination address. '
|
|
73
|
+
+ 'Enforces spending limits, category budgets, and address whitelists configured by the user. '
|
|
74
|
+
+ 'Transactions above the approval threshold require explicit human confirmation. '
|
|
75
|
+
+ 'Supports SOL, ETH (on Ethereum/Base), USDC, and USDT.',
|
|
76
|
+
category: 'wallet',
|
|
77
|
+
version: '1.0.0',
|
|
78
|
+
hasSideEffects: true,
|
|
79
|
+
requiredCapabilities: ['capability:wallet', 'capability:wallet_send'],
|
|
80
|
+
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
to: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'Destination wallet address.',
|
|
87
|
+
},
|
|
88
|
+
amount: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Amount to send in human-readable units (e.g. "0.5" for 0.5 SOL).',
|
|
91
|
+
},
|
|
92
|
+
chain: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
enum: ['solana', 'ethereum', 'base', 'polygon'],
|
|
95
|
+
description: 'Blockchain to use. Defaults to solana.',
|
|
96
|
+
},
|
|
97
|
+
token: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
enum: ['SOL', 'ETH', 'USDC', 'USDT'],
|
|
100
|
+
description: 'Token to send. Defaults to native token (SOL/ETH).',
|
|
101
|
+
},
|
|
102
|
+
category: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
enum: [
|
|
105
|
+
'api_costs', 'web_services', 'shopping', 'subscriptions',
|
|
106
|
+
'transfers', 'defi', 'dining', 'travel', 'entertainment',
|
|
107
|
+
'utilities', 'other',
|
|
108
|
+
],
|
|
109
|
+
description: 'Spending category for budget tracking. Defaults to "transfers".',
|
|
110
|
+
},
|
|
111
|
+
description: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
description: 'Optional note for the transaction log.',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
required: ['to', 'amount'],
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async execute(args: SendCryptoInput, context: ToolExecutionContext): Promise<ToolExecutionResult<SendCryptoOutput>> {
|
|
120
|
+
try {
|
|
121
|
+
const chain: ChainId = args.chain || 'solana';
|
|
122
|
+
const nativeToken: TokenSymbol = chain === 'solana' ? 'SOL' : 'ETH';
|
|
123
|
+
const token: TokenSymbol = args.token || nativeToken;
|
|
124
|
+
const category: SpendCategory = args.category || 'transfers';
|
|
125
|
+
const agentId = context.gmiId;
|
|
126
|
+
const decimals = DECIMALS[token] || 18;
|
|
127
|
+
|
|
128
|
+
// Validate amount
|
|
129
|
+
const amountNum = parseFloat(args.amount);
|
|
130
|
+
if (isNaN(amountNum) || amountNum <= 0) {
|
|
131
|
+
return { success: false, error: `Invalid amount: "${args.amount}". Must be a positive number.` };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const amountRaw = parseAmountToRaw(args.amount, decimals);
|
|
135
|
+
|
|
136
|
+
// Use configurable reference prices until a live oracle/provider is wired.
|
|
137
|
+
const estimatedUsd = amountNum * getReferenceUsdPrice(token);
|
|
138
|
+
|
|
139
|
+
// Only native sends are implemented in Phase 1
|
|
140
|
+
if (token !== nativeToken) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
error: `Token transfers (${token}) are not yet supported. Only native ${nativeToken} sends are available in this version.`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const txRecord = await walletManager.sendNative(
|
|
148
|
+
agentId,
|
|
149
|
+
chain,
|
|
150
|
+
args.to,
|
|
151
|
+
amountRaw,
|
|
152
|
+
estimatedUsd,
|
|
153
|
+
category,
|
|
154
|
+
args.description,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
output: {
|
|
160
|
+
txHash: txRecord.txHash || '',
|
|
161
|
+
chain,
|
|
162
|
+
fromAddress: txRecord.fromAddress || '',
|
|
163
|
+
toAddress: args.to,
|
|
164
|
+
amount: args.amount,
|
|
165
|
+
token,
|
|
166
|
+
status: txRecord.status,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
} catch (err: any) {
|
|
170
|
+
// Surface spending policy violations cleanly
|
|
171
|
+
if (err.name === 'SpendingPolicyViolation' || err.name === 'ApprovalRequiredError') {
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
error: err.message,
|
|
175
|
+
details: {
|
|
176
|
+
policyViolation: true,
|
|
177
|
+
requiresApproval: err.name === 'ApprovalRequiredError',
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return { success: false, error: err.message || String(err) };
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Agent-callable tool for unfreezing (resuming) a card.
|
|
4
|
+
* @module wallet/tools/UnfreezeCardTool
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ITool, ToolExecutionContext, ToolExecutionResult } from '@framers/agentos/core/tools/ITool';
|
|
8
|
+
import type { CardManager } from '../cards/CardManager.js';
|
|
9
|
+
|
|
10
|
+
interface UnfreezeCardInput {}
|
|
11
|
+
|
|
12
|
+
interface UnfreezeCardOutput {
|
|
13
|
+
state: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createUnfreezeCardTool(cardManager: CardManager): ITool<UnfreezeCardInput, UnfreezeCardOutput> {
|
|
18
|
+
return {
|
|
19
|
+
id: 'wallet-unfreeze-card-v1',
|
|
20
|
+
name: 'unfreeze_card',
|
|
21
|
+
displayName: 'Unfreeze Card',
|
|
22
|
+
description:
|
|
23
|
+
'Unfreeze the agent\'s virtual card, resuming normal transaction processing. '
|
|
24
|
+
+ 'The card must be in a PAUSED state.',
|
|
25
|
+
category: 'wallet',
|
|
26
|
+
version: '1.0.0',
|
|
27
|
+
hasSideEffects: true,
|
|
28
|
+
requiredCapabilities: ['capability:wallet', 'capability:wallet_card'],
|
|
29
|
+
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {},
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
async execute(_args: UnfreezeCardInput, context: ToolExecutionContext): Promise<ToolExecutionResult<UnfreezeCardOutput>> {
|
|
36
|
+
try {
|
|
37
|
+
const agentId = context.gmiId;
|
|
38
|
+
await cardManager.unfreezeCard(agentId);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
output: {
|
|
43
|
+
state: 'OPEN',
|
|
44
|
+
message: 'Card has been unfrozen. Transactions are now accepted.',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
} catch (err: any) {
|
|
48
|
+
return { success: false, error: err.message || String(err) };
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Agent-callable tool for viewing wallet transaction history.
|
|
4
|
+
* @module wunderland/wallet/tools/WalletHistoryTool
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ITool, ToolExecutionContext, ToolExecutionResult } from '@framers/agentos/core/tools/ITool';
|
|
8
|
+
import type { AgentWalletManager } from '../AgentWalletManager.js';
|
|
9
|
+
import type { ChainId, WalletTransactionRecord } from '../types.js';
|
|
10
|
+
|
|
11
|
+
interface WalletHistoryInput {
|
|
12
|
+
/** Chain to query. Defaults to 'solana'. */
|
|
13
|
+
chain?: ChainId;
|
|
14
|
+
/** Max number of transactions to return. Defaults to 10. */
|
|
15
|
+
limit?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TransactionSummary {
|
|
19
|
+
id: string;
|
|
20
|
+
txHash?: string;
|
|
21
|
+
direction: string;
|
|
22
|
+
to?: string;
|
|
23
|
+
from?: string;
|
|
24
|
+
amount: string;
|
|
25
|
+
token: string;
|
|
26
|
+
category?: string;
|
|
27
|
+
status: string;
|
|
28
|
+
date: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface WalletHistoryOutput {
|
|
33
|
+
chain: ChainId;
|
|
34
|
+
address: string;
|
|
35
|
+
transactions: TransactionSummary[];
|
|
36
|
+
totalCount: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createWalletHistoryTool(walletManager: AgentWalletManager): ITool<WalletHistoryInput, WalletHistoryOutput> {
|
|
40
|
+
return {
|
|
41
|
+
id: 'wallet-transaction-history-v1',
|
|
42
|
+
name: 'wallet_transaction_history',
|
|
43
|
+
displayName: 'Wallet Transaction History',
|
|
44
|
+
description:
|
|
45
|
+
'View recent transaction history for the agent\'s personal wallet. '
|
|
46
|
+
+ 'Shows inbound and outbound transfers with amounts, status, and categories.',
|
|
47
|
+
category: 'wallet',
|
|
48
|
+
version: '1.0.0',
|
|
49
|
+
hasSideEffects: false,
|
|
50
|
+
requiredCapabilities: ['capability:wallet'],
|
|
51
|
+
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
chain: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
enum: ['solana', 'ethereum', 'base', 'polygon'],
|
|
58
|
+
description: 'Blockchain to query. Defaults to solana.',
|
|
59
|
+
},
|
|
60
|
+
limit: {
|
|
61
|
+
type: 'integer',
|
|
62
|
+
minimum: 1,
|
|
63
|
+
maximum: 50,
|
|
64
|
+
description: 'Max number of transactions to return. Defaults to 10.',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async execute(args: WalletHistoryInput, context: ToolExecutionContext): Promise<ToolExecutionResult<WalletHistoryOutput>> {
|
|
70
|
+
try {
|
|
71
|
+
const chain: ChainId = args.chain || 'solana';
|
|
72
|
+
const limit = args.limit || 10;
|
|
73
|
+
const agentId = context.gmiId;
|
|
74
|
+
|
|
75
|
+
const wallet = await walletManager.getWallet(agentId, chain);
|
|
76
|
+
if (!wallet) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: `No ${chain} wallet found for this agent.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const records = await walletManager.getTransactionHistory(agentId, chain, limit);
|
|
84
|
+
|
|
85
|
+
const transactions: TransactionSummary[] = records.map((tx: WalletTransactionRecord) => ({
|
|
86
|
+
id: tx.id,
|
|
87
|
+
txHash: tx.txHash,
|
|
88
|
+
direction: tx.direction,
|
|
89
|
+
to: tx.toAddress,
|
|
90
|
+
from: tx.fromAddress,
|
|
91
|
+
amount: tx.amountUsd != null ? `$${tx.amountUsd.toFixed(2)}` : tx.amountRaw,
|
|
92
|
+
token: tx.token,
|
|
93
|
+
category: tx.category,
|
|
94
|
+
status: tx.status,
|
|
95
|
+
date: new Date(tx.createdAt).toISOString(),
|
|
96
|
+
description: tx.description,
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
output: {
|
|
102
|
+
chain,
|
|
103
|
+
address: wallet.address,
|
|
104
|
+
transactions,
|
|
105
|
+
totalCount: transactions.length,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
} catch (err: any) {
|
|
109
|
+
return { success: false, error: err.message || String(err) };
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|