@coinbase/agentkit 0.10.3 → 0.10.4
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 +47 -10
- package/dist/action-providers/across/acrossActionProvider.js +2 -4
- package/dist/action-providers/across/acrossActionProvider.test.js +10 -5
- package/dist/action-providers/baseAccount/baseAccountActionProvider.js +5 -7
- package/dist/action-providers/clanker/utils.d.ts +2 -1
- package/dist/action-providers/clanker/utils.js +2 -2
- package/dist/action-providers/index.d.ts +1 -0
- package/dist/action-providers/index.js +1 -0
- package/dist/action-providers/jupiter/jupiterActionProvider.js +2 -2
- package/dist/action-providers/spl/splActionProvider.js +12 -13
- package/dist/action-providers/superfluid/graphQueries/superfluidGraphQueries.js +2 -2
- package/dist/action-providers/sushi/constants.d.ts +35 -0
- package/dist/action-providers/sushi/constants.js +7 -0
- package/dist/action-providers/sushi/index.d.ts +4 -0
- package/dist/action-providers/sushi/index.js +20 -0
- package/dist/action-providers/sushi/sushiDataActionProvider.d.ts +32 -0
- package/dist/action-providers/sushi/sushiDataActionProvider.js +113 -0
- package/dist/action-providers/sushi/sushiDataSchemas.d.ts +11 -0
- package/dist/action-providers/sushi/sushiDataSchemas.js +16 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.d.ts +40 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.js +386 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.test.d.ts +1 -0
- package/dist/action-providers/sushi/sushiRouterActionProvider.test.js +392 -0
- package/dist/action-providers/sushi/sushiRouterSchemas.d.ts +36 -0
- package/dist/action-providers/sushi/sushiRouterSchemas.js +55 -0
- package/dist/action-providers/vaultsfyi/constants.d.ts +8 -12
- package/dist/action-providers/vaultsfyi/constants.js +47 -13
- package/dist/action-providers/vaultsfyi/schemas.d.ts +120 -65
- package/dist/action-providers/vaultsfyi/schemas.js +72 -38
- package/dist/action-providers/vaultsfyi/sdk.d.ts +8 -0
- package/dist/action-providers/vaultsfyi/sdk.js +15 -0
- package/dist/action-providers/vaultsfyi/utils.d.ts +151 -55
- package/dist/action-providers/vaultsfyi/utils.js +29 -75
- package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.d.ts +55 -16
- package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.js +413 -217
- package/dist/action-providers/vaultsfyi/vaultsfyiActionProvider.test.js +509 -316
- package/dist/action-providers/x402/constants.d.ts +67 -0
- package/dist/action-providers/x402/constants.js +37 -0
- package/dist/action-providers/x402/schemas.d.ts +45 -5
- package/dist/action-providers/x402/schemas.js +81 -11
- package/dist/action-providers/x402/utils.d.ts +85 -10
- package/dist/action-providers/x402/utils.js +302 -35
- package/dist/action-providers/x402/x402ActionProvider.d.ts +15 -1
- package/dist/action-providers/x402/x402ActionProvider.js +230 -179
- package/dist/action-providers/x402/x402ActionProvider.test.js +222 -262
- package/dist/action-providers/zora/zoraActionProvider.js +4 -5
- package/package.json +10 -7
- package/dist/action-providers/vaultsfyi/api/actions.d.ts +0 -41
- package/dist/action-providers/vaultsfyi/api/actions.js +0 -28
- package/dist/action-providers/vaultsfyi/api/historicalData.d.ts +0 -31
- package/dist/action-providers/vaultsfyi/api/historicalData.js +0 -44
- package/dist/action-providers/vaultsfyi/api/types.d.ts +0 -34
- package/dist/action-providers/vaultsfyi/api/types.js +0 -2
- package/dist/action-providers/vaultsfyi/api/vaults.d.ts +0 -66
- package/dist/action-providers/vaultsfyi/api/vaults.js +0 -57
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const sushiRouterSchemas_1 = require("./sushiRouterSchemas");
|
|
4
|
+
const evm_1 = require("sushi/evm");
|
|
5
|
+
const sushiRouterActionProvider_1 = require("./sushiRouterActionProvider");
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
// Mock the entire module
|
|
9
|
+
jest.mock("sushi/evm", () => {
|
|
10
|
+
const originalModule = jest.requireActual("sushi/evm");
|
|
11
|
+
return {
|
|
12
|
+
__esModule: true,
|
|
13
|
+
...originalModule,
|
|
14
|
+
getSwap: jest.fn(originalModule.getSwap),
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
const mockedGetSwap = evm_1.getSwap;
|
|
18
|
+
describe("Sushi Action Provider Input Schemas", () => {
|
|
19
|
+
describe("Swap Schema", () => {
|
|
20
|
+
it("should successfully parse valid input", () => {
|
|
21
|
+
const validInput = {
|
|
22
|
+
fromAssetAddress: "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83",
|
|
23
|
+
amount: "0.0001",
|
|
24
|
+
toAssetAddress: "0x1234567890123456789012345678901234567890",
|
|
25
|
+
maxSlippage: 0.005,
|
|
26
|
+
};
|
|
27
|
+
const result = sushiRouterSchemas_1.SushiSwapSchema.safeParse(validInput);
|
|
28
|
+
expect(result.success).toBe(true);
|
|
29
|
+
expect(result.data).toEqual(validInput);
|
|
30
|
+
});
|
|
31
|
+
it("should fail parsing invalid fromAssetAddress", () => {
|
|
32
|
+
const invalidInput = {
|
|
33
|
+
fromAssetAddress: "invalid-address",
|
|
34
|
+
amount: "0.0001",
|
|
35
|
+
toAssetAddress: "0x1234567890123456789012345678901234567890",
|
|
36
|
+
maxSlippage: 0.005,
|
|
37
|
+
};
|
|
38
|
+
const result = sushiRouterSchemas_1.SushiSwapSchema.safeParse(invalidInput);
|
|
39
|
+
expect(result.success).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
it("should fail parsing invalid toAssetAddress", () => {
|
|
42
|
+
const invalidInput = {
|
|
43
|
+
fromAssetAddress: "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83",
|
|
44
|
+
amount: "0.0001",
|
|
45
|
+
toAssetAddress: "invalid-address",
|
|
46
|
+
maxSlippage: 0.005,
|
|
47
|
+
};
|
|
48
|
+
const result = sushiRouterSchemas_1.SushiSwapSchema.safeParse(invalidInput);
|
|
49
|
+
expect(result.success).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it("should fail parsing invalid maxSlippage (>1)", () => {
|
|
52
|
+
const invalidInput = {
|
|
53
|
+
fromAssetAddress: "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83",
|
|
54
|
+
amount: "0.0001",
|
|
55
|
+
toAssetAddress: "0x1234567890123456789012345678901234567890",
|
|
56
|
+
maxSlippage: 1.1,
|
|
57
|
+
};
|
|
58
|
+
const result = sushiRouterSchemas_1.SushiSwapSchema.safeParse(invalidInput);
|
|
59
|
+
expect(result.success).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it("should fail parsing invalid maxSlippage (<0)", () => {
|
|
62
|
+
const invalidInput = {
|
|
63
|
+
fromAssetAddress: "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83",
|
|
64
|
+
amount: "0.0001",
|
|
65
|
+
toAssetAddress: "0x1234567890123456789012345678901234567890",
|
|
66
|
+
maxSlippage: -1.1,
|
|
67
|
+
};
|
|
68
|
+
const result = sushiRouterSchemas_1.SushiSwapSchema.safeParse(invalidInput);
|
|
69
|
+
expect(result.success).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("Quote Schema", () => {
|
|
73
|
+
it("should successfully parse valid input", () => {
|
|
74
|
+
const validInput = {
|
|
75
|
+
fromAssetAddress: "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83",
|
|
76
|
+
amount: "0.0001",
|
|
77
|
+
toAssetAddress: "0x1234567890123456789012345678901234567890",
|
|
78
|
+
};
|
|
79
|
+
const result = sushiRouterSchemas_1.SushiQuoteSchema.safeParse(validInput);
|
|
80
|
+
expect(result.success).toBe(true);
|
|
81
|
+
expect(result.data).toEqual(validInput);
|
|
82
|
+
});
|
|
83
|
+
it("should fail parsing invalid fromAssetAddress", () => {
|
|
84
|
+
const invalidInput = {
|
|
85
|
+
fromAssetAddress: "invalid-address",
|
|
86
|
+
amount: "0.0001",
|
|
87
|
+
toAssetAddress: "0x1234567890123456789012345678901234567890",
|
|
88
|
+
maxSlippage: 0.005,
|
|
89
|
+
};
|
|
90
|
+
const result = sushiRouterSchemas_1.SushiQuoteSchema.safeParse(invalidInput);
|
|
91
|
+
expect(result.success).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
it("should fail parsing invalid toAssetAddress", () => {
|
|
94
|
+
const invalidInput = {
|
|
95
|
+
fromAssetAddress: "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83",
|
|
96
|
+
amount: "0.0001",
|
|
97
|
+
toAssetAddress: "invalid-address",
|
|
98
|
+
maxSlippage: 0.005,
|
|
99
|
+
};
|
|
100
|
+
const result = sushiRouterSchemas_1.SushiQuoteSchema.safeParse(invalidInput);
|
|
101
|
+
expect(result.success).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe("Sushi Action Provider", () => {
|
|
106
|
+
let actionProvider;
|
|
107
|
+
let mockWallet;
|
|
108
|
+
const amountIn = BigInt(1000000);
|
|
109
|
+
const amountOut = BigInt(500000);
|
|
110
|
+
const nativeToken = {
|
|
111
|
+
address: evm_1.nativeAddress,
|
|
112
|
+
symbol: "ETH",
|
|
113
|
+
name: "Ether",
|
|
114
|
+
decimals: 18,
|
|
115
|
+
};
|
|
116
|
+
const tokenIn = {
|
|
117
|
+
address: "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa",
|
|
118
|
+
symbol: "TIN",
|
|
119
|
+
name: "Token In",
|
|
120
|
+
decimals: 18,
|
|
121
|
+
};
|
|
122
|
+
const tokenOut = {
|
|
123
|
+
address: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
|
|
124
|
+
symbol: "TOU",
|
|
125
|
+
name: "Token Out",
|
|
126
|
+
decimals: 18,
|
|
127
|
+
};
|
|
128
|
+
const user = "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF";
|
|
129
|
+
const txHash = "0xhash";
|
|
130
|
+
const chainId = 1;
|
|
131
|
+
const getRouteLog = ({ tokenIn, tokenOut, amountIn, amountOut, }) => [
|
|
132
|
+
{
|
|
133
|
+
data: (0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)("address to, address tokenOut, uint256 amountIn, uint256 amountOut, int256 slippage, bytes32 diagnosticsFirst32"), [user, tokenOut.address, amountIn, amountOut, 0n, `0x${"00".repeat(32)}`]),
|
|
134
|
+
topics: (0, viem_1.encodeEventTopics)({
|
|
135
|
+
abi: constants_1.routeProcessor9Abi_Route,
|
|
136
|
+
eventName: "Route",
|
|
137
|
+
args: {
|
|
138
|
+
from: user,
|
|
139
|
+
tokenIn: tokenIn.address,
|
|
140
|
+
referralCode: 0,
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
];
|
|
145
|
+
const getSuccessfullSwapResponse = async ({ tokenIn, amountIn, tokenOut, amountOut, }) => ({
|
|
146
|
+
amountIn: String(amountIn),
|
|
147
|
+
assumedAmountOut: String(amountOut),
|
|
148
|
+
priceImpact: 0,
|
|
149
|
+
status: evm_1.RouteStatus.Success,
|
|
150
|
+
swapPrice: 1,
|
|
151
|
+
tokens: [tokenIn, tokenOut],
|
|
152
|
+
tokenFrom: tokenIn,
|
|
153
|
+
tokenTo: tokenOut,
|
|
154
|
+
tx: {
|
|
155
|
+
to: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
|
|
156
|
+
data: "0x",
|
|
157
|
+
from: user,
|
|
158
|
+
value: BigInt(0),
|
|
159
|
+
gas: "1000000",
|
|
160
|
+
gasPrice: 1000000000,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
// Reset all mocks before each test
|
|
165
|
+
jest.clearAllMocks();
|
|
166
|
+
actionProvider = new sushiRouterActionProvider_1.SushiRouterActionProvider();
|
|
167
|
+
mockWallet = {
|
|
168
|
+
readContract: jest.fn(),
|
|
169
|
+
sendTransaction: jest.fn(),
|
|
170
|
+
waitForTransactionReceipt: jest.fn(),
|
|
171
|
+
getBalance: jest.fn(),
|
|
172
|
+
getNetwork: jest.fn().mockResolvedValue({
|
|
173
|
+
protocolFamily: "evm",
|
|
174
|
+
networkId: "ethereum-mainnet",
|
|
175
|
+
chainId: String(chainId),
|
|
176
|
+
}),
|
|
177
|
+
getAddress: jest.fn().mockReturnValue(user),
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
describe("swap", () => {
|
|
181
|
+
it("should successfully perform a swap (token -> token)", async () => {
|
|
182
|
+
const args = {
|
|
183
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
184
|
+
fromAssetAddress: tokenIn.address,
|
|
185
|
+
toAssetAddress: tokenOut.address,
|
|
186
|
+
maxSlippage: 0.005,
|
|
187
|
+
};
|
|
188
|
+
/*
|
|
189
|
+
* 1. Mock the readContract which checks the decimals of the fromAssetAddress token (18, default)
|
|
190
|
+
* 2. Mock the readContract which checks for the balance of the fromAssetAddress token (1000000, enough balance)
|
|
191
|
+
* 3. Mock the readContract which checks for the approval (0, not approved)
|
|
192
|
+
*/
|
|
193
|
+
mockWallet.readContract
|
|
194
|
+
.mockResolvedValueOnce(tokenIn.decimals)
|
|
195
|
+
.mockResolvedValueOnce(amountIn)
|
|
196
|
+
.mockResolvedValueOnce(BigInt(0));
|
|
197
|
+
mockWallet.sendTransaction.mockResolvedValue(txHash);
|
|
198
|
+
/*
|
|
199
|
+
* 1. Mock the waitForTransactionReceipt to return success for the approval tx
|
|
200
|
+
* 2. Mock the waitForTransactionReceipt to return success for the swap tx, including the Route log
|
|
201
|
+
*/
|
|
202
|
+
mockWallet.waitForTransactionReceipt
|
|
203
|
+
.mockResolvedValueOnce({
|
|
204
|
+
status: "success",
|
|
205
|
+
})
|
|
206
|
+
.mockResolvedValueOnce({
|
|
207
|
+
status: "success",
|
|
208
|
+
logs: getRouteLog({
|
|
209
|
+
tokenIn,
|
|
210
|
+
tokenOut,
|
|
211
|
+
amountIn,
|
|
212
|
+
amountOut,
|
|
213
|
+
}),
|
|
214
|
+
});
|
|
215
|
+
mockedGetSwap.mockReturnValue(getSuccessfullSwapResponse({
|
|
216
|
+
tokenIn,
|
|
217
|
+
amountIn,
|
|
218
|
+
tokenOut,
|
|
219
|
+
amountOut,
|
|
220
|
+
}));
|
|
221
|
+
const result = await actionProvider.swap(mockWallet, args);
|
|
222
|
+
expect(mockWallet.readContract).toHaveBeenCalledTimes(3); // Decimals + Balance + Approval
|
|
223
|
+
expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(2); // Approval + Swap
|
|
224
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(2);
|
|
225
|
+
expect(result).toContain(`Swapped ${(0, viem_1.formatUnits)(amountIn, tokenIn.decimals)} of ${tokenIn.symbol} (${tokenIn.address}) for ${(0, viem_1.formatUnits)(amountOut, tokenOut.decimals)} of ${tokenOut.symbol} (${tokenOut.address})`);
|
|
226
|
+
expect(result).toContain(`Transaction hash: ${txHash}`);
|
|
227
|
+
expect(result).toContain(`Transaction link: ${(0, evm_1.getEvmChainById)(chainId).getTransactionUrl(txHash)}`);
|
|
228
|
+
expect(result).toContain(`on ${(0, evm_1.getEvmChainById)(chainId).shortName}`);
|
|
229
|
+
});
|
|
230
|
+
it("should successfully perform a swap (native -> token)", async () => {
|
|
231
|
+
const args = {
|
|
232
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
233
|
+
fromAssetAddress: nativeToken.address,
|
|
234
|
+
toAssetAddress: tokenOut.address,
|
|
235
|
+
maxSlippage: 0.005,
|
|
236
|
+
};
|
|
237
|
+
// Mock the readContract which checks for the balance of the fromAssetAddress token (1000000, enough balance)
|
|
238
|
+
mockWallet.getBalance.mockResolvedValue(amountIn);
|
|
239
|
+
mockWallet.sendTransaction.mockResolvedValue(txHash);
|
|
240
|
+
// Mock the waitForTransactionReceipt to return success for the swap tx, including the Route log
|
|
241
|
+
mockWallet.waitForTransactionReceipt.mockResolvedValueOnce({
|
|
242
|
+
status: "success",
|
|
243
|
+
logs: getRouteLog({
|
|
244
|
+
tokenIn: nativeToken,
|
|
245
|
+
tokenOut,
|
|
246
|
+
amountIn,
|
|
247
|
+
amountOut,
|
|
248
|
+
}),
|
|
249
|
+
});
|
|
250
|
+
mockedGetSwap.mockReturnValue(getSuccessfullSwapResponse({
|
|
251
|
+
tokenIn: nativeToken,
|
|
252
|
+
amountIn,
|
|
253
|
+
tokenOut,
|
|
254
|
+
amountOut,
|
|
255
|
+
}));
|
|
256
|
+
const result = await actionProvider.swap(mockWallet, args);
|
|
257
|
+
expect(mockWallet.getBalance).toHaveBeenCalledTimes(1);
|
|
258
|
+
expect(mockWallet.readContract).toHaveBeenCalledTimes(0); // No balance check nor approval
|
|
259
|
+
expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(1); // Swap
|
|
260
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(2);
|
|
261
|
+
expect(result).toContain(`Swapped ${(0, viem_1.formatUnits)(amountIn, nativeToken.decimals)} of ${nativeToken.symbol} (${nativeToken.address}) for ${(0, viem_1.formatUnits)(amountOut, tokenOut.decimals)} of ${tokenOut.symbol} (${tokenOut.address})`);
|
|
262
|
+
expect(result).toContain(`Transaction hash: ${txHash}`);
|
|
263
|
+
expect(result).toContain(`Transaction link: ${(0, evm_1.getEvmChainById)(chainId).getTransactionUrl(txHash)}`);
|
|
264
|
+
expect(result).toContain(`on ${(0, evm_1.getEvmChainById)(chainId).shortName}`);
|
|
265
|
+
});
|
|
266
|
+
it("should fail if there isn't enough balance (native)", async () => {
|
|
267
|
+
const args = {
|
|
268
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
269
|
+
fromAssetAddress: nativeToken.address,
|
|
270
|
+
toAssetAddress: tokenOut.address,
|
|
271
|
+
maxSlippage: 0.005,
|
|
272
|
+
};
|
|
273
|
+
// Mock the readContract which checks for the balance of the fromAssetAddress token (99, not enough balance)
|
|
274
|
+
mockWallet.getBalance.mockResolvedValue(amountIn - BigInt(1));
|
|
275
|
+
const result = await actionProvider.swap(mockWallet, args);
|
|
276
|
+
expect(mockWallet.getBalance).toHaveBeenCalledTimes(1);
|
|
277
|
+
expect(mockWallet.readContract).toHaveBeenCalledTimes(0);
|
|
278
|
+
expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(0);
|
|
279
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(1);
|
|
280
|
+
expect(result).toContain(`Swap failed: Insufficient balance for ${nativeToken.symbol} (${nativeToken.address})`);
|
|
281
|
+
});
|
|
282
|
+
it("should fail if there isn't enough balance (token)", async () => {
|
|
283
|
+
const args = {
|
|
284
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
285
|
+
fromAssetAddress: tokenIn.address,
|
|
286
|
+
toAssetAddress: tokenOut.address,
|
|
287
|
+
maxSlippage: 0.005,
|
|
288
|
+
};
|
|
289
|
+
/*
|
|
290
|
+
* 1. Mock the readContract which checks the decimals of the fromAssetAddress token (18, default)
|
|
291
|
+
* 2. Mock the readContract which checks for the balance of the fromAssetAddress token (1000000, enough balance)
|
|
292
|
+
*/
|
|
293
|
+
mockWallet.readContract.mockResolvedValueOnce(18).mockResolvedValue(amountIn - BigInt(1));
|
|
294
|
+
mockedGetSwap.mockReturnValue(getSuccessfullSwapResponse({
|
|
295
|
+
tokenIn,
|
|
296
|
+
amountIn,
|
|
297
|
+
tokenOut,
|
|
298
|
+
amountOut,
|
|
299
|
+
}));
|
|
300
|
+
const result = await actionProvider.swap(mockWallet, args);
|
|
301
|
+
expect(mockWallet.getBalance).toHaveBeenCalledTimes(0);
|
|
302
|
+
expect(mockWallet.readContract).toHaveBeenCalledTimes(2);
|
|
303
|
+
expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(0);
|
|
304
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(1);
|
|
305
|
+
expect(result).toContain(`Swap failed: Insufficient balance for ${tokenIn.symbol} (${tokenIn.address})`);
|
|
306
|
+
});
|
|
307
|
+
it("should not approve if already approved", async () => {
|
|
308
|
+
const args = {
|
|
309
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
310
|
+
fromAssetAddress: tokenIn.address,
|
|
311
|
+
toAssetAddress: tokenOut.address,
|
|
312
|
+
maxSlippage: 0.005,
|
|
313
|
+
};
|
|
314
|
+
/*
|
|
315
|
+
* 1. Mock the readContract which checks the decimals of the fromAssetAddress token (18, default)
|
|
316
|
+
* 2. Mock the readContract which checks for the balance of the fromAssetAddress token (1000000, enough balance)
|
|
317
|
+
* 3. Mock the readContract which checks for the approval (1000000, approved)
|
|
318
|
+
*/
|
|
319
|
+
mockWallet.readContract
|
|
320
|
+
.mockResolvedValueOnce(tokenIn.decimals)
|
|
321
|
+
.mockResolvedValueOnce(amountIn)
|
|
322
|
+
.mockResolvedValueOnce(amountIn);
|
|
323
|
+
mockWallet.sendTransaction.mockResolvedValue(txHash);
|
|
324
|
+
// Mock the waitForTransactionReceipt to return success for the swap tx, including the Route log
|
|
325
|
+
mockWallet.waitForTransactionReceipt.mockResolvedValueOnce({
|
|
326
|
+
status: "success",
|
|
327
|
+
logs: getRouteLog({
|
|
328
|
+
tokenIn,
|
|
329
|
+
tokenOut,
|
|
330
|
+
amountIn,
|
|
331
|
+
amountOut,
|
|
332
|
+
}),
|
|
333
|
+
});
|
|
334
|
+
mockedGetSwap.mockReturnValue(getSuccessfullSwapResponse({
|
|
335
|
+
tokenIn,
|
|
336
|
+
amountIn,
|
|
337
|
+
tokenOut,
|
|
338
|
+
amountOut,
|
|
339
|
+
}));
|
|
340
|
+
const result = await actionProvider.swap(mockWallet, args);
|
|
341
|
+
expect(mockWallet.getBalance).toHaveBeenCalledTimes(0);
|
|
342
|
+
expect(mockWallet.readContract).toHaveBeenCalledTimes(3); // Decimals + Balance + Allowance
|
|
343
|
+
expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(1); // Swap
|
|
344
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(2);
|
|
345
|
+
expect(result).toContain(`Swapped`);
|
|
346
|
+
});
|
|
347
|
+
it("should fail if there's no route", async () => {
|
|
348
|
+
const args = {
|
|
349
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
350
|
+
fromAssetAddress: tokenIn.address,
|
|
351
|
+
toAssetAddress: tokenOut.address,
|
|
352
|
+
maxSlippage: 0.005,
|
|
353
|
+
};
|
|
354
|
+
/*
|
|
355
|
+
* 1. Mock the readContract which checks for decimals of the fromAssetAddress token (18, default)
|
|
356
|
+
* 2. Mock the readContract which checks for the balance of the fromAssetAddress token (1000000, enough balance)
|
|
357
|
+
*/
|
|
358
|
+
mockWallet.readContract
|
|
359
|
+
.mockResolvedValueOnce(tokenIn.decimals)
|
|
360
|
+
.mockResolvedValueOnce(amountIn);
|
|
361
|
+
mockedGetSwap.mockReturnValue(new Promise(r => r({
|
|
362
|
+
status: evm_1.RouteStatus.NoWay,
|
|
363
|
+
})));
|
|
364
|
+
const result = await actionProvider.swap(mockWallet, args);
|
|
365
|
+
expect(mockWallet.getBalance).toHaveBeenCalledTimes(0);
|
|
366
|
+
expect(mockWallet.readContract).toHaveBeenCalledTimes(1); // Decimals
|
|
367
|
+
expect(mockWallet.sendTransaction).toHaveBeenCalledTimes(0);
|
|
368
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(1);
|
|
369
|
+
expect(result).toContain(`No route found to swap ${amountIn} of ${tokenIn.address} for ${tokenOut.address}`);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
describe("quote", () => {
|
|
373
|
+
it("should successfully fetch a quote (token -> token)", async () => {
|
|
374
|
+
const args = {
|
|
375
|
+
amount: (0, viem_1.formatUnits)(amountIn, tokenIn.decimals),
|
|
376
|
+
fromAssetAddress: tokenIn.address,
|
|
377
|
+
toAssetAddress: tokenOut.address,
|
|
378
|
+
};
|
|
379
|
+
mockedGetSwap.mockReturnValue(getSuccessfullSwapResponse({
|
|
380
|
+
tokenIn,
|
|
381
|
+
amountIn,
|
|
382
|
+
tokenOut,
|
|
383
|
+
amountOut,
|
|
384
|
+
}));
|
|
385
|
+
const result = await actionProvider.quote(mockWallet, args);
|
|
386
|
+
expect(mockedGetSwap).toHaveBeenCalledTimes(1);
|
|
387
|
+
expect(result).toContain(`Found a quote for ${tokenIn.symbol} (${tokenIn.address}) -> ${tokenOut.symbol} (${tokenOut.address})`);
|
|
388
|
+
expect(result).toContain(`AmountIn: ${(0, viem_1.formatUnits)(amountIn, tokenIn.decimals)}`);
|
|
389
|
+
expect(result).toContain(`AmountOut: ${(0, viem_1.formatUnits)(amountOut, tokenOut.decimals)}`);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Input schema for asset swap action
|
|
4
|
+
*/
|
|
5
|
+
export declare const SushiSwapSchema: z.ZodObject<{
|
|
6
|
+
fromAssetAddress: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
7
|
+
amount: z.ZodString;
|
|
8
|
+
toAssetAddress: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
9
|
+
maxSlippage: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
amount: string;
|
|
12
|
+
fromAssetAddress: `0x${string}`;
|
|
13
|
+
toAssetAddress: `0x${string}`;
|
|
14
|
+
maxSlippage: number;
|
|
15
|
+
}, {
|
|
16
|
+
amount: string;
|
|
17
|
+
fromAssetAddress: string;
|
|
18
|
+
toAssetAddress: string;
|
|
19
|
+
maxSlippage?: number | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Input schema for quote action
|
|
23
|
+
*/
|
|
24
|
+
export declare const SushiQuoteSchema: z.ZodObject<{
|
|
25
|
+
fromAssetAddress: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
26
|
+
amount: z.ZodString;
|
|
27
|
+
toAssetAddress: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
amount: string;
|
|
30
|
+
fromAssetAddress: `0x${string}`;
|
|
31
|
+
toAssetAddress: `0x${string}`;
|
|
32
|
+
}, {
|
|
33
|
+
amount: string;
|
|
34
|
+
fromAssetAddress: string;
|
|
35
|
+
toAssetAddress: string;
|
|
36
|
+
}>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SushiQuoteSchema = exports.SushiSwapSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Input schema for asset swap action
|
|
7
|
+
*/
|
|
8
|
+
exports.SushiSwapSchema = zod_1.z
|
|
9
|
+
.object({
|
|
10
|
+
fromAssetAddress: zod_1.z
|
|
11
|
+
.string()
|
|
12
|
+
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
|
|
13
|
+
.transform(val => val)
|
|
14
|
+
.describe("The Ethereum address of the input asset"),
|
|
15
|
+
amount: zod_1.z
|
|
16
|
+
.string()
|
|
17
|
+
.regex(/^\d+(\.\d+)?$/, "Invalid number format")
|
|
18
|
+
.describe("The amount of the input asset to swap, in the human readable format"),
|
|
19
|
+
toAssetAddress: zod_1.z
|
|
20
|
+
.string()
|
|
21
|
+
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
|
|
22
|
+
.transform(val => val)
|
|
23
|
+
.describe("The Ethereum address of the output asset"),
|
|
24
|
+
maxSlippage: zod_1.z
|
|
25
|
+
.number()
|
|
26
|
+
.min(0)
|
|
27
|
+
.max(1)
|
|
28
|
+
.optional()
|
|
29
|
+
.default(0.005) // 0.05%
|
|
30
|
+
.describe("The maximum slippage allowed for the swap, where 0 is 0% and 1 is 100%"),
|
|
31
|
+
})
|
|
32
|
+
.strip()
|
|
33
|
+
.describe("Instructions for trading assets");
|
|
34
|
+
/**
|
|
35
|
+
* Input schema for quote action
|
|
36
|
+
*/
|
|
37
|
+
exports.SushiQuoteSchema = zod_1.z
|
|
38
|
+
.object({
|
|
39
|
+
fromAssetAddress: zod_1.z
|
|
40
|
+
.string()
|
|
41
|
+
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
|
|
42
|
+
.transform(val => val)
|
|
43
|
+
.describe("The Ethereum address of the input asset"),
|
|
44
|
+
amount: zod_1.z
|
|
45
|
+
.string()
|
|
46
|
+
.regex(/^\d+(\.\d+)?$/, "Invalid number format")
|
|
47
|
+
.describe("The amount of the input asset to get a quote for"),
|
|
48
|
+
toAssetAddress: zod_1.z
|
|
49
|
+
.string()
|
|
50
|
+
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
|
|
51
|
+
.transform(val => val)
|
|
52
|
+
.describe("The Ethereum address of the output asset"),
|
|
53
|
+
})
|
|
54
|
+
.strip()
|
|
55
|
+
.describe("Instructions for fetching a quote for a trade");
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
readonly 130: "unichain";
|
|
10
|
-
readonly 1923: "swellchain";
|
|
11
|
-
readonly 42220: "celo";
|
|
12
|
-
};
|
|
1
|
+
export declare const SUPPORTED_CHAIN_IDS: string[];
|
|
2
|
+
/**
|
|
3
|
+
* Get the network name from a chain id
|
|
4
|
+
*
|
|
5
|
+
* @param chainId - The chain id
|
|
6
|
+
* @returns The network name
|
|
7
|
+
*/
|
|
8
|
+
export declare function getNetworkNameFromChainId(chainId: string): string | undefined;
|
|
@@ -1,15 +1,49 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
36
|
+
exports.SUPPORTED_CHAIN_IDS = void 0;
|
|
37
|
+
exports.getNetworkNameFromChainId = getNetworkNameFromChainId;
|
|
38
|
+
const sdk_1 = require("@vaultsfyi/sdk");
|
|
39
|
+
const viemChains = __importStar(require("viem/chains"));
|
|
40
|
+
exports.SUPPORTED_CHAIN_IDS = sdk_1.SUPPORTED_NETWORKS.map(network => viemChains[network]?.id.toString()).filter(id => id !== undefined);
|
|
41
|
+
/**
|
|
42
|
+
* Get the network name from a chain id
|
|
43
|
+
*
|
|
44
|
+
* @param chainId - The chain id
|
|
45
|
+
* @returns The network name
|
|
46
|
+
*/
|
|
47
|
+
function getNetworkNameFromChainId(chainId) {
|
|
48
|
+
return sdk_1.SUPPORTED_NETWORKS.find(network => viemChains[network]?.id.toString() === chainId);
|
|
49
|
+
}
|