-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added Compound Finance Python Actions
Adds Compound Finance actions get portfolio details, supply, borrow, repay, and withdraw. Includes unit and integration (with CDP) tests for the actions. Utility functions are added to make getting borrow, supply, and health details easily accessible.
- Loading branch information
Showing
26 changed files
with
2,674 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
python/cdp-agentkit-core/cdp_agentkit_core/actions/compound/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Compound AgentKit Actions | ||
These actions allow you to supply ETH or USDC to Compound V3 markets on Base. | ||
|
||
## Actions | ||
The actions in this package are intended to support agents that want to interact with Compound V3 markets on Base. It supports the following actions: | ||
|
||
- `supply`: Supply ETH or USDC to Compound V3 markets on Base. | ||
- `borrow`: Borrow ETH or USDC from Compound V3 markets on Base. | ||
- `repay`: Repay ETH or USDC to Compound V3 markets on Base. | ||
- `withdraw`: Withdraw ETH or USDC from Compound V3 markets on Base. | ||
- `get_portfolio_details`: Get the portfolio details for the Compound V3 markets on Base. | ||
|
||
## Supported Compound Markets (aka. Comets) | ||
|
||
### Base | ||
- USDC Comet | ||
- Supply Assets: USDC, WETH, cbBTC, cbETH, wstETH | ||
- Borrow Asset: USDC | ||
|
||
### Base Sepolia | ||
- USDC Comet | ||
- Supply Assets: USDC, WETH | ||
- Borrow Asset: USDC | ||
|
||
## Limitations and Assumptions | ||
- Only supports the default wallet (i.e., `wallet.default_address`). | ||
- Only supports one Comet contract, the Base/Base Sepolia USDC Comet | ||
- The only borrowable asset is USDC as a result of the above. | ||
- Native ETH is not supported, supply must be done with Wrapped ETH (WETH). | ||
- The `approve` transaction needed for `supply` and `repay` is included in the action. | ||
- The amounts sent to these actions are _whole units_ of the asset (e.g., 0.01 ETH, 100 USDC). | ||
- Token symbols are the `asset_id` (lowercase) rather than the symbol. There's currently no way to get the symbol from the cdp `Asset` model object. | ||
|
||
## Funded by Compound Grants Program | ||
Compound Actions for AgentKit is funded by the Compound Grants Program. Learn more about the Grant on Questbook [here](https://new.questbook.app/dashboard/?role=builder&chainId=10&proposalId=678c218180bdbe26619c3ae8&grantId=66f29bb58868f5130abc054d). | ||
|
||
## Future Work | ||
- [ ] Support for bulk actions that perform common operations like leverage, deleverage, swap collateral, etc. | ||
- [ ] Add `symbol` to the `Asset` model in the Coinbase CDP SDK, a correctly cased version of the `asset_id`. | ||
- [ ] Add Compound Base Sepolia cbETH Comet to support on Base Sepolia testnet CDP API. |
13 changes: 13 additions & 0 deletions
13
python/cdp-agentkit-core/cdp_agentkit_core/actions/compound/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from cdp_agentkit_core.actions.compound.borrow import CompoundBorrowAction | ||
from cdp_agentkit_core.actions.compound.portfolio_details import CompoundPortfolioDetailsAction | ||
from cdp_agentkit_core.actions.compound.repay import CompoundRepayAction | ||
from cdp_agentkit_core.actions.compound.supply import CompoundSupplyAction | ||
from cdp_agentkit_core.actions.compound.withdraw import CompoundWithdrawAction | ||
|
||
__all__ = [ | ||
"CompoundBorrowAction", | ||
"CompoundPortfolioDetailsAction", | ||
"CompoundRepayAction", | ||
"CompoundSupplyAction", | ||
"CompoundWithdrawAction" | ||
] |
102 changes: 102 additions & 0 deletions
102
python/cdp-agentkit-core/cdp_agentkit_core/actions/compound/borrow.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
from collections.abc import Callable | ||
from decimal import Decimal | ||
from typing import Literal | ||
|
||
from cdp import Asset, Wallet | ||
from pydantic import BaseModel, Field | ||
|
||
from cdp_agentkit_core.actions import CdpAction | ||
from cdp_agentkit_core.actions.compound.constants import ( | ||
CUSDCV3_ABI, | ||
CUSDCV3_MAINNET_ADDRESS, | ||
CUSDCV3_TESTNET_ADDRESS, | ||
) | ||
from cdp_agentkit_core.actions.compound.utils import get_health_ratio_after_borrow | ||
|
||
# Constants | ||
COMPOUND_BORROW_PROMPT = """ | ||
This tool allows you to borrow WETH or USDC from Compound V3 markets on Base. | ||
It takes the following inputs: | ||
- asset_id: The asset to borrow, either `weth` or `usdc` | ||
- amount: The amount of assets to borrow in whole units | ||
Examples for WETH: | ||
- 1 WETH | ||
- 0.1 WETH | ||
- 0.01 WETH | ||
Important notes: | ||
- Ensure you have sufficient collateral to borrow against | ||
- For any borrowing, make sure to have some ETH for gas fees | ||
- Be aware of your borrowing capacity and liquidation risks | ||
""" | ||
|
||
|
||
class CompoundBorrowInput(BaseModel): | ||
"""Input argument schema for borrowing assets from a Compound market.""" | ||
|
||
asset_id: Literal["weth", "usdc"] = Field( | ||
..., | ||
description="The asset ID to borrow from the Compound market, either `weth` or `usdc`", | ||
) | ||
amount: str = Field( | ||
..., | ||
description="The amount of the asset to borrow from the Compound market, e.g. 0.125 weth; 19.99 usdc", | ||
) | ||
|
||
|
||
def compound_borrow(wallet: Wallet, asset_id: Literal["weth", "usdc"], amount: str) -> str: | ||
"""Borrow assets from a Compound market. | ||
Args: | ||
wallet (Wallet): The wallet to receive the borrowed assets. | ||
asset_id (Literal['weth', 'usdc']): The asset ID to borrow from the Compound market. | ||
amount (str): The amount of the asset to borrow from the Compound market. | ||
Returns: | ||
str: A message containing the borrowing details. | ||
""" | ||
# Get the asset details | ||
asset = Asset.fetch(wallet.network_id, asset_id) | ||
adjusted_amount = str(int(asset.to_atomic_amount(Decimal(amount)))) | ||
|
||
# Determine which Compound market to use based on network | ||
is_mainnet = wallet.network_id == "base-mainnet" | ||
compound_address = CUSDCV3_MAINNET_ADDRESS if is_mainnet else CUSDCV3_TESTNET_ADDRESS | ||
|
||
# Check if position would be healthy after borrow | ||
projected_health_ratio = get_health_ratio_after_borrow( | ||
wallet, | ||
compound_address, | ||
adjusted_amount | ||
) | ||
|
||
if projected_health_ratio < 1: | ||
return f"Error: Borrowing {amount} {asset_id.upper()} would result in an unhealthy position. Health ratio would be {projected_health_ratio:.2f}" | ||
|
||
try: | ||
# Use withdraw method to borrow from Compound | ||
borrow_result = wallet.invoke_contract( | ||
contract_address=compound_address, | ||
method="withdraw", | ||
args={ | ||
"asset": asset.contract_address, | ||
"amount": adjusted_amount | ||
}, | ||
abi=CUSDCV3_ABI, | ||
).wait() | ||
|
||
return f"Borrowed {amount} {asset_id.upper()} from Compound V3.\nTransaction hash: {borrow_result.transaction_hash}\nTransaction link: {borrow_result.transaction_link}" | ||
|
||
except Exception as e: | ||
return f"Error borrowing {amount} {asset_id.upper()} from Compound: {e!s}" | ||
|
||
|
||
class CompoundBorrowAction(CdpAction): | ||
"""Compound borrow action.""" | ||
|
||
name: str = "compound_borrow" | ||
description: str = COMPOUND_BORROW_PROMPT | ||
args_schema: type[BaseModel] | None = CompoundBorrowInput | ||
func: Callable[..., str] = compound_borrow |
111 changes: 111 additions & 0 deletions
111
python/cdp-agentkit-core/cdp_agentkit_core/actions/compound/constants.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
CUSDCV3_MAINNET_ADDRESS = "0xb125E6687d4313864e53df431d5425969c15Eb2F" # Base USDC | ||
CUSDCV3_TESTNET_ADDRESS = "0x571621Ce60Cebb0c1D442B5afb38B1663C6Bf017" # Base Sepolia USDC | ||
|
||
CUSDCV3_ABI = [ | ||
{ | ||
"inputs": [ | ||
{"internalType": "address", "name": "asset", "type": "address"}, | ||
{"internalType": "uint256", "name": "amount", "type": "uint256"} | ||
], | ||
"name": "supply", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [ | ||
{"internalType": "address", "name": "asset", "type": "address"}, | ||
{"internalType": "uint256", "name": "amount", "type": "uint256"} | ||
], | ||
"name": "withdraw", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [ | ||
{"internalType": "address", "name": "priceFeed", "type": "address"} | ||
], | ||
"name": "getPrice", | ||
"outputs": [ | ||
{"internalType": "uint256", "name": "", "type": "uint256"} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [ | ||
{"internalType": "address", "name": "account", "type": "address"} | ||
], | ||
"name": "borrowBalanceOf", | ||
"outputs": [ | ||
{"internalType": "uint256", "name": "", "type": "uint256"} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [], | ||
"name": "numAssets", | ||
"outputs": [ | ||
{"internalType": "uint8", "name": "", "type": "uint8"} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [ | ||
{"internalType": "uint8", "name": "i", "type": "uint8"} | ||
], | ||
"name": "getAssetInfo", | ||
"outputs": [ | ||
{ | ||
"components": [ | ||
{"internalType": "uint8", "name": "offset", "type": "uint8"}, | ||
{"internalType": "address", "name": "asset", "type": "address"}, | ||
{"internalType": "address", "name": "priceFeed", "type": "address"}, | ||
{"internalType": "uint64", "name": "scale", "type": "uint64"}, | ||
{"internalType": "uint64", "name": "borrowCollateralFactor", "type": "uint64"}, | ||
{"internalType": "uint64", "name": "liquidateCollateralFactor", "type": "uint64"}, | ||
{"internalType": "uint64", "name": "liquidationFactor", "type": "uint64"}, | ||
{"internalType": "uint128", "name": "supplyCap", "type": "uint128"} | ||
], | ||
"internalType": "struct CometCore.AssetInfo", | ||
"name": "", | ||
"type": "tuple" | ||
} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [], | ||
"name": "baseToken", | ||
"outputs": [ | ||
{"internalType": "address", "name": "", "type": "address"} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [], | ||
"name": "baseTokenPriceFeed", | ||
"outputs": [ | ||
{"internalType": "address", "name": "", "type": "address"} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, | ||
{ | ||
"inputs": [ | ||
{"internalType": "address", "name": "account", "type": "address"}, | ||
{"internalType": "address", "name": "asset", "type": "address"} | ||
], | ||
"name": "collateralBalanceOf", | ||
"outputs": [ | ||
{"internalType": "uint128", "name": "balance", "type": "uint128"}, | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
} | ||
] |
Oops, something went wrong.