Skip to content

Commit

Permalink
feat(PSDK-854): Morpho Vault Deposit/Withdraw Actions
Browse files Browse the repository at this point in the history
  • Loading branch information
John-peterson-coinbase committed Jan 14, 2025
1 parent 638244c commit b78ce96
Show file tree
Hide file tree
Showing 11 changed files with 578 additions and 0 deletions.
5 changes: 5 additions & 0 deletions cdp-agentkit-core/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.8] - 2025-01-13

### Added
Expand Down
4 changes: 4 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from cdp_agentkit_core.actions.get_balance import GetBalanceAction
from cdp_agentkit_core.actions.get_wallet_details import GetWalletDetailsAction
from cdp_agentkit_core.actions.mint_nft import MintNftAction
from cdp_agentkit_core.actions.morpho.deposit import MorphoDepositAction
from cdp_agentkit_core.actions.morpho.withdraw import MorphoWithdrawAction
from cdp_agentkit_core.actions.register_basename import RegisterBasenameAction
from cdp_agentkit_core.actions.request_faucet_funds import RequestFaucetFundsAction
from cdp_agentkit_core.actions.trade import TradeAction
Expand Down Expand Up @@ -42,4 +44,6 @@ def get_all_cdp_actions() -> list[type[CdpAction]]:
"WowCreateTokenAction",
"WowSellTokenAction",
"WrapEthAction",
"MorphoDepositAction",
"MorphoWithdrawAction",
]
Empty file.
102 changes: 102 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/morpho/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
MORPHO_BASE_ADDRESS = "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"

ERC20_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",
},
{
"constant": True,
"inputs": [
{"internalType": "address", "name": "owner", "type": "address"},
{"internalType": "address", "name": "spender", "type": "address"},
],
"name": "allowance",
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
"payable": False,
"stateMutability": "view",
"type": "function",
},
{
"constant": True,
"inputs": [{"internalType": "address", "name": "account", "type": "address"}],
"name": "balanceOf",
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
"payable": False,
"stateMutability": "view",
"type": "function",
},
{
"constant": False,
"inputs": [
{"internalType": "address", "name": "recipient", "type": "address"},
{"internalType": "uint256", "name": "amount", "type": "uint256"},
],
"name": "transfer",
"outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
"payable": False,
"stateMutability": "nonpayable",
"type": "function",
},
{
"anonymous": False,
"inputs": [
{"indexed": True, "internalType": "address", "name": "owner", "type": "address"},
{"indexed": True, "internalType": "address", "name": "spender", "type": "address"},
{"indexed": False, "internalType": "uint256", "name": "value", "type": "uint256"},
],
"name": "Approval",
"type": "event",
},
{
"anonymous": False,
"inputs": [
{"indexed": True, "internalType": "address", "name": "from", "type": "address"},
{"indexed": True, "internalType": "address", "name": "to", "type": "address"},
{"indexed": False, "internalType": "uint256", "name": "value", "type": "uint256"},
],
"name": "Transfer",
"type": "event",
},
{
"constant": True,
"inputs": [],
"name": "decimals",
"outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}],
"payable": False,
"stateMutability": "view",
"type": "function",
},
]

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",
},
]
88 changes: 88 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/morpho/deposit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from collections.abc import Callable
from decimal import Decimal

from cdp import Asset, Wallet
from pydantic import BaseModel

from cdp_agentkit_core.actions import CdpAction
from cdp_agentkit_core.actions.morpho.constants import METAMORPHO_ABI
from cdp_agentkit_core.actions.morpho.utils import approve


class MorphoDepositInput(BaseModel):
"""Input schema for Morpho Vault deposit action."""

vault_address: str
assets: str
receiver: str
token_address: str


DEPOSIT_PROMPT = """
This tool allows depositing assets into a Morpho Vault. It takes:
- vault_address: The address of the Morpho Vault to deposit to
- assets: The amount of assets to deposit in native units
Examples for WETH:
- 1 WETH
- 0.1 WETH
- 0.01 WETH
- receiver: The address to receive the shares
- token_address: The address of the token to approve
"""


