Skip to content

Commit

Permalink
Merge pull request #1 from serrrfirat/rebase
Browse files Browse the repository at this point in the history
Rebase
  • Loading branch information
serrrfirat authored Dec 3, 2024
2 parents 438c1f1 + 913651d commit dbe0f21
Show file tree
Hide file tree
Showing 11 changed files with 666 additions and 5 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,13 @@ WHATSAPP_API_VERSION=v17.0 # WhatsApp API version (default: v17.0)
# ICP
INTERNET_COMPUTER_PRIVATE_KEY=
INTERNET_COMPUTER_ADDRESS=

# Near
NEAR_WALLET_SECRET_KEY=
NEAR_WALLET_PUBLIC_KEY=
NEAR_ADDRESS=
SLIPPAGE=1
RPC_URL=

# Add this line to your existing .env.example
NEAR_NETWORK=testnet # or mainnet
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
/out


.env
.env.production
concatenated-output.ts
Expand Down
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@ai16z/plugin-icp": "workspace:*",
"@ai16z/plugin-tee": "workspace:*",
"@ai16z/plugin-coinbase": "workspace:*",
"@ai16z/plugin-near": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"@ai16z/plugin-evm": "workspace:*",
Expand Down
9 changes: 4 additions & 5 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
validateCharacterConfig,
} from "@ai16z/eliza";
import { zgPlugin } from "@ai16z/plugin-0g";
import { goatPlugin } from "@ai16z/plugin-goat";
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
// import { buttplugPlugin } from "@ai16z/plugin-buttplug";
import {
Expand All @@ -37,6 +36,7 @@ import { evmPlugin } from "@ai16z/plugin-evm";
import { createNodePlugin } from "@ai16z/plugin-node";
import { solanaPlugin } from "@ai16z/plugin-solana";
import { teePlugin } from "@ai16z/plugin-tee";
import { nearPlugin } from "@ai16z/plugin-near";
import Database from "better-sqlite3";
import fs from "fs";
import path from "path";
Expand Down Expand Up @@ -161,7 +161,7 @@ export async function loadCharacters(
return importedPlugin.default;
})
);
character.plugins = importedPlugins;
character.plugins = importedPlugins.filter(Boolean);
}

loadedCharacters.push(character);
Expand Down Expand Up @@ -372,7 +372,8 @@ export function createAgent(
!getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
? solanaPlugin
: null,
getSecret(character, "EVM_PRIVATE_KEY") ||
nearPlugin,
getSecret(character, "EVM_PUBLIC_KEY") ||
(getSecret(character, "WALLET_PUBLIC_KEY") &&
!getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
? evmPlugin
Expand All @@ -391,7 +392,6 @@ export function createAgent(
? [coinbaseMassPaymentsPlugin, tradePlugin]
: []),
getSecret(character, "WALLET_SECRET_SALT") ? teePlugin : null,
getSecret(character, "ALCHEMY_API_KEY") ? goatPlugin : null,
].filter(Boolean),
providers: [],
actions: [],
Expand Down Expand Up @@ -433,7 +433,6 @@ async function startAgent(character: Character, directClient) {

const cache = intializeDbCache(character, db);
const runtime = createAgent(character, db, cache, token);

await runtime.initialize();

const clients = await initializeClients(character, runtime);
Expand Down
32 changes: 32 additions & 0 deletions packages/plugin-near/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@ai16z/plugin-near",
"version": "0.0.1",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@ai16z/eliza": "workspace:*",
"@ai16z/plugin-trustdb": "workspace:*",
"@ref-finance/ref-sdk": "^1.4.6",
"tsup": "8.3.5",
"near-api-js": "5.0.1",
"bignumber.js": "9.1.2",
"node-cache": "5.1.2"
},
"devDependencies": {
"eslint": "^9.15.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-vitest": "0.5.4"
},
"scripts": {
"build": "tsup --format esm,cjs --dts",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint . --fix"
},
"peerDependencies": {
"whatwg-url": "7.1.0",
"form-data": "4.0.1"
}
}
241 changes: 241 additions & 0 deletions packages/plugin-near/src/actions/swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import {
ActionExample,
HandlerCallback,
IAgentRuntime,
Memory,
ModelClass,
State,
type Action,
composeContext,
generateObject,
} from "@ai16z/eliza";
import { connect, keyStores, utils } from "near-api-js";
import BigNumber from "bignumber.js";
import { init_env, ftGetTokenMetadata, estimateSwap, instantSwap, fetchAllPools, FT_MINIMUM_STORAGE_BALANCE_LARGE } from '@ref-finance/ref-sdk';
import { walletProvider } from "../providers/wallet";
import { KeyPairString } from "near-api-js/lib/utils";

// Initialize Ref SDK with testnet environment
init_env('testnet');

