@continuumdao/ctm-mpc-defi 0.2.25 → 0.2.26

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.
@@ -0,0 +1,901 @@
1
+ import { parseAbi, getAddress, formatUnits, parseUnits, encodeFunctionData, defineChain, createPublicClient, http, parseGwei, serializeTransaction, keccak256 } from 'viem';
2
+ import { fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase, getClientIdFromKeyGenResult } from '@continuumdao/continuum-node-sdk';
3
+
4
+ // src/core/registry.ts
5
+ var modules = [];
6
+ function registerProtocolModule(mod) {
7
+ const existing = modules.findIndex((m) => m.id === mod.id);
8
+ if (existing >= 0) {
9
+ modules[existing] = mod;
10
+ } else {
11
+ modules.push(mod);
12
+ }
13
+ }
14
+ var VENICE_PROTOCOL_ID = "venice";
15
+ var VENICE_BASE_CHAIN_ID = 8453;
16
+ var VVV_BASE = "0xacfE6019Ed1A7Dc6f7B508C02d1b04ec88cC21bf";
17
+ var DIEM_BASE = "0xF4d97F2da56e8c3098f3a8D538DB630A2606a024";
18
+ var VENICE_STAKING_BASE = "0x321b7ff75154472B18EDb199033fF4D116F340Ff";
19
+ var VENICE_TOKEN_DECIMALS = 18;
20
+ var DIEM_TARGET_SUPPLY_HUMAN = 38e3;
21
+ var VENICE_DOCS_VVV_DIEM = "https://docs.venice.ai/overview/vvv-diem";
22
+ var VENICE_DIEM_CALCULATOR = "https://diem-calculator.venice.ai/";
23
+ var VENICE_TOKEN_DASHBOARD = "https://venice.ai/token";
24
+ function isVeniceBaseChain(chainId) {
25
+ return chainId === VENICE_BASE_CHAIN_ID;
26
+ }
27
+ function isVeniceVvvOnBase(chainId, tokenAddress) {
28
+ if (!isVeniceBaseChain(chainId)) return false;
29
+ try {
30
+ return getAddress(tokenAddress).toLowerCase() === getAddress(VVV_BASE).toLowerCase();
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ function isVeniceDiemOnBase(chainId, tokenAddress) {
36
+ if (!isVeniceBaseChain(chainId)) return false;
37
+ try {
38
+ return getAddress(tokenAddress).toLowerCase() === getAddress(DIEM_BASE).toLowerCase();
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ function isVeniceTokenOnBase(chainId, tokenAddress) {
44
+ return isVeniceVvvOnBase(chainId, tokenAddress) || isVeniceDiemOnBase(chainId, tokenAddress);
45
+ }
46
+ function computeMintRateSvvcPerDiem(diemTotalSupplyWei) {
47
+ const supplyHuman = Number(formatUnits(diemTotalSupplyWei, VENICE_TOKEN_DECIMALS));
48
+ if (!Number.isFinite(supplyHuman) || supplyHuman < 0) return 90;
49
+ const ratio = supplyHuman / DIEM_TARGET_SUPPLY_HUMAN;
50
+ return 90 * Math.exp(2 * ratio ** 3);
51
+ }
52
+ function previewDiemMintedFromSvvcLock(args) {
53
+ const { svvvLockWei, diemTotalSupplyWei } = args;
54
+ if (svvvLockWei <= 0n) return 0n;
55
+ const rate = computeMintRateSvvcPerDiem(diemTotalSupplyWei);
56
+ if (!Number.isFinite(rate) || rate <= 0) return 0n;
57
+ const svvvHuman = Number(formatUnits(svvvLockWei, VENICE_TOKEN_DECIMALS));
58
+ const diemHuman = svvvHuman / rate;
59
+ if (!Number.isFinite(diemHuman) || diemHuman <= 0) return 0n;
60
+ return parseUnits(diemHuman.toFixed(12), VENICE_TOKEN_DECIMALS);
61
+ }
62
+ function applyMinOutSlippage(amountWei, slippageBps) {
63
+ if (amountWei <= 0n) return 0n;
64
+ const bps = Math.max(0, Math.min(1e4, slippageBps));
65
+ return amountWei * BigInt(1e4 - bps) / 10000n;
66
+ }
67
+ var erc20Abi = parseAbi([
68
+ "function balanceOf(address account) view returns (uint256)",
69
+ "function decimals() view returns (uint8)",
70
+ "function totalSupply() view returns (uint256)"
71
+ ]);
72
+ var diemStakingAbi = parseAbi([
73
+ "function stakedInfos(address) view returns (uint256 amountStaked, uint256 coolDownEnd, uint256 coolDownAmount)",
74
+ "function cooldownDuration() view returns (uint256)"
75
+ ]);
76
+ var stakingCooldownAbi = parseAbi(["function cooldownDuration() view returns (uint256)"]);
77
+ function basePublicClient(rpcUrl) {
78
+ const ch = defineChain({
79
+ id: VENICE_BASE_CHAIN_ID,
80
+ name: "Base",
81
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
82
+ rpcUrls: { default: { http: [rpcUrl.trim()] } }
83
+ });
84
+ return createPublicClient({ chain: ch, transport: http(rpcUrl.trim()) });
85
+ }
86
+ async function readVeniceDiemStakedInfo(args) {
87
+ const client = basePublicClient(args.rpcUrl);
88
+ const wallet = getAddress(args.wallet);
89
+ const diem = getAddress(DIEM_BASE);
90
+ const [amountStaked, coolDownEnd, coolDownAmount] = await client.readContract({
91
+ address: diem,
92
+ abi: diemStakingAbi,
93
+ functionName: "stakedInfos",
94
+ args: [wallet]
95
+ });
96
+ return {
97
+ amountStakedWei: amountStaked,
98
+ coolDownEndSec: coolDownEnd,
99
+ coolDownAmountWei: coolDownAmount
100
+ };
101
+ }
102
+ async function readVeniceWalletStakingState(args) {
103
+ const client = basePublicClient(args.rpcUrl);
104
+ const wallet = getAddress(args.wallet);
105
+ const vvv = getAddress(VVV_BASE);
106
+ const diem = getAddress(DIEM_BASE);
107
+ const staking = getAddress(VENICE_STAKING_BASE);
108
+ const [vvvBalanceWei, svvvBalanceWei, diemBalanceWei, diemTotalSupplyWei, diemStaked, diemCooldown, svvvCooldown] = await Promise.all([
109
+ client.readContract({ address: vvv, abi: erc20Abi, functionName: "balanceOf", args: [wallet] }),
110
+ client.readContract({ address: staking, abi: erc20Abi, functionName: "balanceOf", args: [wallet] }),
111
+ client.readContract({ address: diem, abi: erc20Abi, functionName: "balanceOf", args: [wallet] }),
112
+ client.readContract({ address: diem, abi: erc20Abi, functionName: "totalSupply" }),
113
+ readVeniceDiemStakedInfo({ rpcUrl: args.rpcUrl, wallet }),
114
+ client.readContract({ address: diem, abi: diemStakingAbi, functionName: "cooldownDuration" }),
115
+ client.readContract({ address: staking, abi: stakingCooldownAbi, functionName: "cooldownDuration" })
116
+ ]);
117
+ const mintRateSvvcPerDiem = computeMintRateSvvcPerDiem(diemTotalSupplyWei);
118
+ const stakedDiemHuman = formatUnits(diemStaked.amountStakedWei, VENICE_TOKEN_DECIMALS);
119
+ return {
120
+ chainId: VENICE_BASE_CHAIN_ID,
121
+ wallet,
122
+ vvvBalanceWei,
123
+ svvvBalanceWei,
124
+ diemBalanceWei,
125
+ diemStaked,
126
+ diemTotalSupplyWei,
127
+ mintRateSvvcPerDiem,
128
+ diemCooldownDurationSec: diemCooldown,
129
+ svvvCooldownDurationSec: svvvCooldown,
130
+ estimatedApiCreditUsdPerDay: stakedDiemHuman
131
+ };
132
+ }
133
+ function formatVeniceStakingStateSummary(state) {
134
+ return {
135
+ chainId: state.chainId,
136
+ wallet: state.wallet,
137
+ balances: {
138
+ vvvWei: state.vvvBalanceWei.toString(),
139
+ svvvWei: state.svvvBalanceWei.toString(),
140
+ diemWei: state.diemBalanceWei.toString(),
141
+ diemStakedWei: state.diemStaked.amountStakedWei.toString()
142
+ },
143
+ diemCooldown: {
144
+ pendingUnstakeWei: state.diemStaked.coolDownAmountWei.toString(),
145
+ unlockAtSec: state.diemStaked.coolDownEndSec.toString(),
146
+ cooldownDurationSec: state.diemCooldownDurationSec.toString()
147
+ },
148
+ svvvCooldownDurationSec: state.svvvCooldownDurationSec.toString(),
149
+ mintRateSvvcPerDiem: state.mintRateSvvcPerDiem,
150
+ diemTotalSupplyWei: state.diemTotalSupplyWei.toString(),
151
+ estimatedApiCreditUsdPerDay: state.estimatedApiCreditUsdPerDay
152
+ };
153
+ }
154
+
155
+ // src/core/purpose.ts
156
+ function mergePurposeText(purposeText, purposeSuffix) {
157
+ const t = (purposeText ?? "").trim();
158
+ const suffix = (purposeSuffix ?? "").trim();
159
+ if (!suffix) return t;
160
+ return t ? `${t}
161
+
162
+ ${suffix}` : suffix;
163
+ }
164
+
165
+ // src/core/envelope.ts
166
+ function finalizeMultisign(input) {
167
+ const { keyGen, destinationChainID, legs } = input;
168
+ if (legs.length === 0) {
169
+ throw new Error("finalizeMultisign requires at least one leg");
170
+ }
171
+ const ph = (keyGen.pubkeyhex ?? "").trim();
172
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
173
+ const keyList = keyGen.keylist ?? [];
174
+ const clientId = getClientIdFromKeyGenResult(keyGen);
175
+ const first = legs[0];
176
+ const messageHashes = legs.map((l) => l.msgHash);
177
+ const messageRawBatch = legs.map((l) => l.msgRaw);
178
+ const batchMeta = legs.map((l) => ({
179
+ destinationAddress: l.destinationAddress,
180
+ signatureText: l.signatureText,
181
+ ...l.audit
182
+ }));
183
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
184
+ const extraPayload = {
185
+ batchMeta,
186
+ ...input.extraJSON ?? {}
187
+ };
188
+ const extraJSON = JSON.stringify(extraPayload, (_, v) => typeof v === "bigint" ? v.toString() : v);
189
+ const bodyForSign = {
190
+ keyList,
191
+ pubKey: ph,
192
+ msgHash: messageHashes[0],
193
+ msgRaw: first.msgRaw,
194
+ destinationChainID,
195
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
196
+ extraJSON,
197
+ signatureText: first.signatureText,
198
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
199
+ ...first.feeSnapshot
200
+ };
201
+ if (legs.length > 1) {
202
+ bodyForSign.messageHashes = messageHashes;
203
+ bodyForSign.messageRawBatch = messageRawBatch;
204
+ }
205
+ if (proposalTxParams.length > 0) {
206
+ bodyForSign.proposalTxParams = proposalTxParams;
207
+ }
208
+ const valueWei = first.valueWei;
209
+ if (valueWei != null && valueWei > 0n) {
210
+ bodyForSign.value = valueWei.toString();
211
+ }
212
+ if (clientId) bodyForSign.clientId = clientId;
213
+ if (input.expiryDate != null && input.expiryDate > 0) {
214
+ bodyForSign.expiryDate = Math.floor(input.expiryDate);
215
+ }
216
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
217
+ }
218
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
219
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
220
+ return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
221
+ }
222
+ return (estimatedGas * 12n + 9n) / 10n;
223
+ }
224
+
225
+ // src/chains/evm/buildBatch.ts
226
+ async function buildEvmMultisignBatch(args) {
227
+ const { context, steps } = args;
228
+ const {
229
+ chainId,
230
+ rpcUrl,
231
+ executorAddress,
232
+ chainDetail,
233
+ useCustomGas,
234
+ customGasChainDetails,
235
+ keyGen,
236
+ purposeText
237
+ } = context;
238
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
239
+ const ch = defineChain({
240
+ id: chainId,
241
+ name: "Destination",
242
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
243
+ rpcUrls: { default: { http: [rpcUrl] } }
244
+ });
245
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpcUrl) });
246
+ const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
247
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
248
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
249
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
250
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
251
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
252
+ const executor = getAddress(executorAddress);
253
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
254
+ const legs = [];
255
+ for (let i = 0; i < steps.length; i++) {
256
+ const step = steps[i];
257
+ const currentNonce = baseNonce + i;
258
+ let estimatedGas;
259
+ if (args.estimateGasForStep) {
260
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
261
+ } else {
262
+ try {
263
+ estimatedGas = await publicClient.estimateGas({
264
+ to: step.to,
265
+ data: step.data,
266
+ value: step.value,
267
+ account: executor
268
+ });
269
+ } catch {
270
+ estimatedGas = step.fallbackGas ?? 100000n;
271
+ }
272
+ }
273
+ let gasLimitI;
274
+ if (args.resolveGasLimit) {
275
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
276
+ } else if (step.routerSwap) {
277
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
278
+ } else {
279
+ gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
280
+ }
281
+ let proposalTxParams;
282
+ let feeSnapshot;
283
+ let serialized;
284
+ if (legacy) {
285
+ let gasPriceWei = await publicClient.getGasPrice();
286
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
287
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
288
+ }
289
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
290
+ const configured = parseGwei(gweiToDecimalString(Number(chainDetail.gasPrice)));
291
+ if (configured > gasPriceWei) gasPriceWei = configured;
292
+ }
293
+ serialized = serializeTransaction({
294
+ type: "legacy",
295
+ to: step.to,
296
+ data: step.data,
297
+ value: step.value,
298
+ gas: gasLimitI,
299
+ gasPrice: gasPriceWei,
300
+ nonce: currentNonce,
301
+ chainId
302
+ });
303
+ proposalTxParams = {
304
+ nonce: currentNonce,
305
+ gasLimit: gasLimitI.toString(),
306
+ txType: "legacy",
307
+ gasPrice: gasPriceWei.toString()
308
+ };
309
+ feeSnapshot = proposalTxParamsToFeeSnapshot(proposalTxParams);
310
+ } else {
311
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
312
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
313
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
314
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
315
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
316
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
317
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
318
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
319
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
320
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
321
+ let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
322
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
323
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
324
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
325
+ }
326
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
327
+ maxFeePerGas,
328
+ maxPriorityFeePerGas,
329
+ latestBaseFeeWei
330
+ ));
331
+ serialized = serializeTransaction({
332
+ type: "eip1559",
333
+ to: step.to,
334
+ data: step.data,
335
+ value: step.value,
336
+ gas: gasLimitI,
337
+ maxFeePerGas,
338
+ maxPriorityFeePerGas,
339
+ nonce: currentNonce,
340
+ chainId
341
+ });
342
+ proposalTxParams = {
343
+ nonce: currentNonce,
344
+ gasLimit: gasLimitI.toString(),
345
+ txType: "eip1559",
346
+ maxFeePerGas: maxFeePerGas.toString(),
347
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
348
+ };
349
+ feeSnapshot = i === 0 ? proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
350
+ }
351
+ const h = keccak256(serialized);
352
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
353
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
354
+ legs.push({
355
+ msgHash,
356
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
357
+ destinationAddress: step.to,
358
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
359
+ audit: batchMetaExtra,
360
+ feeSnapshot: i === 0 ? feeSnapshot : {},
361
+ proposalTxParams,
362
+ valueWei: i === 0 ? step.value : void 0
363
+ });
364
+ if (i === 0 && args.firstMsgRawNo0x != null) {
365
+ legs[0].msgRaw = args.firstMsgRawNo0x;
366
+ }
367
+ }
368
+ const extraJSON = {};
369
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
370
+ extraJSON.customGasChainDetails = customGasChainDetails;
371
+ }
372
+ const result = finalizeMultisign({
373
+ keyGen,
374
+ purposeText,
375
+ purposeSuffix: args.purposeSuffix,
376
+ destinationChainID: String(chainId),
377
+ destinationAddress: args.destinationAddress ?? steps[0].to,
378
+ legs,
379
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0,
380
+ expiryDate: context.expiryDate
381
+ });
382
+ const pv = args.payableValueWei;
383
+ if (pv != null && pv > 0n) {
384
+ result.bodyForSign.value = pv.toString();
385
+ }
386
+ return result;
387
+ }
388
+ var ERC20_APPROVE_FALLBACK = 100000n;
389
+ var VENICE_STAKE_VVV_FALLBACK = 800000n;
390
+ var VENICE_STAKING_ACTION_FALLBACK = 600000n;
391
+ var VENICE_MINT_DIEM_FALLBACK = 1200000n;
392
+ var erc20AllowanceAbi = parseAbi([
393
+ "function allowance(address owner, address spender) view returns (uint256)",
394
+ "function decimals() view returns (uint8)",
395
+ "function totalSupply() view returns (uint256)"
396
+ ]);
397
+ var erc20ApproveAbi = parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
398
+ var stakingAbi = parseAbi([
399
+ "function stake(uint256 amount)",
400
+ "function initiateUnstake(uint256 amount)",
401
+ "function unstake()",
402
+ "function mintDiem(uint256 sVVVAmountToLock, uint256 minDiemAmountOut)",
403
+ "function burnDiem(uint256 diemAmountToBurn)"
404
+ ]);
405
+ var diemStakingAbi2 = parseAbi([
406
+ "function stake(uint256 amount)",
407
+ "function initiateUnstake(uint256 amount)",
408
+ "function unstake()"
409
+ ]);
410
+ function assertVeniceBase(chainId) {
411
+ if (!isVeniceBaseChain(chainId)) {
412
+ throw new Error(`Venice protocol is only supported on Base (chain id ${VENICE_BASE_CHAIN_ID}).`);
413
+ }
414
+ }
415
+ function basePublicClient2(rpcUrl) {
416
+ const ch = defineChain({
417
+ id: VENICE_BASE_CHAIN_ID,
418
+ name: "Base",
419
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
420
+ rpcUrls: { default: { http: [rpcUrl.trim()] } }
421
+ });
422
+ return createPublicClient({ chain: ch, transport: http(rpcUrl.trim()) });
423
+ }
424
+ async function appendErc20ApproveIfNeeded(args) {
425
+ const client = basePublicClient2(args.rpcUrl);
426
+ const allowance = await client.readContract({
427
+ address: args.token,
428
+ abi: erc20AllowanceAbi,
429
+ functionName: "allowance",
430
+ args: [args.owner, args.spender]
431
+ });
432
+ if (allowance >= args.amountWei) return;
433
+ if (allowance > 0n) {
434
+ args.steps.push({
435
+ kind: "approve",
436
+ to: args.token,
437
+ data: encodeFunctionData({ abi: erc20ApproveAbi, functionName: "approve", args: [args.spender, 0n] }),
438
+ value: 0n
439
+ });
440
+ }
441
+ args.steps.push({
442
+ kind: "approve",
443
+ to: args.token,
444
+ data: encodeFunctionData({
445
+ abi: erc20ApproveAbi,
446
+ functionName: "approve",
447
+ args: [args.spender, args.amountWei]
448
+ }),
449
+ value: 0n
450
+ });
451
+ }
452
+ async function buildVeniceBatch(args) {
453
+ assertVeniceBase(args.chainId);
454
+ const executor = getAddress(args.executorAddress);
455
+ const evmSteps = args.steps.map((s) => ({
456
+ to: s.to,
457
+ data: s.data,
458
+ value: s.value,
459
+ fallbackGas: s.kind === "approve" ? ERC20_APPROVE_FALLBACK : args.actionFallbackGas
460
+ }));
461
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
462
+ const gasLimitConfig = args.useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
463
+ return buildEvmMultisignBatch({
464
+ context: {
465
+ chainCategory: "evm",
466
+ keyGen: args.keyGen,
467
+ purposeText: args.purposeText,
468
+ chainId: VENICE_BASE_CHAIN_ID,
469
+ rpcUrl: args.rpcUrl.trim(),
470
+ executorAddress: executor,
471
+ chainDetail: args.chainDetail,
472
+ useCustomGas: args.useCustomGas,
473
+ customGasChainDetails: args.customGasChainDetails
474
+ },
475
+ steps: evmSteps,
476
+ purposeSuffix: args.purposeSuffix,
477
+ firstMsgRawNo0x: firstDataNo0x,
478
+ destinationAddress: args.steps[0].to,
479
+ resolveGasLimit: ({ index, estimatedGas }) => {
480
+ const s = args.steps[index];
481
+ if (s.kind === "action" && index > 0) {
482
+ const gas = args.actionFallbackGas;
483
+ return args.useCustomGas ? gasLimitFromEstimateAndChainConfig(gas, gasLimitConfig) : gas;
484
+ }
485
+ return estimatedGas;
486
+ },
487
+ buildBatchMeta: ({ index, gasLimit }) => args.makeBatchMeta({ index, step: args.steps[index], gasLimit: BigInt(gasLimit) })
488
+ });
489
+ }
490
+ async function parseHumanAmount(args) {
491
+ const client = basePublicClient2(args.rpcUrl);
492
+ let dec = VENICE_TOKEN_DECIMALS;
493
+ try {
494
+ dec = Number(
495
+ await client.readContract({ address: args.token, abi: erc20AllowanceAbi, functionName: "decimals" })
496
+ );
497
+ } catch {
498
+ }
499
+ const amountWei = parseUnits(args.amountHuman.trim(), dec);
500
+ if (amountWei === 0n) throw new Error("Amount is zero after converting with token decimals.");
501
+ return amountWei;
502
+ }
503
+ async function buildEvmMultisignBodyVeniceStakeVvv(args) {
504
+ assertVeniceBase(args.chainId);
505
+ const vvv = getAddress(VVV_BASE);
506
+ const staking = getAddress(VENICE_STAKING_BASE);
507
+ const executor = getAddress(args.executorAddress);
508
+ const amountWei = await parseHumanAmount({ rpcUrl: args.rpcUrl, token: vvv, amountHuman: args.amountHuman });
509
+ const steps = [];
510
+ await appendErc20ApproveIfNeeded({ steps, rpcUrl: args.rpcUrl, token: vvv, spender: staking, owner: executor, amountWei });
511
+ steps.push({
512
+ kind: "action",
513
+ to: staking,
514
+ data: encodeFunctionData({ abi: stakingAbi, functionName: "stake", args: [amountWei] }),
515
+ value: 0n
516
+ });
517
+ const n = steps.length;
518
+ return buildVeniceBatch({
519
+ ...args,
520
+ executorAddress: executor,
521
+ steps,
522
+ actionFallbackGas: VENICE_STAKE_VVV_FALLBACK,
523
+ purposeSuffix: n === 1 ? "Venice: stake VVV \u2192 sVVV on Base (allowance already set)." : `Venice: ${n}-tx batch \u2014 approve VVV, then stake on Base.`,
524
+ makeBatchMeta: ({ step }) => ({
525
+ signatureText: JSON.stringify({
526
+ kind: "Venice",
527
+ step: step.kind === "approve" ? "approve_vvv" : "stake_vvv",
528
+ amountHuman: args.amountHuman
529
+ }),
530
+ evm: { type: "venice_stake_vvv", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
531
+ })
532
+ });
533
+ }
534
+ async function buildEvmMultisignBodyVeniceInitiateUnstakeSvvv(args) {
535
+ assertVeniceBase(args.chainId);
536
+ const staking = getAddress(VENICE_STAKING_BASE);
537
+ const amountWei = await parseHumanAmount({
538
+ rpcUrl: args.rpcUrl,
539
+ token: staking,
540
+ amountHuman: args.amountHuman
541
+ });
542
+ const steps = [
543
+ {
544
+ kind: "action",
545
+ to: staking,
546
+ data: encodeFunctionData({ abi: stakingAbi, functionName: "initiateUnstake", args: [amountWei] }),
547
+ value: 0n
548
+ }
549
+ ];
550
+ return buildVeniceBatch({
551
+ ...args,
552
+ steps,
553
+ actionFallbackGas: VENICE_STAKING_ACTION_FALLBACK,
554
+ purposeSuffix: "Venice: initiate sVVV unstake on Base (7-day cooldown before claim).",
555
+ makeBatchMeta: () => ({
556
+ signatureText: JSON.stringify({ kind: "Venice", step: "initiate_unstake_svvv", amountHuman: args.amountHuman }),
557
+ evm: { type: "venice_initiate_unstake_svvv", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
558
+ })
559
+ });
560
+ }
561
+ async function buildEvmMultisignBodyVeniceCompleteUnstakeSvvv(args) {
562
+ assertVeniceBase(args.chainId);
563
+ const staking = getAddress(VENICE_STAKING_BASE);
564
+ const steps = [
565
+ {
566
+ kind: "action",
567
+ to: staking,
568
+ data: encodeFunctionData({ abi: stakingAbi, functionName: "unstake", args: [] }),
569
+ value: 0n
570
+ }
571
+ ];
572
+ return buildVeniceBatch({
573
+ ...args,
574
+ steps,
575
+ actionFallbackGas: VENICE_STAKING_ACTION_FALLBACK,
576
+ purposeSuffix: "Venice: claim VVV after sVVV unstake cooldown on Base.",
577
+ makeBatchMeta: () => ({
578
+ signatureText: JSON.stringify({ kind: "Venice", step: "complete_unstake_svvv" }),
579
+ evm: { type: "venice_complete_unstake_svvv", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
580
+ })
581
+ });
582
+ }
583
+ async function buildEvmMultisignBodyVeniceMintDiem(args) {
584
+ assertVeniceBase(args.chainId);
585
+ const staking = getAddress(VENICE_STAKING_BASE);
586
+ const lockWei = await parseHumanAmount({
587
+ rpcUrl: args.rpcUrl,
588
+ token: staking,
589
+ amountHuman: args.svvvLockAmountHuman
590
+ });
591
+ const client = basePublicClient2(args.rpcUrl);
592
+ const diemTotalSupply = await client.readContract({
593
+ address: getAddress(DIEM_BASE),
594
+ abi: erc20AllowanceAbi,
595
+ functionName: "totalSupply"
596
+ });
597
+ const expectedDiem = previewDiemMintedFromSvvcLock({ svvvLockWei: lockWei, diemTotalSupplyWei: diemTotalSupply });
598
+ const minDiem = applyMinOutSlippage(expectedDiem, args.slippageBps ?? 50);
599
+ const steps = [
600
+ {
601
+ kind: "action",
602
+ to: staking,
603
+ data: encodeFunctionData({
604
+ abi: stakingAbi,
605
+ functionName: "mintDiem",
606
+ args: [lockWei, minDiem]
607
+ }),
608
+ value: 0n
609
+ }
610
+ ];
611
+ return buildVeniceBatch({
612
+ ...args,
613
+ steps,
614
+ actionFallbackGas: VENICE_MINT_DIEM_FALLBACK,
615
+ purposeSuffix: "Venice: lock sVVV and mint DIEM on Base.",
616
+ makeBatchMeta: () => ({
617
+ signatureText: JSON.stringify({
618
+ kind: "Venice",
619
+ step: "mint_diem",
620
+ svvvLockAmountHuman: args.svvvLockAmountHuman,
621
+ minDiemOutWei: minDiem.toString()
622
+ }),
623
+ evm: { type: "venice_mint_diem", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
624
+ })
625
+ });
626
+ }
627
+ async function buildEvmMultisignBodyVeniceBurnDiem(args) {
628
+ assertVeniceBase(args.chainId);
629
+ const staking = getAddress(VENICE_STAKING_BASE);
630
+ const diem = getAddress(DIEM_BASE);
631
+ const executor = getAddress(args.executorAddress);
632
+ const amountWei = await parseHumanAmount({ rpcUrl: args.rpcUrl, token: diem, amountHuman: args.amountHuman });
633
+ const steps = [];
634
+ await appendErc20ApproveIfNeeded({ steps, rpcUrl: args.rpcUrl, token: diem, spender: staking, owner: executor, amountWei });
635
+ steps.push({
636
+ kind: "action",
637
+ to: staking,
638
+ data: encodeFunctionData({ abi: stakingAbi, functionName: "burnDiem", args: [amountWei] }),
639
+ value: 0n
640
+ });
641
+ const n = steps.length;
642
+ return buildVeniceBatch({
643
+ ...args,
644
+ executorAddress: executor,
645
+ steps,
646
+ actionFallbackGas: VENICE_MINT_DIEM_FALLBACK,
647
+ purposeSuffix: n === 1 ? "Venice: burn DIEM to unlock sVVV on Base (allowance already set)." : `Venice: ${n}-tx batch \u2014 approve DIEM, then burn to unlock sVVV.`,
648
+ makeBatchMeta: ({ step }) => ({
649
+ signatureText: JSON.stringify({
650
+ kind: "Venice",
651
+ step: step.kind === "approve" ? "approve_diem_burn" : "burn_diem",
652
+ amountHuman: args.amountHuman
653
+ }),
654
+ evm: { type: "venice_burn_diem", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
655
+ })
656
+ });
657
+ }
658
+ async function buildEvmMultisignBodyVeniceStakeDiem(args) {
659
+ assertVeniceBase(args.chainId);
660
+ const diem = getAddress(DIEM_BASE);
661
+ const executor = getAddress(args.executorAddress);
662
+ const amountWei = await parseHumanAmount({ rpcUrl: args.rpcUrl, token: diem, amountHuman: args.amountHuman });
663
+ const steps = [];
664
+ await appendErc20ApproveIfNeeded({ steps, rpcUrl: args.rpcUrl, token: diem, spender: diem, owner: executor, amountWei });
665
+ steps.push({
666
+ kind: "action",
667
+ to: diem,
668
+ data: encodeFunctionData({ abi: diemStakingAbi2, functionName: "stake", args: [amountWei] }),
669
+ value: 0n
670
+ });
671
+ const n = steps.length;
672
+ return buildVeniceBatch({
673
+ ...args,
674
+ executorAddress: executor,
675
+ steps,
676
+ actionFallbackGas: VENICE_STAKE_VVV_FALLBACK,
677
+ purposeSuffix: n === 1 ? "Venice: stake DIEM for API credits on Base (allowance already set)." : `Venice: ${n}-tx batch \u2014 approve DIEM, then stake for API credits.`,
678
+ makeBatchMeta: ({ step }) => ({
679
+ signatureText: JSON.stringify({
680
+ kind: "Venice",
681
+ step: step.kind === "approve" ? "approve_diem_stake" : "stake_diem",
682
+ amountHuman: args.amountHuman
683
+ }),
684
+ evm: { type: "venice_stake_diem", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
685
+ })
686
+ });
687
+ }
688
+ async function buildEvmMultisignBodyVeniceInitiateUnstakeDiem(args) {
689
+ assertVeniceBase(args.chainId);
690
+ const diem = getAddress(DIEM_BASE);
691
+ const amountWei = await parseHumanAmount({ rpcUrl: args.rpcUrl, token: diem, amountHuman: args.amountHuman });
692
+ const steps = [
693
+ {
694
+ kind: "action",
695
+ to: diem,
696
+ data: encodeFunctionData({ abi: diemStakingAbi2, functionName: "initiateUnstake", args: [amountWei] }),
697
+ value: 0n
698
+ }
699
+ ];
700
+ return buildVeniceBatch({
701
+ ...args,
702
+ steps,
703
+ actionFallbackGas: VENICE_STAKING_ACTION_FALLBACK,
704
+ purposeSuffix: "Venice: initiate DIEM unstake on Base (1-day cooldown).",
705
+ makeBatchMeta: () => ({
706
+ signatureText: JSON.stringify({ kind: "Venice", step: "initiate_unstake_diem", amountHuman: args.amountHuman }),
707
+ evm: { type: "venice_initiate_unstake_diem", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
708
+ })
709
+ });
710
+ }
711
+ async function buildEvmMultisignBodyVeniceCompleteUnstakeDiem(args) {
712
+ assertVeniceBase(args.chainId);
713
+ const diem = getAddress(DIEM_BASE);
714
+ const steps = [
715
+ {
716
+ kind: "action",
717
+ to: diem,
718
+ data: encodeFunctionData({ abi: diemStakingAbi2, functionName: "unstake", args: [] }),
719
+ value: 0n
720
+ }
721
+ ];
722
+ return buildVeniceBatch({
723
+ ...args,
724
+ steps,
725
+ actionFallbackGas: VENICE_STAKING_ACTION_FALLBACK,
726
+ purposeSuffix: "Venice: claim DIEM after unstake cooldown on Base.",
727
+ makeBatchMeta: () => ({
728
+ signatureText: JSON.stringify({ kind: "Venice", step: "complete_unstake_diem" }),
729
+ evm: { type: "venice_complete_unstake_diem", version: 1, chainId: String(VENICE_BASE_CHAIN_ID) }
730
+ })
731
+ });
732
+ }
733
+
734
+ // src/protocols/evm/venice/veniceApi.ts
735
+ var VENICE_MODELS_URL = "https://api.venice.ai/api/v1/models";
736
+ async function fetchVeniceModels(args) {
737
+ const type = (args?.type ?? "all").trim() || "all";
738
+ const url = `${VENICE_MODELS_URL}?type=${encodeURIComponent(type)}`;
739
+ const headers = { Accept: "application/json" };
740
+ const key = args?.apiKey?.trim();
741
+ if (key) headers.Authorization = `Bearer ${key}`;
742
+ const res = await fetch(url, { headers, signal: AbortSignal.timeout(3e4) });
743
+ if (!res.ok) {
744
+ const text = await res.text().catch(() => "");
745
+ throw new Error(`Venice models API ${res.status}: ${text.slice(0, 200)}`);
746
+ }
747
+ const json = await res.json();
748
+ const raw = Array.isArray(json.data) ? json.data : Array.isArray(json.models) ? json.models : [];
749
+ const models = [];
750
+ for (const item of raw) {
751
+ if (!item || typeof item !== "object") continue;
752
+ const o = item;
753
+ const id = String(o.id ?? o.model ?? o.name ?? "").trim();
754
+ if (!id) continue;
755
+ const spec = o.model_spec ?? o.spec;
756
+ models.push({
757
+ id,
758
+ type: String(o.type ?? spec?.type ?? "").trim() || void 0,
759
+ offline: Boolean(o.offline ?? spec?.offline),
760
+ beta: Boolean(o.beta ?? spec?.beta)
761
+ });
762
+ }
763
+ return { models };
764
+ }
765
+ async function veniceListModelsSummary(args) {
766
+ const { models } = await fetchVeniceModels(args);
767
+ return {
768
+ type: args?.type ?? "all",
769
+ count: models.length,
770
+ models
771
+ };
772
+ }
773
+
774
+ // src/protocols/evm/venice/mcpReads.ts
775
+ async function veniceReadStakingStateSummary(args) {
776
+ const chainId = args.chainId ?? VENICE_BASE_CHAIN_ID;
777
+ if (chainId !== VENICE_BASE_CHAIN_ID) {
778
+ throw new Error(`Venice staking reads require Base chain id ${VENICE_BASE_CHAIN_ID}.`);
779
+ }
780
+ const wallet = getAddress(args.executorAddress);
781
+ const state = await readVeniceWalletStakingState({ rpcUrl: args.rpcUrl, wallet });
782
+ return formatVeniceStakingStateSummary(state);
783
+ }
784
+ async function veniceReadMintDiemPreviewSummary(args) {
785
+ const ch = defineChain({
786
+ id: VENICE_BASE_CHAIN_ID,
787
+ name: "Base",
788
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
789
+ rpcUrls: { default: { http: [args.rpcUrl.trim()] } }
790
+ });
791
+ const client = createPublicClient({ chain: ch, transport: http(args.rpcUrl.trim()) });
792
+ const lockWei = parseUnits(args.svvvLockAmountHuman.trim(), VENICE_TOKEN_DECIMALS);
793
+ const diemTotalSupply = await client.readContract({
794
+ address: getAddress(DIEM_BASE),
795
+ abi: parseAbi(["function totalSupply() view returns (uint256)"]),
796
+ functionName: "totalSupply"
797
+ });
798
+ const mintRate = computeMintRateSvvcPerDiem(diemTotalSupply);
799
+ const expectedDiemWei = previewDiemMintedFromSvvcLock({ svvvLockWei: lockWei, diemTotalSupplyWei: diemTotalSupply });
800
+ return {
801
+ svvvLockAmountHuman: args.svvvLockAmountHuman,
802
+ svvvLockWei: lockWei.toString(),
803
+ mintRateSvvcPerDiem: mintRate,
804
+ expectedDiemWei: expectedDiemWei.toString(),
805
+ diemTotalSupplyWei: diemTotalSupply.toString(),
806
+ stakingContract: getAddress(VENICE_STAKING_BASE)
807
+ };
808
+ }
809
+
810
+ // src/protocols/evm/venice/index.ts
811
+ var veniceProtocolModule = {
812
+ id: VENICE_PROTOCOL_ID,
813
+ chainCategory: "evm",
814
+ isChainSupported(ctx) {
815
+ if (ctx.chainCategory !== "evm") return false;
816
+ const n = typeof ctx.chainId === "number" ? ctx.chainId : Number.parseInt(String(ctx.chainId), 10);
817
+ return isVeniceBaseChain(n);
818
+ },
819
+ isTokenSupported(token) {
820
+ return token.category === "evm" && token.kind === "erc20";
821
+ },
822
+ actions: [
823
+ {
824
+ id: "venice.stake-vvv",
825
+ protocolId: VENICE_PROTOCOL_ID,
826
+ chainCategory: "evm",
827
+ description: "Stake VVV \u2192 sVVV on Base",
828
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
829
+ params: { amountHuman: { type: "string", required: true, description: "VVV amount" } }
830
+ },
831
+ {
832
+ id: "venice.initiate-unstake-svvv",
833
+ protocolId: VENICE_PROTOCOL_ID,
834
+ chainCategory: "evm",
835
+ description: "Start sVVV unstake (7-day cooldown)",
836
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
837
+ params: { amountHuman: { type: "string", required: true, description: "sVVV amount" } }
838
+ },
839
+ {
840
+ id: "venice.complete-unstake-svvv",
841
+ protocolId: VENICE_PROTOCOL_ID,
842
+ chainCategory: "evm",
843
+ description: "Claim VVV after sVVV unstake cooldown",
844
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
845
+ params: {}
846
+ },
847
+ {
848
+ id: "venice.mint-diem",
849
+ protocolId: VENICE_PROTOCOL_ID,
850
+ chainCategory: "evm",
851
+ description: "Lock sVVV and mint DIEM",
852
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
853
+ params: {
854
+ svvvLockAmountHuman: { type: "string", required: true, description: "sVVV to lock" },
855
+ slippageBps: { type: "number", required: false, description: "Min DIEM slippage bps (default 50)" }
856
+ }
857
+ },
858
+ {
859
+ id: "venice.burn-diem",
860
+ protocolId: VENICE_PROTOCOL_ID,
861
+ chainCategory: "evm",
862
+ description: "Burn DIEM to unlock locked sVVV",
863
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
864
+ params: { amountHuman: { type: "string", required: true, description: "DIEM to burn" } }
865
+ },
866
+ {
867
+ id: "venice.stake-diem",
868
+ protocolId: VENICE_PROTOCOL_ID,
869
+ chainCategory: "evm",
870
+ description: "Stake DIEM for Venice API credits ($1/day per DIEM)",
871
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
872
+ params: { amountHuman: { type: "string", required: true, description: "DIEM amount" } }
873
+ },
874
+ {
875
+ id: "venice.initiate-unstake-diem",
876
+ protocolId: VENICE_PROTOCOL_ID,
877
+ chainCategory: "evm",
878
+ description: "Start DIEM unstake (1-day cooldown)",
879
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
880
+ params: { amountHuman: { type: "string", required: true, description: "Staked DIEM amount" } }
881
+ },
882
+ {
883
+ id: "venice.complete-unstake-diem",
884
+ protocolId: VENICE_PROTOCOL_ID,
885
+ chainCategory: "evm",
886
+ description: "Claim DIEM after unstake cooldown",
887
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
888
+ params: {}
889
+ }
890
+ ]
891
+ };
892
+ registerProtocolModule(veniceProtocolModule);
893
+ var VENICE_BASE_TOKENS = {
894
+ chainId: VENICE_BASE_CHAIN_ID,
895
+ vvv: VVV_BASE,
896
+ diem: DIEM_BASE
897
+ };
898
+
899
+ export { DIEM_BASE, DIEM_TARGET_SUPPLY_HUMAN, VENICE_BASE_CHAIN_ID, VENICE_BASE_TOKENS, VENICE_DIEM_CALCULATOR, VENICE_DOCS_VVV_DIEM, VENICE_PROTOCOL_ID, VENICE_STAKING_BASE, VENICE_TOKEN_DASHBOARD, VENICE_TOKEN_DECIMALS, VVV_BASE, applyMinOutSlippage, buildEvmMultisignBodyVeniceBurnDiem, buildEvmMultisignBodyVeniceCompleteUnstakeDiem, buildEvmMultisignBodyVeniceCompleteUnstakeSvvv, buildEvmMultisignBodyVeniceInitiateUnstakeDiem, buildEvmMultisignBodyVeniceInitiateUnstakeSvvv, buildEvmMultisignBodyVeniceMintDiem, buildEvmMultisignBodyVeniceStakeDiem, buildEvmMultisignBodyVeniceStakeVvv, computeMintRateSvvcPerDiem, fetchVeniceModels, formatVeniceStakingStateSummary, isVeniceBaseChain, isVeniceDiemOnBase, isVeniceTokenOnBase, isVeniceVvvOnBase, previewDiemMintedFromSvvcLock, readVeniceDiemStakedInfo, readVeniceWalletStakingState, veniceListModelsSummary, veniceProtocolModule, veniceReadMintDiemPreviewSummary, veniceReadStakingStateSummary };
900
+ //# sourceMappingURL=index.js.map
901
+ //# sourceMappingURL=index.js.map