-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
integrate transfer and balance smart agent
- Loading branch information
Showing
8 changed files
with
295 additions
and
11 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
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,62 @@ | ||
export const MOR_PROMPT = | ||
`###System: | ||
You are MORPHEUS, an AI assistant, but you prefer to be called a SmartAgent. You respond to any question users may have and assist them in sending transactions with metamask by creating a valid transaction object. | ||
Respond in a valid JSON to be consumed by an application following this pattern: | ||
{"response", "your response goes here", "transaction", "user transaction object goes here"}. | ||
Only respond with the JSON, NEVER provide any text outside of the json. Your respond only in a valid JSON, nothing else. If the user wants to initate a transaction with their question, create a valid transaction object from the information in their question. Structure the object based off the type of transaction they want to intiate. | ||
For Transfer transactions create a transaction object following this pattern: | ||
{"type": "Transfer", "targetAddress", "target address goes here", "ethAmount", "amount of eth to transfer goes here"} | ||
For Balance transactions create a transaction object following this pattern: | ||
{"type": "Balance"} | ||
Here are examples on how to create the transaction object from the user's question: | ||
###Examples: | ||
Example 1: | ||
Question: "transfer 43 eth to 0x223738a369F0804c091e13740D26D1269294bc1b", //User is initiating a transfer transaction with their question. | ||
Response: "{ | ||
response: "Of course! The transaction details are prepared for you. Please double-check the parameters before confirming on Metamask.", | ||
transaction: { | ||
"type": "transfer", | ||
"targetAddress": "0x223738a369F0804c091e13740D26D1269294bc1b", | ||
"ethAmount": "43" | ||
} | ||
}" | ||
Example 3: | ||
Question: "Hey Morpheus, whats my balance" | ||
Response: "{ | ||
response: "Your balance is: ", | ||
transaction: { | ||
"type": "Balance" | ||
} | ||
}" | ||
Example 4: | ||
Question: "Why is the sky blue" //the user's question does not initiate a transaction, leave the transaction field empty. | ||
Response: "{ | ||
response: "The sky is blue because of a thing called Rayleigh scattering. When sunlight enters the Earth's atmosphere, it hits air and other tiny particles. This light is made of many colors. Blue light scatters more because it travels as shorter, smaller waves. So, when we look up, we see more blue light than other colors.", | ||
transaction: {} | ||
}" | ||
Example 5: | ||
Question: "What is stETH" //the user's question does not initiate a transaction, leave the transaction field empty. | ||
Response: "{ | ||
response: "stETH stands for staked Ether. It's a type of cryptocurrency. When people stake their Ether (ETH) in a blockchain network to support it, they get stETH in return. This shows they have ETH locked up, and they can still use stETH in other crypto activities while earning rewards.", | ||
transaction: {} | ||
}" | ||
`; | ||
|
||
const errorHandling = `If a question is initiating a buy or transfer transaction and the user doesn't specify an amount in ETH. Gently decline to send the transaction | ||
and request the amount to buy or transfer (depending on their transaction type) in ethereum. | ||
If a question is initiating a sell transaction and the user doesn't specify an amount in tokens. Gently decline to send the transaction | ||
and request the amount to sell in tokens. `; | ||
//TODO: allow for staking MOR and swap tokens | ||
//TODO: use RAG to include a database to tokenAddresses and symbols | ||
//TODO: include chat history | ||
//TODO: include error handling in prompt |
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,99 @@ | ||
import { ethers } from "ethers"; | ||
import { SDKProvider } from "@metamask/sdk"; | ||
import { transactionParams } from "./types"; | ||
|
||
export const isTransactionIntiated = (transaction: transactionParams) => { | ||
return !(Object.keys(transaction).length === 0); | ||
} | ||
|
||
export const buildTransaction = (transaction: transactionParams, account: string | undefined, gasPrice: string, provider: SDKProvider | undefined) => { | ||
const transactionType = transaction.type.toLowerCase(); | ||
|
||
let tx: any | ||
switch (transactionType) { | ||
case "transfer": | ||
tx = buildTransferTransaction(transaction, account, gasPrice); | ||
break; | ||
default: | ||
throw Error(`Transaction of type ${transactionType} is not yet supported`); | ||
} | ||
//returned wrapped call with method for metamask with transaction params | ||
return { | ||
"method": "eth_sendTransaction", | ||
"params": [tx] | ||
} | ||
} | ||
|
||
const buildTransferTransaction = (transaction: transactionParams, account: string | undefined, gasPrice: any) => { | ||
return { | ||
from: account, | ||
to: transaction.targetAddress, | ||
gas: "0x76c0", //for more complex tasks estimate this from metamast | ||
gasPrice: gasPrice, | ||
value: '0x' + ethers.parseEther(transaction.ethAmount).toString(16), | ||
data: "0x000000" | ||
} | ||
} | ||
|
||
//TODO: take chain ID to get arb balance or w/e chain | ||
const formatWalletBalance = (balanceWeiHex: string) => { | ||
const balanceBigInt = BigInt(balanceWeiHex) | ||
const balance = ethers.formatUnits(balanceBigInt, "ether"); | ||
return parseFloat(balance).toFixed(2) + " " + "ETH"; | ||
} | ||
|
||
export const handleBalanceRequest = async (provider: SDKProvider | undefined, account: string | undefined, response: string) => { | ||
const blockNumber = await provider?.request({ | ||
"method": "eth_blockNumber", | ||
"params": [] | ||
}); | ||
|
||
const balanceWeiHex = await provider?.request({ | ||
"method": "eth_getBalance", | ||
"params": [ | ||
account, | ||
blockNumber | ||
] | ||
}); | ||
if(typeof balanceWeiHex === 'string'){ | ||
return response + " " + formatWalletBalance(balanceWeiHex); | ||
} else { | ||
console.error('Failed to retrieve a valid balance.'); | ||
throw Error('Invalid Balance Recieved from MetaMask.') | ||
} | ||
} | ||
|
||
const estimateGasWithOverHead = (estimatedGasMaybe: string) => { | ||
const estimatedGas = parseInt(estimatedGasMaybe, 16); | ||
//console.log("Gas Limit: " + estimatedGas) | ||
const gasLimitWithOverhead = Math.ceil(estimatedGas * 5); | ||
return "0x" + gasLimitWithOverhead.toString(16); | ||
} | ||
|
||
export const handleTransactionRequest = async (provider: SDKProvider | undefined, transaction: transactionParams, account: string | undefined) => { | ||
const gasPrice = await provider?.request({ | ||
"method": "eth_gasPrice", | ||
"params": [] | ||
}); | ||
//Sanity Check | ||
if(typeof gasPrice !== 'string'){ | ||
console.error('Failed to retrieve a valid gasPrice'); | ||
throw new Error('Invalid gasPrice received'); | ||
} | ||
let builtTx = buildTransaction(transaction, account, gasPrice, provider); | ||
|
||
let estimatedGas = await provider?.request({ | ||
"method": "eth_estimateGas", | ||
"params": [builtTx] | ||
}); | ||
//Sanity Check | ||
if(typeof estimatedGas !== 'string'){ | ||
console.error('Failed to estimate Gas with metamask'); | ||
throw new Error('Invalid gasPrice received'); | ||
} | ||
|
||
const gasLimitWithOverhead = estimateGasWithOverHead(estimatedGas) | ||
builtTx.params[0].gas = gasLimitWithOverhead; // Update the transaction with the new gas limit in hex | ||
return builtTx | ||
} | ||
|
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,8 @@ | ||
export type ModelResponse = { | ||
response: string; | ||
transaction: transactionParams | ||
}; | ||
|
||
export type transactionParams = { | ||
[key: string]: string | ||
} |
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,16 @@ | ||
import { ModelResponse } from "./types"; | ||
|
||
export function parseResponse(jsonString: string){ | ||
// Assert the type of the parsed object. | ||
const parsed = JSON.parse(jsonString); | ||
|
||
if (isModelResponse(parsed)) { | ||
return { response: parsed.response, transaction: parsed.transaction }; | ||
} else { | ||
throw new Error("Invalid ModelResponse format"); | ||
} | ||
} | ||
|
||
function isModelResponse(object: any): object is ModelResponse { | ||
return 'response' in object && 'transaction' in object; | ||
} |
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
Oops, something went wrong.