async function swapToken(
runtime: IAgentRuntime,
inputTokenId: string,
outputTokenId: string,
amount: string,
slippageTolerance: number = 0.1
): Promise<any> {
try {
// Get token metadata
const tokenIn = await ftGetTokenMetadata(inputTokenId);
const tokenOut = await ftGetTokenMetadata(outputTokenId);

// Get all pools for estimation
const { ratedPools, unRatedPools, simplePools} = await fetchAllPools(200);

console.log("Pools:", simplePools);
const swapTodos = await estimateSwap({
tokenIn,
tokenOut,
amountIn: amount,
simplePools,
options: {
enableSmartRouting: true,
}
});

if (!swapTodos || swapTodos.length === 0) {
throw new Error('No valid swap route found');
}

// Get account ID from runtime settings
const accountId = runtime.getSetting("NEAR_ADDRESS");
if (!accountId) {
throw new Error("NEAR_ADDRESS not configured");
}

// Execute swap
const transactions = await instantSwap({
tokenIn,
tokenOut,
amountIn: amount,
swapTodos,
slippageTolerance,
AccountId: accountId
});

return transactions;
} catch (error) {
console.error("Error in swapToken:", error);
throw error;
}
}

const swapTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
Example response:
\`\`\`json
{
"inputTokenId": "wrap.testnet",
"outputTokenId": "ref.fakes.testnet",
"amount": "1.5"
}
\`\`\`
{{recentMessages}}
Given the recent messages and wallet information below:
{{walletInfo}}
Extract the following information about the requested token swap:
- Input token ID (the token being sold)
- Output token ID (the token being bought)
- Amount to swap
Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. The result should be a valid JSON object with the following schema:
\`\`\`json
{
"inputTokenId": string | null,
"outputTokenId": string | null,
"amount": string | null
}
\`\`\``;

export const executeSwap: Action = {
name: "EXECUTE_SWAP_NEAR",
similes: ["SWAP_TOKENS_NEAR", "TOKEN_SWAP_NEAR", "TRADE_TOKENS_NEAR", "EXCHANGE_TOKENS_NEAR"],
validate: async (runtime: IAgentRuntime, message: Memory) => {
console.log("Message:", message);
return true;
},
description: "Perform a token swap using Ref Finance.",
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_options: { [key: string]: unknown },
callback?: HandlerCallback
): Promise<boolean> => {
// Compose state
if (!state) {
state = (await runtime.composeState(message)) as State;
} else {
state = await runtime.updateRecentMessageState(state);
}

const walletInfo = await walletProvider.get(runtime, message, state);
state.walletInfo = walletInfo;

const swapContext = composeContext({
state,
template: swapTemplate,
});

const response = await generateObject({
runtime,
context: swapContext,
modelClass: ModelClass.LARGE,
});

console.log("Response:", response);

if (!response.inputTokenId || !response.outputTokenId || !response.amount) {
console.log("Missing required parameters, skipping swap");
const responseMsg = {
text: "I need the input token ID, output token ID, and amount to perform the swap",
};
callback?.(responseMsg);
return true;
}

try {
// Get account credentials
const accountId = runtime.getSetting("NEAR_ADDRESS");
const secretKey = runtime.getSetting("NEAR_WALLET_SECRET_KEY");

if (!accountId || !secretKey) {
throw new Error("NEAR wallet credentials not configured");
}

// Create keystore and connect to NEAR
const keyStore = new keyStores.InMemoryKeyStore();
const keyPair = utils.KeyPair.fromString(secretKey as KeyPairString);
await keyStore.setKey("testnet", accountId, keyPair);

const nearConnection = await connect({
networkId: "testnet",
keyStore,
nodeUrl: "https://rpc.testnet.near.org",
});

// Execute swap
const swapResult = await swapToken(
runtime,
response.inputTokenId,
response.outputTokenId,
response.amount,
0.1 // 1% slippage tolerance
);

// Sign and send transactions
const account = await nearConnection.account(accountId);
const results = [];

for (const tx of swapResult) {
for (const functionCall of tx.functionCalls) {
const result = await account.functionCall({
contractId: tx.receiverId,
methodName: functionCall.methodName,
args: functionCall.args,
gas: functionCall.gas,
attachedDeposit: BigInt(1),
});
results.push(result);
}
}

console.log("Swap completed successfully!");
const txHashes = results.map(r => r.transaction.hash).join(", ");

const responseMsg = {
text: `Swap completed successfully! Transaction hashes: ${txHashes}`,
};

callback?.(responseMsg);
return true;
} catch (error) {
console.error("Error during token swap:", error);
const responseMsg = {
text: `Error during swap: ${error instanceof Error ? error.message : String(error)}`,
};
callback?.(responseMsg);
return false;
}
},
examples: [
[
{
user: "{{user1}}",
content: {
inputTokenId: "wrap.testnet",
outputTokenId: "ref.fakes.testnet",
amount: "1.0",
},
},
{
user: "{{user2}}",
content: {
text: "Swapping 1.0 NEAR for REF...",
action: "TOKEN_SWAP",
},
},
{
user: "{{user2}}",
content: {
text: "Swap completed successfully! Transaction hash: ...",
},
},
],
] as ActionExample[][],
} as Action;
Loading

0 comments on commit dbe0f21

Please sign in to comment.