Skip to content

Commit dbe0f21

Browse files
authoredDec 3, 2024
Merge pull request #1 from serrrfirat/rebase
Rebase
2 parents 438c1f1 + 913651d commit dbe0f21

File tree

11 files changed

+666
-5
lines changed

11 files changed

+666
-5
lines changed
 

‎.env.example

+10
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,13 @@ WHATSAPP_API_VERSION=v17.0 # WhatsApp API version (default: v17.0)
185185
# ICP
186186
INTERNET_COMPUTER_PRIVATE_KEY=
187187
INTERNET_COMPUTER_ADDRESS=
188+
189+
# Near
190+
NEAR_WALLET_SECRET_KEY=
191+
NEAR_WALLET_PUBLIC_KEY=
192+
NEAR_ADDRESS=
193+
SLIPPAGE=1
194+
RPC_URL=
195+
196+
# Add this line to your existing .env.example
197+
NEAR_NETWORK=testnet # or mainnet

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules
22
/out
33

4+
45
.env
56
.env.production
67
concatenated-output.ts

‎agent/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@ai16z/plugin-icp": "workspace:*",
3838
"@ai16z/plugin-tee": "workspace:*",
3939
"@ai16z/plugin-coinbase": "workspace:*",
40+
"@ai16z/plugin-near": "workspace:*",
4041
"readline": "1.3.0",
4142
"ws": "8.18.0",
4243
"@ai16z/plugin-evm": "workspace:*",

‎agent/src/index.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
validateCharacterConfig,
2424
} from "@ai16z/eliza";
2525
import { zgPlugin } from "@ai16z/plugin-0g";
26-
import { goatPlugin } from "@ai16z/plugin-goat";
2726
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
2827
// import { buttplugPlugin } from "@ai16z/plugin-buttplug";
2928
import {
@@ -37,6 +36,7 @@ import { evmPlugin } from "@ai16z/plugin-evm";
3736
import { createNodePlugin } from "@ai16z/plugin-node";
3837
import { solanaPlugin } from "@ai16z/plugin-solana";
3938
import { teePlugin } from "@ai16z/plugin-tee";
39+
import { nearPlugin } from "@ai16z/plugin-near";
4040
import Database from "better-sqlite3";
4141
import fs from "fs";
4242
import path from "path";
@@ -161,7 +161,7 @@ export async function loadCharacters(
161161
return importedPlugin.default;
162162
})
163163
);
164-
character.plugins = importedPlugins;
164+
character.plugins = importedPlugins.filter(Boolean);
165165
}
166166

