Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ts): morpho vault deposit & withdraw actions #114

Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
209243c
first pass implementing morpho value nodejs actions deposit and withd…
stat Jan 15, 2025
7c904fe
linting
stat Jan 15, 2025
afff0ed
Update cdp-agentkit-core/typescript/src/actions/morpho/utils.ts
stat Jan 15, 2025
a1bb812
Update cdp-agentkit-core/typescript/src/actions/morpho/withdraw.ts
stat Jan 15, 2025
789f7c9
feedback
stat Jan 15, 2025
2a2d80d
Update cdp-agentkit-core/typescript/src/actions/morpho/constants.ts
stat Jan 15, 2025
a44c296
Update cdp-agentkit-core/typescript/src/actions/morpho/deposit.ts
stat Jan 15, 2025
7d1140b
Update cdp-agentkit-core/typescript/src/actions/morpho/deposit.ts
stat Jan 15, 2025
1a8ca2b
feedback
stat Jan 15, 2025
f75e2c2
parseint to bigint
stat Jan 16, 2025
949918b
refinements
stat Jan 16, 2025
8e68fa9
input schema assets validation fix and additional tests
stat Jan 16, 2025
9356943
additional refinements and tests
stat Jan 16, 2025
2cc9cef
changelog
stat Jan 16, 2025
fd0c7b0
strings adjustments
stat Jan 16, 2025
b313780
additional strings adjustments
stat Jan 16, 2025
f2c5aea
additional strings adjustments
stat Jan 16, 2025
fe87285
rebasing to master
stat Jan 17, 2025
be12c6b
Update cdp-agentkit-core/typescript/src/actions/cdp/morpho/deposit.ts
stat Jan 22, 2025
14a6396
Update cdp-agentkit-core/typescript/src/actions/cdp/morpho/deposit.ts
stat Jan 22, 2025
f2c42f2
Update cdp-agentkit-core/typescript/src/actions/cdp/morpho/utils.ts
stat Jan 22, 2025
27f0ff7
implementing feedback
stat Jan 22, 2025
37b72e7
feedback
stat Jan 22, 2025
801e995
linting
stat Jan 22, 2025
945fcfe
moving ERC20_APPROVE_ABI into top level constants
stat Jan 22, 2025
c574716
the rest
stat Jan 22, 2025
e2c166b
Update cdp-agentkit-core/typescript/src/actions/cdp/defi/morpho/depos…
stat Jan 22, 2025
e9e4373
Update cdp-agentkit-core/typescript/src/actions/cdp/index.ts
stat Jan 22, 2025
bbf998d
feedback
stat Jan 22, 2025
0aa1f28
linting
stat Jan 22, 2025
d4919b9
Update cdp-agentkit-core/typescript/src/tests/defi_morpho_deposit_tes…
stat Jan 22, 2025
2d082bb
removing code comment
stat Jan 22, 2025
fb88c5c
removing unused import
stat Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cdp-agentkit-core/typescript/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Added

- Added `morpho_deposit` action to deposit to Morpho Vault.
- Added `morpho_withdrawal` action to withdraw from Morpho Vault.

## [0.0.12] - 2025-01-17

### Added
Expand Down
14 changes: 14 additions & 0 deletions cdp-agentkit-core/typescript/src/actions/cdp/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const ERC20_APPROVE_ABI = [
{
constant: false,
inputs: [
{ internalType: "address", name: "spender", type: "address" },
{ internalType: "uint256", name: "value", type: "uint256" },
],
name: "approve",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const MORPHO_BASE_ADDRESS = "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb";

export const METAMORPHO_ABI = [
{
inputs: [
{ internalType: "uint256", name: "assets", type: "uint256" },
{ internalType: "address", name: "receiver", type: "address" },
],
name: "deposit",
outputs: [{ internalType: "uint256", name: "shares", type: "uint256" }],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{ internalType: "uint256", name: "assets", type: "uint256" },
{ internalType: "address", name: "receiver", type: "address" },
{ internalType: "address", name: "owner", type: "address" },
],
name: "withdraw",
outputs: [{ internalType: "uint256", name: "shares", type: "uint256" }],
stateMutability: "nonpayable",
type: "function",
},
];
113 changes: 113 additions & 0 deletions cdp-agentkit-core/typescript/src/actions/cdp/defi/morpho/deposit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Asset, Wallet } from "@coinbase/coinbase-sdk";
import { z } from "zod";
import { Decimal } from "decimal.js";

