Skip to content

sdk/shreds: add the feed subscription program's FeedDistribution account - #4216

Open
bgm-malbeclabs wants to merge 1 commit into
mainfrom
sdk/shreds-feed-distribution
Open

sdk/shreds: add the feed subscription program's FeedDistribution account#4216
bgm-malbeclabs wants to merge 1 commit into
mainfrom
sdk/shreds-feed-distribution

Conversation

@bgm-malbeclabs

@bgm-malbeclabs bgm-malbeclabs commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary of Changes

  • sdk/shreds/go now carries the feed subscription program's FeedDistribution account: how much USDC one feed collected for one calendar month. That program is a second program alongside shred subscription and had nothing in this SDK, so each Go caller decoded the account at fixed byte offsets itself. malbeclabs/lake holds one such copy today and can delete it once this lands.
  • FeedDistribution is a bytemuck Pod onchain (#[repr(C, align(8))]), read here field by field. That agrees with the Pod bytes only because the field order leaves no interior padding, and nothing in the Rust struct states that. Two tests pin it: the 120-byte total in TestStructSizes, and every field of a real mainnet account.
  • Client is built around one program ID, so it gains no fetch method. DeserializeFeedDistribution is exported instead, for a caller that makes its own getProgramAccounts call. FeedProgramID names the program.
  • make sdk-test never ran ./sdk/shreds/go/..., so this package's layout pins have never run in CI. It runs them now, and the package passes.

Read a month as an allocation, not as cash that arrived in it: one subscription payment is credited across the calendar months the subscription spans, in day fractions, so an account can exist for a month that has not started. Sum every month for cash collected to date.

Diff Breakdown

Category Files Lines (+/-) Net
Tests 2 +89 / -0 +89
Core logic 2 +34 / -0 +34
Scaffolding 2 +17 / -0 +17
Config/build 1 +1 / -0 +1
Docs 1 +2 / -0 +2
Total 8 +143 / -0 +143

Additive only, and more than half of it is the layout guard.

Key files (click to expand)
  • sdk/shreds/go/state_test.go — the layout guard: a real mainnet account decoded field by field, and a v1 discriminator rejected
  • sdk/shreds/go/state.go — the FeedDistribution struct, under a --- Feed subscription program --- banner
  • sdk/shreds/go/discriminator_test.goFeedDistribution in the uniqueness map, plus a hex pin on the seed hash
  • sdk/shreds/go/client.goDeserializeFeedDistribution, the exported door onto the shared decoder
  • sdk/shreds/go/discriminator.gosha256First8("dz::account::feed_distribution::v2")
  • sdk/shreds/go/config.goFeedProgramID

Testing Verification

  • The layout guard decodes account crW8HCYDpQVyCxYG7m3hXeC42rAnjoLroGGfgGLLXM2, read from mainnet-beta on 2026-08-19, and checks every field. The three bump seeds around a zero proportion (255, 0, 253, 255) are what make a one-byte shift fail rather than merely look wrong.
  • Flipping a discriminator byte fails the decode. Every v1 account is orphaned, since v2 replaced the per-month vault with a per-feed vault, so reading one as v2 has to be an error rather than a wrong amount.
  • Ran against mainnet live: 43 program accounts, 42 FeedDistribution decoded and one ProgramConfig correctly skipped by discriminator, totalling 51,205.50 USDC collected to date. The pinned account reads 6,145.16 today against 2,080.65 on 19 Aug, which matches the field only ever increasing.

Note for reviewers

doublezero-shreds now ships generated bindings for this program at sdk/go/feedgen, and this struct describes the same bytes by hand. That repository is private, so lake and any other public consumer cannot import it: pulling it in needs GOPRIVATE, a git URL rewrite, a Dockerfile build secret, and a token that does not exist in lake today. Hence the hand-written copy.

The cost is that a program change regenerates feedgen and leaves this struct stale. The size and field pins turn that into a test failure rather than a wrong number, which is the best this arrangement can do. Making one place canonical is tracked in malbeclabs/infra#2330.

The feed subscription program is a second program alongside shred
subscription, and this SDK carried nothing for it, so every consumer
decoded the account at fixed byte offsets itself. Lake has one such copy
today.

FeedDistribution is a bytemuck Pod onchain, read here field by field,
which agrees with the Pod bytes only because the field order leaves no
interior padding. Two tests pin that: the 120-byte size in
TestStructSizes, and every field of a real mainnet account, whose three
bump seeds around a zero proportion make a one-byte shift detectable
rather than merely plausible.

Client is built around one program ID, so it gains no fetch method.
DeserializeFeedDistribution is exported instead, for a caller that brings
its own getProgramAccounts call.

sdk-test never ran ./sdk/shreds/go/..., so this package's layout pins have
never run in CI. Added, and the package passes.
@bgm-malbeclabs
bgm-malbeclabs force-pushed the sdk/shreds-feed-distribution branch from c54019a to 540df10 Compare August 20, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants