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
use serde::{Deserialize, Serialize};
use steel::*;
use crate::state::rig_pda;
use super::OilAccount;
/// Rig account (one per NFT mint, links NFT to on-chain stats)
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
pub struct Rig {
/// The authority (owner) of this rig (matches NFT owner)
pub authority: Pubkey,
/// NFT mint address (used for PDA derivation)
pub mint: Pubkey,
/// Rig type ID (0-14)
pub rig_type: u64,
/// Unique rig ID (can be NFT edition number)
pub rig_id: u64,
/// Mining power (snapshotted at mint, immutable)
pub mining_power: u64,
/// Fuel requirement for placement (snapshotted at mint, immutable)
pub fuel_requirement: u64,
/// Fuel consumption rate per block (snapshotted at mint, immutable, in atomic units with 11 decimals)
pub fuel_consumption_rate: u64,
/// Purchase price in OIL (minting cost)
pub purchase_price: u64,
/// Timestamp when rig was purchased/minted
pub purchased_at: i64,
/// Plot slot (0 = not placed, 1-5 = slot number)
pub plot_slot: u64,
/// Buffer field (for future extensions)
pub buffer_a: u64,
/// Buffer field (for future extensions)
pub buffer_b: u64,
/// Buffer field (for future extensions)
pub buffer_c: u64,
}
impl Rig {
pub fn pda(&self) -> (Pubkey, u8) {
rig_pda(self.mint)
}
/// Get rig purchase cost in atomic units (11 decimals)
/// Returns None for rig_type 0 (Basic Rig - special starter pack)
pub fn get_purchase_cost(rig_type: u64) -> Option<u64> {
use crate::consts::ONE_OIL;
// Costs in OIL (multiply by 10^11 for atomic units)
const RIG_COSTS: [Option<u64>; 15] = [
Some(11 * ONE_OIL), // 0: Basic Rig (11 OIL)
Some(21 * ONE_OIL), // 1: Small Rig
Some(42 * ONE_OIL), // 2: Sensor Rig
Some(84 * ONE_OIL), // 3: Compressor Rig
Some(168 * ONE_OIL), // 4: Medium Rig
Some(336 * ONE_OIL), // 5: Large Rig
Some(672 * ONE_OIL), // 6: Advanced Sensor Rig
Some(1_344 * ONE_OIL), // 7: Turbo Compressor Rig
Some(2_688 * ONE_OIL), // 8: Deep Rig
Some(5_376 * ONE_OIL), // 9: Mega Rig
Some(10_752 * ONE_OIL), // 10: Epic Sensor Rig
Some(21_504 * ONE_OIL), // 11: Ultra Compressor Rig
Some(43_008 * ONE_OIL), // 12: Legendary Rig
Some(86_016 * ONE_OIL), // 13: Master Rig
Some(172_032 * ONE_OIL), // 14: Ultimate Rig
];
if rig_type >= 15 {
return None;
}
RIG_COSTS[rig_type as usize]
}
/// Display name for metadata; e.g. "Basic Rig #1", "Small Rig #47"
pub fn get_rig_name(rig_type: u64, rig_id: u64) -> String {
if rig_type >= 15 {
return format!("Unknown Rig #{}", rig_id + 1);
}
let type_name = Self::rig_type_display_name(rig_type);
format!("{} #{}", type_name, rig_id + 1)
}
/// Human-readable name for rig type (for metadata display)
pub fn rig_type_display_name(rig_type: u64) -> &'static str {
const NAMES: [&str; 15] = [
"Basic Rig",
"Small Rig",
"Sensor Rig",
"Compressor Rig",
"Medium Rig",
"Large Rig",
"Advanced Sensor Rig",
"Turbo Compressor Rig",
"Deep Rig",
"Mega Rig",
"Epic Sensor Rig",
"Ultra Compressor Rig",
"Legendary Rig",
"Master Rig",
"Ultimate Rig",
];
if rig_type >= 15 {
"Unknown Rig"
} else {
NAMES[rig_type as usize]
}
}
/// Metadata URI by type (rig_1.json .. rig_15.json)
pub fn get_rig_metadata_uri(rig_type: u64) -> String {
if rig_type >= 15 {
return String::new();
}
format!(
"https://raw.githubusercontent.com/oil-protocol/token-metadata/main/metadata/rigs/rig_{}.json",
rig_type + 1
)
}
/// Model slug for display/traits (rig_1 .. rig_15). Use for UI or off-chain metadata attributes.
pub fn model_name(rig_type: u64) -> &'static str {
const MODELS: [&str; 15] = [
"rig_1", "rig_2", "rig_3", "rig_4", "rig_5", "rig_6", "rig_7", "rig_8",
"rig_9", "rig_10", "rig_11", "rig_12", "rig_13", "rig_14", "rig_15",
];
if rig_type >= 15 {
"rig_unknown"
} else {
MODELS[rig_type as usize]
}
}
pub fn initialize(
&mut self,
authority: Pubkey,
mint: Pubkey,
rig_type: u64,
rig_id: u64,
mining_power: u64,
fuel_requirement: u64,
fuel_consumption_rate: u64,
purchase_price: u64,
clock: &Clock,
) {
self.authority = authority;
self.mint = mint;
self.rig_type = rig_type;
self.rig_id = rig_id;
self.mining_power = mining_power;
self.fuel_requirement = fuel_requirement;
self.fuel_consumption_rate = fuel_consumption_rate;
self.purchase_price = purchase_price;
self.purchased_at = clock.unix_timestamp;
self.plot_slot = 0; // Not placed initially
self.buffer_a = 0;
self.buffer_b = 0;
self.buffer_c = 0;
}
/// Check if rig is staked (placed on plot)
pub fn is_staked(&self) -> bool {
self.plot_slot > 0
}
/// Place rig on plot (stake)
pub fn place(&mut self, slot: u64) {
self.plot_slot = slot;
}
/// Remove rig from plot (unstake)
pub fn remove(&mut self) {
self.plot_slot = 0;
}
}
account!(OilAccount, Rig);