import { CdpAction } from "../../cdp_action";
import { approve } from "../../utils";

import { METAMORPHO_ABI } from "./constants";

const DEPOSIT_PROMPT = `
This tool allows depositing assets into a Morpho Vault.

It takes:
- vaultAddress: The address of the Morpho Vault to deposit to
- assets: The amount of assets to deposit in whole units
Examples for WETH:
- 1 WETH
- 0.1 WETH
- 0.01 WETH
- receiver: The address to receive the shares
- tokenAddress: The address of the token to approve

Important notes:
- Make sure to use the exact amount provided. Do not convert units for assets for this action.
- Please use a token address (example 0x4200000000000000000000000000000000000006) for the tokenAddress field. If you are unsure of the token address, please clarify what the requested token address is before continuing.
`;

/**
* Input schema for Morpho Vault deposit action.
*/
export const MorphoDepositInput = z
.object({
assets: z
.string()
.regex(/^\d+(\.\d+)?$/, "Must be a valid integer or decimal value")
.describe("The quantity of assets to deposit, in whole units"),
receiver: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe(
"The address that will own the position on the vault which will receive the shares",
),
tokenAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The address of the assets token to approve for deposit"),
vaultAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The address of the Morpho Vault to deposit to"),
})
.describe("Input schema for Morpho Vault deposit action");

/**
* Deposits assets into a Morpho Vault
* @param Wallet - The wallet instance to execute the transaction
* @param args - The input arguments for the action
* @returns A success message with transaction details or an error message
*/
export async function depositToMorpho(
wallet: Wallet,
args: z.infer<typeof MorphoDepositInput>,
): Promise<string> {
const assets = new Decimal(args.assets);

if (assets.comparedTo(new Decimal(0.0)) != 1) {
return "Error: Assets amount must be greater than 0";
}

try {
const tokenAsset = await Asset.fetch(wallet.getNetworkId(), args.tokenAddress);
const atomicAssets = tokenAsset.toAtomicAmount(assets);
// const atomicAssets = BigInt(tokenAsset.toAtomicAmount(assets).toString());

const approvalResult = await approve(
wallet,
args.tokenAddress,
args.vaultAddress,
atomicAssets,
);
if (approvalResult.startsWith("Error")) {
return `Error approving Morpho Vault as spender: ${approvalResult}`;
}

const contractArgs = {
assets: atomicAssets.toString(),
receiver: args.receiver,
};

const invocation = await wallet.invokeContract({
contractAddress: args.vaultAddress,
method: "deposit",
abi: METAMORPHO_ABI,
args: contractArgs,
});

const result = await invocation.wait();

return `Deposited ${args.assets} to Morpho Vault ${args.vaultAddress} with transaction hash: ${result.getTransactionHash()} and transaction link: ${result.getTransactionLink()}`;
} catch (error) {
return `Error depositing to Morpho Vault: ${error}`;
}
}

/**
* Morpho Vault deposit action.
*/
export class MorphoDepositAction implements CdpAction<typeof MorphoDepositInput> {
public name = "morpho_deposit";
public description = DEPOSIT_PROMPT;
public argsSchema = MorphoDepositInput;
public func = depositToMorpho;
}
18 changes: 18 additions & 0 deletions cdp-agentkit-core/typescript/src/actions/cdp/defi/morpho/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CdpAction, CdpActionSchemaAny } from "../../cdp_action";

import { MorphoDepositAction } from "./deposit";
import { MorphoWithdrawAction } from "./withdraw";

/**
* Retrieves all Morpho action instances.
* WARNING: All new Morpho action classes must be instantiated here to be discovered.
*
* @returns - Array of Morpho action instances
*/
export function getAllMorphoActions(): CdpAction<CdpActionSchemaAny>[] {
return [new MorphoDepositAction(), new MorphoWithdrawAction()];
}

export const MORPHO_ACTIONS = getAllMorphoActions();

export { MorphoDepositAction, MorphoWithdrawAction };
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Asset, Wallet } from "@coinbase/coinbase-sdk";
import { z } from "zod";

import { CdpAction } from "../../cdp_action";
import { METAMORPHO_ABI } from "./constants";

