/ contracts / delta / staking / src / main.adl
main.adl
  1  // Staking Contract for Delta Chain
  2  // DX token staking, delegation, and rewards
  3  //
  4  // Target: Delta (account-based model)
  5  
  6  program staking.adl;
  7  
  8  // Validator status
  9  enum ValidatorStatus {
 10      Active,
 11      Jailed,
 12      Unbonding,
 13      Inactive,
 14  }
 15  
 16  // Storage mappings
 17  mapping validators: address => Validator;
 18  mapping delegations: field => Delegation;
 19  mapping unbondings: field => Unbonding;
 20  mapping total_staked: u8 => u64;  // key 0 = total
 21  
 22  struct Validator {
 23      address: address,
 24      commission: u16,  // basis points (100 = 1%)
 25      total_stake: u64,
 26      self_stake: u64,
 27      status: ValidatorStatus,
 28      jailed_until: u64,
 29  }
 30  
 31  struct Delegation {
 32      delegator: address,
 33      validator: address,
 34      amount: u64,
 35      reward_debt: u64,
 36      delegated_at: u64,
 37  }
 38  
 39  struct Unbonding {
 40      delegator: address,
 41      validator: address,
 42      amount: u64,
 43      complete_height: u64,
 44  }
 45  
 46  // Configuration
 47  const MIN_SELF_STAKE: u64 = 10_000_000_000_000u64;  // 10k DX
 48  const MAX_COMMISSION: u16 = 2000u16;  // 20%
 49  const UNBONDING_PERIOD: u64 = 151200u64;  // ~21 days
 50  
 51  // Register as validator
 52  transition register_validator(
 53      public commission: u16,
 54      public self_stake: u64,
 55  ) {
 56      assert(commission <= MAX_COMMISSION);
 57      assert(self_stake >= MIN_SELF_STAKE);
 58  
 59      // Check not already registered
 60      let existing = validators.get_or_use(self.caller, Validator {
 61          address: self.caller,
 62          commission: 0u16,
 63          total_stake: 0u64,
 64          self_stake: 0u64,
 65          status: ValidatorStatus::Inactive,
 66          jailed_until: 0u64,
 67      });
 68      assert(existing.status == ValidatorStatus::Inactive);
 69  
 70      let validator = Validator {
 71          address: self.caller,
 72          commission: commission,
 73          total_stake: self_stake,
 74          self_stake: self_stake,
 75          status: ValidatorStatus::Active,
 76          jailed_until: 0u64,
 77      };
 78  
 79      validators.set(self.caller, validator);
 80  
 81      // Update total staked
 82      let current_total = total_staked.get_or_use(0u8, 0u64);
 83      total_staked.set(0u8, current_total + self_stake);
 84  }
 85  
 86  // Delegate to validator
 87  transition delegate(
 88      public validator_addr: address,
 89      public amount: u64,
 90  ) {
 91      // Get validator
 92      let validator = validators.get(validator_addr);
 93      assert(validator.status == ValidatorStatus::Active);
 94  
 95      // Create or update delegation
 96      let delegation_key = BHP256::hash_to_field(self.caller, validator_addr);
 97      let existing = delegations.get_or_use(delegation_key, Delegation {
 98          delegator: self.caller,
 99          validator: validator_addr,
100          amount: 0u64,
101          reward_debt: 0u64,
102          delegated_at: block.height,
103      });
104  
105      let delegation = Delegation {
106          delegator: self.caller,
107          validator: validator_addr,
108          amount: existing.amount + amount,
109          reward_debt: existing.reward_debt,
110          delegated_at: existing.delegated_at,
111      };
112  
113      delegations.set(delegation_key, delegation);
114  
115      // Update validator total
116      let updated_validator = Validator {
117          address: validator.address,
118          commission: validator.commission,
119          total_stake: validator.total_stake + amount,
120          self_stake: validator.self_stake,
121          status: validator.status,
122          jailed_until: validator.jailed_until,
123      };
124  
125      validators.set(validator_addr, updated_validator);
126  
127      // Update global total
128      let current_total = total_staked.get_or_use(0u8, 0u64);
129      total_staked.set(0u8, current_total + amount);
130  }
131  
132  // Undelegate from validator
133  transition undelegate(
134      public validator_addr: address,
135      public amount: u64,
136  ) {
137      // Get delegation
138      let delegation_key = BHP256::hash_to_field(self.caller, validator_addr);
139      let delegation = delegations.get(delegation_key);
140  
141      assert(delegation.delegator == self.caller);
142      assert(delegation.amount >= amount);
143  
144      // Update delegation
145      let updated_delegation = Delegation {
146          delegator: delegation.delegator,
147          validator: delegation.validator,
148          amount: delegation.amount - amount,
149          reward_debt: delegation.reward_debt,
150          delegated_at: delegation.delegated_at,
151      };
152  
153      delegations.set(delegation_key, updated_delegation);
154  
155      // Create unbonding entry
156      let unbonding_key = BHP256::hash_to_field(self.caller, validator_addr, block.height);
157      let unbonding = Unbonding {
158          delegator: self.caller,
159          validator: validator_addr,
160          amount: amount,
161          complete_height: block.height + UNBONDING_PERIOD,
162      };
163  
164      unbondings.set(unbonding_key, unbonding);
165  
166      // Update validator
167      let validator = validators.get(validator_addr);
168      let updated_validator = Validator {
169          address: validator.address,
170          commission: validator.commission,
171          total_stake: validator.total_stake - amount,
172          self_stake: validator.self_stake,
173          status: validator.status,
174          jailed_until: validator.jailed_until,
175      };
176  
177      validators.set(validator_addr, updated_validator);
178  
179      // Update global total
180      let current_total = total_staked.get_or_use(0u8, 0u64);
181      total_staked.set(0u8, current_total - amount);
182  }
183  
184  // Claim unbonded tokens
185  transition claim_unbonded(
186      public validator_addr: address,
187      public unbonding_height: u64,
188  ) -> u64 {
189      let unbonding_key = BHP256::hash_to_field(self.caller, validator_addr, unbonding_height);
190      let unbonding = unbondings.get(unbonding_key);
191  
192      assert(unbonding.delegator == self.caller);
193      assert(block.height >= unbonding.complete_height);
194  
195      // Remove unbonding entry
196      unbondings.remove(unbonding_key);
197  
198      return unbonding.amount;
199  }
200  
201  // Claim staking rewards
202  transition claim_rewards(
203      public validator_addr: address,
204  ) -> u64 {
205      let delegation_key = BHP256::hash_to_field(self.caller, validator_addr);
206      let delegation = delegations.get(delegation_key);
207  
208      assert(delegation.delegator == self.caller);
209  
210      // Calculate rewards (simplified)
211      // In production, this would track accumulated rewards per share
212      let rewards = 0u64;  // Placeholder
213  
214      // Update reward debt
215      let updated = Delegation {
216          delegator: delegation.delegator,
217          validator: delegation.validator,
218          amount: delegation.amount,
219          reward_debt: delegation.reward_debt + rewards,
220          delegated_at: delegation.delegated_at,
221      };
222  
223      delegations.set(delegation_key, updated);
224  
225      return rewards;
226  }