Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f726465
refactor: updage agent code & agent info retrieval from HB node
pawanpaudel93 Oct 8, 2025
4021d06
refactor: update tier info retrieval logic to fetch from HB node with…
pawanpaudel93 Oct 8, 2025
5d32bbc
chore: update agent version to 1.0.2
pawanpaudel93 Oct 9, 2025
8e0fd36
refactor: enhance agent version handling and sync logic
pawanpaudel93 Oct 9, 2025
7f0105b
refactor: enable logging for AGENTS and TIERS in development environment
pawanpaudel93 Oct 9, 2025
b8cca7e
chore: update URLs to use production HB node for agent and tier info …
pawanpaudel93 Oct 9, 2025
178903e
Merge branch 'development' into refactor/hb-migration
pawanpaudel93 Oct 9, 2025
11ab8b8
refactor: add agent update functionality with modal and version check
pawanpaudel93 Oct 9, 2025
46f1ff1
refactor: add success message for agent update
pawanpaudel93 Oct 10, 2025
f1d7391
refactor: implement rate-limited toast notification for agent update …
pawanpaudel93 Oct 10, 2025
c9f1ce8
refactor: optimize token info extraction by using a loop for tag values
pawanpaudel93 Oct 10, 2025
6eef932
refactor: update URL construction for tier info retrieval to use dyna…
pawanpaudel93 Oct 10, 2025
d70e5b4
refactor: integrate logging for agent info retrieval and update URL c…
pawanpaudel93 Oct 10, 2025
433e02a
refactor: enhance agent info retrieval with attempt tracking and dyna…
pawanpaudel93 Oct 11, 2025
302d2da
refactor: standardize data key access in agent and tier utilities for…
pawanpaudel93 Oct 12, 2025
1088b8e
refactor: introduce new HB node constants for improved URL management…
pawanpaudel93 Oct 12, 2025
a3c2b96
chore: update agent code
pawanpaudel93 Oct 12, 2025
1e408e3
refactor: add 'no changes to save' message and enhance agent update l…
pawanpaudel93 Oct 13, 2025
6f744ea
chore: update agent code
pawanpaudel93 Oct 13, 2025
e73773f
refactor: add useHasActiveAOYieldAgent hook and update button logic t…
pawanpaudel93 Oct 13, 2025
cb6a783
refactor: remove WNDR token from ardrive cu usage
pawanpaudel93 Oct 14, 2025
86c7a39
refactor: enhance agent synchronization logic with a dedicated AgentS…
pawanpaudel93 Oct 16, 2025
6f81c40
refactor: improve sync logic in AgentSyncManager to conditionally ski…
pawanpaudel93 Oct 16, 2025
da0474b
refactor: streamline AgentSyncManager by removing unused metrics and …
pawanpaudel93 Oct 16, 2025
b6b3e14
refactor: enhance AgentSyncManager with improved logging and active s…
pawanpaudel93 Oct 17, 2025
2dad928
refactor: optimize agent synchronization process in AgentSyncManager …
pawanpaudel93 Oct 17, 2025
8765dc4
refactor: adjust layout spacing in AgentsView for improved UI consist…
pawanpaudel93 Oct 17, 2025
349cb0c
refactor: enhance agent information synchronization with new useSyncA…
pawanpaudel93 Oct 17, 2025
5a102a6
Merge branch 'development' into refactor/hb-migration
pawanpaudel93 Oct 17, 2025
b397103
refactor: update getAOYieldAgentInfo to improve error handling and en…
pawanpaudel93 Oct 17, 2025
eab94be
refactor: standardize agent version property in AOYieldAgentInfo and …
pawanpaudel93 Oct 17, 2025
b68cf1e
fix: add sorting option to transactions query
pawanpaudel93 Oct 17, 2025
e4961d0
refactor: remove WNDR_PROCESS_ID from ARDRIVE_PROCESSES array
pawanpaudel93 Oct 17, 2025
01e23f6
refactor: add createdAt property to AOYieldAgent and enhance agent sy…
pawanpaudel93 Oct 17, 2025
d92f4d5
refactor: replace dryrun with fetch for savings retrieval and streaml…
pawanpaudel93 Oct 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/agents/contracts/ao-yield-agent.lua

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/routes/popup/agents/ao-yield/confirm-agent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ export function ConfirmAOYieldAgentView() {
name: "Slippage",
value: aoYieldAgent.slippage.toString(),
},
{
name: "Agent-Version",
value: AGENT_VERSION,
},
],
forceSpawn: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const AOYieldAgentCreateListItem = () => {
const AOYieldAgentActiveListItem = ({ aoAgent, isHistory }: AOYieldAgentListItemProps) => {
const { navigate } = useLocation();
const { data: mintingStatus } = useAOMintingStatus();
const { data: agentInfo } = useAOYieldAgentInfo(aoAgent?.id);
const { data: agentInfo } = useAOYieldAgentInfo(aoAgent?.id, aoAgent?.version);
const theme = useTheme();

useAsyncEffect(async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/agents/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ export const AO_YIELD_AGENT_RECENT_TXS_CHECK_ALARM_NAME = "ao-yield-agent-recent
export const AO_YIELD_AGENT_SYNC_ALARM_NAME_PREFIX = "ao-yield-agent-sync-alarm-";
export const AO_YIELD_AGENT_SYNC_STATUS_PREFIX_KEY = "ao-yield-agent-sync-status-";

export const AGENT_VERSION = "1.0.0";
export const AGENT_VERSION = "1.0.2";
4 changes: 2 additions & 2 deletions src/utils/agents/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ export function useAOYieldAgent(agentId: string, status?: AOYieldAgentStatus) {
}, [agents, agentId, status]);
}

export function useAOYieldAgentInfo(agentId: string) {
export function useAOYieldAgentInfo(agentId: string, currentAgentVersion?: string) {
return useQuery<AOYieldAgentInfo>({
queryKey: ["ao-yield-agent-info", agentId],
queryFn: () => getAOYieldAgentInfo(agentId),
queryFn: () => getAOYieldAgentInfo(agentId, currentAgentVersion),
enabled: !!agentId,
refetchInterval: 60_000,
staleTime: 60_000,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/agents/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ async function processAgentSwap(agent: AOYieldAgent, walletAddress: string): Pro
// Fetch fresh agent info
const agentInfo = await queryClient.fetchQuery({
queryKey: ["ao-yield-agent-info", agent.id],
queryFn: () => getAOYieldAgentInfo(agent.id),
queryFn: () => getAOYieldAgentInfo(agent.id, agent.version),
staleTime: 0, // Force fresh data
gcTime: 0,
retry: 3,
Expand Down
14 changes: 9 additions & 5 deletions src/utils/agents/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { pLimit } from "plimit-lit";
import { ExtensionStorage } from "~utils/storage";
import { queryClient } from "~utils/tanstack";
import { isWalletUnlocked } from "~wallets/auth";
import { getTagValue } from "~tokens/aoTokens/ao";

const limit = pLimit(10);

Expand Down Expand Up @@ -52,24 +53,27 @@ export async function checkAndSyncAgents(address: string): Promise<void> {
return aDate.getTime() - bDate.getTime();
});

const agentIds = sortedEdges.map((edge) => edge.node.id);
const foundAgents = sortedEdges.map((edge) => {
const agentVersion = getTagValue("Agent-Version", edge?.node?.tags) || "1.0.0";
return { agentId: edge.node.id, agentVersion };
});

// Set extension storage values immediately since we know agents exist
await ExtensionStorage.set(HAS_SHOWN_AGENTS_EXPLAINER_POPUP, true);
await ExtensionStorage.set(SHOW_CREATE_WANDER_AGENT_CTA, false);

// Read existing agents once and maintain ordered slots
const currentAgents = await getAOYieldAgents(address);
const agentSlots: (AOYieldAgent | null)[] = new Array(agentIds.length).fill(null);
const agentSlots: (AOYieldAgent | null)[] = new Array(foundAgents.length).fill(null);
let successCount = 0;

const agentInfoPromises = agentIds.map((agentId, index) =>
const agentInfoPromises = foundAgents.map(({ agentId, agentVersion }, index) =>
limit(async () => {
try {
log(LOG_GROUP.AGENTS, `Fetching agent info for ${agentId}`);
const agentInfo = await queryClient.fetchQuery({
queryKey: ["ao-yield-agent-info", agentId],
queryFn: () => getAOYieldAgentInfo(agentId),
queryFn: () => getAOYieldAgentInfo(agentId, agentVersion),
staleTime: 0, // Force fresh data
gcTime: 0,
retry: 1,
Expand Down Expand Up @@ -105,7 +109,7 @@ export async function checkAndSyncAgents(address: string): Promise<void> {
const orderedNewAgents = agentSlots.filter((agent): agent is AOYieldAgent => agent !== null);
await setAOYieldAgents(address, [...currentAgents, ...orderedNewAgents]);
successCount++;
log(LOG_GROUP.AGENTS, `Agent ${agentId} added at position ${index} (${successCount}/${agentIds.length})`);
log(LOG_GROUP.AGENTS, `Agent ${agentId} added at position ${index} (${successCount}/${foundAgents.length})`);

return agent;
} catch (error) {
Expand Down
185 changes: 139 additions & 46 deletions src/utils/agents/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,33 @@ const agentStorageMutex = new Mutex();
*/
export const arweave = Arweave.init(defaultGateway);

/**
* Checks if version a is greater than or equal to version b
* @param a - The first version to compare
* @param b - The second version to compare
* @returns True if version a is greater than or equal to version b, false otherwise
*/
function isVersionGte(a: string, b: string): boolean {
try {
const pa = a.split(".").map(Number);
const pb = b.split(".").map(Number);
const len = Math.max(pa.length, pb.length);

for (let i = 0; i < len; i++) {
const na = pa[i] ?? 0;
const nb = pb[i] ?? 0;

if (na > nb) return true;
if (na < nb) return false;
}

return true; // Equal versions
} catch (error) {
console.error("Error comparing versions: ", a, b, error);
return false;
}
}

/**
* Parses a gateway URL and returns an object containing the host, port, and protocol.
*
Expand Down Expand Up @@ -232,55 +259,121 @@ export async function getAOYieldActiveAgent() {
return agents[agents.length - 1];
}

export async function getAOYieldAgentInfo(agentId: string) {
const aoInstance = connect(defaultConfig);
export async function getAOYieldAgentInfo(agentId: string, currentAgentVersion?: string) {
try {
if (currentAgentVersion && isVersionGte(currentAgentVersion, "1.0.2")) {
throw new Error("Fetch agent info from the HB node");
}

const aoInstance = connect(defaultConfig);

const dryrunRes = await aoInstance.dryrun({
Id,
Owner,
process: agentId,
tags: [{ name: "Action", value: "Info" }],
});
const dryrunRes = await aoInstance.dryrun({
Id,
Owner,
process: agentId,
tags: [{ name: "Action", value: "Info" }],
});

const message = dryrunRes.Messages?.[0];
const tags = message?.Tags;

const dex = getTagValue("Dex", tags);
const status = getTagValue("Status", tags);
const tokenOut = getTagValue("Token-Out", tags);
const conversionPercentage = getTagValue("Conversion-Percentage", tags);
const startDate = getTagValue("Start-Date", tags);
const endDate = getTagValue("End-Date", tags);
const runIndefinitely = getTagValue("Run-Indefinitely", tags);
const slippage = getTagValue("Slippage", tags);
const totalAOSold = getTagValue("Total-AO-Sold", tags);
const totalBought = getTagValue("Total-Bought", tags);
const totalTransactions = getTagValue("Total-Transactions", tags);
const totalWanderFee = getTagValue("Total-Wander-Fee", tags);
const swapInProgress = getTagValue("Swap-In-Progress", tags);
const processedUpToDate = getTagValue("Processed-Up-To-Date", tags);
const swappedUpToDate = getTagValue("Swapped-Up-To-Date", tags);
const agentVersion = getTagValue("Agent-Version", tags);
const message = dryrunRes.Messages?.[0];
const tags = message?.Tags;

const dex = getTagValue("Dex", tags);
const status = getTagValue("Status", tags);
const tokenOut = getTagValue("Token-Out", tags);
const conversionPercentage = getTagValue("Conversion-Percentage", tags);
const startDate = getTagValue("Start-Date", tags);
const endDate = getTagValue("End-Date", tags);
const runIndefinitely = getTagValue("Run-Indefinitely", tags);
const slippage = getTagValue("Slippage", tags);
const totalAOSold = getTagValue("Total-AO-Sold", tags);
const totalBought = getTagValue("Total-Bought", tags);
const totalTransactions = getTagValue("Total-Transactions", tags);
const totalWanderFee = getTagValue("Total-Wander-Fee", tags);
const swapInProgress = getTagValue("Swap-In-Progress", tags);
const processedUpToDate = getTagValue("Processed-Up-To-Date", tags);
const swappedUpToDate = getTagValue("Swapped-Up-To-Date", tags);
const agentVersion = getTagValue("Agent-Version", tags);

return {
id: agentId,
status,
dex,
tokenOut,
conversionPercentage: Number(conversionPercentage),
startDate: Number(startDate),
endDate: Number(endDate),
runIndefinitely: runIndefinitely === "true",
slippage: Number(slippage),
totalAOSold,
totalBought: JSON.parse(totalBought),
totalTransactions: Number(totalTransactions),
totalWanderFee,
swapInProgress: swapInProgress === "true",
processedUpToDate: processedUpToDate !== "nil" ? Number(processedUpToDate) : undefined,
swappedUpToDate: swappedUpToDate !== "nil" ? Number(swappedUpToDate) : undefined,
agentVersion,
} as AOYieldAgentInfo;
return {
id: agentId,
status,
dex,
tokenOut,
conversionPercentage: Number(conversionPercentage),
startDate: Number(startDate),
endDate: Number(endDate),
runIndefinitely: runIndefinitely === "true",
slippage: Number(slippage),
totalAOSold,
totalBought: JSON.parse(totalBought),
totalTransactions: Number(totalTransactions),
totalWanderFee,
swapInProgress: swapInProgress === "true",
processedUpToDate: processedUpToDate && processedUpToDate !== "nil" ? Number(processedUpToDate) : undefined,
swappedUpToDate: swappedUpToDate && swappedUpToDate !== "nil" ? Number(swappedUpToDate) : undefined,
agentVersion,
} as AOYieldAgentInfo;
} catch (error) {
if (!isVersionGte(currentAgentVersion, "1.0.2")) {
throw new Error("Agent version is required & must be greater than 1.0.2");
}

console.log("Fetching agent info from the HB node with agent version: ", currentAgentVersion);
// TODO: Update this with the actual HB node
const response = await fetch(
`http://localhost:10000/${agentId}/~process@1.0/now/agent-info/~json@1.0/serialize?bundle`,
Comment thread
pawanpaudel93 marked this conversation as resolved.
Outdated
);
if (!response.ok) {
throw new Error("Failed to fetch agent info");
}
const data = await response.json();

const dex = data.dex;
const status = data.status;
const tokenOut = data.tokenOut ?? data.tokenout;
const conversionPercentage = data.conversionPercentage ?? data.conversionpercentage;
const startDate = data.startDate ?? data.startdate;
const endDate = data.endDate ?? data.enddate;
const runIndefinitely = data.runIndefinitely ?? data.runindefinitely;
const slippage = data.slippage ?? data.slippage;
const totalAOSold = data.totalAOSold ?? data.totalaosold;
const totalBought = data.totalBought ?? data.totalbought;
const totalTransactions = data.totalTransactions ?? data.totaltransactions;
const totalWanderFee = data.totalWanderFee ?? data.totalwanderfee;
const swapInProgress = data.swapInProgress ?? data.swapinprogress;
const processedUpToDate = data.processedUpToDate ?? data.processeduptodate;
const swappedUpToDate = data.swappedUpToDate ?? data.swappeduptodate;
const agentVersion = data.agentVersion ?? data.agentversion;

let totalBoughtObj = {};
if (typeof totalBought === "object" && totalBought !== null) {
totalBoughtObj = totalBought;
} else {
try {
totalBoughtObj = JSON.parse(totalBought);
} catch {}
}

return {
id: agentId,
status,
dex,
tokenOut,
conversionPercentage: Number(conversionPercentage),
startDate: Number(startDate),
endDate: Number(endDate),
runIndefinitely: runIndefinitely === "true",
slippage: Number(slippage),
totalAOSold,
totalBought: totalBoughtObj,
totalTransactions: Number(totalTransactions),
totalWanderFee,
swapInProgress: swapInProgress === "true",
processedUpToDate: processedUpToDate && processedUpToDate !== "nil" ? Number(processedUpToDate) : undefined,
swappedUpToDate: swappedUpToDate && swappedUpToDate !== "nil" ? Number(swappedUpToDate) : undefined,
agentVersion,
} as AOYieldAgentInfo;
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/utils/log/log.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const LOG_GROUPS_ENABLED: Record<LOG_GROUP, boolean> = {
[LOG_GROUP.WALLET_GENERATION]: false,
[LOG_GROUP.SESSION]: false,
[LOG_GROUP.STORAGE]: false,
[LOG_GROUP.AGENTS]: false,
[LOG_GROUP.TIERS]: false,
[LOG_GROUP.AGENTS]: process.env.NODE_ENV === "development",
[LOG_GROUP.TIERS]: process.env.NODE_ENV === "development",
[LOG_GROUP.TRANSAK]: false,
[LOG_GROUP.FAIR_LAUNCH]: false,
[LOG_GROUP.TRANSACTIONS]: false,
Expand Down
54 changes: 41 additions & 13 deletions src/utils/tier/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,55 @@ export async function getActiveTier(walletAddress: string, retry = false): Promi

data = responseData;
} catch {
const dryrunParams = {
Id,
Owner: walletAddress,
process: TIER_PROCESS_ID,
tags: [{ name: "Action", value: "Get-Wallet-Info" }],
};
// TODO: Update this with the actual HB node & process ID
const url = `http://localhost:10000/QC6z9NZYtVYn0Elx40iUmeIYvzKvuqk-OmfoleUxpSQ~process@1.0/now/wallets-tier-info/${walletAddress}/~json@1.0/serialize`;
Comment thread
pawanpaudel93 marked this conversation as resolved.
Outdated

const dryrunRes = retry
const response = retry
? await retryWithDelay(
() => dryrun(dryrunParams),
async () => {
const response = await fetch(url);
console.log(response.ok, response.status, typeof response.status);
if (!response.ok && response.status !== 404) {
throw new Error("Failed to fetch tier info from HB node");
}
return response;
},
3,
1000,
(attempt) => Math.min(1000 * 2 ** attempt, 30000),
)
: await dryrun(dryrunParams);

const message = dryrunRes.Messages?.[0];
const parsedData = JSON.parse(message?.Data || "{}");
: await fetch(url);

let parsedData: ActiveTierFromApi;

if (response.status === 404) {
// TODO: Update this with the actual HB node
const response = await fetch(
`http://localhost:10000/QC6z9NZYtVYn0Elx40iUmeIYvzKvuqk-OmfoleUxpSQ~process@1.0/now/tier-info/~json@1.0/serialize`,
Comment thread
pawanpaudel93 marked this conversation as resolved.
Outdated
);
const responseData = await response.json();
parsedData = {
balance: "0",
progress: 0,
rank: "",
snapshotTimestamp: responseData.snapshotTimestamp,
tier: 5,
totalHolders: responseData.totalHolders,
} as ActiveTierFromApi;
} else {
const responseData = await response.json();
parsedData = {
balance: responseData.balance,
progress: responseData.progress,
rank: responseData.rank,
snapshotTimestamp: responseData.snapshotTimestamp,
tier: responseData.tier,
totalHolders: responseData.totalHolders,
} as ActiveTierFromApi;
}

if (!isValidTierInfo(parsedData)) {
throw new Error("Invalid tier info data from WNDR tier process");
throw new Error("Invalid tier info data from HB node");
}

data = parsedData;
Expand Down