Building on Sui: Why We Chose Sui for Kairo's Policy Engine
A deep dive into why we chose Sui blockchain for Kairo Guard's policy-gated transaction infrastructure, exploring the technical advantages that make Sui ideal for crypto security.
Building on Sui: Why We Chose Sui for Kairo's Policy Engine
When we set out to build Kairo Guard—a browser extension that brings policy-gated security to crypto transactions—we faced a critical architectural decision: which blockchain would serve as the foundation for our policy engine? After months of research, prototyping, and stress-testing, we committed to building on Sui blockchain. This wasn't a marketing decision or a bet on token prices. It was a deeply technical choice rooted in Sui's unique architecture that aligns perfectly with what we're trying to accomplish.
In this post, we'll share the technical reasoning behind this decision, compare our options, and explain how Sui's object-centric model enables capabilities that would be difficult or impossible on other chains.
Kairo's Mission: Policy-Gated Crypto Transactions
Before diving into blockchain comparisons, let's establish what Kairo actually does. At its core, Kairo Guard is a security layer that sits between users and their crypto transactions. Think of it as a programmable firewall for your wallet.
Every transaction must pass through a policy check before execution. These policies can enforce rules like:
- Spending limits: "No single transaction over $10,000 without 2FA"
- Allowlists: "Only interact with these verified contract addresses"
- Time locks: "No transactions between 2 AM and 6 AM"
- Velocity controls: "Maximum $50,000 outflow per 24-hour period"
- Multi-party approval: "Transfers over $100,000 require CFO signature"
The key insight is that these policies must be on-chain and immutable once deployed. If policies lived on a centralized server, they'd be vulnerable to the same attack vectors we're trying to protect against. A compromised server could simply disable all policies.
This requirement—on-chain, immutable, but highly configurable policies that execute with every transaction—drove our blockchain selection criteria.
Why Blockchain Choice Matters for Security Infrastructure
Not all blockchains are created equal, especially when you're building security infrastructure. Here's what we needed:
1. Sub-Second Finality
When a user clicks "approve" on a transaction, they can't wait 12 seconds (Ethereum) or even 400ms (Solana) for policy validation. We needed true sub-second finality with cryptographic certainty—not probabilistic confirmation.
2. Low, Predictable Gas Costs
Policy checks happen on every transaction. If gas costs are high or unpredictable, we'd be adding friction to every user interaction. This rules out Ethereum L1 immediately.
3. Native Support for Complex Objects
Policies aren't simple key-value pairs. They're rich objects with nested conditions, time-based rules, and relational constraints. We needed a data model that treats objects as first-class citizens.
4. Security-First Smart Contract Language
Our policy engine is security infrastructure. The smart contract language must prevent entire classes of vulnerabilities by design, not by developer discipline.
5. Parallel Execution
Different users' policies are independent. We needed a runtime that could validate thousands of policy checks simultaneously without artificial serialization.
With these requirements in mind, let's examine our options.
The Technical Comparison: Sui vs Ethereum vs Solana
We seriously evaluated three blockchains: Ethereum (and its L2s), Solana, and Sui. Here's our honest assessment:
Ethereum / L2s
Pros:
- Largest developer ecosystem
- Battle-tested security
- Abundant tooling
Cons for our use case:
- Account-based model requires global state access for every transaction
- EVM's sequential execution creates bottlenecks
- Solidity's footguns (reentrancy, integer overflow historically) require defensive coding
- Even on L2s, finality takes 1-2 blocks minimum
- Gas costs on L1 prohibitive; L2 gas still adds up for high-frequency operations
The fundamental issue is Ethereum's account model. Every token transfer modifies a global balance mapping, which means transactions must be sequenced even when they're logically independent. For policy validation at scale, this creates artificial bottlenecks.
// Ethereum: Every transfer touches global state
mapping(address => uint256) balances;
function transfer(address to, uint256 amount) public {
balances[msg.sender] -= amount; // Global state
balances[to] += amount; // Global state
}
Solana
Pros:
- High throughput (theoretically 65k TPS)
- Low transaction costs
- Parallel execution via Sealevel
Cons for our use case:
- Rust/Anchor learning curve is steep
- Account model still requires declaring all touched accounts upfront
- Probabilistic finality (optimistic confirmation isn't finality)
- Complexity of PDAs and account sizing
- Historical reliability concerns for critical infrastructure
Solana's parallel execution is impressive, but it requires developers to explicitly declare all accounts a transaction will touch. This works well for simple transfers but becomes unwieldy for complex policy objects with dynamic references.
// Solana: Must declare all accounts upfront
#[derive(Accounts)]
pub struct ValidatePolicy<'info> {
#[account(mut)]
pub policy: Account<'info, Policy>,
#[account(mut)]
pub user_config: Account<'info, UserConfig>,
#[account(mut)]
pub spending_tracker: Account<'info, SpendingTracker>,
// What if policy references vary by user?
}
Sui
Pros:
- Object-centric model matches our mental model perfectly
- True sub-second finality (~480ms for simple transactions)
- Move language eliminates entire vulnerability classes
- Parallel execution is automatic and implicit
- Predictable, low gas costs
Cons:
- Smaller ecosystem (but growing rapidly)
- Fewer battle-tested patterns to copy
- Learning Move is an investment
Let's dive deeper into why Sui won.
Sui's Unique Advantages for Kairo
1. The Object-Centric Model: Policies as First-Class Objects
This is the killer feature. In Sui, everything is an object with a unique ID, ownership, and type. Policies aren't entries in a mapping—they're actual objects that can be:
- Owned: A policy object can be owned by a user, a multi-sig, or another object
- Shared: A policy can be shared for multi-party access with controlled mutability
- Composed: Policies can contain other policies, enabling hierarchical rules
- Transferred: Policy ownership can be transferred or delegated
Here's a simplified example of how we model policies:
module kairo::policy {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
/// A Policy is a first-class object with its own identity
public struct Policy has key, store {
id: UID,
owner: address,
spending_limit: u64,
daily_limit: u64,
allowlisted_contracts: vector<address>,
time_restrictions: Option<TimeWindow>,
requires_2fa: bool,
cooldown_period: u64,
}
public struct TimeWindow has store, drop {
allowed_start_hour: u8, // 0-23
allowed_end_hour: u8, // 0-23
timezone_offset: i8, // UTC offset
}
/// Create a new policy - returns an owned object
public fun create_policy(
spending_limit: u64,
daily_limit: u64,
ctx: &mut TxContext
): Policy {
Policy {
id: object::new(ctx),
owner: tx_context::sender(ctx),
spending_limit,
daily_limit,
allowlisted_contracts: vector::empty(),
time_restrictions: option::none(),
requires_2fa: false,
cooldown_period: 0,
}
}
}
This maps directly to how users think about policies. "I have a policy" is a literal statement—the policy object exists in their wallet.
2. Parallel Transaction Execution (No Artificial Bottlenecks)
Sui's runtime automatically parallelizes transactions that touch different objects. When User A validates against Policy A and User B validates against Policy B, these execute simultaneously—no coordination required.
This is possible because Sui's transaction model explicitly declares object inputs:
Transaction {
inputs: [Policy_A, UserConfig_A],
...
}
The runtime sees that Policy_A and Policy_B are independent objects and schedules them on different cores. We measured 4,200+ policy validations per second in our benchmarks without any optimization on our end—just clean object boundaries.
Compare this to Ethereum, where every policy check might need to read from a shared registry contract, serializing all validations.
3. Sub-Second Finality: Security You Can Feel
Sui achieves finality in approximately 480ms for simple transactions. For Kairo, this means:
- User initiates transaction
- Policy check executes on-chain
- Result returns to browser extension
- User sees approval/denial
Total time: under 1 second. This is critical for user experience. Security that slows you down gets disabled.
Under the hood, Sui achieves this through its unique consensus mechanism. For "owned object" transactions (which most policy checks are), Sui can bypass consensus entirely and use a simpler Byzantine Consistent Broadcast protocol. The policy object is owned by the user, so there's no contention—just cryptographic verification.
4. Move Language: Security by Construction
Move was designed for financial infrastructure. It eliminates entire classes of vulnerabilities:
No Reentrancy: Resources in Move have linear types. You can't "call back" in a way that observes intermediate state because the type system prevents it.
// This pattern is impossible in Move - resources are linear
public fun bad_transfer(coin: Coin<SUI>) {
// Can't "use" coin twice - compiler prevents it
deposit(coin);
external_call(); // Even if this calls back...
deposit(coin); // This line won't compile - coin is moved
}
No Integer Overflow (by default): Move's integers are checked. Overflow panics unless you explicitly use wrapping operations.
No Null References: The type system distinguishes between values that exist and values that might not exist (Option<T>).
Explicit Ownership: Every value has exactly one owner. This prevents the "confused deputy" attacks common in Solidity.
For security infrastructure, these aren't nice-to-haves—they're requirements.
5. Gas Costs: Predictable and Low
Sui's gas model charges for computation and storage separately, with storage being refundable when objects are deleted. Our policy checks cost approximately 0.003-0.008 SUI depending on complexity—roughly $0.01-0.03 at current prices.
More importantly, costs are predictable. We can tell users exactly what policy management will cost without worrying about gas wars or network congestion spikes.
How Kairo's Policy Objects Work on Sui
Let's get concrete about the architecture. Here's a simplified flow of what happens when a user tries to execute a transaction:
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ User's Browser │
│ ┌─────────────┐ ┌─────────────────────────────────────┐ │
│ │ dApp UI │───▶│ Kairo Guard Extension │ │
│ └─────────────┘ │ ┌─────────────────────────────────┐│ │
│ │ │ Transaction Interceptor ││ │
│ │ └──────────────┬──────────────────┘│ │
│ └─────────────────┼───────────────────┘ │
└───────────────────────────────────────┼─────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Sui Network │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Kairo Policy Module │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Policy A │ │ Policy B │ │ Policy C │ │ │
│ │ │ (User 1) │ │ (User 2) │ │ (Team X) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Validation Logic │ │ │
│ │ │ • Check spending limits │ │ │
│ │ │ • Verify allowlist membership │ │ │
│ │ │ • Enforce time restrictions │ │ │
│ │ │ • Validate velocity constraints │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Policy Validation Flow
module kairo::validator {
use kairo::policy::Policy;
use sui::clock::Clock;
/// Result of policy validation
public struct ValidationResult has drop {
approved: bool,
reason: Option<String>,
requires_additional_auth: bool,
}
/// Validate a transaction against a policy
public fun validate_transaction(
policy: &Policy,
amount: u64,
recipient: address,
clock: &Clock,
): ValidationResult {
// Check 1: Spending limit
if (amount > policy.spending_limit) {
return ValidationResult {
approved: false,
reason: option::some(string::utf8(b"Exceeds spending limit")),
requires_additional_auth: false,
}
};
// Check 2: Allowlist (if configured)
if (!vector::is_empty(&policy.allowlisted_contracts)) {
if (!vector::contains(&policy.allowlisted_contracts, &recipient)) {
return ValidationResult {
approved: false,
reason: option::some(string::utf8(b"Recipient not in allowlist")),
requires_additional_auth: false,
}
}
};
// Check 3: Time restrictions
if (option::is_some(&policy.time_restrictions)) {
let time_window = option::borrow(&policy.time_restrictions);
let current_hour = get_current_hour(clock, time_window.timezone_offset);
if (!is_within_window(current_hour, time_window)) {
return ValidationResult {
approved: false,
reason: option::some(string::utf8(b"Outside allowed time window")),
requires_additional_auth: false,
}
}
};
// Check 4: 2FA requirement
if (policy.requires_2fa && amount > policy.spending_limit / 2) {
return ValidationResult {
approved: true,
reason: option::none(),
requires_additional_auth: true, // Trigger 2FA flow
}
};
// All checks passed
ValidationResult {
approved: true,
reason: option::none(),
requires_additional_auth: false,
}
}
}
Daily Spending Tracker (Shared Object Example)
For velocity controls, we need to track spending across transactions. This uses Sui's shared object model:
module kairo::spending_tracker {
use sui::object::{Self, UID};
use sui::table::{Self, Table};
use sui::clock::Clock;
/// Shared object tracking daily spending per user
public struct SpendingTracker has key {
id: UID,
// Maps user address -> (day_timestamp, amount_spent)
daily_totals: Table<address, DailyTotal>,
}
public struct DailyTotal has store, drop {
day_start: u64, // Timestamp of day start
amount: u64, // Total spent today
}
/// Record spending and check against daily limit
public fun record_and_validate(
tracker: &mut SpendingTracker,
user: address,
amount: u64,
daily_limit: u64,
clock: &Clock,
): bool {
let today = get_day_start(clock::timestamp_ms(clock));
if (table::contains(&tracker.daily_totals, user)) {
let total = table::borrow_mut(&mut tracker.daily_totals, user);
// Reset if new day
if (total.day_start < today) {
total.day_start = today;
total.amount = 0;
};
// Check limit
if (total.amount + amount > daily_limit) {
return false
};
// Record spending
total.amount = total.amount + amount;
} else {
// First transaction of the day
if (amount > daily_limit) {
return false
};
table::add(&mut tracker.daily_totals, user, DailyTotal {
day_start: today,
amount,
});
};
true
}
}
The dWallet Network Integration: 2PC-MPC for True Non-Custodial Security
Here's where Kairo's architecture gets really interesting. We don't just validate policies—we enforce them cryptographically using dWallet Network's 2PC-MPC protocol.
The Problem with Traditional Policy Enforcement
Most "policy engines" have a fatal flaw: they're advisory. The policy check says "no," but if an attacker controls the signing key, they can ignore the policy and sign anyway.
Traditional Approach:
User Key → [Advisory Policy Check] → Sign → Blockchain
↓
(Attacker bypasses)
dWallet's 2PC-MPC Solution
dWallet Network enables threshold signing where the signing key is split between multiple parties. For Kairo, the split is:
- User's share: Stored in the browser extension
- Policy enforcer's share: Held by dWallet Network nodes
Neither party can sign alone. A valid signature requires both shares, and dWallet nodes will only contribute their share if the policy check passes.
Kairo + dWallet Approach:
┌─────────────────────┐
│ Policy Validation │
│ (Sui Network) │
└──────────┬──────────┘
│ Proof
▼
User Share ──────────────▶ [2PC-MPC Protocol] ──────────────▶ Signature
dWallet Share ────────────▶ │
│
(Both required - cryptographically enforced)
How the Integration Works
- User initiates transaction in Kairo Guard
- Policy validation executes on Sui (as shown above)
- Validation proof is generated (Sui's BCS-serialized transaction effects)
- User's MPC session sends proof + their signature share to dWallet
- dWallet nodes verify the policy passed on-chain
- Threshold signature is produced only if verification succeeds
- Transaction broadcasts to the destination chain
This means policies aren't just rules—they're cryptographic constraints. Even if an attacker compromises the user's device, they can't bypass policy checks without also compromising a threshold of dWallet nodes.
module kairo::mpc_integration {
use kairo::validator::ValidationResult;
use dwallet::signature_request::{Self, SignatureRequest};
/// Request MPC signature after policy validation
public fun request_signature(
validation: &ValidationResult,
tx_hash: vector<u8>,
user_share_commitment: vector<u8>,
): Option<SignatureRequest> {
// Only create signature request if policy approved
if (!validation.approved) {
return option::none()
};
// If additional auth required, this must be called
// after 2FA verification
if (validation.requires_additional_auth) {
return option::none()
};
option::some(signature_request::create(
tx_hash,
user_share_commitment,
))
}
}
Real Performance Numbers
We've been running Kairo on Sui testnet for three months. Here are real numbers:
| Metric | Value | |--------|-------| | Average policy validation time | 483ms | | 99th percentile validation time | 892ms | | Policy validations per second (peak) | 4,247 | | Average gas cost per validation | 0.0047 SUI | | Policy creation gas cost | 0.0312 SUI | | Policy update gas cost | 0.0089 SUI | | Uptime (testnet) | 99.94% |
For context, the Sui testnet had a brief outage during our testing period that accounts for most of that 0.06% downtime. Mainnet has been even more stable.
Throughput Under Load
We stress-tested policy validations with concurrent users:
| Concurrent Users | Validations/sec | Avg Latency | |-----------------|-----------------|-------------| | 100 | 847 | 491ms | | 500 | 2,134 | 523ms | | 1,000 | 3,891 | 587ms | | 2,000 | 4,247 | 721ms |
Latency increases modestly under load, but throughput scales nearly linearly until we hit network-level bottlenecks. This is Sui's parallel execution at work—independent policy objects don't contend.
Developer Experience: Building on Sui
Switching to a new blockchain means learning new tools. Here's our honest assessment of the developer experience building on Sui:
The Good
Move is delightful once you grok it. The learning curve is real—probably 2-3 weeks to feel productive if you're coming from Solidity. But once it clicks, you write code with confidence. The type system catches so many bugs at compile time that we rarely hit runtime errors.
Sui's tooling is solid:
sui move buildhas clear error messages- The Move analyzer VSCode extension works well
sui clientCLI is intuitive- Explorer and indexer are feature-complete
Testing is first-class:
#[test]
fun test_spending_limit_enforcement() {
let mut ctx = tx_context::dummy();
let policy = policy::create_policy(
1000, // spending_limit
5000, // daily_limit
&mut ctx,
);
let clock = clock::create_for_testing(&mut ctx);
// Should pass: under limit
let result = validator::validate_transaction(
&policy,
500,
@0x123,
&clock,
);
assert!(result.approved, 0);
// Should fail: over limit
let result = validator::validate_transaction(
&policy,
1500,
@0x123,
&clock,
);
assert!(!result.approved, 1);
}
Documentation has improved dramatically. The Sui docs were sparse in early 2024 but are now comprehensive. The Move Book is excellent.
The Challenges
Ecosystem is younger. Fewer Stack Overflow answers, fewer example codebases to learn from. We often had to figure things out from first principles or read Sui's own codebase.
Some patterns aren't obvious. "How do I do X in Move?" often requires rethinking the problem rather than translating from Solidity. This is usually good (forces better design) but slows initial development.
Testnet can be flaky. We hit maybe 3-4 testnet issues over three months. Minor annoyance, not a blocker.
Our Recommendation
If you're building security infrastructure, financial primitives, or anything where correctness matters more than time-to-market, Sui is worth the investment. The object model and Move's safety guarantees pay dividends over the lifetime of your protocol.
If you're building a quick DeFi fork or need maximum ecosystem compatibility, Ethereum L2s might still make sense. Know your tradeoffs.
Future Roadmap: What We're Building Next on Sui
Kairo on Sui is just the beginning. Here's what's coming:
Q2 2025: Policy Marketplace
Shared policy templates that users can fork and customize. "Import Coinbase Custody's policy set with one click."
Q3 2025: Cross-Chain Policy Enforcement
Using dWallet's universal signatures, enforce Sui-defined policies on Ethereum, Solana, and Bitcoin transactions. Your policy object lives on Sui; it protects assets everywhere.
Q4 2025: Programmable Policy Agents
LLM-powered policy assistants that help users define policies in natural language, then generate the Move code. "Block any transaction to an address I haven't interacted with before" → Policy object.
2026: Institutional Policy Engine
Enterprise features: role-based policy inheritance, audit trails as Sui events, compliance reporting, integration with traditional risk systems.
Conclusion: The Right Tool for the Job
Building on Sui blockchain was the right choice for Kairo. Not because Sui is "better" in some abstract sense, but because its specific technical properties—object-centric model, parallel execution, Move's safety guarantees, sub-second finality—align precisely with what policy-gated transaction infrastructure requires.
If you're building something where security correctness matters, where you need rich on-chain objects rather than simple state mappings, and where user experience demands fast finality, Sui deserves serious consideration.
We're excited about what the Sui ecosystem is becoming. The technology is sound, the community is growing, and the applications being built are genuinely novel—not just Ethereum ports. Kairo is proud to be part of that ecosystem.
Building something on Sui? Have questions about our architecture? Reach out on Twitter or join our Discord.
Kairo Guard is currently in private beta. Join the waitlist for early access.
Ready to secure your crypto?
Kairo Guard brings 2PC-MPC security and policy-gated transactions to your existing wallet. No seed phrases, no single points of failure.
Get Early Access