def deposit_to_morpho(
wallet: Wallet,
vault_address: str,
assets: str,
receiver: str,
token_address: str,
) -> str:
"""Deposit assets into a Morpho Vault.
Args:
wallet (Wallet): The wallet to execute the deposit from
vault_address (str): The address of the Morpho Vault
assets (str): The amount of assets to deposit in native units (e.g., 0.01 WETH)
receiver (str): The address to receive the shares
token_address (str): The address of the token to approve
Returns:
str: A success message with transaction hash or error message
"""
if float(assets) <= 0:
return "Error: Assets amount must be greater than 0"

try:
token_asset = Asset.fetch(wallet.network_id, token_address)

atomic_assets = str(int(token_asset.to_atomic_amount(Decimal(assets))))

approval_result = approve(wallet, token_address, vault_address, atomic_assets)
if approval_result.startswith("Error"):
return f"Error approving Morpho Vault as spender: {approval_result}"

deposit_args = {"assets": atomic_assets, "receiver": receiver}

invocation = wallet.invoke_contract(
contract_address=vault_address,
method="deposit",
abi=METAMORPHO_ABI,
args=deposit_args,
).wait()

return f"Deposited {assets} to Morpho Vault {vault_address} with transaction hash: {invocation.transaction_hash} and transaction link: {invocation.transaction_link}"

except Exception as e:
return f"Error depositing to Morpho Vault: {e!s}"


class MorphoDepositAction(CdpAction):
"""Morpho Vault deposit action."""

name: str = "morpho_deposit"
description: str = DEPOSIT_PROMPT
args_schema: type[BaseModel] = MorphoDepositInput
func: Callable[..., str] = deposit_to_morpho
35 changes: 35 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/morpho/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from cdp import Wallet

from cdp_agentkit_core.actions.morpho.constants import ERC20_ABI


def approve(wallet: Wallet, token_address: str, spender: str, amount: int) -> str:
"""Approve a spender to spend a specified amount of tokens.
Args:
wallet (Wallet): The wallet to execute the approval from
token_address (str): The address of the token contract
spender (str): The address of the spender
amount (int): The amount of tokens to approve
Returns:
str: A success message with transaction hash or error message
"""
try:
amount_str = str(amount)

invocation = wallet.invoke_contract(
contract_address=token_address,
method="approve",
abi=ERC20_ABI,
args={
"spender": spender,
"value": amount_str,
},
).wait()

return f"Approved {amount} tokens for {spender} with transaction hash: {invocation.transaction_hash} and transaction link: {invocation.transaction_link}"

except Exception as e:
return f"Error approving tokens: {e!s}"
67 changes: 67 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/morpho/withdraw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel

from cdp_agentkit_core.actions import CdpAction
from cdp_agentkit_core.actions.morpho.constants import METAMORPHO_ABI


class MorphoWithdrawInput(BaseModel):
"""Input schema for Morpho Vault withdraw action."""

vault_address: str
assets: str
receiver: str


WITHDRAW_PROMPT = """
This tool allows withdrawing assets from a Morpho Vault. It takes:
- vault_address: 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
"""


def withdraw_from_morpho(wallet: Wallet, vault_address: str, assets: str, receiver: str) -> str:
"""Withdraw assets from a Morpho Vault.
Args:
wallet (Wallet): The wallet to execute the deposit from
vault_address (str): The address of the Morpho Vault
assets (str): The amount of assets to withdraw in atomic units
receiver (str): The address to receive the shares
Returns:
str: A success message with transaction hash or error message
"""
if int(assets) <= 0:
return "Error: Assets amount must be greater than 0"

try:
invocation = wallet.invoke_contract(
contract_address=vault_address,
method="withdraw",
abi=METAMORPHO_ABI,
args={
"assets": assets,
"receiver": receiver,
"owner": receiver,
},
).wait()

return f"Withdrawn {assets} from Morpho Vault {vault_address} with transaction hash: {invocation.transaction_hash} and transaction link: {invocation.transaction_link}"

except Exception as e:
return f"Error withdrawing from Morpho Vault: {e!s}"


class MorphoWithdrawAction(CdpAction):
"""Morpho Vault withdraw action."""

name: str = "morpho_withdraw"
description: str = WITHDRAW_PROMPT
args_schema: type[BaseModel] = MorphoWithdrawInput
func: Callable[..., str] = withdraw_from_morpho
Loading

0 comments on commit b78ce96

Please sign in to comment.