@coinbase/agentkit 0.1.2 → 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/README.md +319 -5
- package/dist/action-providers/erc20/schemas.d.ts +2 -2
- package/dist/action-providers/index.d.ts +8 -7
- package/dist/action-providers/index.js +8 -7
- package/dist/action-providers/morpho/schemas.d.ts +4 -4
- package/dist/action-providers/spl/index.d.ts +1 -0
- package/dist/action-providers/spl/index.js +17 -0
- package/dist/action-providers/spl/schemas.d.ts +17 -0
- package/dist/action-providers/spl/schemas.js +14 -0
- package/dist/action-providers/spl/splActionProvider.d.ts +37 -0
- package/dist/action-providers/spl/splActionProvider.js +112 -0
- package/dist/action-providers/spl/splActionProvider.test.d.ts +1 -0
- package/dist/action-providers/spl/splActionProvider.test.js +206 -0
- package/dist/action-providers/wallet/walletActionProvider.d.ts +1 -1
- package/dist/action-providers/wallet/walletActionProvider.js +31 -18
- package/dist/action-providers/wallet/walletActionProvider.test.js +101 -23
- package/dist/network/index.d.ts +2 -1
- package/dist/network/index.js +2 -1
- package/dist/network/network.d.ts +7 -0
- package/dist/network/network.js +46 -1
- package/dist/network/svm.d.ts +14 -0
- package/dist/network/svm.js +33 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +14 -0
- package/dist/wallet-providers/cdpWalletProvider.d.ts +13 -0
- package/dist/wallet-providers/cdpWalletProvider.js +14 -7
- package/dist/wallet-providers/index.d.ts +3 -0
- package/dist/wallet-providers/index.js +3 -0
- package/dist/wallet-providers/privyWalletProvider.d.ts +70 -0
- package/dist/wallet-providers/privyWalletProvider.js +139 -0
- package/dist/wallet-providers/solanaKeypairWalletProvider.d.ts +143 -0
- package/dist/wallet-providers/solanaKeypairWalletProvider.js +280 -0
- package/dist/wallet-providers/solanaKeypairWalletProvider.test.d.ts +1 -0
- package/dist/wallet-providers/solanaKeypairWalletProvider.test.js +24 -0
- package/dist/wallet-providers/svmWalletProvider.d.ts +56 -0
- package/dist/wallet-providers/svmWalletProvider.js +13 -0
- package/dist/wallet-providers/viemWalletProvider.d.ts +15 -1
- package/dist/wallet-providers/viemWalletProvider.js +22 -3
- package/dist/wallet-providers/walletProvider.d.ts +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.splActionProvider = exports.SplActionProvider = void 0;
|
|
13
|
+
const actionProvider_1 = require("../actionProvider");
|
|
14
|
+
const svmWalletProvider_1 = require("../../wallet-providers/svmWalletProvider");
|
|
15
|
+
const zod_1 = require("zod");
|
|
16
|
+
const actionDecorator_1 = require("../actionDecorator");
|
|
17
|
+
const schemas_1 = require("./schemas");
|
|
18
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
19
|
+
/**
|
|
20
|
+
* SplActionProvider serves as a provider for SPL token actions.
|
|
21
|
+
* It provides SPL token transfer functionality.
|
|
22
|
+
*/
|
|
23
|
+
class SplActionProvider extends actionProvider_1.ActionProvider {
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new SplActionProvider instance.
|
|
26
|
+
*/
|
|
27
|
+
constructor() {
|
|
28
|
+
super("spl", []);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Transfer SPL tokens to another address.
|
|
32
|
+
*
|
|
33
|
+
* @param walletProvider - The wallet provider to use for the transfer
|
|
34
|
+
* @param args - Transfer parameters including recipient address, mint address, and amount
|
|
35
|
+
* @returns A message indicating success or failure with transaction details
|
|
36
|
+
*/
|
|
37
|
+
async transfer(walletProvider, args) {
|
|
38
|
+
try {
|
|
39
|
+
const connection = walletProvider.getConnection();
|
|
40
|
+
const fromPubkey = walletProvider.getPublicKey();
|
|
41
|
+
const toPubkey = new web3_js_1.PublicKey(args.recipient);
|
|
42
|
+
const mintPubkey = new web3_js_1.PublicKey(args.mintAddress);
|
|
43
|
+
const { getMint, getAssociatedTokenAddress, getAccount, createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, } = await import("@solana/spl-token");
|
|
44
|
+
const mintInfo = await getMint(connection, mintPubkey);
|
|
45
|
+
const adjustedAmount = args.amount * Math.pow(10, mintInfo.decimals);
|
|
46
|
+
const sourceAta = await getAssociatedTokenAddress(mintPubkey, fromPubkey);
|
|
47
|
+
const destinationAta = await getAssociatedTokenAddress(mintPubkey, toPubkey);
|
|
48
|
+
const instructions = [];
|
|
49
|
+
const sourceAccount = await getAccount(connection, sourceAta);
|
|
50
|
+
if (sourceAccount.amount < BigInt(adjustedAmount)) {
|
|
51
|
+
throw new Error(`Insufficient token balance. Have ${sourceAccount.amount}, need ${adjustedAmount}`);
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
await getAccount(connection, destinationAta);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
instructions.push(createAssociatedTokenAccountInstruction(fromPubkey, destinationAta, toPubkey, mintPubkey));
|
|
58
|
+
}
|
|
59
|
+
instructions.push(createTransferCheckedInstruction(sourceAta, mintPubkey, destinationAta, fromPubkey, adjustedAmount, mintInfo.decimals));
|
|
60
|
+
const tx = new web3_js_1.VersionedTransaction(web3_js_1.MessageV0.compile({
|
|
61
|
+
payerKey: fromPubkey,
|
|
62
|
+
instructions: instructions,
|
|
63
|
+
recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
|
|
64
|
+
}));
|
|
65
|
+
const signature = await walletProvider.signAndSendTransaction(tx);
|
|
66
|
+
await walletProvider.waitForSignatureResult(signature);
|
|
67
|
+
return [
|
|
68
|
+
`Successfully transferred ${args.amount} tokens to ${args.recipient}`,
|
|
69
|
+
`Token mint: ${args.mintAddress}`,
|
|
70
|
+
`Signature: ${signature}`,
|
|
71
|
+
].join("\n");
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return `Error transferring SPL tokens: ${error}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Checks if the action provider supports the given network.
|
|
79
|
+
* Only supports Solana networks.
|
|
80
|
+
*
|
|
81
|
+
* @param network - The network to check support for
|
|
82
|
+
* @returns True if the network is a Solana network
|
|
83
|
+
*/
|
|
84
|
+
supportsNetwork(network) {
|
|
85
|
+
return network.protocolFamily === "svm";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.SplActionProvider = SplActionProvider;
|
|
89
|
+
__decorate([
|
|
90
|
+
(0, actionDecorator_1.CreateAction)({
|
|
91
|
+
name: "transfer",
|
|
92
|
+
description: `
|
|
93
|
+
This tool will transfer SPL tokens to another address.
|
|
94
|
+
- Amount should be specified in token units (not raw)
|
|
95
|
+
- Recipient must be a valid Solana address
|
|
96
|
+
- Mint address must be a valid SPL token mint
|
|
97
|
+
- Ensures sufficient token balance before transfer
|
|
98
|
+
- Returns transaction details
|
|
99
|
+
`,
|
|
100
|
+
schema: schemas_1.TransferTokenSchema,
|
|
101
|
+
}),
|
|
102
|
+
__metadata("design:type", Function),
|
|
103
|
+
__metadata("design:paramtypes", [svmWalletProvider_1.SvmWalletProvider, void 0]),
|
|
104
|
+
__metadata("design:returntype", Promise)
|
|
105
|
+
], SplActionProvider.prototype, "transfer", null);
|
|
106
|
+
/**
|
|
107
|
+
* Factory function to create a new SplActionProvider instance.
|
|
108
|
+
*
|
|
109
|
+
* @returns A new SplActionProvider instance
|
|
110
|
+
*/
|
|
111
|
+
const splActionProvider = () => new SplActionProvider();
|
|
112
|
+
exports.splActionProvider = splActionProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
4
|
+
const splActionProvider_1 = require("./splActionProvider");
|
|
5
|
+
jest.mock("@solana/web3.js", () => ({
|
|
6
|
+
...jest.requireActual("@solana/web3.js"),
|
|
7
|
+
Connection: jest.fn(),
|
|
8
|
+
SendTransactionError: jest.fn().mockReturnValue({
|
|
9
|
+
message: "Failed to send",
|
|
10
|
+
toString: () => "Failed to send",
|
|
11
|
+
}),
|
|
12
|
+
VersionedTransaction: jest.fn().mockReturnValue({
|
|
13
|
+
sign: jest.fn(),
|
|
14
|
+
}),
|
|
15
|
+
MessageV0: {
|
|
16
|
+
compile: jest.fn().mockReturnValue({}),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
jest.mock("@solana/spl-token", () => ({
|
|
20
|
+
getAssociatedTokenAddress: jest.fn(),
|
|
21
|
+
getMint: jest.fn(),
|
|
22
|
+
getAccount: jest.fn(),
|
|
23
|
+
createAssociatedTokenAccountInstruction: jest.fn(),
|
|
24
|
+
createTransferCheckedInstruction: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
jest.mock("../../wallet-providers/svmWalletProvider");
|
|
27
|
+
describe("SplActionProvider", () => {
|
|
28
|
+
let actionProvider;
|
|
29
|
+
let mockWallet;
|
|
30
|
+
let mockConnection;
|
|
31
|
+
let mockGetAssociatedTokenAddress;
|
|
32
|
+
let mockGetMint;
|
|
33
|
+
let mockGetAccount;
|
|
34
|
+
/**
|
|
35
|
+
* Set up test environment before each test.
|
|
36
|
+
* Initializes mocks and creates fresh instances of required objects.
|
|
37
|
+
*/
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
const mocked = jest.requireMock("@solana/spl-token");
|
|
41
|
+
mockGetAssociatedTokenAddress = mocked.getAssociatedTokenAddress;
|
|
42
|
+
mockGetMint = mocked.getMint;
|
|
43
|
+
mockGetAccount = mocked.getAccount;
|
|
44
|
+
mockGetMint.mockResolvedValue({ decimals: 6 });
|
|
45
|
+
mockGetAccount.mockRejectedValue(new Error("getAccount mock not implemented for this test"));
|
|
46
|
+
actionProvider = new splActionProvider_1.SplActionProvider();
|
|
47
|
+
mockConnection = {
|
|
48
|
+
getLatestBlockhash: jest.fn().mockResolvedValue({ blockhash: "mockedBlockhash" }),
|
|
49
|
+
};
|
|
50
|
+
const MOCK_SIGNATURE = "mock-signature";
|
|
51
|
+
const mockSignatureReceipt = {
|
|
52
|
+
context: { slot: 1234 },
|
|
53
|
+
value: { err: null },
|
|
54
|
+
};
|
|
55
|
+
mockWallet = {
|
|
56
|
+
getConnection: jest.fn().mockReturnValue(mockConnection),
|
|
57
|
+
getPublicKey: jest.fn().mockReturnValue(new web3_js_1.PublicKey("11111111111111111111111111111111")),
|
|
58
|
+
signAndSendTransaction: jest.fn().mockResolvedValue(MOCK_SIGNATURE),
|
|
59
|
+
waitForSignatureResult: jest.fn().mockResolvedValue(mockSignatureReceipt),
|
|
60
|
+
getAddress: jest.fn().mockReturnValue("11111111111111111111111111111111"),
|
|
61
|
+
getNetwork: jest.fn().mockReturnValue({ protocolFamily: "svm", networkId: "mainnet" }),
|
|
62
|
+
getName: jest.fn().mockReturnValue("mock-wallet"),
|
|
63
|
+
getBalance: jest.fn().mockResolvedValue(BigInt(1000000000)),
|
|
64
|
+
nativeTransfer: jest.fn(),
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
describe("constructor", () => {
|
|
68
|
+
/**
|
|
69
|
+
* Test that the SPL action provider is created with the correct name.
|
|
70
|
+
*/
|
|
71
|
+
it("should create a provider with correct name", () => {
|
|
72
|
+
expect(actionProvider["name"]).toBe("spl");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe("supportsNetwork", () => {
|
|
76
|
+
/**
|
|
77
|
+
* Test that the provider correctly identifies Solana networks as supported.
|
|
78
|
+
*/
|
|
79
|
+
it("should return true for Solana networks", () => {
|
|
80
|
+
const network = {
|
|
81
|
+
protocolFamily: "svm",
|
|
82
|
+
networkId: "solana-mainnet",
|
|
83
|
+
};
|
|
84
|
+
expect(actionProvider.supportsNetwork(network)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Test that the provider correctly identifies non-Solana networks as unsupported.
|
|
88
|
+
*/
|
|
89
|
+
it("should return false for non-Solana networks", () => {
|
|
90
|
+
const network = {
|
|
91
|
+
protocolFamily: "evm",
|
|
92
|
+
networkId: "ethereum-mainnet",
|
|
93
|
+
};
|
|
94
|
+
expect(actionProvider.supportsNetwork(network)).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe("transfer", () => {
|
|
98
|
+
const MINT_ADDRESS = "So11111111111111111111111111111111111111112";
|
|
99
|
+
const RECIPIENT_ADDRESS = "DjXsn34uz8yCBQ8bevLrEPYYC1RvhHvjzuVF8opNc4K2";
|
|
100
|
+
const SENDER_ADDRESS = "11111111111111111111111111111111";
|
|
101
|
+
const MOCK_SIGNATURE = "mock-signature";
|
|
102
|
+
const transferArgs = {
|
|
103
|
+
recipient: RECIPIENT_ADDRESS,
|
|
104
|
+
mintAddress: MINT_ADDRESS,
|
|
105
|
+
amount: 100,
|
|
106
|
+
};
|
|
107
|
+
const mockTokenAccount = {
|
|
108
|
+
amount: BigInt(1000000000),
|
|
109
|
+
address: new web3_js_1.PublicKey(MINT_ADDRESS),
|
|
110
|
+
mint: new web3_js_1.PublicKey(MINT_ADDRESS),
|
|
111
|
+
owner: new web3_js_1.PublicKey(RECIPIENT_ADDRESS),
|
|
112
|
+
delegate: null,
|
|
113
|
+
delegatedAmount: BigInt(0),
|
|
114
|
+
closeAuthority: null,
|
|
115
|
+
isFrozen: false,
|
|
116
|
+
isNative: false,
|
|
117
|
+
rentExemptReserve: null,
|
|
118
|
+
isInitialized: true,
|
|
119
|
+
tlvData: new Map(),
|
|
120
|
+
};
|
|
121
|
+
const mockSignatureReceipt = {
|
|
122
|
+
context: { slot: 1234 },
|
|
123
|
+
value: { err: null },
|
|
124
|
+
};
|
|
125
|
+
beforeEach(() => {
|
|
126
|
+
mockWallet.getPublicKey.mockReturnValue(new web3_js_1.PublicKey(SENDER_ADDRESS));
|
|
127
|
+
mockWallet.getAddress.mockReturnValue(SENDER_ADDRESS);
|
|
128
|
+
mockWallet.signAndSendTransaction.mockResolvedValue(MOCK_SIGNATURE);
|
|
129
|
+
mockWallet.waitForSignatureResult.mockResolvedValue(mockSignatureReceipt);
|
|
130
|
+
});
|
|
131
|
+
/**
|
|
132
|
+
* Test successful SPL token transfer with all required steps:
|
|
133
|
+
* - Account validation
|
|
134
|
+
* - Instruction creation
|
|
135
|
+
* - Signing
|
|
136
|
+
* - Sending
|
|
137
|
+
* - Receipt confirmation
|
|
138
|
+
*/
|
|
139
|
+
it("should successfully transfer SPL tokens", async () => {
|
|
140
|
+
mockGetAccount.mockResolvedValue(mockTokenAccount);
|
|
141
|
+
const result = await actionProvider.transfer(mockWallet, transferArgs);
|
|
142
|
+
expect(mockGetAssociatedTokenAddress).toHaveBeenNthCalledWith(1, new web3_js_1.PublicKey(transferArgs.mintAddress), new web3_js_1.PublicKey(SENDER_ADDRESS));
|
|
143
|
+
expect(mockGetAssociatedTokenAddress).toHaveBeenNthCalledWith(2, new web3_js_1.PublicKey(transferArgs.mintAddress), new web3_js_1.PublicKey(transferArgs.recipient));
|
|
144
|
+
expect(mockGetMint).toHaveBeenCalledWith(mockConnection, new web3_js_1.PublicKey(transferArgs.mintAddress));
|
|
145
|
+
expect(mockGetAccount).toHaveBeenCalled();
|
|
146
|
+
expect(mockWallet.signAndSendTransaction).toHaveBeenCalled();
|
|
147
|
+
expect(mockWallet.waitForSignatureResult).toHaveBeenCalledWith(MOCK_SIGNATURE);
|
|
148
|
+
expect(result).toContain(`Successfully transferred ${transferArgs.amount} tokens`);
|
|
149
|
+
expect(result).toContain(`to ${transferArgs.recipient}`);
|
|
150
|
+
expect(result).toContain(`Token mint: ${transferArgs.mintAddress}`);
|
|
151
|
+
expect(result).toContain(`Signature: ${MOCK_SIGNATURE}`);
|
|
152
|
+
});
|
|
153
|
+
/**
|
|
154
|
+
* Test handling of insufficient balance.
|
|
155
|
+
* Verifies that the provider properly checks token balances and prevents transfers when funds are insufficient.
|
|
156
|
+
*/
|
|
157
|
+
it("should handle insufficient balance", async () => {
|
|
158
|
+
mockGetAccount.mockResolvedValue({
|
|
159
|
+
...mockTokenAccount,
|
|
160
|
+
amount: BigInt(10000000),
|
|
161
|
+
});
|
|
162
|
+
const result = await actionProvider.transfer(mockWallet, transferArgs);
|
|
163
|
+
expect(result).toBe("Error transferring SPL tokens: Error: Insufficient token balance. Have 10000000, need 100000000");
|
|
164
|
+
});
|
|
165
|
+
/**
|
|
166
|
+
* Test handling of Solana-specific send errors.
|
|
167
|
+
* Verifies that the provider properly handles and reports SendTransactionError instances.
|
|
168
|
+
*/
|
|
169
|
+
it("should handle SendTransactionError", async () => {
|
|
170
|
+
mockGetAccount.mockResolvedValue(mockTokenAccount);
|
|
171
|
+
const error = new web3_js_1.SendTransactionError({
|
|
172
|
+
logs: [],
|
|
173
|
+
action: "send",
|
|
174
|
+
signature: "mock-signature",
|
|
175
|
+
transactionMessage: "Failed to send",
|
|
176
|
+
});
|
|
177
|
+
mockWallet.signAndSendTransaction.mockRejectedValue(error);
|
|
178
|
+
const result = await actionProvider.transfer(mockWallet, transferArgs);
|
|
179
|
+
expect(result).toBe("Error transferring SPL tokens: Failed to send");
|
|
180
|
+
});
|
|
181
|
+
/**
|
|
182
|
+
* Test handling of general errors during transfer.
|
|
183
|
+
* Verifies that the provider properly handles and reports unexpected errors.
|
|
184
|
+
*/
|
|
185
|
+
it("should handle regular errors", async () => {
|
|
186
|
+
mockGetAccount.mockResolvedValue(mockTokenAccount);
|
|
187
|
+
const error = new Error("Regular error message");
|
|
188
|
+
mockWallet.signAndSendTransaction.mockRejectedValue(error);
|
|
189
|
+
const result = await actionProvider.transfer(mockWallet, transferArgs);
|
|
190
|
+
expect(result).toBe("Error transferring SPL tokens: Error: Regular error message");
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* Test that ATA is created by default when missing
|
|
194
|
+
*/
|
|
195
|
+
it("should create ATA by default when missing", async () => {
|
|
196
|
+
mockGetAccount
|
|
197
|
+
.mockResolvedValueOnce(mockTokenAccount)
|
|
198
|
+
.mockRejectedValueOnce(new Error("Account does not exist"))
|
|
199
|
+
.mockResolvedValue(mockTokenAccount);
|
|
200
|
+
const result = await actionProvider.transfer(mockWallet, transferArgs);
|
|
201
|
+
const { createAssociatedTokenAccountInstruction } = jest.requireMock("@solana/spl-token");
|
|
202
|
+
expect(createAssociatedTokenAccountInstruction).toHaveBeenCalled();
|
|
203
|
+
expect(result).toContain(`Successfully transferred ${transferArgs.amount} tokens`);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -20,7 +20,7 @@ export declare class WalletActionProvider extends ActionProvider {
|
|
|
20
20
|
*/
|
|
21
21
|
getWalletDetails(walletProvider: WalletProvider, _: z.infer<typeof GetWalletDetailsSchema>): Promise<string>;
|
|
22
22
|
/**
|
|
23
|
-
* Transfers a specified amount of
|
|
23
|
+
* Transfers a specified amount of native currency to a destination onchain.
|
|
24
24
|
*
|
|
25
25
|
* @param walletProvider - The wallet provider to transfer from.
|
|
26
26
|
* @param args - The input arguments for the action.
|
|
@@ -10,12 +10,16 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.walletActionProvider = exports.WalletActionProvider = void 0;
|
|
13
|
-
const decimal_js_1 = require("decimal.js");
|
|
14
13
|
const zod_1 = require("zod");
|
|
15
14
|
const actionDecorator_1 = require("../actionDecorator");
|
|
16
15
|
const actionProvider_1 = require("../actionProvider");
|
|
17
16
|
const wallet_providers_1 = require("../../wallet-providers");
|
|
18
17
|
const schemas_1 = require("./schemas");
|
|
18
|
+
const PROTOCOL_FAMILY_TO_TERMINOLOGY = {
|
|
19
|
+
evm: { unit: "WEI", displayUnit: "ETH", type: "Transaction hash", verb: "transaction" },
|
|
20
|
+
svm: { unit: "LAMPORTS", displayUnit: "SOL", type: "Signature", verb: "transfer" },
|
|
21
|
+
};
|
|
22
|
+
const DEFAULT_TERMINOLOGY = { unit: "", displayUnit: "", type: "Hash", verb: "transfer" };
|
|
19
23
|
/**
|
|
20
24
|
* WalletActionProvider provides actions for getting basic wallet information.
|
|
21
25
|
*/
|
|
@@ -47,24 +51,24 @@ class WalletActionProvider extends actionProvider_1.ActionProvider {
|
|
|
47
51
|
const network = walletProvider.getNetwork();
|
|
48
52
|
const balance = await walletProvider.getBalance();
|
|
49
53
|
const name = walletProvider.getName();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- Network:
|
|
56
|
-
* Protocol Family: ${network.protocolFamily}
|
|
57
|
-
* Network ID: ${network.networkId || "N/A"}
|
|
58
|
-
* Chain ID: ${network.chainId || "N/A"}
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
const terminology = PROTOCOL_FAMILY_TO_TERMINOLOGY[network.protocolFamily] || DEFAULT_TERMINOLOGY;
|
|
55
|
+
return [
|
|
56
|
+
"Wallet Details:",
|
|
57
|
+
`- Provider: ${name}`,
|
|
58
|
+
`- Address: ${address}`,
|
|
59
|
+
"- Network:",
|
|
60
|
+
` * Protocol Family: ${network.protocolFamily}`,
|
|
61
|
+
` * Network ID: ${network.networkId || "N/A"}`,
|
|
62
|
+
` * Chain ID: ${network.chainId || "N/A"}`,
|
|
63
|
+
`- Native Balance: ${balance.toString()} ${terminology.unit}`,
|
|
64
|
+
].join("\n");
|
|
61
65
|
}
|
|
62
66
|
catch (error) {
|
|
63
67
|
return `Error getting wallet details: ${error}`;
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
/**
|
|
67
|
-
* Transfers a specified amount of
|
|
71
|
+
* Transfers a specified amount of native currency to a destination onchain.
|
|
68
72
|
*
|
|
69
73
|
* @param walletProvider - The wallet provider to transfer from.
|
|
70
74
|
* @param args - The input arguments for the action.
|
|
@@ -72,11 +76,21 @@ class WalletActionProvider extends actionProvider_1.ActionProvider {
|
|
|
72
76
|
*/
|
|
73
77
|
async nativeTransfer(walletProvider, args) {
|
|
74
78
|
try {
|
|
79
|
+
const { protocolFamily } = walletProvider.getNetwork();
|
|
80
|
+
const terminology = PROTOCOL_FAMILY_TO_TERMINOLOGY[protocolFamily] || DEFAULT_TERMINOLOGY;
|
|
81
|
+
if (protocolFamily === "evm" && !args.to.startsWith("0x")) {
|
|
82
|
+
args.to = `0x${args.to}`;
|
|
83
|
+
}
|
|
75
84
|
const result = await walletProvider.nativeTransfer(args.to, args.value);
|
|
76
|
-
return
|
|
85
|
+
return [
|
|
86
|
+
`Transferred ${args.value} ${terminology.displayUnit} to ${args.to}`,
|
|
87
|
+
`${terminology.type}: ${result}`,
|
|
88
|
+
].join("\n");
|
|
77
89
|
}
|
|
78
90
|
catch (error) {
|
|
79
|
-
|
|
91
|
+
const { protocolFamily } = walletProvider.getNetwork();
|
|
92
|
+
const terminology = PROTOCOL_FAMILY_TO_TERMINOLOGY[protocolFamily] || DEFAULT_TERMINOLOGY;
|
|
93
|
+
return `Error during ${terminology.verb}: ${error}`;
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
96
|
}
|
|
@@ -88,8 +102,7 @@ __decorate([
|
|
|
88
102
|
This tool will return the details of the connected wallet including:
|
|
89
103
|
- Wallet address
|
|
90
104
|
- Network information (protocol family, network ID, chain ID)
|
|
91
|
-
-
|
|
92
|
-
- Native token balance
|
|
105
|
+
- Native token balance (ETH for EVM networks, SOL for Solana networks)
|
|
93
106
|
- Wallet provider name
|
|
94
107
|
`,
|
|
95
108
|
schema: schemas_1.GetWalletDetailsSchema,
|
|
@@ -105,7 +118,7 @@ __decorate([
|
|
|
105
118
|
This tool will transfer native tokens from the wallet to another onchain address.
|
|
106
119
|
|
|
107
120
|
It takes the following inputs:
|
|
108
|
-
- amount: The amount to transfer in whole units e.g. 1 ETH
|
|
121
|
+
- amount: The amount to transfer in whole units (e.g. 1 ETH, 0.1 SOL)
|
|
109
122
|
- destination: The address to receive the funds
|
|
110
123
|
|
|
111
124
|
Important notes:
|
|
@@ -4,41 +4,82 @@ const walletActionProvider_1 = require("./walletActionProvider");
|
|
|
4
4
|
const schemas_1 = require("./schemas");
|
|
5
5
|
describe("Wallet Action Provider", () => {
|
|
6
6
|
const MOCK_ADDRESS = "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83";
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const MOCK_ETH_BALANCE = 1000000000000000000n;
|
|
8
|
+
const MOCK_SOL_BALANCE = 1000000000n;
|
|
9
|
+
const MOCK_EVM_NETWORK = {
|
|
9
10
|
protocolFamily: "evm",
|
|
10
11
|
networkId: "base-sepolia",
|
|
11
12
|
chainId: "123",
|
|
12
13
|
};
|
|
14
|
+
const MOCK_SOLANA_NETWORK = {
|
|
15
|
+
protocolFamily: "svm",
|
|
16
|
+
networkId: "mainnet",
|
|
17
|
+
};
|
|
18
|
+
const MOCK_UNKNOWN_NETWORK = {
|
|
19
|
+
protocolFamily: "unknown",
|
|
20
|
+
networkId: "testnet",
|
|
21
|
+
};
|
|
13
22
|
const MOCK_PROVIDER_NAME = "TestWallet";
|
|
14
23
|
const MOCK_TRANSACTION_HASH = "0xghijkl987654321";
|
|
24
|
+
const MOCK_SIGNATURE = "mock-signature";
|
|
15
25
|
let mockWallet;
|
|
16
26
|
const actionProvider = (0, walletActionProvider_1.walletActionProvider)();
|
|
17
27
|
beforeEach(() => {
|
|
18
28
|
mockWallet = {
|
|
19
29
|
getAddress: jest.fn().mockReturnValue(MOCK_ADDRESS),
|
|
20
|
-
getNetwork: jest.fn().mockReturnValue(
|
|
21
|
-
getBalance: jest.fn().mockResolvedValue(
|
|
30
|
+
getNetwork: jest.fn().mockReturnValue(MOCK_EVM_NETWORK),
|
|
31
|
+
getBalance: jest.fn().mockResolvedValue(MOCK_ETH_BALANCE),
|
|
22
32
|
getName: jest.fn().mockReturnValue(MOCK_PROVIDER_NAME),
|
|
23
33
|
nativeTransfer: jest.fn().mockResolvedValue(MOCK_TRANSACTION_HASH),
|
|
24
34
|
};
|
|
25
35
|
});
|
|
26
36
|
describe("getWalletDetails", () => {
|
|
27
|
-
it("should
|
|
37
|
+
it("should show WEI balance for EVM networks", async () => {
|
|
38
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_EVM_NETWORK);
|
|
39
|
+
mockWallet.getBalance.mockResolvedValue(MOCK_ETH_BALANCE);
|
|
40
|
+
const response = await actionProvider.getWalletDetails(mockWallet, {});
|
|
41
|
+
const expectedResponse = [
|
|
42
|
+
"Wallet Details:",
|
|
43
|
+
`- Provider: ${MOCK_PROVIDER_NAME}`,
|
|
44
|
+
`- Address: ${MOCK_ADDRESS}`,
|
|
45
|
+
"- Network:",
|
|
46
|
+
` * Protocol Family: ${MOCK_EVM_NETWORK.protocolFamily}`,
|
|
47
|
+
` * Network ID: ${MOCK_EVM_NETWORK.networkId}`,
|
|
48
|
+
` * Chain ID: ${MOCK_EVM_NETWORK.chainId}`,
|
|
49
|
+
`- Native Balance: ${MOCK_ETH_BALANCE.toString()} WEI`,
|
|
50
|
+
].join("\n");
|
|
51
|
+
expect(response).toBe(expectedResponse);
|
|
52
|
+
});
|
|
53
|
+
it("should show LAMPORTS balance for Solana networks", async () => {
|
|
54
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_SOLANA_NETWORK);
|
|
55
|
+
mockWallet.getBalance.mockResolvedValue(MOCK_SOL_BALANCE);
|
|
56
|
+
const response = await actionProvider.getWalletDetails(mockWallet, {});
|
|
57
|
+
const expectedResponse = [
|
|
58
|
+
"Wallet Details:",
|
|
59
|
+
`- Provider: ${MOCK_PROVIDER_NAME}`,
|
|
60
|
+
`- Address: ${MOCK_ADDRESS}`,
|
|
61
|
+
"- Network:",
|
|
62
|
+
` * Protocol Family: ${MOCK_SOLANA_NETWORK.protocolFamily}`,
|
|
63
|
+
` * Network ID: ${MOCK_SOLANA_NETWORK.networkId}`,
|
|
64
|
+
` * Chain ID: N/A`,
|
|
65
|
+
`- Native Balance: ${MOCK_SOL_BALANCE.toString()} LAMPORTS`,
|
|
66
|
+
].join("\n");
|
|
67
|
+
expect(response).toBe(expectedResponse);
|
|
68
|
+
});
|
|
69
|
+
it("should handle unknown protocol families", async () => {
|
|
70
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_UNKNOWN_NETWORK);
|
|
71
|
+
mockWallet.getBalance.mockResolvedValue(MOCK_ETH_BALANCE);
|
|
28
72
|
const response = await actionProvider.getWalletDetails(mockWallet, {});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* Chain ID: ${MOCK_NETWORK.chainId}
|
|
40
|
-
- ETH Balance: 1.000000 ETH
|
|
41
|
-
- Native Balance: ${MOCK_BALANCE.toString()} WEI`;
|
|
73
|
+
const expectedResponse = [
|
|
74
|
+
"Wallet Details:",
|
|
75
|
+
`- Provider: ${MOCK_PROVIDER_NAME}`,
|
|
76
|
+
`- Address: ${MOCK_ADDRESS}`,
|
|
77
|
+
"- Network:",
|
|
78
|
+
` * Protocol Family: ${MOCK_UNKNOWN_NETWORK.protocolFamily}`,
|
|
79
|
+
` * Network ID: ${MOCK_UNKNOWN_NETWORK.networkId}`,
|
|
80
|
+
` * Chain ID: N/A`,
|
|
81
|
+
`- Native Balance: ${MOCK_ETH_BALANCE.toString()} `,
|
|
82
|
+
].join("\n");
|
|
42
83
|
expect(response).toBe(expectedResponse);
|
|
43
84
|
});
|
|
44
85
|
it("should handle missing network IDs gracefully", async () => {
|
|
@@ -48,6 +89,7 @@ describe("Wallet Action Provider", () => {
|
|
|
48
89
|
const response = await actionProvider.getWalletDetails(mockWallet, {});
|
|
49
90
|
expect(response).toContain("Network ID: N/A");
|
|
50
91
|
expect(response).toContain("Chain ID: N/A");
|
|
92
|
+
expect(response).toContain(`Native Balance: ${MOCK_ETH_BALANCE.toString()} WEI`);
|
|
51
93
|
});
|
|
52
94
|
it("should handle errors when getting wallet details", async () => {
|
|
53
95
|
const error = new Error("Failed to get wallet details");
|
|
@@ -57,7 +99,7 @@ describe("Wallet Action Provider", () => {
|
|
|
57
99
|
});
|
|
58
100
|
});
|
|
59
101
|
describe("Native Transfer", () => {
|
|
60
|
-
const MOCK_AMOUNT = "1.5"; // 1.5 ETH
|
|
102
|
+
const MOCK_AMOUNT = "1.5"; // 1.5 ETH/SOL
|
|
61
103
|
const MOCK_DESTINATION = "0x321";
|
|
62
104
|
it("should successfully parse valid input", () => {
|
|
63
105
|
const validInput = {
|
|
@@ -75,16 +117,52 @@ describe("Wallet Action Provider", () => {
|
|
|
75
117
|
const result = schemas_1.NativeTransferSchema.safeParse(emptyInput);
|
|
76
118
|
expect(result.success).toBe(false);
|
|
77
119
|
});
|
|
78
|
-
it("should successfully transfer
|
|
120
|
+
it("should successfully transfer ETH", async () => {
|
|
121
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_EVM_NETWORK);
|
|
122
|
+
mockWallet.nativeTransfer.mockResolvedValue(MOCK_TRANSACTION_HASH);
|
|
79
123
|
const args = {
|
|
80
124
|
to: MOCK_DESTINATION,
|
|
81
125
|
value: MOCK_AMOUNT,
|
|
82
126
|
};
|
|
83
127
|
const response = await actionProvider.nativeTransfer(mockWallet, args);
|
|
84
128
|
expect(mockWallet.nativeTransfer).toHaveBeenCalledWith(MOCK_DESTINATION, MOCK_AMOUNT);
|
|
85
|
-
expect(response).toBe(`Transferred ${MOCK_AMOUNT} ETH to ${MOCK_DESTINATION}
|
|
129
|
+
expect(response).toBe(`Transferred ${MOCK_AMOUNT} ETH to ${MOCK_DESTINATION}\nTransaction hash: ${MOCK_TRANSACTION_HASH}`);
|
|
130
|
+
});
|
|
131
|
+
it("should successfully transfer SOL", async () => {
|
|
132
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_SOLANA_NETWORK);
|
|
133
|
+
mockWallet.nativeTransfer.mockResolvedValue(MOCK_SIGNATURE);
|
|
134
|
+
const args = {
|
|
135
|
+
to: MOCK_DESTINATION,
|
|
136
|
+
value: MOCK_AMOUNT,
|
|
137
|
+
};
|
|
138
|
+
const response = await actionProvider.nativeTransfer(mockWallet, args);
|
|
139
|
+
expect(mockWallet.nativeTransfer).toHaveBeenCalledWith(MOCK_DESTINATION, MOCK_AMOUNT);
|
|
140
|
+
expect(response).toBe(`Transferred ${MOCK_AMOUNT} SOL to ${MOCK_DESTINATION}\nSignature: ${MOCK_SIGNATURE}`);
|
|
141
|
+
});
|
|
142
|
+
it("should handle ETH transfer errors", async () => {
|
|
143
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_EVM_NETWORK);
|
|
144
|
+
const args = {
|
|
145
|
+
to: MOCK_DESTINATION,
|
|
146
|
+
value: MOCK_AMOUNT,
|
|
147
|
+
};
|
|
148
|
+
const error = new Error("Failed to execute transfer");
|
|
149
|
+
mockWallet.nativeTransfer.mockRejectedValue(error);
|
|
150
|
+
const response = await actionProvider.nativeTransfer(mockWallet, args);
|
|
151
|
+
expect(response).toBe(`Error during transaction: ${error}`);
|
|
152
|
+
});
|
|
153
|
+
it("should handle SOL transfer errors", async () => {
|
|
154
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_SOLANA_NETWORK);
|
|
155
|
+
const args = {
|
|
156
|
+
to: MOCK_DESTINATION,
|
|
157
|
+
value: MOCK_AMOUNT,
|
|
158
|
+
};
|
|
159
|
+
const error = new Error("Failed to execute transfer");
|
|
160
|
+
mockWallet.nativeTransfer.mockRejectedValue(error);
|
|
161
|
+
const response = await actionProvider.nativeTransfer(mockWallet, args);
|
|
162
|
+
expect(response).toBe(`Error during transfer: ${error}`);
|
|
86
163
|
});
|
|
87
|
-
it("should handle transfer errors", async () => {
|
|
164
|
+
it("should handle unknown protocol family transfer errors", async () => {
|
|
165
|
+
mockWallet.getNetwork.mockReturnValue(MOCK_UNKNOWN_NETWORK);
|
|
88
166
|
const args = {
|
|
89
167
|
to: MOCK_DESTINATION,
|
|
90
168
|
value: MOCK_AMOUNT,
|
|
@@ -92,7 +170,7 @@ describe("Wallet Action Provider", () => {
|
|
|
92
170
|
const error = new Error("Failed to execute transfer");
|
|
93
171
|
mockWallet.nativeTransfer.mockRejectedValue(error);
|
|
94
172
|
const response = await actionProvider.nativeTransfer(mockWallet, args);
|
|
95
|
-
expect(response).toBe(`Error
|
|
173
|
+
expect(response).toBe(`Error during transfer: ${error}`);
|
|
96
174
|
});
|
|
97
175
|
});
|
|
98
176
|
describe("supportsNetwork", () => {
|
package/dist/network/index.d.ts
CHANGED
package/dist/network/index.js
CHANGED
|
@@ -14,5 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./types"), exports);
|
|
18
17
|
__exportStar(require("./network"), exports);
|
|
18
|
+
__exportStar(require("./svm"), exports);
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
@@ -11,3 +11,10 @@ export declare const NETWORK_ID_TO_CHAIN_ID: Record<string, string>;
|
|
|
11
11
|
* Maps Coinbase network IDs to Viem chain objects
|
|
12
12
|
*/
|
|
13
13
|
export declare const NETWORK_ID_TO_VIEM_CHAIN: Record<string, Chain>;
|
|
14
|
+
/**
|
|
15
|
+
* Get a chain from the viem chains object
|
|
16
|
+
*
|
|
17
|
+
* @param id - The chain ID
|
|
18
|
+
* @returns The chain
|
|
19
|
+
*/
|
|
20
|
+
export declare const getChain: (id: string) => Chain;
|