const WITHDRAW_PROMPT = `
This tool allows withdrawing assets from a Morpho Vault. It takes:

- vaultAddress: The address of the Morpho Vault to withdraw from
- assets: The amount of assets to withdraw in atomic units
- receiver: The address to receive the shares
`;

/**
* Input schema for Morpho Vault withdraw action.
*/
export const MorphoWithdrawInput = z
.object({
vaultAddress: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The address of the Morpho Vault to withdraw from"),
assets: z
.string()
.regex(/^\d+$/, "Must be a valid whole number")
.describe("The amount of assets to withdraw in atomic units e.g. 1"),
receiver: z
.string()
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format")
.describe("The address to receive the shares"),
})
.strip()
.describe("Input schema for Morpho Vault withdraw action");

/**
* Withdraw assets from a Morpho Vault.
*
* @param wallet - The wallet to execute the withdrawal from
* @param args - The input arguments for the action
* @returns A success message with transaction details or error message
*/
export async function withdrawFromMorpho(
wallet: Wallet,
args: z.infer<typeof MorphoWithdrawInput>,
): Promise<string> {
if (BigInt(args.assets) <= 0) {
return "Error: Assets amount must be greater than 0";
}

try {
const invocation = await wallet.invokeContract({
contractAddress: args.vaultAddress,
method: "withdraw",
abi: METAMORPHO_ABI,
args: {
assets: args.assets,
receiver: args.receiver,
owner: args.receiver,
},
});

const result = await invocation.wait();

return `Withdrawn ${args.assets} from Morpho Vault ${args.vaultAddress} with transaction hash: ${result.getTransaction().getTransactionHash()} and transaction link: ${result.getTransaction().getTransactionLink()}`;
} catch (error) {
return `Error withdrawing from Morpho Vault: ${error}`;
}
}

/**
* Morpho Vault withdraw action.
*/
export class MorphoWithdrawAction implements CdpAction<typeof MorphoWithdrawInput> {
public name = "morpho_withdraw";
public description = WITHDRAW_PROMPT;
public argsSchema = MorphoWithdrawInput;
public func = withdrawFromMorpho;
}
9 changes: 7 additions & 2 deletions cdp-agentkit-core/typescript/src/actions/cdp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { TradeAction } from "./trade";
import { TransferAction } from "./transfer";
import { TransferNftAction } from "./transfer_nft";
import { WrapEthAction } from "./wrap_eth";
import { WOW_ACTIONS } from "./defi/wow";

import { MORPHO_ACTIONS } from "./defi/morpho";
import { PYTH_ACTIONS } from "./data/pyth";
import { WOW_ACTIONS } from "./defi/wow";

/**
* Retrieves all CDP action instances.
Expand All @@ -37,7 +39,10 @@ export function getAllCdpActions(): CdpAction<CdpActionSchemaAny>[] {
];
}

export const CDP_ACTIONS = getAllCdpActions().concat(WOW_ACTIONS).concat(PYTH_ACTIONS);
export const CDP_ACTIONS = getAllCdpActions()
.concat(MORPHO_ACTIONS)
.concat(PYTH_ACTIONS)
.concat(WOW_ACTIONS);

export {
CdpAction,
Expand Down
36 changes: 36 additions & 0 deletions cdp-agentkit-core/typescript/src/actions/cdp/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Wallet } from "@coinbase/coinbase-sdk";

import { ERC20_APPROVE_ABI } from "./constants";

/**
* Approve a spender to spend a specified amount of tokens.
* @param wallet - The wallet to execute the approval from
* @param tokenAddress - The address of the token contract
* @param spender - The address of the spender
* @param amount - The amount of tokens to approve
* @returns A success message with transaction hash or error message
*/
export async function approve(
wallet: Wallet,
tokenAddress: string,
spender: string,
amount: bigint,
): Promise<string> {
try {
const invocation = await wallet.invokeContract({
contractAddress: tokenAddress,
method: "approve",
abi: ERC20_APPROVE_ABI,
args: {
spender: spender,
value: amount.toString(),
},
});

const result = await invocation.wait();

return `Approved ${amount} tokens for ${spender} with transaction hash: ${result.getTransactionHash()}`;
} catch (error) {
return `Error approving tokens: ${error}`;
}
}
Loading
Loading