167167
loadedCharacters.push(character);
@@ -372,7 +372,8 @@ export function createAgent(
372372
!getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
373373
? solanaPlugin
374374
: null,
375-
getSecret(character, "EVM_PRIVATE_KEY") ||
375+
nearPlugin,
376+
getSecret(character, "EVM_PUBLIC_KEY") ||
376377
(getSecret(character, "WALLET_PUBLIC_KEY") &&
377378
!getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
378379
? evmPlugin
@@ -391,7 +392,6 @@ export function createAgent(
391392
? [coinbaseMassPaymentsPlugin, tradePlugin]
392393
: []),
393394
getSecret(character, "WALLET_SECRET_SALT") ? teePlugin : null,
394-
getSecret(character, "ALCHEMY_API_KEY") ? goatPlugin : null,
395395
].filter(Boolean),
396396
providers: [],
397397
actions: [],
@@ -433,7 +433,6 @@ async function startAgent(character: Character, directClient) {
433433

434434
const cache = intializeDbCache(character, db);
435435
const runtime = createAgent(character, db, cache, token);
436-
437436
await runtime.initialize();
438437

439438
const clients = await initializeClients(character, runtime);

‎packages/plugin-near/package.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "@ai16z/plugin-near",
3+
"version": "0.0.1",
4+
"main": "dist/index.js",
5+
"type": "module",
6+
"types": "dist/index.d.ts",
7+
"dependencies": {
8+
"@ai16z/eliza": "workspace:*",
9+
"@ai16z/plugin-trustdb": "workspace:*",
10+
"@ref-finance/ref-sdk": "^1.4.6",
11+
"tsup": "8.3.5",
12+
"near-api-js": "5.0.1",
13+
"bignumber.js": "9.1.2",
14+
"node-cache": "5.1.2"
15+
},
16+
"devDependencies": {
17+
"eslint": "^9.15.0",
18+
"eslint-config-prettier": "9.1.0",
19+
"eslint-plugin-prettier": "5.2.1",
20+
"eslint-plugin-vitest": "0.5.4"
21+
},
22+
"scripts": {
23+
"build": "tsup --format esm,cjs --dts",
24+
"test": "vitest run",
25+
"test:watch": "vitest",
26+
"lint": "eslint . --fix"
27+
},
28+
"peerDependencies": {
29+
"whatwg-url": "7.1.0",
30+
"form-data": "4.0.1"
31+
}
32+
}
+241
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import {
2+
ActionExample,
3+
HandlerCallback,
4+
IAgentRuntime,
5+
Memory,
6+
ModelClass,
7+
State,
8+
type Action,
9+
composeContext,
10+
generateObject,
11+
} from "@ai16z/eliza";
12+
import { connect, keyStores, utils } from "near-api-js";
13+
import BigNumber from "bignumber.js";
14+
import { init_env, ftGetTokenMetadata, estimateSwap, instantSwap, fetchAllPools, FT_MINIMUM_STORAGE_BALANCE_LARGE } from '@ref-finance/ref-sdk';
15+
import { walletProvider } from "../providers/wallet";
16+
import { KeyPairString } from "near-api-js/lib/utils";
17+
18+
// Initialize Ref SDK with testnet environment
19+
init_env('testnet');
20+
21+
async function swapToken(
22+
runtime: IAgentRuntime,
23+
inputTokenId: string,
24+
outputTokenId: string,
25+
amount: string,
26+
slippageTolerance: number = 0.1
27+
): Promise<any> {
28+
try {
29+
// Get token metadata
30+
const tokenIn = await ftGetTokenMetadata(inputTokenId);
31+
const tokenOut = await ftGetTokenMetadata(outputTokenId);
32+
33+
// Get all pools for estimation
34+
const { ratedPools, unRatedPools, simplePools} = await fetchAllPools(200);
35+
36+
console.log("Pools:", simplePools);
37+
const swapTodos = await estimateSwap({
38+
tokenIn,
39+
tokenOut,
40+
amountIn: amount,
41+
simplePools,
42+
options: {
43+
enableSmartRouting: true,
44+
}
45+
});
46+
47+
if (!swapTodos || swapTodos.length === 0) {
48+
throw new Error('No valid swap route found');
49+
}
50+
51+
// Get account ID from runtime settings
52+
const accountId = runtime.getSetting("NEAR_ADDRESS");
53+
if (!accountId) {
54+
throw new Error("NEAR_ADDRESS not configured");
55+
}
56+
57+
// Execute swap
58+
const transactions = await instantSwap({
59+
tokenIn,
60+
tokenOut,
61+
amountIn: amount,
62+
swapTodos,
63+
slippageTolerance,
64+
AccountId: accountId
65+
});
66+
67+
return transactions;
68+
} catch (error) {
69+
console.error("Error in swapToken:", error);
70+
throw error;
71+
}
72+
}
73+
74+
const swapTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
75+
76+
Example response:
77+
\`\`\`json
78+
{
79+
"inputTokenId": "wrap.testnet",
80+
"outputTokenId": "ref.fakes.testnet",
81+
"amount": "1.5"
82+
}
83+
\`\`\`
84+
85+
{{recentMessages}}
86+
87+
Given the recent messages and wallet information below:
88+
89+
{{walletInfo}}
90+
91+
Extract the following information about the requested token swap:
92+
- Input token ID (the token being sold)
93+
- Output token ID (the token being bought)
94+
- Amount to swap
95+
96+
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:
97+
\`\`\`json
98+
{
99+
"inputTokenId": string | null,
100+
"outputTokenId": string | null,
101+
"amount": string | null
102+
}
103+
\`\`\``;
104+
105+
export const executeSwap: Action = {
106+
name: "EXECUTE_SWAP_NEAR",
107+
similes: ["SWAP_TOKENS_NEAR", "TOKEN_SWAP_NEAR", "TRADE_TOKENS_NEAR", "EXCHANGE_TOKENS_NEAR"],
108+
validate: async (runtime: IAgentRuntime, message: Memory) => {
109+
console.log("Message:", message);
110+
return true;
111+
},
112+
description: "Perform a token swap using Ref Finance.",
113+
handler: async (
114+
runtime: IAgentRuntime,
115+
message: Memory,
116+
state: State,
117+
_options: { [key: string]: unknown },
118+
callback?: HandlerCallback
119+
): Promise<boolean> => {
120+
// Compose state
121+
if (!state) {
122+
state = (await runtime.composeState(message)) as State;
123+
} else {
124+
state = await runtime.updateRecentMessageState(state);
125+
}
126+
127+
const walletInfo = await walletProvider.get(runtime, message, state);
128+
state.walletInfo = walletInfo;
129+
130+
const swapContext = composeContext({
131+
state,
132+
template: swapTemplate,
133+
});
134+
135+
const response = await generateObject({
136+
runtime,
137+
context: swapContext,
138+
modelClass: ModelClass.LARGE,
139+
});
140+
141+
console.log("Response:", response);
142+
143+
if (!response.inputTokenId || !response.outputTokenId || !response.amount) {
144+
console.log("Missing required parameters, skipping swap");
145+
const responseMsg = {
146+
text: "I need the input token ID, output token ID, and amount to perform the swap",
147+
};
148+
callback?.(responseMsg);
149+
return true;
150+
}
151+
152+
try {
153+
// Get account credentials
154+
const accountId = runtime.getSetting("NEAR_ADDRESS");
155+
const secretKey = runtime.getSetting("NEAR_WALLET_SECRET_KEY");
156+
157+
if (!accountId || !secretKey) {
158+
throw new Error("NEAR wallet credentials not configured");
159+
}
160+
161+
// Create keystore and connect to NEAR
162+
const keyStore = new keyStores.InMemoryKeyStore();
163+
const keyPair = utils.KeyPair.fromString(secretKey as KeyPairString);
164+
await keyStore.setKey("testnet", accountId, keyPair);
165+
166+
const nearConnection = await connect({
167+
networkId: "testnet",
168+
keyStore,
169+
nodeUrl: "https://rpc.testnet.near.org",
170+
});
171+
172+
// Execute swap
173+
const swapResult = await swapToken(
174+
runtime,
175+
response.inputTokenId,
176+
response.outputTokenId,
177+
response.amount,
178+
0.1 // 1% slippage tolerance
179+
);
180+
181+
// Sign and send transactions
182+
const account = await nearConnection.account(accountId);
183+
const results = [];
184+
185+
for (const tx of swapResult) {
186+
for (const functionCall of tx.functionCalls) {
187+
const result = await account.functionCall({
188+
contractId: tx.receiverId,
189+
methodName: functionCall.methodName,
190+
args: functionCall.args,
191+
gas: functionCall.gas,
192+
attachedDeposit: BigInt(1),
193+
});
194+
results.push(result);
195+
}
196+
}
197+
198+
console.log("Swap completed successfully!");
199+
const txHashes = results.map(r => r.transaction.hash).join(", ");
200+
201+
const responseMsg = {
202+
text: `Swap completed successfully! Transaction hashes: ${txHashes}`,
203+
};
204+
205+
callback?.(responseMsg);
206+
return true;
207+
} catch (error) {
208+
console.error("Error during token swap:", error);
209+
const responseMsg = {
210+
text: `Error during swap: ${error instanceof Error ? error.message : String(error)}`,
211+
};
212+
callback?.(responseMsg);
213+
return false;
214+
}
215+
},
216+
examples: [
217+
[
218+
{
219+
user: "{{user1}}",
220+
content: {
221+
inputTokenId: "wrap.testnet",
222+
outputTokenId: "ref.fakes.testnet",
223+
amount: "1.0",
224+
},
225+
},
226+
{
227+
user: "{{user2}}",
228+
content: {
229+
text: "Swapping 1.0 NEAR for REF...",
230+
action: "TOKEN_SWAP",
231+
},
232+
},
233+
{
234+
user: "{{user2}}",
235+
content: {
236+
text: "Swap completed successfully! Transaction hash: ...",
237+
},
238+
},
239+
],
240+
] as ActionExample[][],
241+
} as Action;

0 commit comments

Comments
 (0)
Please sign in to comment.