For Developers
Contract addresses, Move module functions, adapter architecture, and how to run your own agent instance.
Contract addresses (Sui testnet)
| Name | Address |
|---|---|
| Package (latest, v3) | 0x0e97dba4a805060830912e1bbf9836316e51e27553b0f6326d8a48ce99cbb459 |
| Original package (v1, for event queries) | 0xf1e50124f42ded70f3035f63ba490c8c49d4b40713ba1ba081cf360f683dbd8a |
| Vault object | see .env.local |
| USDC coin type | {original_package}::usdc::USDC |
Event types on Sui are always stamped with the original package ID, not the latest upgrade. Use the v1 address when querying events like AllocationUpdated, Deposited, and Withdrawn. Use the v3 address for all contract calls.
Move module: aquifer_vault
deposit
public fun deposit<T>(
vault: &mut AquiferVault<T>,
payment: Coin<T>,
ctx: &mut TxContext
)Deposits a coin into the vault and mints shares to the caller. Share amount is proportional to the current share price.
withdraw
public fun withdraw<T>(
vault: &mut AquiferVault<T>,
shares_amount: u64,
ctx: &mut TxContext
): Coin<T>Burns shares_amount shares and returns the equivalent USDC to the caller. Requires sufficient vault reserves. Pair with pull_from_adapter calls in a PTB if reserves are low.
pull_from_adapter
public fun pull_from_adapter<T>(
vault: &mut AquiferVault<T>,
a: &mut Adapter<T>,
amount: u64,
)Moves up to amount USDC from an adapter back into vault reserves. Public, no auth required. Safe because it only moves funds within the vault. Use this in a PTB before withdraw when reserves are insufficient.
deposit_to_adapter
public fun deposit_to_adapter<T>(
vault: &mut AquiferVault<T>,
a: &mut Adapter<T>,
amount: u64,
ctx: &TxContext,
)Deploys USDC from vault reserves into a protocol adapter. Agent or owner only.
rebalance
public fun rebalance<T>(
vault: &mut AquiferVault<T>,
walrus_blob_id: vector<u8>,
reasoning_excerpt: vector<u8>,
clock: &Clock,
ctx: &TxContext,
)Emits an AllocationUpdated event linking to a Walrus blob. Agent or owner only. Call this after completing adapter moves to create an on-chain audit record.
Events
AllocationUpdated
Emitted after each rebalance cycle.
public struct AllocationUpdated has copy, drop {
walrus_blob_id: vector<u8>, // UTF-8 Walrus blob ID
reasoning_excerpt: vector<u8>, // First ~200 chars of reasoning
timestamp_ms: u64,
}Deposited
public struct Deposited has copy, drop {
user: address,
amount: u64,
shares_minted: u64,
}Withdrawn
public struct Withdrawn has copy, drop {
user: address,
shares_burned: u64,
amount_returned: u64,
}Running your own agent
The agent lives in src/agent.js in the GitHub repo root.
Requirements: Node.js 20+, a Sui wallet with testnet SUI, a Google Gemini API key, a Walrus publisher endpoint.
Setup:
git clone https://github.com/aegonmyy/aquifer
cd aquifer
cp .env.example .env
# fill in SUI_PRIVATE_KEY, GEMINI_API_KEY, PACKAGE_ID, VAULT_ID, ADAPTER_IDS
npm install
npm run agentThe agent polls every few minutes, calls Gemini, parses the JSON allocation response, builds a PTB to rebalance adapters, submits it, then stores the blob on Walrus and calls rebalance on the vault contract.
You can register a different agent address with the vault owner using set_agent on the AdminCap.
Querying events from the frontend
import { SuiJsonRpcClient, getJsonRpcFullnodeUrl } from "@mysten/sui/jsonRpc";
const client = new SuiJsonRpcClient({
url: getJsonRpcFullnodeUrl("testnet"),
network: "testnet",
});
// Use original package ID for event queries
const ORIGINAL_PACKAGE_ID = "0xf1e5...";
const events = await client.queryEvents({
query: {
MoveEventType: `${ORIGINAL_PACKAGE_ID}::aquifer_vault::AllocationUpdated`
},
limit: 20,
order: "descending",
});GitHub
All source code is available at github.com/aegonmyy/aquifer. The repo contains the Move contracts, the Next.js frontend, and the agent.