# Policy-Proved Attestations (PPA) Source: https://docs.skatechain.org/main/infrastructure-components/eigencloud-avs/policy-proved-attestations Skate's implementation for reliable cross-chain attestations on altVM ecosystems Policy-Proved Attestations (PPA) is Skate's upcoming implementation that enables EigenCloud's cross-chain attestations to work reliably on altVM ecosystems like Solana and Sui. Rather than forcing every blockchain to implement complex signature verification logic independently, PPA moves that verification into Trusted Execution Environments operated by the Execution Network. This allows Solana, Sui, and other high-throughput chains to benefit from EigenCloud's restaked economic security without architectural friction. ## What Problem Does PPA Solve? While ELIP-008 successfully extends operator set replication and signature verification across EVM chains, it does not address critical challenges when scaling to high-throughput altVMs like Solana or Sui, or when building applications with tight latency requirements. ### Latency and Execution Throughput Traditional AVS designs require fully aggregated quorum signatures before executing. This creates a bottleneck. On high-throughput blockchains like Solana or Sui, where thousands of transactions must clear within sub-second latency windows, waiting for full quorum can breach application SLAs. Some AVSs like EOracle only attest to data, allowing consuming applications to act on un-finalized information. But for task-based AVSs like Skate, the constraint is different. Execution directly touches capital. Traditional optimistic execution (execute now, punish later) is too risky. A malicious executor could drain liquidity and cause catastrophic losses before any slashing mechanism activates. ### Cross-VM Verification Complexity ELIP-008 works smoothly on EVM chains where the CertificateVerifier contract can be deployed natively. But extending this to different virtual machines is challenging. Even if operator sets, weights, and BLS keys are replicated everywhere, implementing signature verification independently per VM becomes intractable. Many chains lack the necessary cryptographic precompiles, support different curves, or require custom verification code. Maintaining this complexity across dozens of ecosystems is neither sustainable nor secure. ### Security Curve Choice ELIP-008 currently supports BN254 for efficiency on EVM. But for long-term security, Ethereum consensus uses BLS12-381. On most non-EVM chains, efficient BLS12-381 verification is either prohibitively expensive or unsupported, making it unrealistic to enforce stake-weighted verification on-chain across the board. ## How Policy-Proved Attestations Work Policy-Proved Attestation (PPA) introduces a novel approach: instead of replicating full verification logic across every chain, executors enforce execution policies inside trusted execution environments (TEEs). These policies encode the conditions under which a task may execute, such as quorum requirements or stake thresholds. Skate uses Cubist's programmable policy engine to define and enforce these policies. ### The Architecture **Operator Identity and Weights Live on Ethereum**: Operators register BLS public keys and stake weights in EigenCloud's Ethereum contracts. This remains the single source of truth. **Policy Verification in TEE**: Executors run inside Trusted Execution Environments using Cubist's programmable policy engine. Before execution, the TEE evaluates tasks against custom, programmable policies. A policy might specify: "Execute only if at least 60% of operator stake has attested to this task" or "For latency-sensitive swaps, require attestations from 3 fast operators." Policies are defined as WASM modules and deployed to the policy engine. **Chain-Native Approval**: Rather than sending aggregated signatures to every destination chain, the TEE produces a lightweight, chain-native approval that each destination chain can verify. This approval is small and efficient. **Slashing Remains on Ethereum**: Even if executors act on partial quorums, accountability is anchored in Ethereum. If an executor used false or invalid attestations, slashing can still be enforced on Ethereum against the operators who signed incorrectly. ### Execution Flow 1. **Operators Sign Off-Chain**: EigenCloud operators sign tasks using their BLS keys registered on Ethereum. Multiple operators can sign the same task. 2. **Partial Attestation Set**: Instead of waiting for all operators to sign, a smaller subset provides attestations. This subset is tuned to the application's security needs and latency constraints. 3. **TEE Policy Check**: An executor running in a TEE validates that the partial attestation set meets the programmable policy. For example: "At least X% of the total restaked stake has signed this task." 4. **Policy-Proved Execution**: If the policy check passes, the executor acts on the task. It executes the transaction and settles results on the destination chain. 5. **Post-Facto Accountability**: Once full attestations arrive, if any operator signed incorrectly or the partial set was fraudulent, the incorrect behavior is logged and slashing can be triggered on Ethereum. This model gives the best of both worlds: low-latency execution at the edge with high-stakes security guarantees anchored at the core. ## Why This Matters Policy-Proved Attestation transforms executors from simple message relays into policy-enforcing agents. They act only when programmable policies anchored in EigenCloud's Ethereum contracts say it's safe to do so. This means applications can scale across chains and VMs without each chain reimplementing signature verification logic. Skate can operate on Solana's sub-second settlement without waiting for heavyweight aggregated signatures. Solana traders still benefit from EigenCloud's \$4.5B+ in restaked economic security, but they experience fast, responsive execution. ### Practical Benefits For **AMMs and Swaps**: Execute trades atomically and instantly across all chains. A swap on Solana settles at the same second as a swap on Ethereum. Users enjoy low-latency execution with economic security backing every trade. For **Cross-Chain Protocols**: Build complex multi-step transactions that touch multiple blockchains. The policy engine ensures all steps either succeed together or fail safely. For **Builders Across VMs**: Deploy once on the hub chain. The stateless pattern handles distribution across Solana, Sui, Ethereum, and other VMs. Skate's PPA mechanism ensures every chain's execution is verified and accountable without forcing each chain to implement complex cryptography. ## For Developers: Defining Custom Policies Applications can define custom policies tailored to their security and latency needs. Policies are programmable and can encode rules like: * Quorum thresholds: "Require signatures from at least 10 operators" or "Require 50% of restaked stake" * Latency requirements: "For trades under 100k USD, use fast quorum. For trades over 1M USD, require 90% quorum." * Operator requirements: "Require at least 3 validators from diverse geographic regions" Policies are encoded as WASM modules that run inside Cubist's programmable policy engine. This allows fine-grained control over the security-latency tradeoff. ### BLS Signature Verification Demo The following Rust example demonstrates how operator signatures are verified in a policy. In this demo, two operators (A and B) sign the same message using BLS keys. The policy engine verifies the aggregated signature: ```rust theme={null} use blsful::{ Bls12381G1Impl, MultiPublicKey, MultiSignature, ProofOfPossession, PublicKey, SecretKey, Signature, SignatureSchemes, }; fn main() -> Result<(), String> { // Create operator keys let sk_a: SecretKey = SecretKey::from_hash(b"operator-A-seed"); let sk_b: SecretKey = SecretKey::from_hash(b"operator-B-seed"); let pk_a = sk_a.public_key(); let pk_b = sk_b.public_key(); // Message to sign let msg = b"gSkate"; // Operators sign the message let sig_a = sk_a.sign(msg); let sig_b = sk_b.sign(msg); // Aggregate signatures let agg_sig = MultiSignature::aggregate(vec![sig_a, sig_b])?; // Create multi-public key for expected operator set let mpk_ab: MultiPublicKey = MultiPublicKey::from_public_keys(&[pk_a.clone(), pk_b.clone()]); // Verify aggregated signature match agg_sig.verify(mpk_ab, msg) { Ok(_) => println!("Policy check PASSED: 2-of-2 operators signed the message"), Err(e) => println!("Policy check FAILED: {}", e), } Ok(()) } ``` This demonstrates the core verification logic: if the aggregated signature matches the expected operator set and message, the policy passes. ### Cubist Policy SDK Example The following example shows how to build a policy using the Cubist SDK. This policy enforces a 2-of-2 operator requirement, approving execution only if both designated operators have attested: ```rust theme={null} use cubist_policy_sdk::{error::Result, policy, AccessDecision, AccessRequest}; use blsful::{Bls12381G1Impl, MultiPublicKey, MultiSignature, PublicKey, SecretKey}; use serde::Deserialize; const OPERATOR_A_SEED: &[u8] = b"operator-A-seed"; const OPERATOR_B_SEED: &[u8] = b"operator-B-seed"; #[derive(Deserialize)] struct InvokeInput { #[serde(default)] msg_utf8: Option, #[serde(default)] msg_hex: Option, agg_sig_hex: String, // Aggregated BLS signature } #[policy] async fn main(req: AccessRequest) -> Result { // Parse the request body let input = match parse_body(&req) { Ok(v) => v, Err(e) => return Ok(AccessDecision::Deny(format!("invalid input: {e}"))), }; // Build expected operator set (A, B) let (pk_a, pk_b) = operator_public_keys(); let mpk_ab: MultiPublicKey = MultiPublicKey::from_public_keys(&[pk_a.clone(), pk_b.clone()]); // Parse message let msg = match pick_message_bytes(&input) { Ok(m) => m, Err(e) => return Ok(AccessDecision::Deny(format!("message parse error: {e}"))), }; // Parse aggregated signature let agg_sig_bytes = match parse_hex(&input.agg_sig_hex) { Ok(b) => b, Err(e) => return Ok(AccessDecision::Deny(format!("agg_sig_hex parse error: {e}"))), }; let msig = match multi_sig_from_bytes(&agg_sig_bytes) { Ok(s) => s, Err(e) => return Ok(AccessDecision::Deny(format!("invalid aggregated signature: {e}"))), }; // Verify 2-of-2 policy (both A and B must have signed) match msig.verify(mpk_ab, &msg) { Ok(_) => Ok(AccessDecision::Allow), Err(err) => Ok(AccessDecision::Deny(format!( "policy check failed: aggregated signature does not match operator set (A,B): {err:?}" ))), } } fn operator_public_keys() -> (PublicKey, PublicKey) { let sk_a: SecretKey = SecretKey::from_hash(OPERATOR_A_SEED); let sk_b: SecretKey = SecretKey::from_hash(OPERATOR_B_SEED); (sk_a.public_key(), sk_b.public_key()) } fn parse_body(req: &AccessRequest) -> std::result::Result { let raw = match &req.request { Some(s) => s.as_str(), None => return Err("missing request body".into()), }; serde_json::from_str::(raw).map_err(|e| format!("serde error: {e}")) } fn pick_message_bytes(input: &InvokeInput) -> std::result::Result, String> { if let Some(s) = &input.msg_utf8 { return Ok(s.as_bytes().to_vec()); } if let Some(h) = &input.msg_hex { return parse_hex(h); } Err("provide either msg_utf8 or msg_hex".into()) } fn parse_hex(s: &str) -> std::result::Result, String> { let s = s.strip_prefix("0x").unwrap_or(s); hex::decode(s).map_err(|e| format!("hex decode error: {e}")) } ``` To invoke this policy, compile it to WASM and deploy to a TEE. The policy will accept a message and aggregated signature, verify that both operators A and B signed the message, and return Allow or Deny based on the result. This is the mechanism by which Executors enforce security policies before executing tasks on destination chains. ## Implementation: Cubist Policy Engine The Cubist programmable policy engine provides the foundation for PPA. Policies are written in Rust, compiled to WASM, and deployed to TEEs operated by the Execution Network. The Cubist policy SDK provides primitives for: * Verifying aggregated BLS signatures * Checking operator stake thresholds * Enforcing quorum requirements * Validating message content against policies This toolkit is available to any EigenCloud AVS, making PPA a public good for the entire EigenCloud ecosystem. ## Security Model Policy-Proved Attestations maintain all security guarantees of the EigenCloud AVS model: **Operator Accountability**: Operators face slashing if they sign fraudulent tasks. Slashing is enforced on Ethereum and is economically meaningful (proportional to their stake). **Policy Transparency**: Policies are deterministic and verifiable. Applications and external observers can inspect policies to understand exactly what conditions must be met for execution. **Chain-Specific Verification**: Each destination chain can independently verify that a task's policy approval is valid. No trust in TEEs is required for settlement finality. **Fallback Mechanisms**: If a TEE is compromised, the policy can be disputed on Ethereum. Slashing mechanisms ensure that bad actors face consequences. ## Conclusion Policy-Proved Attestations extend EigenCloud's economic security model beyond Ethereum's boundaries. By encoding execution policies directly into Executors, Skate enables applications to operate reliably and responsibly across Solana, Sui, Ethereum, and other ecosystems. This shifts EigenCloud from being just Ethereum's security extension into becoming a universal trust substrate for omnichain applications. # Skate EigenCloud AVS Source: https://docs.skatechain.org/main/infrastructure-components/eigencloud-avs/skate-avs Overview of Skate EigenCloud AVS and Othentic's stack ## What is EigenCloud, Othentic? The **Skate EigenCloud AVS** secures all its multi-chain operations, powered by **EigenCloud** (contracts and infra) and **Othentic** (tooling and configs). * **EigenCloud:** The infrastructure layer (formerly EigenLayer) that provides the contracts and an unified operator network for restaked security. More at [https://eigencloud.xyz/](https://www.eigencloud.xyz/) * **Othentic:** The AVS framework that provides the foundational tooling (CLI, governance, and attestation contracts) to manage the AVS. More At [https://othentic.xyz/](https://www.othentic.xyz/) ## EigenCloud Security Model (Restaking, Slashing) The security is rooted in the **EigenCloud restaking model** based on Economic Security: * **Restaking:** Operators secure their role by restaking ETH/LSTs, providing **capital collateral** against misbehavior. * **Slashing:** Malicious activity results in a loss of this collateral. It is enforced for any critical security breach amongst AVS's operators, e.g. attesting to or submitting an invalid proof (which include tasks that was never legitimately executed/submitted). Evidently, the Economic Security Guarantees provides: * **Pooled Security:** The total value of ETH restaked ensures a high **Cost of Corruption**, making a hostile takeover uneconomical. * **Slashing-Based Guarantee:** The security commitment is to maintain sufficient restaked capital to penalize any fraudulent proof submission. This target is calibrated to **secure the aggregate liquidity of the Stateless AMM against invalid cross-chain state updates and fraudulent settlement claims**. ## Skate AVS Network Components The Othentic Stack utilizes libp2p for inter-node communication to ensure a robust, efficient, and decentralized attestation process. Four types of nodes are involved: 1. **Task Performer**
Collect tasks from the Message Box, sign them, and then publish them to aggregator via a JSON-RPC server. For data-intensive applications, the Task Performer acts as an authority to source/pre-validate data for the AVS network, such as an application that requires Oracle 2. **Task Aggregator**
The Aggregator monitors and aggregates attestations within the network. It receive performer update from performer and publish the task to a **p2p channel**, effectively requesting attester to participate in the attestation process. 3. **Attesters**
Subscribe to the **p2p channel** and sign using BLS signatures to participate in the attestation process. These nodes must ensure the validity of the underlying task data (executor calldata) and are therefore subject to rewards or slashing. 4. **Bootstrappers**
These robust nodes keep the p2p network alive by maintaining and updating the Distributed Hash Table for peer discovery. Performer, Aggregator, and Attester must be an AVS operator. A sample attestation flow is depicted in the diagram below: ```mermaid theme={null} flowchart MB[Message Box] performer[Task Performer] p2p[P2P Pubsub Channel] jsonRPC[Json-RPC server] blsAggregator[BLS Aggregation Module] MB -- "collect Tasks" --> performer subgraph aggregator[Task Aggregator] jsonRPC blsAggregator end performer -- "dispatch" --> jsonRPC blsAggregator -- "1) publish" --> p2p attester1[Attester 1] attester2[Attester 2] attester3[Attester 3] attester1 -- "2) subscribe" --> p2p p2p -. "task" .-> attester1 attester2 -- "2) subscribe" --> p2p p2p -. "task" .-> attester2 attester3 -- "2) subscribe" --> p2p p2p -. "task" .-> attester3 attester1 -- "3) vote" --> blsAggregator attester2 -- "3) vote" --> blsAggregator attester3 -- "3) vote" --> blsAggregator quorumReached{"Is Quorum\n Reached?"} blsAggregator -- "4) check" --> quorumReached quorumReached -- "yes" --> AC[Attestation Center] AC -- "OUT" --> data(Verified task data) ``` ## AVS Smart Contracts As mentioned, Skate leverages [Othentic's stack](https://docs.othentic.xyz/main/welcome/architecture#smart-contracts), which comes with the following core contracts: 1. **AVS Governance**
A set of governance contracts oversees the registration, slashing, and reward distribution for all AVS operators. These contracts operate on Ethereum L1, where all EigenLayer restakers reside, as one must be an EigenLayer staker to participate as an AVS operator. 2. **Attestation Center**
These contracts handle attestation processing, including signature aggregation for each quorum, which involves verifying a batch of tasks. They also manage the business logic for distributing rewards for operations and post-processing. Due to the high cost of these processes, the contracts are deployed on L2 and update L1 through the Network Management layer. 3. **Network Management Layer**
A cross-chain messaging layer ensures synchronization between the AVS Governance contracts on L1 and the Attestation Center on L2. # Onboard as a Skate Executor Source: https://docs.skatechain.org/main/infrastructure-components/execution-network/onboard-as-executor ## Incentive Executors receive a share of Skate protocol fees for a filled actions ## Onboarding Currently the executors network is white-listed only. Do reach out to us via social for requests [https://x.com/skate\_chain](https://x.com/skate_chain) [discord.com/invite/skatechain](https://discord.com/invite/skatechain) # Overview Source: https://docs.skatechain.org/main/infrastructure-components/execution-network/overview High level overview of Skate Execution Network ## Components Skate Execution Network consists of **a set of executors**, **a transaction relayer**, and **Executor Registries** on kernel and periphery chains: 1. **Executors** These specialized entities, either from Skate or third-party providers, handle user intents across blockchains. They must manage their own cross-chain inventories and execution plans. In exchange, they receive fees from users who need their transactions settled through Skate. 2. **Relayer** The relayer oversees the confirmation results from [Skate AVS](/main/infrastructure-components/eigencloud-avs) and attests to them, effectively preparing the required settlement data for executors. 3. **Executor Registry** The Executor Registry manages the addition and removal of executors. ## Execution Flow Watch intents on ActionBox from user across periphery. Either via API or self-indexing. Await approved intents by Skate AVS. Then reserve interested intents to fill on Kernel Message Box Intents flow through Kernel Apps are then processed into tasks. Executors need to settle the registered intents based on these tasks information Once approved tasks is acknowledged. Executors schedule settlement on corresponding periphery chains, effectively end a Skate Intent flow The flow diagram below depicts an intent processed by Execution Network ```mermaid theme={null} flowchart TB periphery["Periphery Apps (e.g. Skate AMM)"] AB["Action Box (Periphery)"] executor[Skate Executor] MB["Message Box (Kernel)"] relayer[Relayer] signed_data[Approved Tasks] executor -- "1) Watch intents" --> AB AB -..-> executor executor -- "2) input Intent to reserve execution" --> MB MB -. tasks .-> relayer relayer -- "watch " --> MB relayer -- "validate & sign" --> signed_data executor -- "3) retrieve approved tasks" --> signed_data signed_data -..-> executor executor -- "4) settle with attested data" --> periphery ``` # About Skate Hub Chain Source: https://docs.skatechain.org/main/infrastructure-components/hub-chain/about Skate Hub Chain is the state layer for apps that live across many chains. It’s an OP Stack optimistic rollup that settles to Ethereum and uses Avail for data availability. import { Card, CardGroup } from 'mintlify'; Skate Hub Chain is the **state layer** for apps that live across many chains. It’s an **OP Stack optimistic rollup** that **settles to Ethereum** and **uses Avail for data availability**. Here’s how it works: * **Logic + state live on Skate.** * **Assets stay on periphery chains.** * **Executors** carry messages between them. So an app can run one global state (pools, orderbooks, scores, whatever) while users keep funds on their home chain. AMMs are one example, but the same pattern fits orderbooks, auctions, games, allowlists—any app that wants shared state and local settlement. ## Role in Unified State Management The hub is the **single source of truth**. Periphery chains are **I/O + asset custody**. The flow is as follows: 1. A user calls a **periphery** contract/program. 2. An executor turns that into an **intent** and submits it to the app’s **kernel** on Skate. 3. The kernel updates **canonical state** and emits a **task**. 4. The executor delivers the task back to the **destination periphery** to settle funds or issue a refund. The result is one shared state across all chains, with funds moving only on the chains that actually hold them. ## Deployment Details **Network:** Skate Mainnet
**Chain ID:** `5050`
**Rollup Stack:** OP Stack
**Settlement Layer:** Ethereum (`1`)
**RPC:** `https://rpc.skatechain.org`
**WS:** `wss://rpc.skatechain.org/ws`
**Explorer:** `https://scan.skatechain.org`
`op-node v1.9.3`
`op-geth v1.101408.0`
`op-contracts v1.6.0`

**Batcher:**
`0xE8a61a47CA8373998726F644eE83cfAed4541326`

**Proposer:**
`0xF5cb182AF28D42092739cf04276636DCaA9CdAd2`
**Avail** (primary): sender
`5GCc8penY3wGsmaq8ZgeTW7TgEfN76tZGvAnUZ2ZqmMbPWqj`, server `v1.1.0`

**EigenDA** (integrated): RPC `disperser.eigenda.xyz:443`, signer
`0x4fDbD273b8D2C1c429a7E3078063c49528aA8264`

**Alt DA (plasma-style) proxy:**
`https://skate-mainnet-altda-daproxy.alt.technology`
Public **genesis** and **rollup** JSONs are available for operators.
# Core Contracts Overview Source: https://docs.skatechain.org/main/infrastructure-components/hub-chain/core-contracts Core contracts deployed on Skate chain import { Frame, CodeGroup, Accordion } from 'mintlify'; ## Kernel Core Contracts The kernel components are responsible for managing the **unified state** and **executing essential logic**. The components on the hub are: * **Kernel Manager (control plane):** The entrypoint for intents. It checks permissions, routes calls and emits tasks (including reverts). * **Kernel Logic/State (data plane):** This contains your app’s storage and core math. * **Shared infra:** This includes a mailbox/registry for executors and endpoints. This structure is app-agnostic and can be used for AMMs, orderbooks, RFQ engines, auctions, games, allowlists, and more. An overview of the interaction with Kernel is shown in the diagram below: ### Bytes-first messaging All cross-boundary payloads are **`bytes`**. Each app defines its own encoding and math. ```solidity Intent (periphery → hub) theme={null} struct Intent { bytes32 actionId; // unique within origin chain (see below) bytes32 user; // cross-VM user id uint64 originChainId; // CHAIN enum uint8 originVmType; // e.g., 1=EVM, 2=TON, 3=SVM, 4=Move, ... address appAddress; // kernel manager on hub bytes appCalldata; // app-defined uint64 deadline; // optional } ``` ```solidity Task (hub → periphery) theme={null} struct Task { bytes32 actionId; bytes32 recipient; // cross-VM recipient uint64 destChainId; // explicit destination chain id uint8 destVmType; // destination VM address appAddress; // kernel manager reference bytes taskCalldata; // app-defined settlement/refund payload bool isRevert; // true = refund/revert on source } ``` ### Cross-VM addresses as `bytes32` * **Ethereum/EVM:** 20 bytes left-padded to 32. * **Solana (SVM):** base58 pubkey decoded to 32 bytes. * **Sui/Aptos (Move):** already 32 bytes; store as-is. All comparisons on the kernel use `bytes32`. ### Settlement, failure, refunds * **Success:** task targets a **specific `destChainId` and `destVmType`**; periphery gateway verifies and settles from staged funds. * **Failure (correct):** kernel emits a **revert task** back to the **source** periphery. That periphery runs the app’s refund logic for **that action only**. * **Idempotent:** both settlement and revert must be safe to replay and must reject duplicates by `actionId`. ### Action identity and chain ids * **Action ID:** unique **per origin chain**, computed as a hash of `(actionCount || chainId)`. * **Chain ids (wrappers used for SVM/Move where needed):** ```solidity CHAIN enum theme={null} enum CHAIN { SKATE = 5050, SOLANA = 901, ECLIPSE = 902, SOON = 903, SUI = 1001, APTOS = 1002, MOVEMENT = 1003, BASE = 8453, ARBITRUM = 42161, BSC = 56, MANTLE = 5000, OPTIMISM = 10, ETHEREUM = 1, HYPERLIQUID = 999, PLUME = 98866, ZG = 16661 } ``` ### How any app uses this * **Periphery:** stage assets (if any), pack **action bytes**, emit events. * **Kernel manager:** validate, call core logic, emit **task(s)** with the right `destChainId`. * **Core logic/state:** keep all app state on the hub. * **Periphery gateway:** verify task, settle or refund **only that action**. This is the whole model: one hub state, many periphery endpoints, and clean bytes-based messages. ## Kernel Composition Here’s how the kernel fits together. It links user wallets across chains, accepts signed intents from apps, emits tasks for off-chain executors, and tracks settlement. ### What each contract does * **AccountRegistry**: Binds a user’s wallets across VM types (EVM and non-EVM) into one account. * **MessageBox**: Validates an intent, prevents duplicates, emits tasks, and marks them as settled. * **SkateApp (base)**: App scaffold. Runs app logic, returns tasks, and hands them to MessageBox. ### How they work together 1. The relayer registers VM types and maps chain IDs. 2. A user links wallets. EVM signatures are checked on-chain. Non-EVM is recorded for off-chain checks. 3. The user signs an intent. The app calls `processIntent`. 4. App logic returns `Task[]`. `MessageBox` verifies the intent and emits `TaskSubmitted` events. 5. Executors pick up tasks and execute on the target chain(s). 6. The relayer calls `setTaskAsExecuted` to mark completion. This ensures one user, many chains, a clean intent flow, and no double execution. **Goal**: Keep one account number per user across VMs. **What it stores**: * **VM list**: `vm[vmType]` and `vmCount`. * **Chain → VM type**: `chainIdToVmType`. * **Wallet → account**: `references[keccak256(vmType, wallet)]`. * **Account → wallets**: `accounts[accountNumber]` (array of `{vmType, addr}`). * **Admin**: `_relayer`. **Key calls**: * `initialize(relayer)`: sets the relayer and seeds VM type 1 as "EVM". * `registerVm(name)`: add a VM type (relayer only). * `setVmTypesToChainIds(vmTypes[], chainIds[])`: map chains (relayer only). * `bindWallet(vmType1, wallet1, vmType2, wallet2, sig1, sig2)`: * VM types must be registered and in increasing order. * If `vmType1 == 1`, `sig1` must recover to `wallet1` using the bind hash. * If both wallets are new → create a new account and attach both. * If one is already bound → attach the other to the same account. * If both are already bound (to anything) → reject (no merging two existing accounts). **Reads**: `getWallets`, `getWalletBindingStatus`, `getAccountNumber`, `getVmTypeByChainId`, `getVmCount`. **Signature preimage (EVM binding)**: `getBindEVMHash(wallet1, vmType2, wallet2)` encodes `(0, wallet1, vmType2, wallet2)` and uses the standard Ethereum signed message prefix. **Permissions**: * **Owner**: upgrades, set relayer. * **Relayer**: VM admin + wallet binding entrypoint. **Goal**: Be the outbox everyone trusts. Validate once. Emit tasks once. Mark settlement once. **What it stores**: * `_executorRegistry`: who can originate task submissions (`tx.origin` is checked). * `_relayer`: marks settlement. * `_taskId`: running counter. * `_isTaskExecuted[taskId]`: settlement flag. * `_isActionExecuted[chainId][actionId] → taskId`: global dedup index. * `_nonce[user]`: per-user nonces for replay protection. **Key calls**: * `submitTasks(tasks, intent)`: * Caller must be the app: `msg.sender == intent.intentData.appAddress`. * `tx.origin` must be an approved executor. * `intent.vmType` must be non-zero. * If EVM (`vmType == 1`) and `intent.signature` is present: * Recover signer from `keccak256( user, nonce[user], appAddress, keccak256(intentCalldata) )` with the Ethereum signed message prefix. * Signer must equal `user`. * Non-EVM signatures: emitted as an event for off-chain checks. * Increments the user nonce after checks. * Dedup rule: for each `(srcChainId, actionId)`, ensure it wasn’t executed before. Duplicates inside the same batch are allowed and skip the history check. * Emits one `TaskSubmitted` per task and records `actionId → taskId`. * `setTaskAsExecuted(taskId, settlementInfo)`: * Relayer marks a task as executed. Emits `TaskExecuted`. **Reads**: `executorRegistry`, `taskId`, `isTaskExecuted(taskId)`, `isActionExecuted(chainId, actionId)`, `nonce(user)`, `getDataHashForUser(...)`. **Why it’s safe**: App-only submission, executor allow-list, per-user nonce, and `(chainId, actionId)` dedup keep intents honest and idempotent. **Goal**: Make writing apps simple. You focus on building tasks; the kernel handles the rest. **What it stores**: * `_messageBox` and `_accountRegistry`. * `_chainIdToPeripheryContract[chainId] → bytes32`. * `_chainIds[]` for discovery. **Key calls**: * `__SkateApp_init(messageBox, accountRegistry)`: wire dependencies. * `setChainToPeripheryContract(chainId, peripheryContract)`: * Set or clear the periphery address for a chain. * Keeps `_chainIds` in sync. * `processIntent(intent)`: * Calls `address(this).functionCall(intent.intentData.intentCalldata)`. * Expects your function to return `IMessageBox.Task[]`. * Submits those tasks to `MessageBox`. **Extras**: `onlyContract` modifier is available for internal orchestration. **Lookups**: `chainIdToPeripheryContract`, `messageBox`, `accountRegistry`, `getChainIds`. ### Roles * **Owner (per contract)**: Upgrades and wiring. * **Relayer**: Registry admin; marks settlement in MessageBox. * **Executor (EOA)**: Must be whitelisted; originates task submissions. * **App contract**: Must be the direct caller of `submitTasks`. # Overview Source: https://docs.skatechain.org/main/infrastructure-components/overview Skate's design architecture overview Skate’s infrastructure is built on three foundational layers: 1. **Skate Hub Chain**: The central hub that handles all logic processing and stores the application state. 2. **Executor Network**: A network of executors responsible for executing actions as defined by the application. Each application has its own set of executors. 3. **Skate EigenCloud AVS**: An AVS deployed on Eigenlayer that facilitates the secure delegation of restaked ETH to Skate’s Executor Network. It serves as the primary source of truth, ensuring that Executors perform the required actions on destination chains. As a hub chain, Skate maintains and updates the shared state, providing directives for connected peripheral chains which will respond only to calldata provided by Skate. This is facilitated by our Executor Network, of which, each executor is a registered AVS operator and is responsible for executing these tasks. In the event of any dishonest behaviour, we can depend on the AVS as a source of truth to penalise the offending operators. ### User flow Skate is primarily **intent powered** and each intent encapsulates key information that expresses what a user wants to perform while also defining the necessary parameters and boundaries. Users will only be required to **sign intents through their own native wallet and will only interact from that chain, creating a user native environment.** The end to end intent flow is as described below: **Source chain** 1. User will initiate action on by signing an intent on TON/Solana/EVM. **Skate** 1. Executor receives the intent and call processIntent on Skate. This creates a task that encapsulates key information for Executors for task execution. This also emits a TaskSubmitted event. 2. AVS validators will be actively listening for TaskSubmitted events and will verify the contents of each task. Upon achieving quorum in our AVS, the relayer will issue a signature that is required for task execution **Destination chain** 1. Executor calls executeTask on Gateway contract 2. Gateway contract will verify that task was validated by AVS through the issuance of a valid relayer’s signature before function call defined in task can be performed. 3. Function calldata is executed and intent is marked as complete. # Skate: Infrastructure for Stateless Applications Source: https://docs.skatechain.org/main/introduction/intro Skate is infrastructure that enables applications to operate as stateless entities across multiple blockchains simultaneously. Think of it like Instagram, which runs on iOS, Android, macOS, and Windows as a single cohesive application. In traditional Web2 architecture, applications follow a client-server model. Instagram's core logic and user data live on servers. The application adapts and deploys across different client platforms, each with its own UI and platform-specific handling, but all clients interact with the same backend state. The user's feed, DMs, and follower graph exist once, centrally, regardless of which device they're using. Skate brings this proven architectural pattern to blockchain. It enables a single application to maintain unified state on a hub chain while deploying lightweight client contracts (periphery) across Solana, Ethereum, Sui, and other virtual machines. All components operate in concert as one cohesive system. ## Stateless Applications: The Pattern A stateless application decouples core business logic and canonical state from execution and settlement concerns. **The Kernel (Hub Chain, or "Server")** * Maintains the single source of truth for application state * Executes all core business logic * Enforces protocol invariants and safety guarantees * Generates deterministic task outputs describing state updates **The Periphery (Spoke Chains, or "Clients")** * Handles user-facing interactions on individual blockchains * Manages local asset custody and user onboarding * Stages user intents (actions) for processing on the kernel * Receives task callbacks and finalizes execution results * Maintains no core state. It is purely a reflection of hub-chain truth ## How Execution Flows 1. **User Action on Spoke Chains**: A user interacts with a periphery contract on their preferred blockchain (Solana, Ethereum, Sui) and creates an Action. This Action describes the user's intent for what they want to do. 2. **Executor Relay to Kernel**: The Execution Network listens to periphery contracts across all chains and picks up these Actions. Executors relay them to the kernel contracts deployed on the hub chain. 3. **Kernel Processing**: The kernel validates the Action against canonical state and applies the application's core business logic. If the action requires effects on other chains (such as settling a swap), the kernel produces a Task. This Task describes the exact state update and settlement requirements. 4. **Task Attestation**: The EigenCloud AVS operators review the Task and attest to its correctness. Once the attestations reach sufficient quorum, the task is considered validated. 5. **Task Settlement on Spoke Chains**: After achieving quorum, the validated Task is settled via callbacks on the destination periphery chains. Assets are transferred and local views update to match kernel truth. The result: a truly omnichain application where users remain on their preferred blockchain while accessing applications designed for unified, cross-chain operation. No bridging complexity. No liquidity fragmentation. One canonical state. # Quick Links Source: https://docs.skatechain.org/main/introduction/quick-links ## Skate Protocol * **Skate Explorer**: [scan.skatechain.org](https://scan.skatechain.org/) * **GitHub**: [github.com/skate-org](https://github.com/skate-org) * **Discord Community**: [discord.com/invite/skatechain](https://discord.com/invite/skatechain) * **Documentation**: [docs.skatechain.org](https://docs.skatechain.org) ## Governance & Security * **Skate Snapshot Governance**: [snapshot.box/#/s:skatedao.eth](https://snapshot.box/#/s:skatedao.eth) * **Governance Forum**: [gov.skatechain.org](https://gov.skatechain.org/) * **Nethermind Audit Report**: [NM0565-FINAL\_Skate.pdf](https://github.com/NethermindEth/PublicAuditReports/blob/main/NM0565-FINAL_Skate.pdf) ## EigenCloud AVS & Restaking * **Skate EigenCloud AVS**: [EigenLayer AVS Dashboard](https://app.eigenlayer.xyz/avs/0xfc569b3b74e15cf48aa684144e072e839fd89380) * **Restaking Risk Dashboard**: [Skate on DotRisk](https://restaking.dotrisk.xyz/eigenlayer/0xfc569b3b74e15cf48aa684144e072e839fd89380) ## Metrics * **5B+** trading volume settled on Skate * **Active Chains**: Solana, Sui, Ethereum, Arbitrum, Base, BNB Chain * **Restaked Security**: \$4.5B+ in restaked capital backing Skate's EigenCloud AVS # Skate's Technology Stack Source: https://docs.skatechain.org/main/introduction/tech-stack Skate's infrastructure is composed of three independent, interoperable layers. This modular design allows the stack to evolve by swapping individual components without affecting the overall system. ## Execution Network The Execution Network consists of independent relayers and operators who listen to Actions across all periphery contracts on supported blockchains. Their primary responsibility is to pick up these Actions, relay them to kernel contracts on the hub chain for processing, and then settle the resulting Tasks back on destination spoke chains. This network operates openly and permissionlessly. New executors can join at any time, which creates competition and redundancy. No single executor is critical to system operation. Skate does not prescribe specific executor implementations or select preferred operators. The network remains fluid and evolves based on market incentives. ## Hub Chain Infrastructure The kernel and all core application logic runs on a dedicated hub chain. Skate's hub chain is built on the OP Stack and runs as an Ethereum L2. It uses Avail for data availability and EigenDA as a sidecar to ensure data is available for verification across all spoke chains and by the Execution Network. ## EigenCloud AVS: Restaked Economic Security Skate's cross-chain callbacks are secured by EigenCloud AVS, an autonomous verification service built on restaking infrastructure. EigenCloud operators, who have committed capital via restaking on Ethereum, attest to the correctness of Tasks before they settle on spoke chains. The security model works as follows: **Restaked Economic Security** means operators have committed capital on Ethereum and face slashing risk for attesting to incorrect state transitions. This economic backing ensures that validators have skin in the game and are economically incentivized to validate Tasks correctly. **AVS Operators and Quorum** comprise a decentralized network of node operators running Skate's verification logic. Operators earn fees for producing valid attestations. Once Tasks reach sufficient operator quorum (configurable by application), they are considered validated and can proceed to settlement on spoke chains. **Othentic Components** power the attestation infrastructure. These components enable efficient signature verification and quorum management at scale. The result: cross-chain callbacks are backed by billions of dollars in restaked capital, providing the same economic security guarantees that power Ethereum consensus. Tasks do not settle on spoke chains until they have been attested by a quorum of EigenCloud operators, ensuring correctness and safety. # Why They Matter Source: https://docs.skatechain.org/main/introduction/why-they-matter ## The Problem: Fragmented Application State and Ecosystem Redundancy Traditional blockchain development follows a "fork everywhere" model. Every application redeploys its logic on every chain. This creates two fundamental inefficiencies: **Fragmented Application State** occurs when the same application maintains separate state on multiple chains. A DEX on Ethereum has completely different liquidity and pricing than its Arbitrum deployment. Users on different chains cannot access the same markets. An AMM pool on Ethereum cannot serve demand on Solana. Liquidity is siloed. Prices diverge. Cross-chain interactions require complex bridging and become error-prone. **Ecosystem Redundancy** means every general-purpose blockchain reimplements common infrastructure from scratch. Liquidity protocols, lending systems, identity services, and other foundational legos are built independently on each chain. This creates massive duplication of effort and capital inefficiency. Each ecosystem bootstraps from zero. ## The Solution: Unified Application State via Stateless Design Skate introduces a new model where applications maintain a single source of truth across all blockchains. Rather than forking applications, developers deploy once and let Skate handle multi-chain distribution. **Skate AMM: The Flagship Application** demonstrates the power of this pattern. Instead of deploying separate AMM instances on Ethereum, Solana, Sui, and Arbitrum with fragmented liquidity, Skate AMM maintains one canonical pool state on the hub chain. This unified pool serves all users across all blockchains simultaneously. ## Advantages of Unified Pool State **Consolidated Liquidity** means all trading volume across all chains flows into a single pool. Ethereum traders, Solana traders, Sui traders, and Arbitrum traders all interact with the same liquidity. The pool grows deeper with every transaction on every chain. **Unified Pricing** ensures consistent execution across blockchains. A swap on Solana executes at the same price as a swap on Ethereum in the same block. Price arbitrage between chains disappears. Users get better execution regardless of which blockchain they trade on. **Capital Efficiency** eliminates the need to bootstrap separate liquidity pools on each chain. Instead of splitting capital across eight fragmented pools, liquidity providers deposit once into one unified pool. That capital serves global demand. Better fills. Better fees. **Atomic Cross-Chain Execution** means complex multi-chain trades happen atomically through a single transaction. Arbitrage traders can execute trades across chains without bridging risk or timing issues. Applications can compose seamlessly across blockchains. ## Practical Implications ### For Liquidity Providers Provide liquidity once and serve all blockchains simultaneously. Earn fees from aggregate trading volume across Solana, Sui, Ethereum, Arbitrum, Base, and BNB Chain. No fragmented pools competing for capital. ### For Traders Access unified, deep liquidity regardless of which blockchain you use. Execute trades at consistent prices. Better slippage and execution quality compared to siloed pools on individual chains. ### For Application Builders Build once, deploy everywhere. Skate's stateless pattern eliminates the "fork everywhere" burden. Deploy to all blockchains simultaneously without reimplementation. Security anchored in EigenCloud's restaked capital, not optimistic assumptions or custom implementations. ### For Ecosystem Builders Reduce redundancy in crypto infrastructure. Common services like the Skate AMM exist once and scale across all chains rather than being rebuilt independently. Resources are freed to build higher-level applications rather than reimplementing foundational legos on every chain. # Design pattern Source: https://docs.skatechain.org/main/stateless-app-fundamentals/design-pattern ### Kernel vs Periphery model * **Kernel:** The **stateful core** of the protocol — responsible for maintaining canonical data such as liquidity states, bonding curves, positions, and accounting. * **Periphery:** The **stateless action layer** — a collection of modular “action boxes” that interact with the Kernel by constructing and sending messages (transactions). Periphery modules handle *user interactions* such as swaps, adding/removing liquidity. ### Why stateless design matters Traditional DeFi protocols tightly couple state and execution logic, making upgrades risky and integrations complex. * **Interoperability** - Simplifies cross-chain message passing — only actions, not state, travel between chains. * **Scalability** - Periphery actions can be deployed or scaled independently of Kernel state. Most existing multichain protocols replicate entire state machines across chains — each chain maintains its own pools, liquidity, and accounting. While this design simplifies deployment, it fragments state, splits liquidity, and complicates synchronization. The Kernel–Periphery model solves this by consolidating all canonical state into a single Kernel, while Peripheries act as lightweight action relayers on any chain or execution environment. ### Comparisons to traditional multichain Most existing multichain protocols replicate entire state machines across chains — each chain maintains its own pools, liquidity, and accounting. While this design simplifies deployment, it fragments state, splits liquidity, and complicates synchronization. The Kernel–Periphery model solves this by consolidating all canonical state into a single Kernel, while Peripheries act as lightweight action relayers on any chain or execution environment. ### Unified Liquidity Layer In traditional multichain deployments, liquidity is replicated — e.g., USDC-ETH pools exist separately on Chain A, Chain B, and Chain C. Each has its own reserves and bonding curve, leading to: * Thin depth per pool * Inefficient capital use * Cross-chain price discrepancies By contrast, a Kernel state architecture aggregates all liquidity into a single global pool: * The bonding curve, tick data, and fee accounting live in the Kernel. * Any Periphery (on any chain) can route liquidity to that same state. This creates a shared liquidity surface — every participant contributes to and benefits from the same pool, regardless of where the transaction originates. ### Consistent Pricing Across Chains Because the Kernel holds the canonical bonding curve, all price calculations reference the same state snapshot. This eliminates chain-level price drift and reduces arbitrage inefficiency. ### Higher Capital Efficiency * Liquidity is **aggregated**, not siloed. * LPs earn yield from **global trade volume**, not just local activity. * The same TVL supports **cross-chain flow** without needing redundant pools. ### Frictionless Cross-Chain Interaction Since Peripheries are stateless, a transaction initiated on any chain only needs to relay an action message — not mirror or replicate state. Liquidity effectively becomes **omnichain**, managed by a single, coherent Kernel. # Example: Build your first stateless app Source: https://docs.skatechain.org/main/stateless-app-fundamentals/example Walkthrough a minimal cross-chain counter app built on Skate **App capabilities** — Managing a shared counter that can be incremented and used across chains. ## 1. Kernel implementation ### A. Skate APP Template ```solidity theme={null} // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.26; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { IMessageBox } from "./interfaces/IMessageBox.sol"; import { ISkateApp } from "./interfaces/ISkateApp.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IExecutorRegistry } from "../common/IExecutorRegistry.sol"; abstract contract SkateApp is OwnableUpgradeable, ISkateApp { using Address for address; IMessageBox _messageBox; mapping(uint256 chainId => bytes32 peripheryContract) _chainIdToPeripheryContract; uint256[] _chainIds; modifier onlyContract() { require(msg.sender == address(this), OnlyContractCanCall()); _; } function __SkateApp_init(address messageBox_) public initializer { __Ownable_init(msg.sender); _messageBox = IMessageBox(messageBox_); } function setChainToPeripheryContract(uint256 chainId, bytes32 peripheryContract, bytes memory metaData) external virtual override onlyOwner { metaData; // silence warning if (peripheryContract == bytes32(0)) { uint256 length = _chainIds.length; for (uint256 i = 0; i < length; i++) { if (_chainIds[i] == chainId) { _chainIds[i] = _chainIds[length - 1]; _chainIds.pop(); break; } } } else if (_chainIdToPeripheryContract[chainId] == bytes32(0)) { _chainIds.push(chainId); } _chainIdToPeripheryContract[chainId] = peripheryContract; emit PeripheryContractSet(peripheryContract, chainId); } function processIntent(IMessageBox.Intent calldata intent) external virtual override { IExecutorRegistry _executorRegistry = IExecutorRegistry(_messageBox.executorRegistry()); require(_executorRegistry.isExecutor(msg.sender), NotAnExecutor(msg.sender)); IntentOutput[] memory outputs = _handleSkateIntent(intent.kernelMethod, intent.kernelData); require(outputs.length > 0, EmptyIntentHandlerOutput(intent.kernelMethod, intent.kernelData)); uint256 srcChainId = intent.chainId; bytes32 actionId = intent.actionId; bytes32 srcApp = chainIdToPeripheryContract(srcChainId); require(srcApp != bytes32(0), UnregisteredPeripheryOnChainId(srcChainId)); require(srcApp == intent.srcApp, UnauthorizedIntentFromApp(actionId, srcChainId, intent.srcApp)); IMessageBox.Task[] memory tasks = new IMessageBox.Task[](outputs.length); for (uint256 i = 0; i < tasks.length; i++) { IntentOutput memory output = outputs[i]; bytes32 targetApp = chainIdToPeripheryContract(output.chainId); require(targetApp != bytes32(0), UnregisteredPeripheryOnChainId(output.chainId)); tasks[i] = IMessageBox.Task({ appAddress: targetApp, user: intent.user, actionId: actionId, srcChainId: srcChainId, srcVmType: intent.vmType, destChainId: output.chainId, destVmType: output.vmType, method: output.method, data: output.data }); } _messageBox.submitTasks(tasks, intent); // This line is reachable in derived contracts } function _handleSkateIntent(string calldata, bytes calldata) internal virtual returns (IntentOutput[] memory outputs) { // NOTE: This empty output will revert if not implemented } function chainIdToPeripheryContract(uint256 chainId) public view override returns (bytes32 peripheryContract) { require((peripheryContract = _chainIdToPeripheryContract[chainId]) != bytes32(0), ZeroPeripheryContractAddress()); } function messageBox() external view override returns (address) { return address(_messageBox); } function getChainIds() external view override returns (uint256[] memory chainIds) { return _chainIds; } function setMessageBox(address newMessageBox) external onlyOwner { _messageBox = IMessageBox(newMessageBox); } } ``` ### B. Kernel Counter App Implementation ```solidity theme={null} // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.26; import { SkateApp } from "src/skate/kernel/SkateApp.sol"; import { IMessageBox } from "src/skate/kernel/interfaces/IMessageBox.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; contract KernelInstance is SkateApp, ReentrancyGuardUpgradeable, UUPSUpgradeable { constructor() { _disableInitializers(); } function initialize(address messageBox_) public initializer { __ReentrancyGuard_init(); __SkateApp_init(messageBox_); } function _authorizeUpgrade(address newImplementation) internal override(UUPSUpgradeable) onlyOwner { } // Universal Skate Intent handler, MUST BE IMPLEMENT // all counter increment from source chains function _handleSkateIntent( string calldata signature, bytes calldata data ) internal override returns (IntentOutput[] memory) { if (keccak256(bytes(signature)) == keccak256(bytes("increaseCounter,uint256,uint256"))) { (uint256 destChain, uint256 destVm) = abi.decode(data, (uint256, uint256)); return increaseCounter(destChain, destVm); } else { revert UnknownIntentMsg(signature); } } uint256 count = 0; function _increaseCounter() internal returns (uint256) { count++; return count; } // NOTE: Core logic to emit the count on a target chain // chainId, vmType ref section 5 function increaseCounter(uint256 destChainId, uint256 destVmType) internal returns (IntentOutput[] memory outputs) { uint256 nextNumber = _increaseCounter(); outputs = new IntentOutput; outputs[0] = IntentOutput({ method: "count,uint256", data: abi.encode(nextNumber), chainId: destChainId, vmType: destVmType }); } } ``` Take note of the `increaseCounter()` and `_handleSkateIntent()` implementations. The **signature format** for methods should follow this structure: 1. Method Name: The first part of the signature must be the **method name**. 2. Parameter Types The subsequent parts represent the **input parameter types**, separated by commas.\ These types **must be supported by Solidity**. 3. Cross-Chain Type Handling For **non-EVM chains**, type transpilation occurs at the **MessageBox** level.\ For example, Solana doesn’t support `uint256` — emit it as `uint128` or `uint64`, then cast to `uint256` at the Kernel layer. 4. Format Rule All parts of the signature must be **comma-separated** with no spaces. Example: `increaseCounter,uint256,uint256` ## 2. Periphery Implementation (EVM example) ```solidity theme={null} // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.26; import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import { SkateAppPeriphery } from "src/skate/periphery/SkateAppPeriphery.sol"; /** * @notice A POC application to demonstrate the working of ActionBox. The function performRequestOnDestChain * is called to create an action on the ActionBox contract that triggers the cross-chain call on * a periphery contract on another chain. */ contract PeripheryInstance is SkateAppPeriphery, ReentrancyGuardUpgradeable, UUPSUpgradeable { constructor() { _disableInitializers(); } function initialize(address gateway_, address actionBox_, address kernelApp_) public initializer { __ReentrancyGuard_init(); __SkateAppPeriphery_init(gateway_, actionBox_, kernelApp_); } /** * @notice performs a simple request of increasing counter on chain specified by * the parameter {destChainId}. */ function increaseCounterOnDestChain(uint256 destChainId, uint256 destVmType) external { _createSkateAction("increaseCounter,uint256,uint256", abi.encode(destChainId, destVmType)); } function matchMethod(string calldata method, string memory target) internal pure returns (bool) { return keccak256(bytes(method)) == keccak256(bytes(target)); } /** * @notice Called by gateway via `receiveSkateCall()` on this app * */ function _handleSkateCall(string calldata signature, bytes calldata data) internal override returns (bool, bytes4) { if (matchMethod(signature, "count,uint256")) { uint256 number = abi.decode(data, (uint256)); _count(number); return (true, 0x0); } revert UnknownHandler(); } event Count(uint256 number); function _count(uint256 number) internal { emit Count(number); } } ``` Take note of the `increaseCounter()` and `_handleSkateIntent()` implementations. The **signature format** for methods should follow similar standard to kernel ## Notes and best practices 1. All deployed periphery should be registered on Kernel App using bytes32 format, see `setChainToPeripheryContract` on Kernel Skate App template. 2. All shared logic should be maintain at Kernel level, periphery should only act as proxy for assets handler. 3. Ensure to used a consistent omnichain assets format across your app, i.e. in case of rebalancing between periphery # Skate Action Flow Source: https://docs.skatechain.org/main/stateless-app-fundamentals/skate-action-flow The end-to-end flow of a user action across chains via **Skate infrastructure** All Skate user Actions/Intents are processed by 3 different stages (on 3 different chains) following the below procedures ## 1. Periphery Source Chain: User-Initiated Action User interacts with **App Periphery Contract** on the source chain, optionally depositing staged assets. The **App Periphery Contract** initiates an action by sending details through the **Skate Action Box**. **Skate Indexing Services** detect the action from **Action Box** and forward it to **EigenCloud** for verification by registered operators. Action struct emitted from Action Box ```solidity theme={null} struct Action { bytes32 user; // Unique identifier for the action address kernelApp; // Skate chain address of the intent receiver string kernelMethod; // Metadata to decode kernel call bytes kernelData; // The data attached to the call } ``` *** ## 2. Kernel Skatechain / EigenCloud: Verification and State Update **EigenCloud Operators** verify the action and instruct the **Executor** to update state on the **KernelAdapter** using **Intents**. The **Executor** submits the intent through `submitTasks` in the **MessageBox**. The **MessageBox** routes the update to the corresponding **KernelAdapter**, which serves as the state machine of the cross-chain application. The **KernelAdapter** defines the logic to construct **Tasks**, each containing settlement information such as: * Token transfers * Attached data * Involved addresses or accounts **Task Events** are picked up and signed by operators for settlement on destination chains. Intent struct verified by EigenCloud before parsing into MessageBox ```solidity theme={null} struct Intent { bytes32 actionId; bytes32 user; bytes32 srcApp; address kernelApp; uint256 chainId; uint256 vmType; string kernelMethod; // EVM Abi bytes kernelData; // EVM abi encoded bytes bytes relayerSignature; } ``` Output Task struct from the MessageBox/KernelApp ```solidity theme={null} struct Task { bytes32 actionId; bytes32 appAddress; bytes32 user; uint256 srcChainId; uint256 srcVmType; uint256 destChainId; uint256 destVmType; string method; bytes data; } ``` *** ## 3. Periphery Destination Chain: Settlement & Execution The **Executor** gathers signed tasks and calls `executeTask` on the **SkateGateway** of the destination periphery chain. The **SkateGateway** verifies each task (checking signatures, preventing double spending, validating accounting state, etc.) and forwards it to the target **PeripheryApp**. The **PeripheryApp** executes the settlement logic: * Releases assets * Closes accounts (if any) * Emits relevant events The **EigenOperator** acknowledges the settled state for potential future disputes.\ The **slashing window** follows **EigenLayer’s** protocol. # Skate AMM Source: https://docs.skatechain.org/main/stateless-app-fundamentals/skate-amm Introducing Skate AMM - The unified liquidity layer Our flagship product, **Skate AMM**, is a **crosschain AMM** designed to elevate the native user experience by introducing an unified liquidity model. We are live on EVM, Solana, Eclipse, and Sui with incentive programs for LPs and traders. More details: [Skate AMM deep dive](/skate-amm/introduction) # Audits and Security Source: https://docs.skatechain.org/resources-and-support/audits ## Governance & Security * **Skate Snapshot Governance**: [snapshot.box/#/s:skatedao.eth](https://snapshot.box/#/s:skatedao.eth) * **Governance Forum**: [gov.skatechain.org](https://gov.skatechain.org/) * **Nethermind Audit Report**: [NM0565-FINAL\_Skate.pdf](https://github.com/NethermindEth/PublicAuditReports/blob/main/NM0565-FINAL_Skate.pdf) # FAQ Source: https://docs.skatechain.org/resources-and-support/faq # Official Links Source: https://docs.skatechain.org/resources-and-support/links Skate socials, articles, and blog posts ## Skate Protocol * **Skate Explorer**: [scan.skatechain.org](https://scan.skatechain.org/) * **GitHub**: [github.com/skate-org](https://github.com/skate-org) * **Discord Community**: [discord.com/invite/skatechain](https://discord.com/invite/skatechain) * **Documentation**: [docs.skatechain.org](https://docs.skatechain.org) # AMM Core Contracts deployments Source: https://docs.skatechain.org/skate-amm/deployments/core-contracts ## SKATE | **Contract** | **Address** | | ------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xca3d6FfB1e6706c3c28BD0bd0fA8e925A5c99105 | | MessageBox | 0x6863b6F2E4E0e212Cc43a460e6a9b49579a7AC8D | | AccountRegistry | 0x97aF2C120F2B87C333a7aE4886387bd4E5a5b694 | | EventEmitter | 0xC8615dcE472fD684D0FDcEC18013EaB402F61ba7 | | KernelManager | 0x46887a1f9885300f4185499Ba48C248445EEcAb1 | | KernelPool Implementation | 0x1D19Cc7dFC87fBb0FA57780D2C8B1C6928877159 | | Multicall | 0xaF31d15b11315E0F24991945FC75AaBd3E0a718f | ## MAINNET | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xca3d6FfB1e6706c3c28BD0bd0fA8e925A5c99105 | | ActionBox | 0x6863b6F2E4E0e212Cc43a460e6a9b49579a7AC8D | | SkateGateway | 0x97aF2C120F2B87C333a7aE4886387bd4E5a5b694 | | PeripheryManager | 0x7477D52Db2904C1C7b47A1680687b999dc7E3cb0 | | PeripheryPool Implementation | 0x81fa269E38184405f282bd46B6dEcb397372963c | | EventEmitter | 0xEFfC0fD0CaD5762c360Eb2158500a5537bb4637d | | Multicall / Executor | 0xaF31d15b11315E0F24991945FC75AaBd3E0a718f | ## BASE | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xFe39EE38afA0a7E1e10E32979Fba97A023E827b6 | | ActionBox | 0xaD1472E9d1E267cd87567776F6dbeec6f05Da82d | | SkateGateway | 0x59964b3af53eb10C596B92B3d6aeAfC038B3Bd8d | | PeripheryManager | 0x68B5c82cAf6e5bc540c8a6D435664dB2303d98C3 | | PeripheryPool Implementation | 0x79e31A114E6D2F16E1E2A3EC47C82FAc520881a4 | | EventEmitter | 0x9b69c72B68B7A7E765335831d6234F593f818e6B | | Multicall / Executor | 0xca3d6FfB1e6706c3c28BD0bd0fA8e925A5c99105 | ## ARBITRUM | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xFe39EE38afA0a7E1e10E32979Fba97A023E827b6 | | ActionBox | 0xaD1472E9d1E267cd87567776F6dbeec6f05Da82d | | SkateGateway | 0x59964b3af53eb10C596B92B3d6aeAfC038B3Bd8d | | PeripheryManager | 0x68B5c82cAf6e5bc540c8a6D435664dB2303d98C3 | | PeripheryPool Implementation | 0x27B0E2e956B410F33Df9296F6BcE8b0c915ba8aE | | EventEmitter | 0xf7b221F8a45a62539a53F20C8D3A5a1edDd1b510 | | Multicall / Executor | 0xca3d6FfB1e6706c3c28BD0bd0fA8e925A5c99105 | ## BSC | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xca3d6FfB1e6706c3c28BD0bd0fA8e925A5c99105 | | ActionBox | 0x6863b6F2E4E0e212Cc43a460e6a9b49579a7AC8D | | SkateGateway | 0x97aF2C120F2B87C333a7aE4886387bd4E5a5b694 | | PeripheryManager | 0x7477D52Db2904C1C7b47A1680687b999dc7E3cb0 | | PeripheryPool Implementation | 0x1192C698cC6D68C3f8EbD980a91C2acd4f770d27 | | EventEmitter | 0xcE9ecebAFE2174FFC9b662FE6cc4610574AB602C | | Multicall / Executor | 0xaccAC7d207b25a16283Ec31E42B0d92b18d42004 | ## Mantle | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xDe827ab5e8C1867b5f5351862b033D10c6e23F54 | | ActionBox | 0xaccAC7d207b25a16283Ec31E42B0d92b18d42004 | | SkateGateway | 0xF730a744a817266A48714bd4D2B959B3e221977f | | PeripheryManager | 0x59964b3af53eb10C596B92B3d6aeAfC038B3Bd8d | | PeripheryPool Implementation | 0xc3406121fba68250af08c4933129e88ca84b81e8 | | EventEmitter | 0x802bE72795617ACec443321504150951f85De652 | | Multicall / Executor | 0x3e053A349f8485f8489937d7e26a7f54dc1c11B5 | ## HyperEVM | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0x5d23264aC572c52454B37DA765969f5511B7578e | | ActionBox | 0x19C5382936b71864d249BBb3A7658764558B5896 | | SkateGateway | 0xd719BE20D005d274Eed297c4865Fd20199A13eFa | | PeripheryManager | 0x94FaAE1E0CfC4717e2832F5559CFe662d698BF09 | | PeripheryPool Implementation | 0xa7482D25bc18fc64aBee988922F4Ce8d7F3DBE8B | | EventEmitter | 0x59964b3af53eb10C596B92B3d6aeAfC038B3Bd8d | | Multicall / Executor | 0xEb397737c075214f6F1F3027aC210Ff412980D50 | ## PLUME | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0xca3d6FfB1e6706c3c28BD0bd0fA8e925A5c99105 | | ActionBox | 0x6863b6F2E4E0e212Cc43a460e6a9b49579a7AC8D | | SkateGateway | 0x97aF2C120F2B87C333a7aE4886387bd4E5a5b694 | | PeripheryManager | 0x7477D52Db2904C1C7b47A1680687b999dc7E3cb0 | | PeripheryPool Implementation | 0xC3406121fbA68250AF08c4933129e88CA84b81E8 | | EventEmitter | 0x1F5C460A8BF18B12fb7218019a1693391A8251a7 | | Multicall / Executor | 0xaccAC7d207b25a16283Ec31E42B0d92b18d42004 | ## 0G mainnet | **Contract** | **Address** | | ---------------------------- | ------------------------------------------ | | ExecutorRegistry | 0x0ca6DB88ad5EeC56ca982FB96a24AD2172B4A0eF | | ActionBox | 0x430b6E7f7D43D70786267AF7a5B2C1831372ca24 | | SkateGateway | 0x79e31A114E6D2F16E1E2A3EC47C82FAc520881a4 | | PeripheryManager | 0xa5646a57EB83Ad2636c08b592D7714d860BE9Fa8 | | PeripheryPool Implementation | 0x7274d6c5d2e70573803b3a14108f62ece32eb305 | | EventEmitter | 0xFBD495862410c549f200Ce224Ad3D02a0bAe260D | | Multicall / Executor | 0x0C1174E911D1F13eF679cFF15041cB01491Af46B | # AMM Pools deployment Source: https://docs.skatechain.org/skate-amm/deployments/pools ### TETH/WETH 1 bps | **Contract** | **Address** | | ------------------------- | ----------------------------------------------------------------------------------------------------------------- | | KernelPool | 0x8549Fa87A0787c5B933E50d86DB6C4a46cE8C51A | | PeripheryPool on Mainnet | 0xeD8c0958C5CCbE61cEF6528E1E217010431b84ca | | WETH on Mainnet | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | | tETH on Mainnet | 0x19e099B7aEd41FA52718D780dDA74678113C0b32 | | Periphery Pool on Eclipse | 0x33e297f4a7a98abea1308a43a519b31610f9ad7aa14d6fb9ddedd1a530707b1d (4VYCCZwVCpuJ4ZbTV8fQDCvmXv99yKNgkVCMxuu95nRz) | | WETH Token0 Eclipse | 0x069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001 (So11111111111111111111111111111111111111112) | | TETH Token1 Eclipse | 0xe5d1301448124bdafbfee23ee5b0d40c51ed6630892b039fa3c3149a135d2cf7 (GU7NS9xCwgNPiAdJ69iusFrRfawjDDPjeMBovhV1d4kn) | ### USDT/USDC 1bps | **Contract** | **Address** | | ------------------------- | ------------------------------------------ | | KernelPool | 0x67ba00cFE83BBCc4BD9fc706805168c55E7c01B9 | | PeripheryPool on Base | 0xc206E55F1595A5656a3e038e21837f8A79409827 | | USDT on Base | 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 | | USDC on Base | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | | PeripheryPool on Arbitrum | 0xc206E55F1595A5656a3e038e21837f8A79409827 | | USDT on Arbitrum | 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 | | USDC on Arbitrum | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 | ### SOL/USDC 5 bps | **Contract** | **Address** | | ------------------------- | ----------------------------------------------------------------------------------------------------------------- | | KernelPool | 0x91f71fEA1Bf39E1771D14CB53985c3ea805383A4 | | PeripheryPool on Arbitrum | 0x343389Fe6A3b869Bc7e96f77d9c94580D80B2Cfd | | SOL on Arbitrum | 0x2bcC6D6CdBbDC0a4071e48bb3B969b06B3330c07 | | USDC on Arbitrum | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 | | PeripheryPool on Base | 0x343389Fe6A3b869Bc7e96f77d9c94580D80B2Cfd | | SOL on Base | 0x1C61629598e4a901136a81BC138E5828dc150d67 | | USDC on Base | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | | PeripheryPool on BSC | 0xeD8c0958C5CCbE61cEF6528E1E217010431b84ca | | SOL on BSC | 0xfA54fF1a158B5189Ebba6ae130CEd6bbd3aEA76e | | USDC on BSC | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | | Periphery Pool Solana | 0xde91335c9e07dd9cad2314deae0534e92fd0fa89ff63757896de54d70ed8f3ef (FyovexpSKpfoQzsgnEwXwYaRhNib9whH4RJ1hXGYZLo8) | | SOL on Solana | 0x069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001 | | USDC on Solana | 0xc6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d61 | ### USR/USDC 1 bps | **Contract** | **Address** | | ------------------------ | ------------------------------------------ | | KernelPool | 0x819fD18ffE67DB126bec1947204EA8E2977836bB | | PeripheryPool on Base | 0x82Ee3DcDAd829152996587322395CdA941E20106 | | USR on Base | 0x35E5dB674D8e93a03d814FA0ADa70731efe8a4b9 | | USDC on Base | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | | PeripheryPool on Mainnet | 0xb17c0032f32536C4C44fd6878D277A4F6d3C574F | | USR on Mainnet | 0x66a1E37c9b0eAddca17d3662D6c05F4DECf3e110 | | USDC on Mainnet | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | | PeripheryPool on BSC | 0xb17c0032f32536C4C44fd6878D277A4F6d3C574F | | USR on BSC | 0x2492D0006411Af6C8bbb1c8afc1B0197350a79e9 | | USDC on BSC | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | ### tUSD/USDC 1 bps | **Contract** | **Address** | | ------------------------ | ----------------------------------------------------------------------------------------------------------------- | | KernelPool | 0x6ec39E9b94A4059177093B4bD0bdF96069329F2c | | PeripheryPool on Mainnet | 0xF9581cdF02C1AAF3F398cD87FE3733E664E4949a | | tUSD on Mainnet | 0x722a851B6798D65b80526562Fc3a36E19b1F883b | | USDC on Mainnet | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | | PeripheryPool on Eclipse | 0xc7a46396d789e0c833d8a57555854a96cedd17c6622882ca62506c62f87f44db (ESKX4gsBScv3QhqfWg2rCFMLGaUSpXTSGtJp86jWB4qp) | | tUSD on Eclipse | 0x107a60bdab0e4b59fd8524a38c7304fad98231c262b43b7d1e86f1334bffdbe8 (27Kkn8PWJbKJsRZrxbsYDdedpUQKnJ5vNfserCxNEJ3R) | | USDC on Eclipse | 0x8a64e7e2cee7bcaa68f163ada67e414d4c5394702747d701508be16b5025747b (AKEWE7Bgh87GPp171b4cJPSSZfmZwQ3KaqYqXoKLNAEE) | ### cmETH/USDe 5bps | **Contract** | **Address** | | ------------------------- | ------------------------------------------ | | KernelPool | 0x43a223A15A5ebf03b2A8f76812386fB9B8084D4F | | PeripheryPool on Mainnet | 0x415f76117fd284D2FeD4CB0deE1dc51fc13342Ea | | cmETH on Mainnet | 0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA | | USDe on Mainnet | 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3 | | PeripheryPool on Mantle | 0x3CCFe69F37f32eE0Dc52CBbf265433345D5105c7 | | cmETH on Mantle | 0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA | | USDe on Mantle | 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34 | | PeripheryPool on HyperEVM | 0x027c74f40d8b803b46d8CE31624bc86FD29654f5 | | cmETH on HyperEVM | 0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA | | USDe on HyperEVM | 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34 | ### PLUME/USDC 30bps | **Contract** | **Address** | | ------------------------ | ----------------------------------------------------------------------------------------------------------------- | | KernelPool | 0x15E543FF98832eF9e0D1463BDa2E16A71720ee1b | | PeripheryPool on Mainnet | 0x657440a33717D754c0dF7732C11932838479bC11 | | PLUME on Mainnet | 0x4C1746A800D224393fE2470C70A35717eD4eA5F1 | | USDC on Mainnet | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | | Periphery on Solana | 0x9988cda224126ea6f5926b215bfccd90d6f40d4ad2a8e384ae1261e124e435fa (BLLNJRt1aW4zQjWQYDCjQFejr679LG1wAh4QUTuwtBtm) | | Plume on Solana | 0xa51ceb27b60cb7ca7a371c1bbbc3eae98f405230c21290e8fe59c40430aaa519 (C7Xr4VM6H3W9HHpVep96Hrknkxv2c33kcBTi6e9ZjphW) | | USDC on Solana | 0xc6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d61 (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) | | PeripheryPool on Plume | 0xeD8c0958C5CCbE61cEF6528E1E217010431b84ca | | Wrapped PLUME on Plume | 0xEa237441c92CAe6FC17Caaf9a7acB3f953be4bd1 | | USDC.e on Plume | 0x78adD880A697070c1e765Ac44D65323a0DcCE913 | ### SKATE/USDC 5bps | **Contract** | **Address** | | ------------------------- | ----------------------------------------------------------------------------------------------------------------- | | KernelPool | 0xC7fFA38dE104fd9ccAB351207313667EbDEb6004 | | PeripheryPool on Mainnet | 0x86ADF47A8F785213bCC1065a27D57a8D655deA21 | | SKATE on Mainnet | 0x61DBbBb552dc893ab3aAd09F289f811E67cEf285 | | USDC on Mainnet | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | | PeripheryPool on BSC | 0xF9581cdF02C1AAF3F398cD87FE3733E664E4949a | | SKATE on BSC | 0x61DBbBb552dc893ab3aAd09F289f811E67cEf285 | | USDC on BSC | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | | PeripheryPool on Arbitrum | 0x82Ee3DcDAd829152996587322395CdA941E20106 | | SKATE on Arbitrum | 0x61DBbBb552dc893ab3aAd09F289f811E67cEf285 | | USDC on Arbitrum | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 | | Periphery on Solana | 0x1f9c6bbb84a634146a79a0eeda3c6dfdbe66641e3fb9cdf28fc4119247146d8e (38Pyj1e3ZKnesbdkwphpDFRF18ZDeg5WpjTGiXkgGzuT) | | SKATE on Solana | 0x8477220618127ebc6465db249ac9602586cf5b287e81083e2d0bb9c6abcb23a9 (9v6BKHg8WWKBPTGqLFQz87RxyaHHDygx8SnZEbBFmns2) | | USDC on Solana | 0xc6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d61 (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) | | | | ### SUI/USDC 5bps | **Contract** | **Address** | | ---------------- | ------------------------------------------------------------------------------ | | KernelPool | 0x4d07a0B3a97d366f7bA8A4041592665A4A04A031 | | Periphery on SUI | 0x6ab1e3d7c02dff309504d53fa06302cb66ce50f576432c369afe07c164c0a853 | | Sui on SUI | 0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI | | USDC on SUI | 0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC | ### WETH/USDC 5bps | **Contract** | **Address** | | ----------------- | ------------------------------------------ | | KernelPool | 0x24ac374d91238AFf8D807361eFD027aB5a21a1E3 | | Periphery on Base | 0x1e3fd498641F5Fa9AF2e08b3Be40e7A0Dc09FfE3 | | WETH on Base | 0x4200000000000000000000000000000000000006 | | USDC on Base | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | ### W0G/USDC 5bps Explorer: [https://chainscan.0g.ai](https://chainscan.0g.ai/) | **Contract** | **Address** | | ------------------------------------ | ------------------------------------------ | | KernelPool | 0x91dE266f85598C59ac6fD655eEC32FE54636E393 | | | | | Periphery on 0G mainnet (W0G/USDC.e) | 0x616b894eDf62369C0ab1a586DB90cf47f76708DB | | W0G on 0G mainnet | 0x1Cd0690fF9a693f5EF2dD976660a8dAFc81A109c | | USDC.e on 0G mainnet | 0x1f3AA82227281cA364bFb3d253B0f1af1Da6473E | | | | | Periphery on BSC | 0x415f76117fd284D2FeD4CB0deE1dc51fc13342Ea | | 0G on BSC | 0x4B948d64dE1F71fCd12fB586f4c776421a35b3eE | | USDC on BSC | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | | | | | Periphery on 0G mainnet (W0G/USDC) | 0x0B6414dF25eb6D23B7089055885C3d9058b33DEe | | USDC on 0G mainnet | 0x8a2B28364102Bea189D99A475C494330Ef2bDD0B | ### st0G/W0G 5bps | **Contract** | **Address** | | ------------------------------------ | ------------------------------------------ | | KernelPool | 0xD037E9BAF8f38c79E45054b797B300C71c7b121e | | | | | Periphery on 0G mainnet (W0G/USDC.e) | 0xC2bE4b8852E37ECf979b3E2b5d58162719303804 | | st0G on 0G mainnet | 0x7bBC63D01CA42491c3E084C941c3E86e55951404 | | W0G on 0G mainnet | 0x1Cd0690fF9a693f5EF2dD976660a8dAFc81A109c | ### oUSDT/USDC.e 1bps | **Contract** | **Address** | | -------------------------------------- | ------------------------------------------ | | KernelPool | 0x2548d1b05aA01341cb01b8738dBF7C4C4C999115 | | | | | Periphery on 0G mainnet (oUSDT/USDC.e) | 0x82B4Bf5311332B5Bb8457eAd465659C206f50604 | | oUSDT on 0G mainnet | 0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189 | | USDC.e on 0G mainnet | 0x1f3AA82227281cA364bFb3d253B0f1af1Da6473E | # EVM SDK Reference Source: https://docs.skatechain.org/skate-amm/developers/chain-specific-integration/evm-sdk-reference Integrate with Skate AMM on Ethereum and EVM-compatible chains using the official SDK. *** ## Installation ```bash theme={null} npm install @skate-org/skate-app-amm ``` *** ## SDK Initialization ```typescript theme={null} import { SkateAmmCoreSDK } from "@skate-org/skate-app-amm"; const sdk = new SkateAmmCoreSDK("PRODUCTION"); ``` *** ## Reference * [SDK README ↗](https://www.npmjs.com/package/@skate-org/skate-app-amm) * Check the README for: * Core Actions * Chain Support * *and more..* # SDK overview Source: https://docs.skatechain.org/skate-amm/developers/chain-specific-integration/sdk-overview Skate provides SDKs for interacting with the **Skate AMM** across multiple chains: * **Kernel SDK (core logic)** — [@skate-org/skate-app-amm](https://www.npmjs.com/package/@skate-org/skate-app-amm) * **Sui Periphery SDK** — [@skate-org/skate\_amm\_sui\_sdk](https://www.npmjs.com/package/@skate-org/skate_amm_sui_sdk) * **Solana Periphery SDK** — [@skate-org/skate\_amm\_solana](https://www.npmjs.com/package/@skate-org/skate_amm_solana) These SDKs enable traders, bots, and market makers to quote and execute swaps directly on Skate AMM’s **unified liquidity layer**. Please refer to the respective repos for the most updated changes and documentation. # Solana SDK Reference Source: https://docs.skatechain.org/skate-amm/developers/chain-specific-integration/solana-sdk-reference Integrate with Skate AMM on Solana using the official SDK. This SDK provides all the helpers for adding liquidity, swapping tokens, and burning liquidity. *** ## Installation ```bash theme={null} npm install @skate-org/skate_amm_solana ``` *** ## SDK Initialization ```typescript theme={null} import { Connection, Keypair } from "@solana/web3.js"; import { Wallet } from "@coral-xyz/anchor"; import { SkateSDK, Environment } from "@skate-org/skate_amm_solana"; // Use the appropriate RPC URL const connection = new Connection(process.env.RPC_URL_902!, "confirmed"); // Create wallet from keypair const keypair = Keypair.generate(); // or load from file const wallet = new Wallet(keypair); // Or initialize for PRODUCTION environment const sdkProduction = new SkateSDK(connection, Environment.PRODUCTION, wallet); ``` *** ## Reference * [SDK README ↗](https://www.npmjs.com/package/@skate-org/skate_amm_solana#installation) * Check the README for: * Configure token pair * Address conversion * Important notes * *and more..* # Sui SDK Reference Source: https://docs.skatechain.org/skate-amm/developers/chain-specific-integration/sui-sdk-reference Integrate with Skate AMM on Sui network using the official SDK. This SDK supports both staging and production environments, which can be selected upon initialization. *** ## Installation ```bash theme={null} npm install @skate-org/skate_amm_sui_sdk ``` *** ## SDK Initialization ```typescript theme={null} import SkateAmmSdk, { Environment, PoolType, } from "@skate-org/skate_amm_sui_sdk"; // Initialize the SDK for the production environment const sdk = new SkateAmmSdk(Environment.Production); ``` > ⚙️ Note: This instance will be pre-configured with the correct contract addresses and pool configurations for the selected environment. *** ## Reference * [SDK README ↗](https://www.npmjs.com/package/@skate-org/skate_amm_sui_sdk#installation) * Check the README for: * Setup and configuration * Usage example * *and more..* # Swap Life Cycle Source: https://docs.skatechain.org/skate-amm/developers/swap-lifecycle How to execute through Skate AMM via SDKs #### 1. Get a Quote via Kernel SDK Use the **Kernel SDK** to fetch a swap quote that references the canonical hub state. ```tsx theme={null} import { getSwapQuote, EnvMode } from "@skate-org/skate-app-amm"; // change id accordingly const chainId = 901 const quote = await getSwapQuote( chainId, { tokenA: tokenIn, tokenB: tokenOut, slippageLimit: 0.001, amount: BigInt(userAmount), }, "PRODUCTION" as EnvMode ); ``` ### 2. Execute Swap via Periphery SDKs Execute the quote using the periphery SDK corresponding to your target chain: ```ts evm.ts theme={null} import { privateKeyToAccount } from "viem/accounts"; import { createPublicClient, Chain, http, createWalletClient, WalletClient, Account, defineChain, } from "viem"; import { peripheryPoolAdapter } from "@skate-org/skate-app-amm"; const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`); const wallet = createWalletClient({ chain, account, transport: http(RPC_URL), }); const recipient = RECIPIENT_ADDRESS; // in bytes32 format const swapData: `0x${string}` = peripheryPoolAdapter.getSwapCalldata({ recipient: evmContext!.account!.address as `0x${string}`, destChainId: chainId, destVmType: 1, amount: amount, zeroForOne: zeroForOne, sqrtPriceLimitX96: sqrtPriceLimitX96, minAmountOut: minAmountOut, }); const executableRequest = await wallet.prepareTransactionRequest({ account: account, chain: wallet.chain, data: swapData, to: poolAddress, }); const signedTx = await wallet.signTransaction(executableRequest); const tx = await wallet.sendRawTransaction({ serializedTransaction: signedTx, }); return tx; ``` ```ts solana.ts theme={null} import { SkateSDK, Environment } from "@skate-org/skate_amm_solana"; import { Transaction, sendAndConfirmTransaction, PublicKey, ComputeBudgetProgram, Keypair, } from "@solana/web3.js"; import { BN } from "@coral-xyz/anchor"; // Set up Solana config const secretKey = Uint8Array.from(bs58.decode(PRIVATE_KEY)); const keypair = Keypair.fromSecretKey(secretKey); const publicKey = keypair.publicKey; const svmWallet = new Wallet(keypair); const sdk = new SkateSDK(connection, Environment.PRODUCTION, svmWallet); const swapIx = await sdk.getSwapIx( config, chainId, base58ToEthBytes32(keypair.publicKey.toString()), 3, zeroForOne, amount, sqrtPriceLimitX96, minAmountOut, Buffer.from([]), publicKey, publicKey ); const priorityFeeIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: SOLANA_PRIORITY_FEE_MICRO_LAMPORTS, }); const swapTx = new Transaction().add(priorityFeeIx); swapTx.add(...swapIx); await sendAndConfirmTransaction(connection, swapTx, [svmContext.keypair], { commitment: "confirmed", }); ``` ```ts sui.ts theme={null} import { PoolType } from "@skate-org/skate_amm_sui_sdk"; import SkateAmmSdk, { Environment } from "@skate-org/skate_amm_sui_sdk"; import { SuiClient } from "@mysten/sui/client"; // Set up Sui client and SDK const client = new SuiClient({ url: rpcUrl }); const sdk = new SkateAmmSdk(Environment.PRODUCTION); // Fetch pool config const poolConfig = sdk.getPoolConfig(poolType); if (!poolConfig) throw new Error(`Pool configuration not found for PoolType: ${poolType}`); const userOwner = USER_ADDRESS; // in bytes32 format const tokenInTypeSwap = zeroForOne ? poolConfig.token0 : poolConfig.token1; const coinsTokenInSwap = await client.getCoins({ owner: userOwner, coinType: tokenInTypeSwap, }); const tx = await suiContext.sdk.swap.swap({ poolType, coinsIn: coinsTokenInSwap.data, recipientStr: userOwner, zeroForOne, amountSpecified: amount, amountIsPositive: BigInt(amount) > BigInt(0), sqrtPriceLimitX96Str: sqrtPriceLimitX96, destChainId: String(chainId), destVmType: 4, extraData: "", actionBoxSeed: new Date().getTime().toString(), amountIn: amount, }); const result = await client.signAndExecuteTransaction({ transaction: tx, signer: keypair, }); ``` # Skate AMM Fundamentals Source: https://docs.skatechain.org/skate-amm/introduction ### What is Skate AMM? Our flagship product, **Skate AMM**, is a **crosschain AMM** designed to elevate the native user experience across chains by introducing a revolutionary pricing and liquidity model.\ Built on Skate’s core design principles of **composability and single application state**, the Skate AMM leverages an **aggregated bitmap that delivers a single, unified price across all connected chains**. This pricing mechanism mirrors the proven structure of a Uniswap V3 pool, yet extends beyond single-chain limitations to provide a truly cross-chain liquidity experience. *** ### Stateless Pattern in Action Adopting the **stateless pattern** that governs all Skate-based applications, the core state for the AMM is hosted on **Skate (the hub chain)**. This implies that: * The **hub chain (Skate)** hosts the **canonical state** of all AMM pools — including tick bitmaps, liquidity positions, and global fee accumulators. * Each connected **periphery chain** (e.g., Solana, Sui, Ethereum, etc.) maintains **minimal periphery state** to enable fast, native execution that references the hub’s canonical state during runtime. *** ### Source Chain (Periphery) 1. **Quote retrieval**\ The user or front-end fetches a quote, either off-chain via an aggregator or directly from pool data hosted on **Skate**.\ This ensures the swap references the canonical, hub-validated state. 2. **Swap initiation**\ The user submits a swap action on the source chain. Their input asset (e.g., USDT) is transferred from the user’s wallet into the **local periphery pool**. 3. **Action registration**\ The swap intent is emitted as an event and indexed by the **executor network**, which observes periphery activity and relays actions to the hub chain for deterministic settlement. *** ### Hub Chain (Skate) 1. **Canonical execution**\ The executor triggers the swap logic within the **Skate kernel**, referencing the pool’s canonical state (tick bitmap, liquidity ranges, and fee growth). 2. **State update**\ The pool state is atomically updated on the hub — reflecting new price ticks, fee accruals, and global liquidity deltas. 3. **Task creation**\ Once executed, the kernel emits a **task event** containing details such as: * user address * tokenIn / tokenOut * slippage * amountIn / amountOut * deadline This task is indexed and queued by executors for dispatch to the relevant destination chain. *** ### Destination Chain (Periphery) 1. **Task execution**\ The executor submits the verified task to the periphery contract on the destination chain. 2. **Asset delivery**\ The pool vault on that chain transfers the output asset (e.g., USDC) to the user’s wallet.\ Final settlement mirrors the hub’s canonical record, ensuring consistency across chains. *** ### Unified Liquidity Model At the heart of Skate AMM lies its **Unified Liquidity Model** — a system that transcends traditional, chain-specific liquidity fragmentation. Instead of each network maintaining its own isolated pool, Skate aggregates liquidity across all connected chains into a **single virtual pool** governed by a unified price curve. Under this model: * **All price discovery and liquidity accounting occur on the hub chain (Skate)**. * **Actual tokens remain native and transacted on the respective periphery chains.** The hub maintains only the **logical state** of the pool — tick bitmaps, liquidity positions, and fee growth — serving as the single source of truth for pricing and liquidity distribution. Each periphery chain executes swaps using the **price and outcome computed by the hub**, moving tokens locally to complete settlement.\ The hub remains the source of truth for all pricing and liquidity accounting. This architecture ensures: * **Unified pricing:** every trade on any chain references the same hub-validated state root, maintaining global price consistency. * **Cross-chain depth aggregation:** liquidity from all chains contributes to a single price curve, maximizing liquidity depth and minimizing slippage. * **Stateless yet verifiable execution:** periphery environments execute locally without persisting pool logic, ensuring lightweight composability. * **Atomic global updates:** the hub serializes all fee, tick, and liquidity updates, preserving deterministic order across chains. In effect, **pricing and state are centralized on the hub**, while **liquidity and execution remain decentralized across periphery chains**, giving users the experience of trading against a single global pool — regardless of where they are. *** ### Multichain Pricing Curve Each connected chain (e.g., Solana, Sui, Ethereum) maintains its own **local liquidity bitmap** for the same pair — such as ETH/USDC — reflecting liquidity distribution and tick activity within that chain’s execution environment. On **Skate**, these individual price maps are **aggregated into a single canonical curve** on the hub. This unified curve represents the **global price state**, combining depth and liquidity from every chain. Tokens remain on their **native chains**, while **pricing and liquidity accounting** are derived centrally on Skate. This ensures every trade, regardless of where it originates, references the **same global ETH/USDC price curve**. > In essence: fragmented local pools → aggregated global price.\ > **One price, many chains.** # How to Add & Remove Liquidity Source: https://docs.skatechain.org/skate-amm/liquidity-providers/actionables When LPs add liquidity on **Skate AMM**, the position is recorded in the **hub’s canonical pool state**, allowing it to support trades and accrue fees across all connected chains. While tokens remain on the chain where they are deposited, the corresponding **liquidity range and fee accounting** are synchronized globally through the hub. LPs can deposit tokens within a **custom or full price range** — similar to Uniswap V3-style concentrated liquidity. * Use **Full range** for passive exposure * Use **Custom range** to concentrate liquidity around active prices for higher capital efficiency All liquidity updates — additions, removals, or range adjustments — are executed natively on the selected chain and reflected in the unified hub state, ensuring that LPs’ positions remain active participants in the global fee-sharing mechanism. # Boosted APY Source: https://docs.skatechain.org/skate-amm/liquidity-providers/boosted-apy Skate LP incentive program on targeted pools ## Boosted Range Boosted ranges are highly concentrated liquidity zones where demand peaks for certain pools on specific chains. We give out incentive to provide deeper liquidity for these ranges. The **displayed boosted APY** is an estimation using current mark price of rewards token per unit of liquidity value provided in the boosted range Rewards for boosted ranges are distributed by the Skate Foundation. For example, all LPs for the **SOL/USDC 5 bps** pool within the **65 – 585 USDC per SOL** range will be eligible for additional rewards. ## Claim Boosted Rewards Boosted rewards for eligible wallet can be claimed by **clicking the bell button at the top right corner** of [amm.skatechain.org](https://amm.skatechain.org) Please ensure you have switched to the correct chain before claiming. # Fee mechanism Source: https://docs.skatechain.org/skate-amm/liquidity-providers/fee-mechanism Skate AMM LP mechanics ## Efficient LP via Skate AMM In traditional multi-chain ecosystems, liquidity is fragmented across networks — each AMM on each chain maintains its own reserves, positions, and fee accruals. This fragmentation lowers **capital efficiency**: liquidity often sits idle on quieter chains, and the same price ranges must be funded multiple times across networks. Skate AMM removes this inefficiency through a **unified liquidity state** hosted on the hub chain. When liquidity is added on any periphery chain, it becomes part of the **same global pool**, contributing to the shared price curve that governs all connected markets. As a result, LP capital is utilized across chains rather than confined to one. ### How Efficiency Improves 1. **Global utilization of liquidity**\ Liquidity provided on one chain earns from trading activity on all chains.\ Even if local volume is low, that liquidity remains productive as part of the global pool. 2. **Reduced redundancy**\ Instead of replicating depth at the same price points on multiple networks, Skate aggregates all positions into one canonical price curve.\ This means less total liquidity is required to achieve the same effective slippage and price stability. 3. **Smoothed fee income**\ Global fee sharing distributes trading fees from the entire network to all active LPs, reducing income variance and keeping capital yield more consistent. 4. **Range-based precision retained**\ LPs still define custom price ranges for their positions, maintaining Uniswap V3-style control — but those ranges now participate in cross-chain price discovery and fee accrual. ### Capital Efficiency Comparison | Feature | **Siloed AMMs (per-chain)** | **Skate AMM (unified)** | | ------------------------------------ | ---------------------------------- | -------------------------------------------------- | | **Liquidity scope** | Confined to a single chain | Shared across all connected chains | | **Price curve** | Independent per network | Canonical global curve on hub | | **Fee accrual** | Local only | Aggregated and distributed globally | | **Idle liquidity risk** | High — unused on low-volume chains | Low — all liquidity contributes to global activity | | **Effective depth per unit capital** | Moderate | Higher, via aggregated tick bitmap | | **Income volatility** | Dependent on local chain demand | Smoothed by network-wide fee sharing | | **Capital efficiency** | Fragmented | **System-wide and optimized** | ## Fee Accrual As swaps occur across chains, trading fees accrue globally and are distributed **pro-rata** to all active LPs based on their position size and range. ## Displayed APY The displayed **APY** reflects the **aggregated yield from all connected networks** — not just the local chain. This means liquidity provided on one chain earns from **cross-chain trading activity**, thanks to Skate’s Unified Liquidity Model.