diff --git a/skills/metamask-agent-wallet/SKILL.md b/skills/metamask-agent-wallet/SKILL.md index 01fc91f..2b39ad4 100644 --- a/skills/metamask-agent-wallet/SKILL.md +++ b/skills/metamask-agent-wallet/SKILL.md @@ -1,6 +1,6 @@ --- name: metamask-agent-wallet -description: Use when the user asks anything about blockchain wallets, transactions, signing, token transfers, supported chains, wallet balances, perpetual futures trading, prediction markets, token swaps, cross-chain bridges, market data, token discovery, decoding EVM calldata, or authentication via the MetaMask Agentic CLI. Single entry point for all mm CLI operations. +description: Use when the user asks anything about blockchain wallets, transactions, signing, token transfers, supported chains, wallet balances, perpetual futures trading, prediction markets, token swaps, cross-chain bridges, market data, token discovery, decoding EVM calldata, Aave V3 lending and borrowing, or authentication via the MetaMask Agentic CLI. Single entry point for all mm CLI operations. license: MIT metadata: author: metamask @@ -121,6 +121,13 @@ CLI behavior lives in `references/`. Repeatable patterns live in `workflows/`. L | View or cancel Predict orders and positions | [predict-manage-orders.md](workflows/predict-manage-orders.md) | | View Predict portfolio and redeem winnings | [predict-portfolio.md](workflows/predict-portfolio.md) | | Token discovery, prices, and market data | [market-data.md](workflows/market-data.md) | +| Supply assets to Aave V3 | [aave-supply.md](workflows/aave-supply.md) | +| Withdraw assets from Aave V3 | [aave-withdraw.md](workflows/aave-withdraw.md) | +| Borrow from Aave V3 | [aave-borrow.md](workflows/aave-borrow.md) | +| Repay Aave V3 debt | [aave-repay.md](workflows/aave-repay.md) | +| Toggle Aave V3 collateral | [aave-collateral.md](workflows/aave-collateral.md) | +| Check Aave V3 positions and health factor | [aave-positions.md](workflows/aave-positions.md) | +| Discover Aave V3 tokens, rates, and liquidity | [aave-markets.md](workflows/aave-markets.md) | ## Global Flags diff --git a/skills/metamask-agent-wallet/references/signing.md b/skills/metamask-agent-wallet/references/signing.md index c8f8022..460296f 100644 --- a/skills/metamask-agent-wallet/references/signing.md +++ b/skills/metamask-agent-wallet/references/signing.md @@ -46,13 +46,14 @@ mm wallet sign-typed-data --chain-id --payload '' [--wait] [--passwor | `--chain-id` | Yes | EVM chain ID as a positive integer (e.g. 1, 137) | | `--payload` | Yes | EIP-712 typed data as a JSON string with `domain`, `types`, `primaryType`, and `message` | | `--wait` | No | Block until the signature request completes (server-wallet mode only; BYOK returns immediately) | +| `--intent` | No | Human-readable summary of what is being signed, forwarded with the request | | `--password` | No | Password to unlock the BYOK mnemonic (BYOK mode only) [env: `MM_PASSWORD`] | ### Example ```bash mm wallet sign-typed-data --chain-id 1 --payload '{"types":...,"primaryType":...,"domain":...,"message":...}' -mm wallet sign-typed-data --chain-id 137 --payload '{"types":...}' --wait +mm wallet sign-typed-data --chain-id 137 --payload '{"types":...}' --wait --intent "Approve 10 USDC" ``` ## EIP-712 Typed Data diff --git a/skills/metamask-agent-wallet/references/transaction.md b/skills/metamask-agent-wallet/references/transaction.md index da2748c..69d0cfa 100644 --- a/skills/metamask-agent-wallet/references/transaction.md +++ b/skills/metamask-agent-wallet/references/transaction.md @@ -19,12 +19,13 @@ mm wallet send-transaction --chain-id --payload '' [--wait] [--passwo | `--chain-id` | Yes | EVM chain ID as a positive integer (e.g. 1, 137) | | `--payload` | Yes | Transaction as a JSON string with at least a `to` address (e.g. `'{"to":"0x...","value":"0x0"}'}`) | | `--wait` | No | Block until the transaction completes (server-wallet mode only; BYOK returns immediately) | +| `--intent` | No | Human-readable summary of what the transaction does, forwarded with the request | | `--password` | No | Password to unlock the BYOK mnemonic (BYOK mode only) [env: `MM_PASSWORD`] | ### Example ```bash -mm wallet send-transaction --chain-id 1 --payload '{"to":"0x742d...","value":"0xde0b6b3a7640000","data":"0x"}' +mm wallet send-transaction --chain-id 1 --payload '{"to":"0x742d...","value":"0xde0b6b3a7640000","data":"0x"}' --intent "Send 1 ETH to 0x742d...f2bD18" mm wallet send-transaction --chain-id 1 --payload '{"to":"0x...","value":"0x0","data":"0xabcdef"}' --wait mm wallet send-transaction --chain-id 1 --payload '...' --toon ``` diff --git a/skills/metamask-agent-wallet/scripts/amount_to_hex.py b/skills/metamask-agent-wallet/scripts/amount_to_hex.py new file mode 100644 index 0000000..fe18b7b --- /dev/null +++ b/skills/metamask-agent-wallet/scripts/amount_to_hex.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Convert a human-readable token amount to 0x-prefixed hex for EVM calldata. + +Usage: + python3 amount_to_hex.py + +Examples: + python3 amount_to_hex.py 1.5 18 # 1.5 ETH -> 0x14d1120d7b160000 + python3 amount_to_hex.py 100 6 # 100 USDC -> 0x5f5e100 + python3 amount_to_hex.py 0.001 8 # 0.001 WBTC -> 0x186a0 +""" + +import sys +from decimal import Decimal + +if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + sys.exit(1) + +decimals = int(sys.argv[2]) +value = int(Decimal(sys.argv[1]) * 10 ** decimals) + +print(hex(value)) diff --git a/skills/metamask-agent-wallet/workflows/aave-borrow.md b/skills/metamask-agent-wallet/workflows/aave-borrow.md new file mode 100644 index 0000000..08372dc --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-borrow.md @@ -0,0 +1,81 @@ +# Aave V3 borrow workflow + +Use this workflow to borrow assets from Aave V3 against supplied collateral. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check collateral and borrow capacity. +3. Preview health factor impact. +4. Query the Aave API for the borrow transaction and execute. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check collateral + +Before borrowing, check the user's positions using `aave-positions.md`. Verify the following. + +1. The user has supplied collateral. If not, follow `aave-supply.md` to supply assets first. +2. Collateral is enabled on at least one supplied asset (`isCollateral` is `true`). If not, follow `aave-collateral.md` to enable it. +3. Available borrow capacity covers the requested amount. + +Query available markets to check the target asset's borrow APY and whether `borrowCapReached` is `true`. See `aave-markets.md`. + +## Preview health factor + +Preview the health factor impact before borrowing: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ healthFactorPreview(request: { action: { borrow: { market: \"\", sender: \"\", chainId: , amount: { erc20: { currency: \"\", value: \"\" } } } } }) { before after } }" + }' +``` + +Show the health factor before and after. If the projected health factor (`after`) drops below 1.5, warn about liquidation risk. If it drops below 1.0, stop and tell the user to reduce the borrow amount or repay existing debt. + +## Query borrow transaction + +Get the wallet address and query the Aave V3 GraphQL API. Don't include `onBehalfOf` when borrowing for the user's own account. It triggers a credit delegation requirement even for self-borrows. + +```bash +mm wallet address +``` + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ borrow(request: { market: \"\", amount: { erc20: { currency: \"\", value: \"\" } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +The `value` in the amount is a human-readable decimal string (e.g., `"2"`, `"100"`). The API handles conversion. + +## Execute borrow + +Confirm the asset, amount, chain, and projected health factor with the user. The `value` field must be `0x`-prefixed hex (typically `"0x0"` for ERC-20 borrows). + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Borrow from Aave V3 on " +``` + +If the response is `InsufficientBalanceError`, show the required and available amounts and stop. + +## Notes + +- After the transaction confirms, use `aave-positions.md` to verify the updated position and health factor. +- The borrowed amount accrues interest over time. Check debt at any time using `aave-positions.md`. +- To repay the borrow, see `aave-repay.md`. diff --git a/skills/metamask-agent-wallet/workflows/aave-collateral.md b/skills/metamask-agent-wallet/workflows/aave-collateral.md new file mode 100644 index 0000000..70df584 --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-collateral.md @@ -0,0 +1,64 @@ +# Aave V3 collateral workflow + +Use this workflow to enable or disable an asset as collateral on Aave V3. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check current collateral status and health factor. +3. Query the Aave API for the collateral toggle transaction. +4. Execute toggle. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check status + +Query the user's positions using `aave-positions.md`. For the target asset, check whether collateral is enabled or disabled (`isCollateral`). + +The user must have a non-zero supply of the asset to toggle collateral. + +When disabling collateral, check the health factor. If the user has outstanding borrows, disabling collateral lowers the health factor. Show the impact. If the health factor would drop below 1.0, stop and tell the user to repay debt first via `aave-repay.md`. + +## Query collateral toggle transaction + +Get the wallet address and query the Aave V3 GraphQL API: + +```bash +mm wallet address +``` + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ collateralToggle(request: { market: \"\", underlyingToken: \"\", user: \"\", chainId: }) { to from data value chainId } }" + }' +``` + +The API returns a `TransactionRequest` with `{to, from, data, value, chainId}`. The toggle direction is determined automatically based on the current collateral state. + +## Execute toggle + +Confirm the asset, toggle direction (enabling or disabling), and chain with the user. The `value` field must be `0x`-prefixed hex. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Toggle as collateral on Aave V3 on " +``` + +## Notes + +- Enabling collateral lets the asset back borrows, increasing borrow capacity. +- Disabling collateral removes it from the borrow calculation. This may trigger liquidation if remaining collateral can't cover existing debt. +- Not all assets support collateral usage. If the transaction reverts, the reserve may not be eligible. +- After the transaction confirms, verify the updated status using `aave-positions.md`. diff --git a/skills/metamask-agent-wallet/workflows/aave-markets.md b/skills/metamask-agent-wallet/workflows/aave-markets.md new file mode 100644 index 0000000..4b791c4 --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-markets.md @@ -0,0 +1,46 @@ +# Aave V3 markets workflow + +Use this workflow to discover available Aave V3 tokens, supply/borrow rates, and borrowing capacity on a chain. + +## Flow + +1. Resolve chain. +2. Query available markets. +3. Present results. + +## Resolve chain + +If the user doesn't specify a chain, ask. Aave V3 is deployed on these chains: + +| Chain | Chain ID | +| --- | --- | +| Ethereum | 1 | +| Polygon | 137 | +| Arbitrum | 42161 | +| Optimism | 10 | +| Avalanche | 43114 | +| Base | 8453 | + +## Query available markets + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ markets(request: { chainIds: [] }) { reserves { underlyingToken { symbol decimals } supplyInfo { apy { formatted } } borrowInfo { apy { formatted } availableLiquidity { amount { value } usd } borrowCapReached } isFrozen isPaused } } }" + }' +``` + +## Present results + +Filter out reserves where `isFrozen` or `isPaused` is `true`. For each active reserve, show: + +- Token symbol and decimals (`underlyingToken.symbol`, `underlyingToken.decimals`) +- Supply APY (`supplyInfo.apy.formatted`) +- Borrow APY (`borrowInfo.apy.formatted`) +- Available liquidity (`borrowInfo.availableLiquidity.amount.value`, `borrowInfo.availableLiquidity.usd`) +- Borrow cap reached (`borrowInfo.borrowCapReached`) + +The `apy.formatted` field returns a percentage directly (e.g., `"2.12"` means 2.12%). No conversion is needed. + +If `borrowCapReached` is `true`, tell the user that borrowing isn't available for that asset. diff --git a/skills/metamask-agent-wallet/workflows/aave-positions.md b/skills/metamask-agent-wallet/workflows/aave-positions.md new file mode 100644 index 0000000..cae93a2 --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-positions.md @@ -0,0 +1,72 @@ +# Aave V3 positions workflow + +Use this workflow to check Aave V3 positions, health factor, interest rates, or reserve data. + +## Flow + +1. Get wallet address and chain. +2. Query supply and borrow positions via GraphQL. +3. Present summary. + +## Resolve chain + +Get the wallet address: + +```bash +mm wallet address +``` + +If the user doesn't specify a chain, ask. Aave V3 is deployed on these chains: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Query positions + +Query supply and borrow positions in a single request: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ userSupplies(request: { markets: [{ address: \"\", chainId: }], user: \"\" }) { currency { symbol decimals } balance { amount { value } usd } apy { formatted } isCollateral } userBorrows(request: { markets: [{ address: \"\", chainId: }], user: \"\" }) { currency { symbol decimals } debt { amount { value } usd } apy { formatted } } }" + }' +``` + +The response contains both `userSupplies` and `userBorrows` arrays. + +## Health factor preview + +Preview the health factor impact of a planned operation: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ healthFactorPreview(request: { action: { : { market: \"\", sender: \"\", chainId: , amount: { erc20: { currency: \"\", value: \"\" } } } } }) { before after } }" + }' +``` + +Replace `` with `supply`, `borrow`, `withdraw`, or `repay`. All action types require `market`, `sender`, and `chainId`. + +## Present summary + +Format the data into three sections: + +Supplied assets: +- Symbol, balance (`balance.amount.value`), USD value (`balance.usd`), supply APY (`apy.formatted`), used as collateral (`isCollateral`) + +Borrowed assets: +- Symbol, debt amount (`debt.amount.value`), USD value (`debt.usd`), borrow APY (`apy.formatted`) + +Account summary: +- Total collateral value, total debt value, available borrows, health factor +- Health factor guidance: above 2.0 is safe, 1.5–2.0 is moderate, 1.0–1.5 is risky and approaching liquidation, below 1.0 is liquidatable + +The `apy.formatted` field returns a percentage directly (e.g., `"2.12"` means 2.12%). No conversion is needed. \ No newline at end of file diff --git a/skills/metamask-agent-wallet/workflows/aave-repay.md b/skills/metamask-agent-wallet/workflows/aave-repay.md new file mode 100644 index 0000000..ac458a6 --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-repay.md @@ -0,0 +1,101 @@ +# Aave V3 repay workflow + +Use this workflow to repay borrowed assets on Aave V3. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check outstanding debt. +3. Query the Aave API for the repay transaction. +4. Handle approval if required, then repay. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check debt + +Query the user's outstanding debt using `userBorrows` from `aave-positions.md`. Identify the following. + +- The asset being repaid and its contract address +- Current debt amount (`debt.amount.value`) +- Current borrow APY (`apy.formatted`) + +Show the debt amount and current rate. + +## Query repay transaction + +Get the wallet address and query the Aave V3 GraphQL API: + +```bash +mm wallet address +``` + +For a specific repayment amount: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ repay(request: { market: \"\", amount: { erc20: { currency: \"\", value: { exact: \"\" } } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { reason requiredAmount { value decimals } currentAllowance { value decimals } approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +To repay the full debt, use `{ max: true }` instead of `{ exact: \"\" }` in the value field. This lets the contract calculate the exact outstanding debt at execution time, including accrued interest. + +You can't use `{ max: true }` when `onBehalfOf` is set in the request. If repaying on behalf of another address, specify the exact repayment amount. Query the current debt via `userBorrows` and add a small buffer (e.g., 0.5%) to account for interest accrued between query and execution. + +## Handle response + +The API returns one of three response types: + +### `TransactionRequest` + +The transaction is ready to send. Confirm with the user, then send. The `value` field must be `0x`-prefixed hex (typically `"0x0"` for ERC-20 repayments). + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Repay on Aave V3 on " +``` + +### `ApprovalRequired` + +An ERC-20 approval is needed before repayment. Confirm the token being approved, the spender, and the amount with the user. Then send the approval. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Approve for Aave V3 Pool on " +``` + +After the approval confirms, send the original repay transaction. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Repay on Aave V3 on " +``` + +ERC-20 approvals are consumed by the repay transaction. If the user approved an exact amount and needs to repay again (e.g., for remaining dust debt), a new approval is required. Consider approving slightly more than the debt amount to avoid this. + +### `InsufficientBalanceError` + +The user doesn't have enough tokens to repay. Show the required and available amounts, then stop. + +## Handling dust debt + +Interest accrues continuously between borrow and repay transactions. When repaying an exact amount equal to the original borrow, a small "dust" debt remains. + +To handle this: +1. Use `{ max: true }` (only works without `onBehalfOf`) to let the contract calculate the exact outstanding debt at execution time. +2. Over-repay slightly: query the current debt via `userBorrows`, then repay with the debt amount plus a small buffer (e.g., 0.5%). The contract only deducts the actual debt and refunds the excess. +3. Acquire more tokens: if the wallet balance equals the exact original borrow amount, acquire slightly more of the token to cover interest. + +## Notes + +- After the transaction confirms, use `aave-positions.md` to verify the debt is cleared or reduced. +- All debt (including dust amounts) must be cleared before a full collateral withdrawal. See `aave-withdraw.md`. diff --git a/skills/metamask-agent-wallet/workflows/aave-supply.md b/skills/metamask-agent-wallet/workflows/aave-supply.md new file mode 100644 index 0000000..1e914d2 --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-supply.md @@ -0,0 +1,100 @@ +# Aave V3 supply workflow + +Use this workflow to supply (deposit) assets into Aave V3 and earn interest. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check wallet balance. +3. Query the Aave API for the supply transaction. +4. Handle approval if required, then supply. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +Resolve the asset's contract address on the target chain. If the user provides a symbol instead of an address, run `mm token list search --query --chain `. + +Aave V3 doesn't accept native ETH on most markets. If the reserve accepts native tokens, use `native` instead of `erc20` in the GraphQL request. + +## Check balance + +Check that the user has sufficient funds: + +```bash +mm wallet balance --chain +``` + +If the user doesn't have enough of the supply token or native gas token, check balances on other chains. If the user has assets on the same chain, prompt them to swap. If the user has assets on another chain, prompt them to bridge to the target chain before proceeding. + +## Query supply transaction + +Get the wallet address and query the Aave V3 GraphQL API for the supply execution plan: + +```bash +mm wallet address +``` + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ supply(request: { market: \"\", amount: { erc20: { currency: \"\", value: \"\" } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { reason requiredAmount { value decimals } currentAllowance { value decimals } approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +The `value` in the amount is a human-readable decimal string (e.g., `"42"`, `"0.5"`). The API handles decimal-to-wei conversion. + +## Handle response + +The API returns one of three response types: + +### `TransactionRequest` + +The transaction is ready to send. Confirm with the user, then send. + +The `value` field in the transaction payload must be `0x`-prefixed hex. For ERC-20 supplies, this is `"0x0"`. For native token supplies, convert the amount to hex using the helper script: + +```bash +python3 scripts/amount_to_hex.py +``` + +If python3 isn't available, multiply the amount by `10^decimals` manually and convert the result to hex. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Supply to Aave V3 on " +``` + +### `ApprovalRequired` + +An ERC-20 approval is needed before supply. Confirm the token being approved, the spender, and the amount with the user. Then send the approval. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Approve for Aave V3 Pool on " +``` + +After the approval confirms, send the original supply transaction. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Supply to Aave V3 on " +``` + +Security note: The Aave API returns `max uint256` (unlimited) as the default approval amount. Tell the user. For better security, construct a limited approval by encoding `approve(address,uint256)` calldata (selector `0x095ea7b3`) with the exact supply amount instead of using the API-provided approval transaction. + +### `InsufficientBalanceError` + +The user doesn't have enough tokens. Show the required and available amounts, then stop. + +## Notes + +- After the transaction confirms, the user receives aTokens representing their deposit. Use `aave-positions.md` to verify the updated position. +- To check current supply rates before supplying, see `aave-positions.md`. diff --git a/skills/metamask-agent-wallet/workflows/aave-withdraw.md b/skills/metamask-agent-wallet/workflows/aave-withdraw.md new file mode 100644 index 0000000..d8ebde8 --- /dev/null +++ b/skills/metamask-agent-wallet/workflows/aave-withdraw.md @@ -0,0 +1,76 @@ +# Aave V3 withdraw workflow + +Use this workflow to withdraw supplied assets from Aave V3. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check positions and health factor. +3. Query the Aave API for the withdraw transaction. +4. Execute withdrawal. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check positions + +Check the user's current positions using `aave-positions.md`. Confirm the asset and available balance. + +Before a full withdrawal, query `userBorrows` and verify there's no outstanding debt. A full collateral withdrawal fails if any debt exists, even tiny "dust" amounts. The transaction reverts with error `0x6679996d`. Follow `aave-repay.md` to clear all debt first. + +For a partial withdrawal with outstanding borrows, preview the health factor impact: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ healthFactorPreview(request: { action: { withdraw: { market: \"\", sender: \"\", chainId: , amount: { erc20: { currency: \"\", value: { exact: \"\" } } } } } }) { before after } }" + }' +``` + +If the projected health factor (`after`) drops below 1.5, warn about liquidation risk. If it drops below 1.0, stop and tell the user to repay debt first. + +## Query withdraw transaction + +Get the wallet address and query the Aave V3 GraphQL API: + +```bash +mm wallet address +``` + +For a specific amount: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ withdraw(request: { market: \"\", amount: { erc20: { currency: \"\", value: { exact: \"\" } } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +To withdraw the full balance, use `{ max: true }` instead of `{ exact: \"\" }` in the value field. + +## Execute withdrawal + +Confirm the asset, amount (or "full balance"), destination, and chain with the user. The `value` field must be `0x`-prefixed hex (typically `"0x0"` for ERC-20 withdrawals). + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Withdraw from Aave V3 on " +``` + +If the response is `ApprovalRequired`, send the approval transaction first, then the withdrawal transaction (same pattern as `aave-supply.md`). + +## Notes + +- After the transaction confirms, use `aave-positions.md` to verify the updated position. +- Add a `recipient` field to the request to withdraw to a different address than the sender. diff --git a/skills/metamask-agent-workflows/SKILL.md b/skills/metamask-agent-workflows/SKILL.md index 0f40f25..ab4962f 100644 --- a/skills/metamask-agent-workflows/SKILL.md +++ b/skills/metamask-agent-workflows/SKILL.md @@ -1,6 +1,6 @@ --- name: metamask-agent-workflows -description: Use when the user needs to perform multi-step operations with the MetaMask Agentic CLI such as onboarding, login, swapping tokens, bridging across chains, opening/closing/modifying perpetual positions, prediction market trading, or troubleshooting CLI issues. +description: Use when the user needs to perform multi-step operations with the MetaMask Agentic CLI such as onboarding, login, swapping tokens, bridging across chains, opening/closing/modifying perpetual positions, prediction market trading, Aave V3 lending and borrowing, or troubleshooting CLI issues. license: MIT metadata: author: metamask @@ -31,6 +31,13 @@ Repeatable multi-step patterns for the `mm` CLI. Load a workflow file when the u | View or cancel Predict orders and positions | [predict-manage-orders.md](workflows/predict-manage-orders.md) | | View Predict portfolio and redeem winnings | [predict-portfolio.md](workflows/predict-portfolio.md) | | Token discovery, prices, and market data | [market-data.md](workflows/market-data.md) | +| Supply assets to Aave V3 | [aave-supply.md](workflows/aave-supply.md) | +| Withdraw assets from Aave V3 | [aave-withdraw.md](workflows/aave-withdraw.md) | +| Borrow from Aave V3 | [aave-borrow.md](workflows/aave-borrow.md) | +| Repay Aave V3 debt | [aave-repay.md](workflows/aave-repay.md) | +| Toggle Aave V3 collateral | [aave-collateral.md](workflows/aave-collateral.md) | +| Check Aave V3 positions and health factor | [aave-positions.md](workflows/aave-positions.md) | +| Discover Aave V3 tokens, rates, and liquidity | [aave-markets.md](workflows/aave-markets.md) | Always use `--toon` for command output unless the user explicitly requests a different format. diff --git a/skills/metamask-agent-workflows/scripts/amount_to_hex.py b/skills/metamask-agent-workflows/scripts/amount_to_hex.py new file mode 100644 index 0000000..fe18b7b --- /dev/null +++ b/skills/metamask-agent-workflows/scripts/amount_to_hex.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Convert a human-readable token amount to 0x-prefixed hex for EVM calldata. + +Usage: + python3 amount_to_hex.py + +Examples: + python3 amount_to_hex.py 1.5 18 # 1.5 ETH -> 0x14d1120d7b160000 + python3 amount_to_hex.py 100 6 # 100 USDC -> 0x5f5e100 + python3 amount_to_hex.py 0.001 8 # 0.001 WBTC -> 0x186a0 +""" + +import sys +from decimal import Decimal + +if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + sys.exit(1) + +decimals = int(sys.argv[2]) +value = int(Decimal(sys.argv[1]) * 10 ** decimals) + +print(hex(value)) diff --git a/skills/metamask-agent-workflows/workflows/aave-borrow.md b/skills/metamask-agent-workflows/workflows/aave-borrow.md new file mode 100644 index 0000000..08372dc --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-borrow.md @@ -0,0 +1,81 @@ +# Aave V3 borrow workflow + +Use this workflow to borrow assets from Aave V3 against supplied collateral. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check collateral and borrow capacity. +3. Preview health factor impact. +4. Query the Aave API for the borrow transaction and execute. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check collateral + +Before borrowing, check the user's positions using `aave-positions.md`. Verify the following. + +1. The user has supplied collateral. If not, follow `aave-supply.md` to supply assets first. +2. Collateral is enabled on at least one supplied asset (`isCollateral` is `true`). If not, follow `aave-collateral.md` to enable it. +3. Available borrow capacity covers the requested amount. + +Query available markets to check the target asset's borrow APY and whether `borrowCapReached` is `true`. See `aave-markets.md`. + +## Preview health factor + +Preview the health factor impact before borrowing: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ healthFactorPreview(request: { action: { borrow: { market: \"\", sender: \"\", chainId: , amount: { erc20: { currency: \"\", value: \"\" } } } } }) { before after } }" + }' +``` + +Show the health factor before and after. If the projected health factor (`after`) drops below 1.5, warn about liquidation risk. If it drops below 1.0, stop and tell the user to reduce the borrow amount or repay existing debt. + +## Query borrow transaction + +Get the wallet address and query the Aave V3 GraphQL API. Don't include `onBehalfOf` when borrowing for the user's own account. It triggers a credit delegation requirement even for self-borrows. + +```bash +mm wallet address +``` + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ borrow(request: { market: \"\", amount: { erc20: { currency: \"\", value: \"\" } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +The `value` in the amount is a human-readable decimal string (e.g., `"2"`, `"100"`). The API handles conversion. + +## Execute borrow + +Confirm the asset, amount, chain, and projected health factor with the user. The `value` field must be `0x`-prefixed hex (typically `"0x0"` for ERC-20 borrows). + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Borrow from Aave V3 on " +``` + +If the response is `InsufficientBalanceError`, show the required and available amounts and stop. + +## Notes + +- After the transaction confirms, use `aave-positions.md` to verify the updated position and health factor. +- The borrowed amount accrues interest over time. Check debt at any time using `aave-positions.md`. +- To repay the borrow, see `aave-repay.md`. diff --git a/skills/metamask-agent-workflows/workflows/aave-collateral.md b/skills/metamask-agent-workflows/workflows/aave-collateral.md new file mode 100644 index 0000000..70df584 --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-collateral.md @@ -0,0 +1,64 @@ +# Aave V3 collateral workflow + +Use this workflow to enable or disable an asset as collateral on Aave V3. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check current collateral status and health factor. +3. Query the Aave API for the collateral toggle transaction. +4. Execute toggle. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check status + +Query the user's positions using `aave-positions.md`. For the target asset, check whether collateral is enabled or disabled (`isCollateral`). + +The user must have a non-zero supply of the asset to toggle collateral. + +When disabling collateral, check the health factor. If the user has outstanding borrows, disabling collateral lowers the health factor. Show the impact. If the health factor would drop below 1.0, stop and tell the user to repay debt first via `aave-repay.md`. + +## Query collateral toggle transaction + +Get the wallet address and query the Aave V3 GraphQL API: + +```bash +mm wallet address +``` + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ collateralToggle(request: { market: \"\", underlyingToken: \"\", user: \"\", chainId: }) { to from data value chainId } }" + }' +``` + +The API returns a `TransactionRequest` with `{to, from, data, value, chainId}`. The toggle direction is determined automatically based on the current collateral state. + +## Execute toggle + +Confirm the asset, toggle direction (enabling or disabling), and chain with the user. The `value` field must be `0x`-prefixed hex. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Toggle as collateral on Aave V3 on " +``` + +## Notes + +- Enabling collateral lets the asset back borrows, increasing borrow capacity. +- Disabling collateral removes it from the borrow calculation. This may trigger liquidation if remaining collateral can't cover existing debt. +- Not all assets support collateral usage. If the transaction reverts, the reserve may not be eligible. +- After the transaction confirms, verify the updated status using `aave-positions.md`. diff --git a/skills/metamask-agent-workflows/workflows/aave-markets.md b/skills/metamask-agent-workflows/workflows/aave-markets.md new file mode 100644 index 0000000..4b791c4 --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-markets.md @@ -0,0 +1,46 @@ +# Aave V3 markets workflow + +Use this workflow to discover available Aave V3 tokens, supply/borrow rates, and borrowing capacity on a chain. + +## Flow + +1. Resolve chain. +2. Query available markets. +3. Present results. + +## Resolve chain + +If the user doesn't specify a chain, ask. Aave V3 is deployed on these chains: + +| Chain | Chain ID | +| --- | --- | +| Ethereum | 1 | +| Polygon | 137 | +| Arbitrum | 42161 | +| Optimism | 10 | +| Avalanche | 43114 | +| Base | 8453 | + +## Query available markets + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ markets(request: { chainIds: [] }) { reserves { underlyingToken { symbol decimals } supplyInfo { apy { formatted } } borrowInfo { apy { formatted } availableLiquidity { amount { value } usd } borrowCapReached } isFrozen isPaused } } }" + }' +``` + +## Present results + +Filter out reserves where `isFrozen` or `isPaused` is `true`. For each active reserve, show: + +- Token symbol and decimals (`underlyingToken.symbol`, `underlyingToken.decimals`) +- Supply APY (`supplyInfo.apy.formatted`) +- Borrow APY (`borrowInfo.apy.formatted`) +- Available liquidity (`borrowInfo.availableLiquidity.amount.value`, `borrowInfo.availableLiquidity.usd`) +- Borrow cap reached (`borrowInfo.borrowCapReached`) + +The `apy.formatted` field returns a percentage directly (e.g., `"2.12"` means 2.12%). No conversion is needed. + +If `borrowCapReached` is `true`, tell the user that borrowing isn't available for that asset. diff --git a/skills/metamask-agent-workflows/workflows/aave-positions.md b/skills/metamask-agent-workflows/workflows/aave-positions.md new file mode 100644 index 0000000..cae93a2 --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-positions.md @@ -0,0 +1,72 @@ +# Aave V3 positions workflow + +Use this workflow to check Aave V3 positions, health factor, interest rates, or reserve data. + +## Flow + +1. Get wallet address and chain. +2. Query supply and borrow positions via GraphQL. +3. Present summary. + +## Resolve chain + +Get the wallet address: + +```bash +mm wallet address +``` + +If the user doesn't specify a chain, ask. Aave V3 is deployed on these chains: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Query positions + +Query supply and borrow positions in a single request: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ userSupplies(request: { markets: [{ address: \"\", chainId: }], user: \"\" }) { currency { symbol decimals } balance { amount { value } usd } apy { formatted } isCollateral } userBorrows(request: { markets: [{ address: \"\", chainId: }], user: \"\" }) { currency { symbol decimals } debt { amount { value } usd } apy { formatted } } }" + }' +``` + +The response contains both `userSupplies` and `userBorrows` arrays. + +## Health factor preview + +Preview the health factor impact of a planned operation: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ healthFactorPreview(request: { action: { : { market: \"\", sender: \"\", chainId: , amount: { erc20: { currency: \"\", value: \"\" } } } } }) { before after } }" + }' +``` + +Replace `` with `supply`, `borrow`, `withdraw`, or `repay`. All action types require `market`, `sender`, and `chainId`. + +## Present summary + +Format the data into three sections: + +Supplied assets: +- Symbol, balance (`balance.amount.value`), USD value (`balance.usd`), supply APY (`apy.formatted`), used as collateral (`isCollateral`) + +Borrowed assets: +- Symbol, debt amount (`debt.amount.value`), USD value (`debt.usd`), borrow APY (`apy.formatted`) + +Account summary: +- Total collateral value, total debt value, available borrows, health factor +- Health factor guidance: above 2.0 is safe, 1.5–2.0 is moderate, 1.0–1.5 is risky and approaching liquidation, below 1.0 is liquidatable + +The `apy.formatted` field returns a percentage directly (e.g., `"2.12"` means 2.12%). No conversion is needed. \ No newline at end of file diff --git a/skills/metamask-agent-workflows/workflows/aave-repay.md b/skills/metamask-agent-workflows/workflows/aave-repay.md new file mode 100644 index 0000000..ac458a6 --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-repay.md @@ -0,0 +1,101 @@ +# Aave V3 repay workflow + +Use this workflow to repay borrowed assets on Aave V3. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check outstanding debt. +3. Query the Aave API for the repay transaction. +4. Handle approval if required, then repay. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check debt + +Query the user's outstanding debt using `userBorrows` from `aave-positions.md`. Identify the following. + +- The asset being repaid and its contract address +- Current debt amount (`debt.amount.value`) +- Current borrow APY (`apy.formatted`) + +Show the debt amount and current rate. + +## Query repay transaction + +Get the wallet address and query the Aave V3 GraphQL API: + +```bash +mm wallet address +``` + +For a specific repayment amount: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ repay(request: { market: \"\", amount: { erc20: { currency: \"\", value: { exact: \"\" } } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { reason requiredAmount { value decimals } currentAllowance { value decimals } approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +To repay the full debt, use `{ max: true }` instead of `{ exact: \"\" }` in the value field. This lets the contract calculate the exact outstanding debt at execution time, including accrued interest. + +You can't use `{ max: true }` when `onBehalfOf` is set in the request. If repaying on behalf of another address, specify the exact repayment amount. Query the current debt via `userBorrows` and add a small buffer (e.g., 0.5%) to account for interest accrued between query and execution. + +## Handle response + +The API returns one of three response types: + +### `TransactionRequest` + +The transaction is ready to send. Confirm with the user, then send. The `value` field must be `0x`-prefixed hex (typically `"0x0"` for ERC-20 repayments). + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Repay on Aave V3 on " +``` + +### `ApprovalRequired` + +An ERC-20 approval is needed before repayment. Confirm the token being approved, the spender, and the amount with the user. Then send the approval. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Approve for Aave V3 Pool on " +``` + +After the approval confirms, send the original repay transaction. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Repay on Aave V3 on " +``` + +ERC-20 approvals are consumed by the repay transaction. If the user approved an exact amount and needs to repay again (e.g., for remaining dust debt), a new approval is required. Consider approving slightly more than the debt amount to avoid this. + +### `InsufficientBalanceError` + +The user doesn't have enough tokens to repay. Show the required and available amounts, then stop. + +## Handling dust debt + +Interest accrues continuously between borrow and repay transactions. When repaying an exact amount equal to the original borrow, a small "dust" debt remains. + +To handle this: +1. Use `{ max: true }` (only works without `onBehalfOf`) to let the contract calculate the exact outstanding debt at execution time. +2. Over-repay slightly: query the current debt via `userBorrows`, then repay with the debt amount plus a small buffer (e.g., 0.5%). The contract only deducts the actual debt and refunds the excess. +3. Acquire more tokens: if the wallet balance equals the exact original borrow amount, acquire slightly more of the token to cover interest. + +## Notes + +- After the transaction confirms, use `aave-positions.md` to verify the debt is cleared or reduced. +- All debt (including dust amounts) must be cleared before a full collateral withdrawal. See `aave-withdraw.md`. diff --git a/skills/metamask-agent-workflows/workflows/aave-supply.md b/skills/metamask-agent-workflows/workflows/aave-supply.md new file mode 100644 index 0000000..1e914d2 --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-supply.md @@ -0,0 +1,100 @@ +# Aave V3 supply workflow + +Use this workflow to supply (deposit) assets into Aave V3 and earn interest. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check wallet balance. +3. Query the Aave API for the supply transaction. +4. Handle approval if required, then supply. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +Resolve the asset's contract address on the target chain. If the user provides a symbol instead of an address, run `mm token list search --query --chain `. + +Aave V3 doesn't accept native ETH on most markets. If the reserve accepts native tokens, use `native` instead of `erc20` in the GraphQL request. + +## Check balance + +Check that the user has sufficient funds: + +```bash +mm wallet balance --chain +``` + +If the user doesn't have enough of the supply token or native gas token, check balances on other chains. If the user has assets on the same chain, prompt them to swap. If the user has assets on another chain, prompt them to bridge to the target chain before proceeding. + +## Query supply transaction + +Get the wallet address and query the Aave V3 GraphQL API for the supply execution plan: + +```bash +mm wallet address +``` + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ supply(request: { market: \"\", amount: { erc20: { currency: \"\", value: \"\" } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { reason requiredAmount { value decimals } currentAllowance { value decimals } approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +The `value` in the amount is a human-readable decimal string (e.g., `"42"`, `"0.5"`). The API handles decimal-to-wei conversion. + +## Handle response + +The API returns one of three response types: + +### `TransactionRequest` + +The transaction is ready to send. Confirm with the user, then send. + +The `value` field in the transaction payload must be `0x`-prefixed hex. For ERC-20 supplies, this is `"0x0"`. For native token supplies, convert the amount to hex using the helper script: + +```bash +python3 scripts/amount_to_hex.py +``` + +If python3 isn't available, multiply the amount by `10^decimals` manually and convert the result to hex. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Supply to Aave V3 on " +``` + +### `ApprovalRequired` + +An ERC-20 approval is needed before supply. Confirm the token being approved, the spender, and the amount with the user. Then send the approval. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Approve for Aave V3 Pool on " +``` + +After the approval confirms, send the original supply transaction. + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Supply to Aave V3 on " +``` + +Security note: The Aave API returns `max uint256` (unlimited) as the default approval amount. Tell the user. For better security, construct a limited approval by encoding `approve(address,uint256)` calldata (selector `0x095ea7b3`) with the exact supply amount instead of using the API-provided approval transaction. + +### `InsufficientBalanceError` + +The user doesn't have enough tokens. Show the required and available amounts, then stop. + +## Notes + +- After the transaction confirms, the user receives aTokens representing their deposit. Use `aave-positions.md` to verify the updated position. +- To check current supply rates before supplying, see `aave-positions.md`. diff --git a/skills/metamask-agent-workflows/workflows/aave-withdraw.md b/skills/metamask-agent-workflows/workflows/aave-withdraw.md new file mode 100644 index 0000000..d8ebde8 --- /dev/null +++ b/skills/metamask-agent-workflows/workflows/aave-withdraw.md @@ -0,0 +1,76 @@ +# Aave V3 withdraw workflow + +Use this workflow to withdraw supplied assets from Aave V3. + +## Flow + +1. Resolve chain, asset address, and pool address. +2. Check positions and health factor. +3. Query the Aave API for the withdraw transaction. +4. Execute withdrawal. + +## Resolve chain and addresses + +If the user doesn't specify a chain, ask. Look up the pool address: + +| Chain | Chain ID | Pool address | +| --- | --- | --- | +| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | +| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | +| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | + +## Check positions + +Check the user's current positions using `aave-positions.md`. Confirm the asset and available balance. + +Before a full withdrawal, query `userBorrows` and verify there's no outstanding debt. A full collateral withdrawal fails if any debt exists, even tiny "dust" amounts. The transaction reverts with error `0x6679996d`. Follow `aave-repay.md` to clear all debt first. + +For a partial withdrawal with outstanding borrows, preview the health factor impact: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ healthFactorPreview(request: { action: { withdraw: { market: \"\", sender: \"\", chainId: , amount: { erc20: { currency: \"\", value: { exact: \"\" } } } } } }) { before after } }" + }' +``` + +If the projected health factor (`after`) drops below 1.5, warn about liquidation risk. If it drops below 1.0, stop and tell the user to repay debt first. + +## Query withdraw transaction + +Get the wallet address and query the Aave V3 GraphQL API: + +```bash +mm wallet address +``` + +For a specific amount: + +```bash +curl -s -X POST https://api.v3.aave.com/graphql \ + -H 'Content-Type: application/json' \ + -d '{ + "query": "{ withdraw(request: { market: \"\", amount: { erc20: { currency: \"\", value: { exact: \"\" } } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { approval { to from data value chainId } originalTransaction { to from data value chainId } } ... on InsufficientBalanceError { required { value decimals } available { value decimals } } } }" + }' +``` + +To withdraw the full balance, use `{ max: true }` instead of `{ exact: \"\" }` in the value field. + +## Execute withdrawal + +Confirm the asset, amount (or "full balance"), destination, and chain with the user. The `value` field must be `0x`-prefixed hex (typically `"0x0"` for ERC-20 withdrawals). + +```bash +mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait --intent "Withdraw from Aave V3 on " +``` + +If the response is `ApprovalRequired`, send the approval transaction first, then the withdrawal transaction (same pattern as `aave-supply.md`). + +## Notes + +- After the transaction confirms, use `aave-positions.md` to verify the updated position. +- Add a `recipient` field to the request to withdraw to a different address than the sender.