# Oculi
[](https://crates.io/crates/oculi)
[](https://docs.rs/oculi)
[](./LICENSE)
**Structured, Borsh-serialized Anchor events — a drop-in replacement for `msg!` that costs ~93% fewer compute units.**
`msg!` formats strings on-chain. Logging a swap (an amount and a pubkey) costs roughly **11,000 CU** per call. Oculi serializes a typed struct into an Anchor event instead — the same data for **~781 CU**:
```text
Standard msg! 11086 CU
Oculi log! 781 CU (92.96% cheaper)
```
The event lands as a `Program data:` log in the transaction metadata and decodes from any Borsh-aware client (TypeScript, Python, Go). No string formatting, no runtime schema reflection, no dynamic dispatch.
## Install
```toml
[dependencies]
oculi = "0.1"
anchor-lang = "0.30"
```
Tested against `anchor-lang` 0.30.x. MSRV: **Rust 1.75**.
## Quick start
Define a payload struct and log it. The first argument is the event name; the second is any `AnchorSerialize` value.
```rust
use anchor_lang::prelude::*;
use oculi::log_info;
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct SwapExecuted {
pub amount: u64,
pub user: Pubkey,
}
pub fn swap(ctx: Context<Swap>, amount: u64) -> Result<()> {
log_info!("swap_executed", SwapExecuted {
amount,
user: ctx.accounts.user.key(),
});
Ok(())
}
```
## Log levels
Each level has a convenience macro that wraps the core `log!` with the level pre-set:
| `log_debug!(name, payload)` | `Debug` | Verbose internal state snapshots |
| `log_info!(name, payload)` | `Info` | Milestones indexers should consume |
| `log_warn!(name, payload)` | `Warn` | Recoverable anomalies monitors should alert on |
| `log_error!(name, payload)` | `Error` | A sub-operation failed but the instruction recovered |
For the most verbose `Trace` level, call the core macro directly:
```rust
use oculi::{log, LogLevel};
log!(LogLevel::Trace, "fn_enter", FnEnter { instruction: 1 });
```
## Zero overhead in production: `devnet-only`
Enable the `devnet-only` feature in your mainnet build and every `log!` — including payload serialization — is compiled out to a no-op. The compiler eliminates the entire logging path, so you pay **nothing** for log statements you leave in the code.
```toml
# mainnet / production
oculi = { version = "0.1", features = ["devnet-only"] }
# devnet / test — logging active
oculi = "0.1"
```
| `devnet-only` | off | Compiles out all `emit!` calls. Enable for mainnet. |
| `no-entrypoint` | off | Forwards to `anchor-lang/no-entrypoint` for CPI dependents. |
## Serialization failures don't panic
If Borsh serialization of a payload ever fails, Oculi emits a sentinel event instead of panicking on-chain:
```text
event_name = "__oculi_serialization_error"
payload = []
```
The failure stays observable off-chain and no compute budget is wasted unwinding a panic.
## Decoding events off-chain
Each event is Borsh-encoded with an 8-byte Anchor discriminator, then:
```text
[discriminator: 8][level: 1][name_len: 4 LE][name: n][payload_len: 4 LE][payload: m]
```
Decoding in TypeScript:
```typescript
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
const logs = txDetails?.meta?.logMessages ?? [];
const dataLog = logs.find((l) => l.includes("Program data: "));
const buffer = Buffer.from(dataLog!.split("Program data: ")[1], "base64");
let offset = 8; // skip 8-byte Anchor discriminator
const level = buffer.readUInt8(offset); offset += 1;
const nameLen = buffer.readUInt32LE(offset); offset += 4;
const eventName = buffer.subarray(offset, offset + nameLen).toString("utf8");
offset += nameLen;
const payloadLen = buffer.readUInt32LE(offset); offset += 4;
const payload = buffer.subarray(offset, offset + payloadLen);
// Decode your struct — e.g. SwapExecuted: u64 (8 bytes LE) + Pubkey (32 bytes)
const amount = new BN(payload.subarray(0, 8), "le").toNumber();
const user = new PublicKey(payload.subarray(8, 40)).toBase58();
```
The `LogLevel` discriminants are stable: `Trace=0`, `Debug=1`, `Info=2`, `Warn=3`, `Error=4`. They are part of the public API and won't change without a major version bump.
## API
| `log!` | macro | Core macro; takes a `LogLevel`, an event name, and a payload |
| `log_debug!` / `log_info!` / `log_warn!` / `log_error!` | macros | Level-specific wrappers over `log!` |
| `LogLevel` | enum | Severity level; `#[repr(u8)]` with stable discriminants |
| `OculiEvent` | struct | The Anchor event type emitted by every `log!` call |
Full documentation: **[docs.rs/oculi](https://docs.rs/oculi)**.
## Contributing
Issues and PRs welcome at [github.com/Oculi-logs/Oculi](https://github.com/Oculi-logs/Oculi). Run `cargo fmt` and `cargo clippy -- -D warnings` before opening a PR — CI enforces both.
## License
MIT — see [LICENSE](./LICENSE).