@symmetry-hq/agents-sdk
Version:
Symmetry Agents SDK
219 lines (201 loc) • 7.19 kB
text/typescript
// Core dependencies
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { AccountInfo, Connection, GetProgramAccountsFilter, GetProgramAccountsResponse, Keypair, PublicKey, TransactionSignature } from "@solana/web3.js";
import { Metaplex } from "@metaplex-foundation/js";
// Local imports
import { IDL } from "../idl/idl";
import { AgentsProgram } from "../idl/types";
import { parse, stringify, v4 } from "uuid";
import { AGENTS_PROGRAM_ID, EVENT_AUTHORITY_SEED, FEES_VAULT_SEED } from "../constants";
import { AGENTS_STATE_SIZE, AgentState, parseAgentState, ParsedAgentState } from "../state/agent";
import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
export function getAgentsProgram(
connection: Connection,
): Program<AgentsProgram> {
const program: Program<AgentsProgram> = new Program(
IDL,
new AnchorProvider(
connection,
new NodeWallet(Keypair.generate())
)
);
return program;
}
export function getAgentStateAccount(
agent: PublicKey,
): PublicKey {
return PublicKey.findProgramAddressSync(
[Uint8Array.from(agent.toBuffer())],
AGENTS_PROGRAM_ID,
)[0];
}
export function getAgent(
agentUuid: number[],
): PublicKey {
return PublicKey.findProgramAddressSync(
[Uint8Array.from(agentUuid)],
AGENTS_PROGRAM_ID,
)[0];
}
export function getEventAuthority(program: Program<AgentsProgram>) {
const [eventAuthority] = PublicKey.findProgramAddressSync(
[Buffer.from(EVENT_AUTHORITY_SEED)],
program.programId
);
return eventAuthority;
}
export function getFeesVault(program: Program<AgentsProgram>) {
const [feeVault] = PublicKey.findProgramAddressSync(
[Buffer.from(FEES_VAULT_SEED)],
program.programId
);
return feeVault;
}
export function getMetadataAccount(
tokenMint: PublicKey
): PublicKey {
const metaplex = Metaplex.make(new Connection("https://api.devnet.solana.com"));
return metaplex.nfts().pdas().metadata({ mint: tokenMint });
}
export function getAta(
wallet: PublicKey,
tokenMint: PublicKey,
): PublicKey {
return getAssociatedTokenAddressSync(tokenMint, wallet, true);
}
export function getRandomSeed(): number[] {
return Array.from(parse(v4()));
}
export function getStringFromSeed(
seed: number[],
): string {
return stringify(Uint8Array.from(seed));
}
export async function getAccountInfos(
connection: Connection,
keys: PublicKey[],
): Promise<(AccountInfo<Buffer> | null)[]> {
const allAccounts: (AccountInfo<Buffer>|null)[] = [];
const batchSize = 100;
for (let i = 0; i < keys.length; i += batchSize) {
const batch = keys.slice(i, i + batchSize);
const batchAccounts = await connection.getMultipleAccountsInfo(batch);
allAccounts.push(...batchAccounts);
}
return allAccounts;
}
export async function getAllAgents(
program: Program<AgentsProgram>,
): Promise<ParsedAgentState[]> {
const accounts: AgentState[] = (await program.account.agent.all()).map(account => account.account);
return accounts.map(account => parseAgentState(account));
}
export async function getAgentsByCreator(
program: Program<AgentsProgram>,
creator: PublicKey,
): Promise<ParsedAgentState[]> {
const accountFilters: GetProgramAccountsFilter[] = [
{
dataSize: AGENTS_STATE_SIZE + 8,
},
{
memcmp: {
offset: 8 + 32 + 16,
bytes: creator.toBase58(),
},
}
]
const accounts: GetProgramAccountsResponse = await program.provider.connection
.getProgramAccounts(
AGENTS_PROGRAM_ID,
{
commitment: "confirmed",
filters: accountFilters,
encoding: 'base64'
}
);
const agents: AgentState[] = accounts.map(account =>
program.coder.accounts.decode("agent", account.account.data)
);
return await Promise.all(agents.map(agent => parseAgentState(agent)));
}
export async function getEventsFromTx(
connection: Connection,
program: Program<AgentsProgram>,
txId: TransactionSignature,
): Promise<any[]> {
const parsed = await connection.getParsedTransaction(
txId,
{ maxSupportedTransactionVersion: 0, commitment: "confirmed"}
).catch((_) => null);
if (!parsed || !parsed.meta || parsed.meta.err || !parsed.meta.innerInstructions)
throw new Error("Transaction is invalid");
const eventAuthority = getEventAuthority(program);
let rawEvents: any[] = [];
for (let i = 0; i < parsed.meta.innerInstructions.length; i++) {
for (let j = 0; j < parsed.meta.innerInstructions[i].instructions.length; j++) {
let ix = parsed.meta.innerInstructions[i].instructions[j];
if (!ix.programId.equals(program.programId)) continue;
//@ts-ignore
let accounts = ix.accounts;
//@ts-ignore
let data = ix.data;
if (!accounts || !data || accounts.length != 1) continue;
if (!accounts[0].equals(eventAuthority)) continue;
let buffer = bs58.decode(data);
let str64 = buffer.subarray(8, buffer.length).toString("base64");
const rawEvent = program.coder.events.decode(str64);
if (!rawEvent) continue;
rawEvents.push(rawEvent);
}
}
return rawEvents;
}
export async function getTopUpBalanceEventFromTx(
connection: Connection,
program: Program<AgentsProgram>,
txId: TransactionSignature,
): Promise<{
user: string,
mint: string,
amount: number,
}> {
const rawEvents = await getEventsFromTx(connection, program, txId);
for (let i = 0; i < rawEvents.length; i++) {
let rawEvent = rawEvents[i];
if (rawEvent.name !== "topUpBalanceEvent") continue;
let parsedEvent = {
user: rawEvent.data.user.toBase58(),
amount: parseInt(rawEvent.data.amount.toString()),
mint: rawEvent.data.mint.toBase58(),
};
return parsedEvent;
}
throw new Error("Top up balance event not found");
}
export async function getCreateAgentEventFromTx(
connection: Connection,
program: Program<AgentsProgram>,
txId: TransactionSignature,
): Promise<{
agent: string,
agentState: string,
agentUuid: string,
creator: string,
}> {
const rawEvents = await getEventsFromTx(connection, program, txId);
for (let i = 0; i < rawEvents.length; i++) {
let rawEvent = rawEvents[i];
if (rawEvent.name !== "createAgentEvent") continue;
let parsedEvent = {
agent: rawEvent.data.agent.toBase58(),
agentState: rawEvent.data.stateAddress.toBase58(),
agentUuid: getStringFromSeed(rawEvent.data.agentUuid),
creator: rawEvent.data.creator.toBase58(),
};
return parsedEvent;
}
throw new Error("Create agent event not found");
}