Skip to content

Commit

Permalink
Merge pull request #373 from enitrat/feat/transfer-token
Browse files Browse the repository at this point in the history
feat: starknet token transfer
  • Loading branch information
ponderingdemocritus authored Nov 17, 2024
2 parents 236ad0c + 24d7f5e commit 648800d
Show file tree
Hide file tree
Showing 7 changed files with 1,147 additions and 47 deletions.
4 changes: 2 additions & 2 deletions packages/plugin-solana/src/actions/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ export default {
/*
const adminIds = runtime.getSetting("ADMIN_USER_IDS")?.split(",") || [];
//console.log("Admin IDs from settings:", adminIds);
const isAdmin = adminIds.includes(message.userId);
if (isAdmin) {
//console.log(`Authorized transfer from user: ${message.userId}`);
return true;
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-starknet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@ai16z/eliza": "workspace:*",
"@ai16z/plugin-trustdb": "workspace:*",
"@avnu/avnu-sdk": "^2.1.1",
"starknet": "^6.11.0",
"starknet": "^6.17.0",
"tsup": "^8.3.5",
"vitest": "^2.1.4"
},
Expand Down
48 changes: 41 additions & 7 deletions packages/plugin-starknet/src/actions/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import {
composeContext,
generateObject,
} from "@ai16z/eliza";
import { validateSettings } from "../utils";
import { getStarknetAccount, validateSettings } from "../utils";
import { Account, RpcProvider } from "starknet";
import { PROVIDER_CONFIG } from "..";
import path from "path";
import fs from "fs";
import { ERC20Token } from "../utils/ERC20Token";

export interface TransferContent extends Content {
tokenAddress: string;
Expand All @@ -27,22 +32,34 @@ function isTransferContent(
content: TransferContent
): content is TransferContent {
console.log("Content for transfer", content);
return (
const validTypes = (
typeof content.tokenAddress === "string" &&
typeof content.recipient === "string" &&
(typeof content.amount === "string" ||
typeof content.amount === "number")
);
if (!validTypes) {
return false;
}

// Addresses must be 32-bytes long with a 0x prefix
const validAddresses = (
content.tokenAddress.startsWith("0x") &&
content.tokenAddress.length === 66 &&
content.recipient.startsWith("0x") &&
content.recipient.length === 66
);
return validTypes && validAddresses;
}

const transferTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
Example response:
\`\`\`json
{
"tokenAddress": "BieefG47jAHCGZBxi2q87RDuHyGZyYC3vAzxpyu8pump",
"recipient": "9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa",
"amount": "1000"
"tokenAddress": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"recipient": "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF",
"amount": "0.001"
}
\`\`\`
Expand Down Expand Up @@ -110,9 +127,20 @@ export default {
}

try {
const account = getStarknetAccount(runtime);
const erc20Token = new ERC20Token(content.tokenAddress, account);
const decimals = await erc20Token.decimals();
const amountWei = BigInt(content.amount) * 10n ** BigInt(decimals);
const transferCall = erc20Token.transferCall(content.recipient, amountWei);

console.log("Transferring", amountWei, "of", content.tokenAddress, "to", content.recipient);

const tx = await account.execute(transferCall);

console.log("Transfer completed successfully! tx: " + tx.transaction_hash);
if (callback) {
callback({
text: `Successfully`,
text: "Transfer completed successfully! tx: " + tx.transaction_hash,
content: {},
});
}
Expand All @@ -135,7 +163,13 @@ export default {
{
user: "{{user1}}",
content: {
text: "Send 69 STRK to 0x1234567890123456789012345678901234567890",
text: "Send 69 STRK to 0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF",
},
},
{
user: "{{user2}}",
content: {
text: "Transfer to 0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF 0.01 ETH",
},
},
],
Expand Down
9 changes: 4 additions & 5 deletions packages/plugin-starknet/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ export const PROVIDER_CONFIG = {
AVNU_API: "https://starknet.impulse.avnu.fi/v1",
MAX_RETRIES: 3,
RETRY_DELAY: 2000,
DEFAULT_RPC: "https://api.mainnet-beta.solana.com",
DEFAULT_RPC: "https://free-rpc.nethermind.io/mainnet-juno/",
TOKEN_ADDRESSES: {
SOL: "",
BTC: "",
ETH: "",
STRK: "",
BTC: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
ETH: "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
STRK: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
},
TOKEN_SECURITY_ENDPOINT: "/defi/token_security?address=",
TOKEN_TRADE_DATA_ENDPOINT: "/defi/v3/token/trade-data/single?address=",
Expand Down
59 changes: 59 additions & 0 deletions packages/plugin-starknet/src/utils/ERC20Token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import path from "path";
import fs from "fs";
import { Account, Call, CallData, Calldata, Contract, cairo } from "starknet";

export type ApproveCall = {
contractAddress: string;
entrypoint: "approve";
calldata: Calldata;
};

export type TransferCall = {
contractAddress: string;
entrypoint: "transfer";
calldata: Calldata;
};

export class ERC20Token {
abi: any;
contract: Contract;
calldata: CallData;
constructor(token: string, account?: Account) {
const erc20AbiPath = path.join(__dirname, "../utils/erc20.json");
const erc20Abi = JSON.parse(fs.readFileSync(erc20AbiPath, "utf8"));
this.contract = new Contract(erc20Abi, token, account);
this.calldata = new CallData(this.contract.abi);
}

public address() {
return this.contract.address;
}

public async decimals() {
const result = await this.contract.call("decimals");
return result as bigint;
}


public approveCall(spender: string, amount: bigint): ApproveCall {
return {
contractAddress: this.contract.address,
entrypoint: "approve",
calldata: this.calldata.compile("approve", {
spender: spender,
amount: cairo.uint256(amount),
}),
};
}

public transferCall(recipient: string, amount: bigint): TransferCall {
return {
contractAddress: this.contract.address,
entrypoint: "transfer",
calldata: this.calldata.compile("transfer", {
recipient: recipient,
amount: cairo.uint256(amount),
}),
};
}
}
Loading

0 comments on commit 648800d

Please sign in to comment.