|
1 | 1 | use { |
| 2 | + chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc}, |
2 | 3 | clap::{ |
3 | 4 | crate_description, crate_name, crate_version, value_t_or_exit, App, AppSettings, Arg, |
4 | 5 | SubCommand, |
|
9 | 10 | }, |
10 | 11 | solana_client::rpc_client::RpcClient, |
11 | 12 | solana_sdk::{ |
| 13 | + clock::UnixTimestamp, |
12 | 14 | commitment_config::CommitmentConfig, |
13 | 15 | program_pack::Pack, |
14 | 16 | pubkey::Pubkey, |
15 | 17 | signature::{read_keypair_file, Keypair, Signer}, |
16 | 18 | transaction::Transaction, |
17 | 19 | }, |
18 | 20 | spl_feature_proposal::state::{AcceptanceCriteria, FeatureProposal}, |
19 | | - std::{fs::File, io::Write}, |
| 21 | + std::{ |
| 22 | + fs::File, |
| 23 | + io::Write, |
| 24 | + time::{Duration, SystemTime, UNIX_EPOCH}, |
| 25 | + }, |
20 | 26 | }; |
21 | 27 |
|
22 | 28 | struct Config { |
@@ -183,12 +189,23 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { |
183 | 189 | let distribution_file = value_t_or_exit!(arg_matches, "distribution_file", String); |
184 | 190 | let percent_stake_required = |
185 | 191 | value_t_or_exit!(arg_matches, "percent_stake_required", u8); |
| 192 | + |
| 193 | + // Hard code deadline for now... |
| 194 | + let fortnight = Duration::from_secs(60 * 60 * 24 * 14); |
| 195 | + let deadline = SystemTime::now() |
| 196 | + .duration_since(UNIX_EPOCH) |
| 197 | + .unwrap() |
| 198 | + .checked_add(fortnight) |
| 199 | + .unwrap() |
| 200 | + .as_secs() as UnixTimestamp; |
| 201 | + |
186 | 202 | process_propose( |
187 | 203 | &rpc_client, |
188 | 204 | &config, |
189 | 205 | &feature_proposal_keypair, |
190 | 206 | distribution_file, |
191 | 207 | percent_stake_required, |
| 208 | + deadline, |
192 | 209 | arg_matches.is_present("confirm"), |
193 | 210 | ) |
194 | 211 | } |
@@ -229,12 +246,25 @@ fn get_feature_proposal( |
229 | 246 | } |
230 | 247 | } |
231 | 248 |
|
| 249 | +fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String { |
| 250 | + format!( |
| 251 | + "{} (UnixTimestamp: {})", |
| 252 | + match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) { |
| 253 | + Some(ndt) => |
| 254 | + DateTime::<Utc>::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true), |
| 255 | + None => "unknown".to_string(), |
| 256 | + }, |
| 257 | + unix_timestamp, |
| 258 | + ) |
| 259 | +} |
| 260 | + |
232 | 261 | fn process_propose( |
233 | 262 | rpc_client: &RpcClient, |
234 | 263 | config: &Config, |
235 | 264 | feature_proposal_keypair: &Keypair, |
236 | 265 | distribution_file: String, |
237 | 266 | percent_stake_required: u8, |
| 267 | + deadline: UnixTimestamp, |
238 | 268 | confirm: bool, |
239 | 269 | ) -> Result<(), Box<dyn std::error::Error>> { |
240 | 270 | let distributor_token_address = |
@@ -295,7 +325,7 @@ fn process_propose( |
295 | 325 | tokens_to_mint, |
296 | 326 | AcceptanceCriteria { |
297 | 327 | tokens_required, |
298 | | - deadline: None, |
| 328 | + deadline, |
299 | 329 | }, |
300 | 330 | )], |
301 | 331 | Some(&config.keypair.pubkey()), |
@@ -347,6 +377,11 @@ fn process_propose( |
347 | 377 | println!("Tallying is permissionless and may be run by anybody."); |
348 | 378 | println!("Once this feature proposal is accepted, the {} feature will be activated at the next epoch.", feature_id_address); |
349 | 379 |
|
| 380 | + println!(); |
| 381 | + println!( |
| 382 | + "Proposal will expire at {}", |
| 383 | + unix_timestamp_to_string(deadline) |
| 384 | + ); |
350 | 385 | println!(); |
351 | 386 | if !confirm { |
352 | 387 | println!("Add --confirm flag to initiate the feature proposal"); |
@@ -396,19 +431,22 @@ fn process_tally( |
396 | 431 | "{} tokens have been received", |
397 | 432 | spl_feature_proposal::amount_to_ui_amount(acceptance_token_balance) |
398 | 433 | ); |
| 434 | + println!( |
| 435 | + "Proposal will expire at {}", |
| 436 | + unix_timestamp_to_string(acceptance_criteria.deadline) |
| 437 | + ); |
399 | 438 | println!(); |
400 | 439 |
|
401 | | - match acceptance_criteria.deadline { |
402 | | - None => { |
403 | | - // Don't bother issuing a transaction if it's clear the Tally won't succeed |
404 | | - if acceptance_token_balance < acceptance_criteria.tokens_required { |
405 | | - println!("Feature proposal pending"); |
406 | | - return Ok(()); |
407 | | - } |
408 | | - } |
409 | | - Some(deadline) => { |
410 | | - println!("Deadline: {}", deadline); // TODO: format deadline nicely |
411 | | - } |
| 440 | + // Don't bother issuing a transaction if it's clear the Tally won't succeed |
| 441 | + if acceptance_token_balance < acceptance_criteria.tokens_required |
| 442 | + && (SystemTime::now() |
| 443 | + .duration_since(UNIX_EPOCH) |
| 444 | + .unwrap() |
| 445 | + .as_secs() as UnixTimestamp) |
| 446 | + < acceptance_criteria.deadline |
| 447 | + { |
| 448 | + println!("Feature proposal pending"); |
| 449 | + return Ok(()); |
412 | 450 | } |
413 | 451 | } |
414 | 452 | FeatureProposal::Accepted { .. } => { |
|
0 commit comments