Skip to content

Commit

Permalink
Merge pull request #21 from starc007/feat/constructor-credentials
Browse files Browse the repository at this point in the history
feat: allow passing credentials via constructor
  • Loading branch information
Dhaiwat10 authored Dec 27, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents d8b72ab + e0e9b01 commit 26467d0
Showing 18 changed files with 201 additions and 137 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -8,15 +8,18 @@ Docs: https://dhaiwatpandya.gitbook.io/fuel-agent-kit/
npm install fuel-agent-kit
```

Make sure you have the following environment variables set:
You will need two things:

- `OPENAI_API_KEY`: Your OpenAI API key
- `FUEL_WALLET_PRIVATE_KEY`: Your Fuel wallet private key
- An OpenAI API key
- A Fuel wallet private key

```ts
import { FuelAgent } from 'fuel-agent-kit';

const agent = new FuelAgent();
const agent = new FuelAgent({
openaiApiKey: process.env.OPENAI_API_KEY,
walletPrivateKey: process.env.FUEL_WALLET_PRIVATE_KEY,
});

// Call different functions
await agent.transfer({
46 changes: 34 additions & 12 deletions src/FuelAgent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { agentExector } from './agent.js';
import { addLiquidity, type AddLiquidityParams } from './mira/addLiquidity.js';
import { swapExactInput, type SwapExactInputParams } from './mira/swap.js';
import { borrowAsset, type BorrowAssetParams } from './swaylend/borrow.js';
@@ -10,43 +9,66 @@ import {
transfer as walletTransfer,
type TransferParams,
} from './transfers/transfers.js';
import { createAgent } from './agent.js';
import { AgentExecutor } from 'langchain/agents';

interface FuelAgentConfig {
walletPrivateKey: string;
openAIKey: string;
}

export class FuelAgent {
constructor() {
if (!process.env.OPENAI_API_KEY) {
throw new Error('OPENAI_API_KEY is not set');
private walletPrivateKey: string;
private openAIKey: string;
private agentExecutor: AgentExecutor;

constructor(config: FuelAgentConfig) {
this.walletPrivateKey = config.walletPrivateKey;
this.openAIKey = config.openAIKey;

if (!this.openAIKey) {
throw new Error('OpenAI API key is required.');
}

if (!process.env.FUEL_WALLET_PRIVATE_KEY) {
throw new Error('FUEL_WALLET_PRIVATE_KEY is not set');
if (!this.walletPrivateKey) {
throw new Error('Fuel wallet private key is required.');
}

this.agentExecutor = createAgent(this.openAIKey);
}

getCredentials() {
return {
walletPrivateKey: this.walletPrivateKey,
openAIKey: this.openAIKey,
};
}

async execute(input: string) {
const response = await agentExector.invoke({
const response = await this.agentExecutor.invoke({
input,
});

return response;
}

async swapExactInput(params: SwapExactInputParams) {
return await swapExactInput(params);
return await swapExactInput(params, this.walletPrivateKey);
}

async transfer(params: TransferParams) {
return await walletTransfer(params);
return await walletTransfer(params, this.walletPrivateKey);
}

async supplyCollateral(params: SupplyCollateralParams) {
return await supplyCollateral(params);
return await supplyCollateral(params, this.walletPrivateKey);
}

async borrowAsset(params: BorrowAssetParams) {
return await borrowAsset(params);
return await borrowAsset(params, this.walletPrivateKey);
}

async addLiquidity(params: AddLiquidityParams) {
return await addLiquidity(params);
return await addLiquidity(params, this.walletPrivateKey);
}
}
29 changes: 11 additions & 18 deletions src/agent.ts
Original file line number Diff line number Diff line change
@@ -19,27 +19,20 @@ export const prompt = ChatPromptTemplate.fromMessages([
['placeholder', '{agent_scratchpad}'],
]);

export const getModel = () => {
export const createAgent = (openAIKey: string) => {
const model = new ChatOpenAI({
modelName: 'gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
apiKey: openAIKey,
});

const boundModel = model.bindTools(tools);
const agent = createToolCallingAgent({
llm: model,
tools,
prompt,
});

return boundModel;
return new AgentExecutor({
agent,
tools,
});
};

export const agent = createToolCallingAgent({
llm: new ChatOpenAI({
modelName: 'gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
}),
tools,
prompt,
});

export const agentExector = new AgentExecutor({
agent,
tools,
});
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_SLIPPAGE = 0.01; // 1%
28 changes: 14 additions & 14 deletions src/mira/addLiquidity.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { bn, Provider } from 'fuels';
import { getAllVerifiedFuelAssets } from '../utils/assets.js';
import { buildPoolId, MiraAmm, ReadonlyMiraAmm } from 'mira-dex-ts';
import { setupWallet } from '../utils/setup.js';
import { DEFAULT_SLIPPAGE } from '../constants.js';

async function futureDeadline(provider: Provider) {
const block = await provider.getBlock('latest');
@@ -15,19 +16,20 @@ export type AddLiquidityParams = {
slippage?: number;
};

export const addLiquidity = async ({
amount0,
asset0Symbol,
asset1Symbol,
slippage = 0.01, // Default slippage of 1%
}: AddLiquidityParams) => {
export const addLiquidity = async (
params: AddLiquidityParams,
privateKey: string,
) => {
const { wallet, provider } = await setupWallet(privateKey);
const assets = await getAllVerifiedFuelAssets();

const asset0 = assets.find((asset) => asset.symbol === asset0Symbol);
const asset1 = assets.find((asset) => asset.symbol === asset1Symbol);
const asset0 = assets.find((asset) => asset.symbol === params.asset0Symbol);
const asset1 = assets.find((asset) => asset.symbol === params.asset1Symbol);

if (!asset0 || !asset1) {
throw new Error(`Asset ${asset0Symbol} or ${asset1Symbol} not found`);
throw new Error(
`Asset ${params.asset0Symbol} or ${params.asset1Symbol} not found`,
);
}

let isStable = asset0.symbol.includes('USD') && asset1.symbol.includes('USD');
@@ -48,9 +50,7 @@ export const addLiquidity = async ({
throw new Error('Invalid asset decimals');
}

const { provider, wallet } = await setupWallet();

const amount0InWei = bn.parseUnits(amount0, asset0Decimals);
const amount0InWei = bn.parseUnits(params.amount0, asset0Decimals);

const poolId = buildPoolId(asset0Id, asset1Id, isStable);

@@ -85,10 +85,10 @@ export const addLiquidity = async ({

// Calculate minimum amounts with slippage
const minAmount0 = amount0InWei
.mul(bn(100 - Math.floor(slippage * 100)))
.mul(bn(100 - Math.floor((params.slippage || DEFAULT_SLIPPAGE) * 100)))
.div(bn(100));
const minAmount1 = amount1InWei
.mul(bn(100 - Math.floor(slippage * 100)))
.mul(bn(100 - Math.floor((params.slippage || DEFAULT_SLIPPAGE) * 100)))
.div(bn(100));

console.log('Min Amount0 (Wei):', minAmount0.toString());
41 changes: 17 additions & 24 deletions src/mira/swap.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import { getAllVerifiedFuelAssets } from '../utils/assets.js';
import { buildPoolId, MiraAmm, ReadonlyMiraAmm } from 'mira-dex-ts';
import { setupWallet } from '../utils/setup.js';
import { getTxExplorerUrl } from '../utils/explorer.js';
import { DEFAULT_SLIPPAGE } from '../constants.js';

async function futureDeadline(provider: Provider) {
const block = await provider.getBlock('latest');
@@ -16,55 +17,47 @@ export type SwapExactInputParams = {
slippage?: number;
};

export const swapExactInput = async ({
amount,
fromSymbol,
toSymbol,
slippage = 0.01,
}: {
amount: string;
fromSymbol: string;
toSymbol: string;
slippage?: number;
}) => {
export const swapExactInput = async (
params: SwapExactInputParams,
privateKey: string,
) => {
const { wallet, provider } = await setupWallet(privateKey);
const assets = await getAllVerifiedFuelAssets();

const fromAsset = assets.find((asset) => asset.symbol === fromSymbol);
const toAsset = assets.find((asset) => asset.symbol === toSymbol);
const fromAsset = assets.find((asset) => asset.symbol === params.fromSymbol);
const toAsset = assets.find((asset) => asset.symbol === params.toSymbol);

if (!fromAsset) {
throw new Error(`Asset ${fromSymbol} not found`);
throw new Error(`Asset ${params.fromSymbol} not found`);
}

if (!toAsset) {
throw new Error(`Asset ${toSymbol} not found`);
throw new Error(`Asset ${params.toSymbol} not found`);
}

const fromAssetId = fromAsset?.assetId;
const toAssetId = toAsset?.assetId;

if (!fromAssetId) {
throw new Error(`Asset ${fromSymbol} not found`);
throw new Error(`Asset ${params.fromSymbol} not found`);
}

if (!toAssetId) {
throw new Error(`Asset ${toSymbol} not found`);
throw new Error(`Asset ${params.toSymbol} not found`);
}

const fromAssetDecimals = fromAsset?.decimals;
const toAssetDecimals = toAsset?.decimals;

if (!fromAssetDecimals) {
throw new Error(`Asset ${fromSymbol} not found`);
throw new Error(`Asset ${params.fromSymbol} not found`);
}

if (!toAssetDecimals) {
throw new Error(`Asset ${toSymbol} not found`);
throw new Error(`Asset ${params.toSymbol} not found`);
}

const { wallet, provider } = await setupWallet();

const amountInWei = bn.parseUnits(amount, fromAssetDecimals);
const amountInWei = bn.parseUnits(params.amount, fromAssetDecimals);

let isStable =
fromAsset.symbol.includes('USD') && toAsset.symbol.includes('USD');
@@ -102,7 +95,7 @@ export const swapExactInput = async ({
console.log('Estimated Amount Out (Wei):', amountOutWei.toString());

const minAmountOut = amountOutWei
.mul(bn(100 - Math.floor(slippage * 100)))
.mul(bn(100 - Math.floor((params.slippage || DEFAULT_SLIPPAGE) * 100)))
.div(bn(100));

const req = await miraAmm.swapExactInput(
@@ -123,5 +116,5 @@ export const swapExactInput = async ({

const { id, status } = await tx.waitForResult();

return `Successfully swapped ${amount} ${fromSymbol} for ${toSymbol}. Explorer link: ${getTxExplorerUrl(id)}`;
return `Successfully swapped ${params.amount} ${params.fromSymbol} for ${params.toSymbol}. Explorer link: ${getTxExplorerUrl(id)}`;
};
13 changes: 8 additions & 5 deletions src/swaylend/borrow.ts
Original file line number Diff line number Diff line change
@@ -10,8 +10,11 @@ export type BorrowAssetParams = {
amount: string;
};

export const borrowAsset = async ({ amount }: BorrowAssetParams) => {
const { wallet, provider } = await setupWallet();
export const borrowAsset = async (
params: BorrowAssetParams,
privateKey: string,
) => {
const { wallet, provider } = await setupWallet(privateKey);

const marketContractId =
'0x657ab45a6eb98a4893a99fd104347179151e8b3828fd8f2a108cc09770d1ebae';
@@ -21,7 +24,7 @@ export const borrowAsset = async ({ amount }: BorrowAssetParams) => {
const asset = allAssets.find((asset) => asset.symbol === 'USDC'); // We can only borrow USDC
const assetId: any = asset?.assetId;

const weiAmount = bn.parseUnits(amount, asset?.decimals);
const weiAmount = bn.parseUnits(params.amount, asset?.decimals);

// fetch configs
const { value: marketConfiguration } = await marketContract.functions
@@ -77,7 +80,7 @@ export const borrowAsset = async ({ amount }: BorrowAssetParams) => {
};

const { waitForResult } = await marketContract.functions
.withdraw_base((+amount).toFixed(0), priceUpdateData)
.withdraw_base((+params.amount).toFixed(0), priceUpdateData)
.callParams({
forward: {
amount: fee,
@@ -91,5 +94,5 @@ export const borrowAsset = async ({ amount }: BorrowAssetParams) => {
const transactionResult = await waitForResult();

// Return the transaction ID
return `Successfully borrowed ${amount} USDC. Explorer link: ${getTxExplorerUrl(transactionResult.transactionId)}`;
return `Successfully borrowed ${params.amount} USDC. Explorer link: ${getTxExplorerUrl(transactionResult.transactionId)}`;
};
16 changes: 8 additions & 8 deletions src/swaylend/supply.ts
Original file line number Diff line number Diff line change
@@ -9,21 +9,21 @@ export type SupplyCollateralParams = {
symbol: string;
};

export const supplyCollateral = async ({
amount,
symbol,
}: SupplyCollateralParams) => {
const { wallet } = await setupWallet();
export const supplyCollateral = async (
params: SupplyCollateralParams,
privateKey: string,
) => {
const { wallet } = await setupWallet(privateKey);

const marketContractId =
'0x657ab45a6eb98a4893a99fd104347179151e8b3828fd8f2a108cc09770d1ebae';
const marketContract: Market = new Market(marketContractId, wallet);

const allAssets = await getAllVerifiedFuelAssets();
const asset = allAssets.find((asset) => asset.symbol === symbol);
const asset = allAssets.find((asset) => asset.symbol === params.symbol);
const assetId = asset?.assetId;

const weiAmount = bn.parseUnits(amount, asset?.decimals);
const weiAmount = bn.parseUnits(params.amount, asset?.decimals);

const tx = await marketContract.functions
.supply_collateral()
@@ -37,5 +37,5 @@ export const supplyCollateral = async ({

const { transactionId } = await tx.waitForResult();

return `Successfully supplied ${amount} ${symbol} as collateral. Explorer link: ${getTxExplorerUrl(transactionId)}`;
return `Successfully supplied ${params.amount} ${params.symbol} as collateral. Explorer link: ${getTxExplorerUrl(transactionId)}`;
};
Loading

0 comments on commit 26467d0

Please sign in to comment.