1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! This code was AUTOGENERATED using the codama library.
//! Please DO NOT EDIT THIS FILE, instead use visitors
//! to add features, then rerun codama to update it.
//!
//! <https://github.com/codama-idl/codama>
//!
use crate::generated::types::RoundState;
use crate::generated::types::TileStake;
use borsh::BorshSerialize;
use borsh::BorshDeserialize;
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
pub struct Round {
pub discriminator: [u8; 8],
/// Struct version.
pub version: u16,
/// PDA bump seed.
pub bump: u8,
/// Sequential round identifier.
pub id: u32,
/// Round state
pub state: RoundState,
/// Raw blockhash entropy, read from the SlotHashes sysvar at reveal.
pub blockhash_entropy: [u8; 32],
/// Winning tile index, revealed upon settlement
pub winning_tile: Option<u8>,
/// Total net USD deployed into the round before the USD->BTC swap
pub deployed_pending_usd_amount: u64,
/// Total net USD stake after USD->BTC swap
pub deployed_usd_amount: u64,
/// Total net BTC stake after USD->BTC swap
pub deployed_btc_amount: u64,
/// Total net USD placed on the winning tile
pub deployed_usd_on_winning_tile_amount: u64,
/// Total amount of players in the round
pub miners_count: u32,
/// Number of miners whose selection is revealed.
pub revealed_miners_count: u32,
/// Total amount of winners, revealed during settlement
pub winners_count: u32,
/// Total amount of miners who claimed their winnings/hashrate, should equal to miners_count
pub settled_miners_count: u32,
/// Strike jackpot USD rolled into this round on trigger, distributed to winners.
pub strike_bonus_usd: u64,
/// Strike jackpot BTC rolled into this round on trigger, distributed to winners.
pub strike_bonus_btc: u64,
/// Per-tile deploy count and stake sum, accumulated as public miners deploy.
/// Indexed by tile (0..TILES_COUNT). Read at reveal to derive the winning
/// tile's totals without a per-deployment settlement pass.
pub public_tile_stakes: [TileStake; 21],
/// Tokens the mint program issued for this round at rotation (the CPI's
/// destination balance delta). Settlement recomputes the winners' and
/// losers' legs from it with the same constants. Carved out of the
/// zeroed `reserved_entropy` pad, so pre-existing rounds read 0.
pub minted_token_amount: u64,
/// Strike jackpot tokens rolled into this round on trigger, distributed
/// to winners alongside `strike_bonus_btc`. Carved out of the pad too.
pub strike_bonus_token: u64,
/// What is left of the pad the per-deploy entropy accumulator used to
/// occupy; zero on every round created since the RNG integration.
pub reserved_entropy: [u8; 16],
/// Slot at which the round reached `Settled` state.
pub settled_at_slot: u64,
/// Epoch Vault fee accrued by this round's deploys, in USD base units.
/// Physically sits in the board's USD pool until swept at rotation.
pub pending_epoch_fee_usd_amount: u64,
/// 1 BTC Vault fee accrued by this round's deploys; same lifecycle.
pub pending_one_btc_fee_usd_amount: u64,
/// Protocol (treasury) fee accrued by this round's deploys; same lifecycle.
pub pending_protocol_fee_usd_amount: u64,
/// Whether deployments settled in this round earn the post-strike
/// hashrate boost (`STRIKE_BOOST_HASHRATE_MULTIPLIER`). Stamped once at
/// round creation in `rotate_round` from the board's strike window;
/// immutable afterwards, so settle rewards are deterministic even if a
/// later strike re-bases the window.
pub is_hashrate_boosted: bool,
/// Affiliate share of the protocol fee accrued this round, moved out of
/// `pending_protocol_fee_usd_amount` per deploy (`record_affiliate_deploy`)
/// — exact USD base units, valued at accrual. Carved out of `reserved`, so
/// pre-existing accounts read 0. Swept into the treasury's unclaimed
/// affiliate balance at rotation.
pub pending_affiliate_fee_usd_amount: u64,
/// Buybacks fee accrued by this round's deploys; same lifecycle as the
/// other fee legs. Carved out of `reserved`, so pre-existing accounts
/// read 0. Swept into the treasury's buybacks balance at rotation.
pub pending_buybacks_fee_usd_amount: u64,
/// Gross USD the round's payers parted with, before any fee leg — the
/// exact sum of every deploy's `amount`. A pure record: never drained by
/// the sweep or rewritten by the swap, so it stays readable at every
/// lifecycle stage. Not recoverable from the other counters (each fee
/// leg rounds up per deploy, and the strike leg lives on the board).
/// Carved out of `reserved`, so pre-existing accounts read 0.
pub deployed_gross_usd_amount: u64,
/// Reserved.
pub reserved: [u8; 15],
}
pub const ROUND_DISCRIMINATOR: [u8; 8] = [87, 127, 165, 51, 73, 78, 116, 174];
impl Round {
#[inline(always)]
pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
let mut data = data;
Self::deserialize(&mut data)
}
}
impl<'a> TryFrom<&solana_account_info::AccountInfo<'a>> for Round {
type Error = std::io::Error;
fn try_from(account_info: &solana_account_info::AccountInfo<'a>) -> Result<Self, Self::Error> {
let mut data: &[u8] = &(*account_info.data).borrow();
Self::deserialize(&mut data)
}
}
#[cfg(feature = "fetch")]
pub fn fetch_round(
rpc: &solana_rpc_client::rpc_client::RpcClient,
address: &solana_address::Address,
) -> Result<crate::shared::DecodedAccount<Round>, std::io::Error> {
let accounts = fetch_all_round(rpc, &[*address])?;
Ok(accounts[0].clone())
}
#[cfg(feature = "fetch")]
pub fn fetch_all_round(
rpc: &solana_rpc_client::rpc_client::RpcClient,
addresses: &[solana_address::Address],
) -> Result<Vec<crate::shared::DecodedAccount<Round>>, std::io::Error> {
let accounts = rpc.get_multiple_accounts(addresses)
.map_err(|e| std::io::Error::other(e.to_string()))?;
let mut decoded_accounts: Vec<crate::shared::DecodedAccount<Round>> = Vec::new();
for i in 0..addresses.len() {
let address = addresses[i];
let account = accounts[i].as_ref()
.ok_or(std::io::Error::other(format!("Account not found: {address}")))?;
let data = Round::from_bytes(&account.data)?;
decoded_accounts.push(crate::shared::DecodedAccount { address, account: account.clone(), data });
}
Ok(decoded_accounts)
}
#[cfg(feature = "fetch")]
pub fn fetch_maybe_round(
rpc: &solana_rpc_client::rpc_client::RpcClient,
address: &solana_address::Address,
) -> Result<crate::shared::MaybeAccount<Round>, std::io::Error> {
let accounts = fetch_all_maybe_round(rpc, &[*address])?;
Ok(accounts[0].clone())
}
#[cfg(feature = "fetch")]
pub fn fetch_all_maybe_round(
rpc: &solana_rpc_client::rpc_client::RpcClient,
addresses: &[solana_address::Address],
) -> Result<Vec<crate::shared::MaybeAccount<Round>>, std::io::Error> {
let accounts = rpc.get_multiple_accounts(addresses)
.map_err(|e| std::io::Error::other(e.to_string()))?;
let mut decoded_accounts: Vec<crate::shared::MaybeAccount<Round>> = Vec::new();
for i in 0..addresses.len() {
let address = addresses[i];
if let Some(account) = accounts[i].as_ref() {
let data = Round::from_bytes(&account.data)?;
decoded_accounts.push(crate::shared::MaybeAccount::Exists(crate::shared::DecodedAccount { address, account: account.clone(), data }));
} else {
decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
}
}
Ok(decoded_accounts)
}
#[cfg(feature = "anchor")]
impl anchor_lang::AccountDeserialize for Round {
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
Ok(Self::deserialize(buf)?)
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::AccountSerialize for Round {}
#[cfg(feature = "anchor")]
impl anchor_lang::Owner for Round {
fn owner() -> anchor_lang::solana_program::pubkey::Pubkey {
anchor_lang::solana_program::pubkey::Pubkey::from(
crate::SATRUSH_ID.to_bytes()
)
}
}
#[cfg(feature = "anchor-idl-build")]
impl anchor_lang::IdlBuild for Round {}
#[cfg(feature = "anchor-idl-build")]
impl anchor_lang::Discriminator for Round {
const DISCRIMINATOR: &[u8] = &[0; 8];
}