Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ axum = { version = "0.8", default-features = false, features = ["tokio", "http1"

[dev-dependencies]
ed25519-dalek = "1.0.1"
solana-program-runtime = "4.2.1"
libsecp256k1 = "0.7.2"
p256 = { version = "0.13", default-features = false, features = ["ecdsa"] }
test-case = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/scenarios/protocols/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod pump;
pub mod tessera;
135 changes: 135 additions & 0 deletions crates/core/src/scenarios/protocols/tessera/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Tessera

Tessera is a proprietary market maker that publishes no IDL. Surfpool writes its market accounts
through the raw byte layout in `v1/overrides.yaml`. It prepares state; it does not construct or
submit a swap.

## Deployment

- Program: `TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH`
- ProgramData: `BzSXM6KLDpHQQChzr7Fdgbzwp8r8zRYWFFrHK2uZmDYV`
- Upgrade authority: `7bJ9xu9UGVZPtYzH1fMwdaKdvfhqeSJtoFc2eGrXBPhK`
- Deploy slot: `446053401`
- ELF SHA-256: `82fd37995fcece47a253b1a00c1dd7e4c3fff706b2e42384110e2cbabf4201b3`

The live suite checks the layout and behavior against this pinned deployment and fails when
its identity changes. A redeploy voids the layout evidence — re-verify
before trusting the templates again.

## The guard, and what it does not cover

The manifest requires 1264 bytes and `05 00 00 00 00 00 00 00` at offset 96, then discovery
validates ownership and mint identities. These bytes are a conservative observed-state filter,
not a version discriminator: the deployed program also reads this field during age adjustment.
A different value may exclude a valid market, and a matching value does not authenticate a future
layout. Deployment revalidation remains necessary.

The shared raw-layout schema has no owner predicate, so a foreign account of the same size carrying
the same eight bytes would pass a raw template. `TesseraMarket` validation adds the ownership
check used by discovery and both scenario tools; the depth builder also validates its input
account. Composing the raw template against an arbitrary address does not. That is a property of the shared schema, not of
this integration, and the raw scenario API is unvalidated by contract.

## Templates

| Template | Prepared state |
|---|---|
| `tessera-fair-value` | both directional atomic-ratio fields |
| `tessera-depth` | all twenty directional capacities on both ladders |
| `tessera-curve` | all twenty directional output factors on both ladders |
| `tessera-halt` | all twenty enabled flags on both ladders |
| `tessera-stale-quote` | offset 120, aged by the lead you pass (default -20) |
| `tessera-freshness` | offset 120, the current materialization slot |

The direct field at offset 128 is quote atomic units per base atomic unit multiplied by `10^15`.
The reciprocal at offset 144 uses the same scale, so their product is approximately `10^30` after
integer-floor rounding. Changing only one of them moves one quote direction and leaves the other
where it was, which is why they are one invariant.

The sell ladder occupies bytes 160 through 639 and the buy ladder 640 through 1119. Each holds
twenty 24-byte records: directional capacity at `+0`, marginal-price factor at `+8`, enabled flag
at `+16`. Capacity and factor changes affect only their active quote direction. For a fill
starting and ending in level zero, the modeled output is
`floor(input_atoms * directional_price * first_level_factor / 10^21)`. The live tests check exact
atomic output after clearing the captured flow counters at 0/8 and neutralizing the five
selectable configurations at 1136 + 12*i (ppm adjustment 0, factor scale 1,000,000, no skipped
levels) in their local fixtures. A small input alone cannot
establish this condition: prior flow or the selected configuration can start at a later level.
These fixture controls are not exposed as scenario properties.

`tessera-halt` writes zero to every enabled flag using two strided byte properties. Its existing
property names are retained, but each now covers twenty levels. Clearing only level zero can
leave later levels tradable. The live regression checks both captured state and an explicit
one-level skip, including a control where clearing only level zero still allows a swap.

Offset 88 stores the age at which the program rejects a quote. Age 19 succeeds and age 20 fails
with custom error 65535 on a market configured at 20.

Both slot templates take the lead from the caller: the value supplied for `last_update_slot` is
added to the materialization slot, and only `null` falls back to the template's own lead. One stale
template therefore covers every market, including one configured at a limit nobody has seen yet.
`list_tessera_markets` returns each market's limit alongside its address; callers pass its negation. Passing a number where you meant the default is the one trap: `0` on the stale
template writes a perfectly fresh quote.

## Live market discovery

`list_tessera_markets` queries Tessera program accounts through the selected Surfnet RPC.
It filters by the manifest's account size and pinned bytes, validates ownership and mint identities,
and reads decimals from the referenced mint accounts. The freshness limit comes from offset 88.
The existing Surfnet account resolver merges remote discovery with local accounts, preferring local
state. Discovery needs a datasource that supports `getProgramAccounts`; offline instances can list
only their local accounts.

The six shared templates retain the SOL/USDC default address for callers that omit an account.

Labels use mint symbols from Surfpool's existing token metadata. An unknown mint is displayed by
its full address, so missing symbol metadata never hides a discovered market. Addresses are the
identities; symbols are not unique. Market membership, decimals and freshness limits are not taken
from the token metadata catalog.

## Builders and tools

The fair-value builder converts a human price into reciprocal atomic ratios using both mints'
decimals. It is a pure function over account data. `create_tessera_fair_value_scenario` reads the market and both mints through the
surfnet's own RPC, so local state wins and only missing accounts fall back to the datasource, then
stages the scenario through the shared path.

Builder-created overrides keep `fetchBeforeUse: false`: creation has already read and hydrated
the target account, and the scenario must use that prepared local snapshot. When composing a
direct template scenario, set `fetchBeforeUse: true` on the first override for each account not
yet in local state. Freshness overrides use `persist: true` and `last_update_slot: null` to write
the current materialization slot on every application.

The `create_tessera_depth_scenario` tool reads current Surfnet state and takes remaining basis points
per direction: 1000 retains 10%, 10000 leaves that direction unchanged. It scales only enabled
capacities, with integer-floor rounding, and rejects zero capacities or increases. Prices, factors
and disabled levels are preserved. The scenario combines `tessera-depth` with persisted freshness;
depth itself is applied once. Creating another scenario reads the then-current state again.
Curve changes remain available through the raw template.

## Behavioral evidence

The live suite loads the pinned ELF into LiteSVM and exercises price direction isolation,
depth reductions, curve factors, freshness boundaries, halted ladders, vault bindings,
invalid sentinel/global account metas, and live market discovery. Raw writes are checked against
complete expected buffers or permitted byte ranges.

Run it serially. The public endpoint sheds queued requests right after a `getProgramAccounts` scan,
sometimes as a 413 that looks like a request-size error:

```bash
SURFPOOL_TEST_RPC_URL=<rpc-url> cargo test -p surfpool-core --features integration-tests \
tests::tessera -- --test-threads=1 --nocapture
```

`SURFPOOL_TEST_RPC_URL` is optional and defaults to the public mainnet endpoint. Set it to a
private endpoint when the public one rate-limits.

## Known boundaries

No separate fee field is exposed. Exact controlled first-level output does not establish how
Tessera decomposes its price factor into spread, fee, or another adjustment. The region from 1120
onward includes configuration-dependent quote adjustments and leading-level selection; its full
economic meaning remains unmodeled and it is not exposed by the templates. Vault depletion is
not exposed by the Tessera templates. The generic SPL Token balance template uses the shared
typed token-account writer, but Tessera vault depletion behavior is outside this suite's coverage.
1 change: 1 addition & 0 deletions crates/core/src/scenarios/protocols/tessera/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod v1;
165 changes: 165 additions & 0 deletions crates/core/src/scenarios/protocols/tessera/v1/depth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use std::collections::HashMap;

use solana_account::Account;
use solana_pubkey::Pubkey;
use surfpool_types::{AccountAddress, OverrideInstance, Scenario};

use super::{
TesseraMarket,
fair_value::{FRESHNESS_TEMPLATE, freshness_override, template},
};
use crate::{
error::{SurfpoolError, SurfpoolResult},
scenarios::TemplateRegistry,
};

pub fn build_tessera_depth_scenario(
market: Pubkey,
account: &Account,
sell_remaining_bps: u16,
buy_remaining_bps: u16,
) -> SurfpoolResult<Scenario> {
if [sell_remaining_bps, buy_remaining_bps]
.iter()
.any(|bps| !(1..=10_000).contains(bps))
{
return Err(SurfpoolError::internal(
"Remaining depth must be 1..10000 basis points; 1000 keeps 10%, 10000 leaves a side unchanged",
));
}
TesseraMarket::mint_addresses(account)?;
let registry = TemplateRegistry::new();
let depth_template = template(&registry, "tessera-depth")?;
template(&registry, FRESHNESS_TEMPLATE)?;
let mut values = HashMap::new();
for property in &depth_template.properties {
let bps = if property.path.starts_with("sell_levels.") {
sell_remaining_bps
} else {
buy_remaining_bps
};
let offset = property.offset.expect("Tessera capacity offset");
// The enabled flag follows capacity and price factor in each 24-byte level.
if bps == 10_000 || account.data[offset + 16] == 0 {
continue;
}
let current = u64::from_le_bytes(account.data[offset..offset + 8].try_into().unwrap());
let scaled = (u128::from(current) * u128::from(bps) / 10_000) as u64;
if scaled == 0 {
return Err(SurfpoolError::internal(format!(
"{} would have zero capacity while enabled; retain more depth",
property.path
)));
}
values.insert(property.path.clone(), serde_json::json!(scaled.to_string()));
}
if values.is_empty() {
return Err(SurfpoolError::internal(
"No enabled levels selected for depth reduction",
));
}
let percent = |bps: u16| format!("{}.{:02}%", bps / 100, bps % 100);
let mut scenario = Scenario::new(
"Tessera depth stress".to_string(),
format!(
"Keep {} of sell depth and {} of buy depth on market {market}, preserving prices and keeping quotes fresh.",
percent(sell_remaining_bps),
percent(buy_remaining_bps)
),
);
let target = AccountAddress::Pubkey(market.to_string());
scenario.add_override(
OverrideInstance::new(depth_template.id.clone(), 0, target.clone())
.with_values(values)
.with_label("Reduce Tessera depth".to_string()),
);
scenario.add_override(freshness_override(target));
scenario.tags = vec![
"tessera".to_string(),
"pmm".to_string(),
"depth-stress".to_string(),
];
Ok(scenario)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::scenarios::protocols::tessera::v1::{TESSERA_PROGRAM_ID, fair_value::MARKET_LAYOUT};

fn market() -> Account {
let mut data = vec![0; MARKET_LAYOUT.account_size];
let magic = MARKET_LAYOUT.magic.as_ref().unwrap();
data[magic.offset..magic.offset + magic.bytes.len()].copy_from_slice(&magic.bytes);
data[24..56].copy_from_slice(Pubkey::new_unique().as_ref());
data[56..88].copy_from_slice(Pubkey::new_unique().as_ref());
for (offset, amount, enabled) in [(160, u64::MAX, 1), (184, 101, 0), (640, 12345, 1)] {
data[offset..offset + 8].copy_from_slice(&amount.to_le_bytes());
data[offset + 16] = enabled;
}
Account {
data,
owner: TESSERA_PROGRAM_ID,
..Account::default()
}
}

#[test]
fn scales_exactly_and_preserves_unselected_bytes() {
let account = market();
let address = Pubkey::new_unique();
for (sell, buy, expected_sell, expected_buy) in [
(5000, 10000, 9223372036854775807u64, 12345u64),
(1000, 2500, 1844674407370955161u64, 3086),
] {
let scenario = build_tessera_depth_scenario(address, &account, sell, buy).unwrap();
assert_eq!(scenario.overrides.len(), 2);
let mut actual = account.data.clone();
let mut expected = actual.clone();
expected[160..168].copy_from_slice(&expected_sell.to_le_bytes());
expected[640..648].copy_from_slice(&expected_buy.to_le_bytes());
expected[120..128].copy_from_slice(&42u64.to_le_bytes());
let registry = TemplateRegistry::new();
for instance in &scenario.overrides {
assert_eq!(
instance.account,
AccountAddress::Pubkey(address.to_string())
);
assert_eq!(instance.scenario_relative_slot, 0);
assert!(!instance.fetch_before_use);
let template = registry.get(&instance.template_id).unwrap();
actual = template
.raw_layout
.as_ref()
.unwrap()
.materialize(&actual, &template.properties, &instance.values, 42)
.unwrap();
}
assert_eq!(actual, expected);
assert!(!scenario.overrides[0].persist);
assert!(scenario.overrides[1].persist);
assert!(
scenario.overrides[0]
.values
.values()
.all(|value| value.is_string())
);
}
}

#[test]
fn rejects_invalid_reductions_and_accounts() {
let mut account = market();
let address = Pubkey::new_unique();
for (sell, buy) in [(0, 10000), (10000, 10001), (10000, 10000)] {
assert!(build_tessera_depth_scenario(address, &account, sell, buy).is_err());
}
account.data[160..168].copy_from_slice(&1u64.to_le_bytes());
assert!(build_tessera_depth_scenario(address, &account, 1000, 10000).is_err());
account.owner = Pubkey::new_unique();
assert!(build_tessera_depth_scenario(address, &account, 1000, 1000).is_err());
account.owner = TESSERA_PROGRAM_ID;
account.data.truncate(100);
assert!(build_tessera_depth_scenario(address, &account, 1000, 1000).is_err());
}
}
Loading
Loading