main.rs 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. io::{stdin, Read},
  20. process::exit,
  21. str::FromStr,
  22. sync::Arc,
  23. };
  24. use prettytable::{format, row, Table};
  25. use rand::rngs::OsRng;
  26. use smol::{fs::read_to_string, stream::StreamExt};
  27. use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
  28. use url::Url;
  29. use darkfi::{
  30. async_daemonize, cli_desc,
  31. util::{
  32. encoding::base64,
  33. parse::{decode_base10, encode_base10},
  34. path::{expand_path, get_config_path},
  35. },
  36. zk::halo2::Field,
  37. Error, Result,
  38. };
  39. use darkfi_dao_contract::{blockwindow, model::DaoProposalBulla, DaoFunction};
  40. use darkfi_money_contract::model::{Coin, CoinAttributes, TokenId};
  41. use darkfi_sdk::{
  42. crypto::{
  43. note::AeadEncryptedNote, BaseBlind, FuncId, FuncRef, PublicKey, SecretKey, DAO_CONTRACT_ID,
  44. },
  45. pasta::{group::ff::PrimeField, pallas},
  46. tx::TransactionHash,
  47. };
  48. use darkfi_serial::{deserialize_async, serialize_async};
  49. use drk::{
  50. cli_util::{
  51. generate_completions, kaching, parse_token_pair, parse_tx_from_stdin, parse_value_pair,
  52. },
  53. dao::{DaoParams, ProposalRecord},
  54. money::BALANCE_BASE10_DECIMALS,
  55. swap::PartialSwapData,
  56. Drk,
  57. };
  58. const CONFIG_FILE: &str = "drk_config.toml";
  59. const CONFIG_FILE_CONTENTS: &str = include_str!("../drk_config.toml");
  60. // Dev Note: when adding/modifying args here,
  61. // don't forget to update cli_util::generate_completions()
  62. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  63. #[serde(default)]
  64. #[structopt(name = "drk", about = cli_desc!())]
  65. struct Args {
  66. #[structopt(short, long)]
  67. /// Configuration file to use
  68. config: Option<String>,
  69. #[structopt(short, long, default_value = "testnet")]
  70. /// Blockchain network to use
  71. network: String,
  72. #[structopt(subcommand)]
  73. /// Sub command to execute
  74. command: Subcmd,
  75. #[structopt(short, long)]
  76. /// Flag indicating whether you want some fun in your life
  77. fun: bool,
  78. #[structopt(short, long)]
  79. /// Set log file to ouput into
  80. log: Option<String>,
  81. #[structopt(short, parse(from_occurrences))]
  82. /// Increase verbosity (-vvv supported)
  83. verbose: u8,
  84. }
  85. // Dev Note: when adding/modifying commands here,
  86. // don't forget to update cli_util::generate_completions()
  87. #[derive(Clone, Debug, Deserialize, StructOpt)]
  88. enum Subcmd {
  89. /// Fun
  90. Kaching,
  91. /// Send a ping request to the darkfid RPC endpoint
  92. Ping,
  93. /// Generate a SHELL completion script and print to stdout
  94. Completions {
  95. /// The Shell you want to generate script for
  96. shell: String,
  97. },
  98. /// Wallet operations
  99. Wallet {
  100. #[structopt(long)]
  101. /// Initialize wallet database
  102. initialize: bool,
  103. #[structopt(long)]
  104. /// Generate a new keypair in the wallet
  105. keygen: bool,
  106. #[structopt(long)]
  107. /// Query the wallet for known balances
  108. balance: bool,
  109. #[structopt(long)]
  110. /// Get the default address in the wallet
  111. address: bool,
  112. #[structopt(long)]
  113. /// Print all the addresses in the wallet
  114. addresses: bool,
  115. #[structopt(long)]
  116. /// Set the default address in the wallet
  117. default_address: Option<usize>,
  118. #[structopt(long)]
  119. /// Print all the secret keys from the wallet
  120. secrets: bool,
  121. #[structopt(long)]
  122. /// Import secret keys from stdin into the wallet, separated by newlines
  123. import_secrets: bool,
  124. #[structopt(long)]
  125. /// Print the Merkle tree in the wallet
  126. tree: bool,
  127. #[structopt(long)]
  128. /// Print all the coins in the wallet
  129. coins: bool,
  130. },
  131. /// Read a transaction from stdin and mark its input coins as spent
  132. Spend,
  133. /// Unspend a coin
  134. Unspend {
  135. /// base58-encoded coin to mark as unspent
  136. coin: String,
  137. },
  138. /// Create a payment transaction
  139. Transfer {
  140. /// Amount to send
  141. amount: String,
  142. /// Token ID to send
  143. token: String,
  144. /// Recipient address
  145. recipient: String,
  146. /// Optional contract spend hook to use
  147. spend_hook: Option<String>,
  148. /// Optional user data to use
  149. user_data: Option<String>,
  150. #[structopt(long)]
  151. /// Split the output coin into two equal halves
  152. half_split: bool,
  153. },
  154. /// OTC atomic swap
  155. Otc {
  156. #[structopt(subcommand)]
  157. /// Sub command to execute
  158. command: OtcSubcmd,
  159. },
  160. /// Attach the fee call to a transaction given from stdin
  161. AttachFee,
  162. /// Inspect a transaction from stdin
  163. Inspect,
  164. /// Read a transaction from stdin and broadcast it
  165. Broadcast,
  166. /// This subscription will listen for incoming blocks from darkfid and look
  167. /// through their transactions to see if there's any that interest us.
  168. /// With `drk` we look at transactions calling the money contract so we can
  169. /// find coins sent to us and fill our wallet with the necessary metadata.
  170. Subscribe,
  171. /// DAO functionalities
  172. Dao {
  173. #[structopt(subcommand)]
  174. /// Sub command to execute
  175. command: DaoSubcmd,
  176. },
  177. /// Scan the blockchain and parse relevant transactions
  178. Scan {
  179. #[structopt(long)]
  180. /// Reset Merkle tree and start scanning from first block
  181. reset: bool,
  182. },
  183. /// Explorer related subcommands
  184. Explorer {
  185. #[structopt(subcommand)]
  186. /// Sub command to execute
  187. command: ExplorerSubcmd,
  188. },
  189. /// Manage Token aliases
  190. Alias {
  191. #[structopt(subcommand)]
  192. /// Sub command to execute
  193. command: AliasSubcmd,
  194. },
  195. /// Token functionalities
  196. Token {
  197. #[structopt(subcommand)]
  198. /// Sub command to execute
  199. command: TokenSubcmd,
  200. },
  201. /// Contract functionalities
  202. Contract {
  203. #[structopt(subcommand)]
  204. /// Sub command to execute
  205. command: ContractSubcmd,
  206. },
  207. }
  208. #[derive(Clone, Debug, Deserialize, StructOpt)]
  209. enum OtcSubcmd {
  210. /// Initialize the first half of the atomic swap
  211. Init {
  212. /// Value pair to send:recv (11.55:99.42)
  213. #[structopt(short, long)]
  214. value_pair: String,
  215. /// Token pair to send:recv (f00:b4r)
  216. #[structopt(short, long)]
  217. token_pair: String,
  218. },
  219. /// Build entire swap tx given the first half from stdin
  220. Join,
  221. /// Inspect a swap half or the full swap tx from stdin
  222. Inspect,
  223. /// Sign a swap transaction given from stdin
  224. Sign,
  225. }
  226. #[derive(Clone, Debug, Deserialize, StructOpt)]
  227. enum DaoSubcmd {
  228. /// Create DAO parameters
  229. Create {
  230. /// The minimum amount of governance tokens needed to open a proposal for this DAO
  231. proposer_limit: String,
  232. /// Minimal threshold of participating total tokens needed for a proposal to pass
  233. quorum: String,
  234. /// The ratio of winning votes/total votes needed for a proposal to pass (2 decimals)
  235. approval_ratio: f64,
  236. /// DAO's governance token ID
  237. gov_token_id: String,
  238. },
  239. /// View DAO data from stdin
  240. View,
  241. /// Import DAO data from stdin
  242. Import {
  243. /// Name identifier for the DAO
  244. name: String,
  245. },
  246. /// List imported DAOs (or info about a specific one)
  247. List {
  248. /// Name identifier for the DAO (optional)
  249. name: Option<String>,
  250. },
  251. /// Show the balance of a DAO
  252. Balance {
  253. /// Name identifier for the DAO
  254. name: String,
  255. },
  256. /// Mint an imported DAO on-chain
  257. Mint {
  258. /// Name identifier for the DAO
  259. name: String,
  260. },
  261. /// Create a transfer proposal for a DAO
  262. ProposeTransfer {
  263. /// Name identifier for the DAO
  264. name: String,
  265. /// Duration of the proposal, in block windows
  266. duration: u64,
  267. /// Amount to send
  268. amount: String,
  269. /// Token ID to send
  270. token: String,
  271. /// Recipient address
  272. recipient: String,
  273. /// Optional contract spend hook to use
  274. spend_hook: Option<String>,
  275. /// Optional user data to use
  276. user_data: Option<String>,
  277. },
  278. /// List DAO proposals
  279. Proposals {
  280. /// Name identifier for the DAO
  281. name: String,
  282. },
  283. /// View a DAO proposal data
  284. Proposal {
  285. /// Bulla identifier for the proposal
  286. bulla: String,
  287. #[structopt(long)]
  288. /// Encrypt the proposal and encode it to base64
  289. export: bool,
  290. #[structopt(long)]
  291. /// Create the proposal transaction
  292. mint_proposal: bool,
  293. },
  294. /// Import a base64 encoded and encrypted proposal from stdin
  295. ProposalImport,
  296. /// Vote on a given proposal
  297. Vote {
  298. /// Bulla identifier for the proposal
  299. bulla: String,
  300. /// Vote (0 for NO, 1 for YES)
  301. vote: u8,
  302. /// Optional vote weight (amount of governance tokens)
  303. vote_weight: Option<String>,
  304. },
  305. /// Execute a DAO proposal
  306. Exec {
  307. /// Bulla identifier for the proposal
  308. bulla: String,
  309. },
  310. /// Print the DAO contract base58-encoded spend hook
  311. SpendHook,
  312. }
  313. #[derive(Clone, Debug, Deserialize, StructOpt)]
  314. enum ExplorerSubcmd {
  315. /// Fetch a blockchain transaction by hash
  316. FetchTx {
  317. /// Transaction hash
  318. tx_hash: String,
  319. #[structopt(long)]
  320. /// Print the full transaction information
  321. full: bool,
  322. #[structopt(long)]
  323. /// Encode transaction to base58
  324. encode: bool,
  325. },
  326. /// Read a transaction from stdin and simulate it
  327. SimulateTx,
  328. /// Fetch broadcasted transactions history
  329. TxsHistory {
  330. /// Fetch specific history record (optional)
  331. tx_hash: Option<String>,
  332. #[structopt(long)]
  333. /// Encode specific history record transaction to base58
  334. encode: bool,
  335. },
  336. }
  337. #[derive(Clone, Debug, Deserialize, StructOpt)]
  338. enum AliasSubcmd {
  339. /// Create a Token alias
  340. Add {
  341. /// Token alias
  342. alias: String,
  343. /// Token to create alias for
  344. token: String,
  345. },
  346. /// Print alias info of optional arguments.
  347. /// If no argument is provided, list all the aliases in the wallet.
  348. Show {
  349. /// Token alias to search for
  350. #[structopt(short, long)]
  351. alias: Option<String>,
  352. /// Token to search alias for
  353. #[structopt(short, long)]
  354. token: Option<String>,
  355. },
  356. /// Remove a Token alias
  357. Remove {
  358. /// Token alias to remove
  359. alias: String,
  360. },
  361. }
  362. #[derive(Clone, Debug, Deserialize, StructOpt)]
  363. enum TokenSubcmd {
  364. /// Import a mint authority
  365. Import {
  366. /// Mint authority secret key
  367. secret_key: String,
  368. /// Mint authority token blind
  369. token_blind: String,
  370. },
  371. /// Generate a new mint authority
  372. GenerateMint,
  373. /// List token IDs with available mint authorities
  374. List,
  375. /// Mint tokens
  376. Mint {
  377. /// Token ID to mint
  378. token: String,
  379. /// Amount to mint
  380. amount: String,
  381. /// Recipient of the minted tokens
  382. recipient: String,
  383. /// Optional contract spend hook to use
  384. spend_hook: Option<String>,
  385. /// Optional user data to use
  386. user_data: Option<String>,
  387. },
  388. /// Freeze a token mint
  389. Freeze {
  390. /// Token ID to freeze
  391. token: String,
  392. },
  393. }
  394. #[derive(Clone, Debug, Deserialize, StructOpt)]
  395. enum ContractSubcmd {
  396. /// Generate a new deploy authority
  397. GenerateDeploy,
  398. /// List deploy authorities in the wallet
  399. List,
  400. /// Deploy a smart contract
  401. Deploy {
  402. /// Contract ID (deploy authority)
  403. deploy_auth: u64,
  404. /// Path to contract wasm bincode
  405. wasm_path: String,
  406. /// Path to serialized deploy instruction
  407. deploy_ix: String,
  408. },
  409. /// Lock a smart contract
  410. Lock {
  411. /// Contract ID (deploy authority)
  412. deploy_auth: u64,
  413. },
  414. }
  415. /// Defines a blockchain network configuration.
  416. /// Default values correspond to a local network.
  417. #[derive(Clone, Debug, serde::Deserialize, structopt::StructOpt, structopt_toml::StructOptToml)]
  418. #[structopt()]
  419. struct BlockchainNetwork {
  420. #[structopt(long, default_value = "~/.local/darkfi/drk/localnet/wallet.db")]
  421. /// Path to wallet database
  422. wallet_path: String,
  423. #[structopt(long, default_value = "changeme")]
  424. /// Password for the wallet database
  425. wallet_pass: String,
  426. #[structopt(short, long, default_value = "tcp://127.0.0.1:8240")]
  427. /// darkfid JSON-RPC endpoint
  428. endpoint: Url,
  429. }
  430. /// Auxiliary function to parse darkfid configuration file and extract requested
  431. /// blockchain network config.
  432. async fn parse_blockchain_config(
  433. config: Option<String>,
  434. network: &str,
  435. ) -> Result<BlockchainNetwork> {
  436. // Grab config path
  437. let config_path = get_config_path(config, CONFIG_FILE)?;
  438. // Parse TOML file contents
  439. let contents = read_to_string(&config_path).await?;
  440. let contents: toml::Value = match toml::from_str(&contents) {
  441. Ok(v) => v,
  442. Err(e) => {
  443. eprintln!("Failed parsing TOML config: {e}");
  444. return Err(Error::ParseFailed("Failed parsing TOML config"))
  445. }
  446. };
  447. // Grab requested network config
  448. let Some(table) = contents.as_table() else { return Err(Error::ParseFailed("TOML not a map")) };
  449. let Some(network_configs) = table.get("network_config") else {
  450. return Err(Error::ParseFailed("TOML does not contain network configurations"))
  451. };
  452. let Some(network_configs) = network_configs.as_table() else {
  453. return Err(Error::ParseFailed("`network_config` not a map"))
  454. };
  455. let Some(network_config) = network_configs.get(network) else {
  456. return Err(Error::ParseFailed("TOML does not contain requested network configuration"))
  457. };
  458. let network_config = toml::to_string(&network_config).unwrap();
  459. let network_config =
  460. match BlockchainNetwork::from_iter_with_toml::<Vec<String>>(&network_config, vec![]) {
  461. Ok(v) => v,
  462. Err(e) => {
  463. eprintln!("Failed parsing requested network configuration: {e}");
  464. return Err(Error::ParseFailed("Failed parsing requested network configuration"))
  465. }
  466. };
  467. Ok(network_config)
  468. }
  469. async_daemonize!(realmain);
  470. async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
  471. // Grab blockchain network configuration
  472. let blockchain_config = match args.network.as_str() {
  473. "localnet" => parse_blockchain_config(args.config, "localnet").await?,
  474. "testnet" => parse_blockchain_config(args.config, "testnet").await?,
  475. "mainnet" => parse_blockchain_config(args.config, "mainnet").await?,
  476. _ => {
  477. eprintln!("Unsupported chain `{}`", args.network);
  478. return Err(Error::UnsupportedChain)
  479. }
  480. };
  481. match args.command {
  482. Subcmd::Kaching => {
  483. if !args.fun {
  484. println!("Apparently you don't like fun...");
  485. return Ok(())
  486. }
  487. kaching().await;
  488. Ok(())
  489. }
  490. Subcmd::Ping => {
  491. let drk = Drk::new(
  492. blockchain_config.wallet_path,
  493. blockchain_config.wallet_pass,
  494. Some(blockchain_config.endpoint),
  495. ex,
  496. args.fun,
  497. )
  498. .await?;
  499. drk.ping().await?;
  500. drk.stop_rpc_client().await
  501. }
  502. Subcmd::Completions { shell } => generate_completions(&shell),
  503. Subcmd::Wallet {
  504. initialize,
  505. keygen,
  506. balance,
  507. address,
  508. addresses,
  509. default_address,
  510. secrets,
  511. import_secrets,
  512. tree,
  513. coins,
  514. } => {
  515. if !initialize &&
  516. !keygen &&
  517. !balance &&
  518. !address &&
  519. !addresses &&
  520. default_address.is_none() &&
  521. !secrets &&
  522. !tree &&
  523. !coins &&
  524. !import_secrets
  525. {
  526. eprintln!("Error: You must use at least one flag for this subcommand");
  527. eprintln!("Run with \"wallet -h\" to see the subcommand usage.");
  528. exit(2);
  529. }
  530. let drk = Drk::new(
  531. blockchain_config.wallet_path,
  532. blockchain_config.wallet_pass,
  533. None,
  534. ex,
  535. args.fun,
  536. )
  537. .await?;
  538. if initialize {
  539. drk.initialize_wallet()?;
  540. if let Err(e) = drk.initialize_money().await {
  541. eprintln!("Failed to initialize Money: {e:?}");
  542. exit(2);
  543. }
  544. if let Err(e) = drk.initialize_dao().await {
  545. eprintln!("Failed to initialize DAO: {e:?}");
  546. exit(2);
  547. }
  548. if let Err(e) = drk.initialize_deployooor() {
  549. eprintln!("Failed to initialize Deployooor: {e:?}");
  550. exit(2);
  551. }
  552. return Ok(())
  553. }
  554. if keygen {
  555. if let Err(e) = drk.money_keygen().await {
  556. eprintln!("Failed to generate keypair: {e:?}");
  557. exit(2);
  558. }
  559. return Ok(())
  560. }
  561. if balance {
  562. let balmap = drk.money_balance().await?;
  563. let aliases_map = drk.get_aliases_mapped_by_token().await?;
  564. // Create a prettytable with the new data:
  565. let mut table = Table::new();
  566. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  567. table.set_titles(row!["Token ID", "Aliases", "Balance"]);
  568. for (token_id, balance) in balmap.iter() {
  569. let aliases = match aliases_map.get(token_id) {
  570. Some(a) => a,
  571. None => "-",
  572. };
  573. table.add_row(row![
  574. token_id,
  575. aliases,
  576. encode_base10(*balance, BALANCE_BASE10_DECIMALS)
  577. ]);
  578. }
  579. if table.is_empty() {
  580. println!("No unspent balances found");
  581. } else {
  582. println!("{table}");
  583. }
  584. return Ok(())
  585. }
  586. if address {
  587. let address = match drk.default_address().await {
  588. Ok(a) => a,
  589. Err(e) => {
  590. eprintln!("Failed to fetch default address: {e:?}");
  591. exit(2);
  592. }
  593. };
  594. println!("{address}");
  595. return Ok(())
  596. }
  597. if addresses {
  598. let addresses = drk.addresses().await?;
  599. // Create a prettytable with the new data:
  600. let mut table = Table::new();
  601. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  602. table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
  603. for (key_id, public_key, secret_key, is_default) in addresses {
  604. let is_default = match is_default {
  605. 1 => "*",
  606. _ => "",
  607. };
  608. table.add_row(row![key_id, public_key, secret_key, is_default]);
  609. }
  610. if table.is_empty() {
  611. println!("No addresses found");
  612. } else {
  613. println!("{table}");
  614. }
  615. return Ok(())
  616. }
  617. if let Some(idx) = default_address {
  618. if let Err(e) = drk.set_default_address(idx) {
  619. eprintln!("Failed to set default address: {e:?}");
  620. exit(2);
  621. }
  622. return Ok(())
  623. }
  624. if secrets {
  625. let v = drk.get_money_secrets().await?;
  626. for i in v {
  627. println!("{i}");
  628. }
  629. return Ok(())
  630. }
  631. if import_secrets {
  632. let mut secrets = vec![];
  633. let lines = stdin().lines();
  634. for (i, line) in lines.enumerate() {
  635. if let Ok(line) = line {
  636. let bytes = bs58::decode(&line.trim()).into_vec()?;
  637. let Ok(secret) = deserialize_async(&bytes).await else {
  638. println!("Warning: Failed to deserialize secret on line {i}");
  639. continue
  640. };
  641. secrets.push(secret);
  642. }
  643. }
  644. let pubkeys = match drk.import_money_secrets(secrets).await {
  645. Ok(p) => p,
  646. Err(e) => {
  647. eprintln!("Failed to import secret keys into wallet: {e:?}");
  648. exit(2);
  649. }
  650. };
  651. for key in pubkeys {
  652. println!("{key}");
  653. }
  654. return Ok(())
  655. }
  656. if tree {
  657. let tree = drk.get_money_tree().await?;
  658. println!("{tree:#?}");
  659. return Ok(())
  660. }
  661. if coins {
  662. let coins = drk.get_coins(true).await?;
  663. let aliases_map = drk.get_aliases_mapped_by_token().await?;
  664. if coins.is_empty() {
  665. return Ok(())
  666. }
  667. let mut table = Table::new();
  668. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  669. table.set_titles(row![
  670. "Coin",
  671. "Spent",
  672. "Token ID",
  673. "Aliases",
  674. "Value",
  675. "Spend Hook",
  676. "User Data",
  677. "Spent TX"
  678. ]);
  679. for coin in coins {
  680. let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
  681. Some(a) => a,
  682. None => "-",
  683. };
  684. let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
  685. format!("{}", coin.0.note.spend_hook)
  686. } else {
  687. String::from("-")
  688. };
  689. let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
  690. bs58::encode(&serialize_async(&coin.0.note.user_data).await)
  691. .into_string()
  692. .to_string()
  693. } else {
  694. String::from("-")
  695. };
  696. table.add_row(row![
  697. bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
  698. .into_string()
  699. .to_string(),
  700. coin.1,
  701. coin.0.note.token_id,
  702. aliases,
  703. format!(
  704. "{} ({})",
  705. coin.0.note.value,
  706. encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
  707. ),
  708. spend_hook,
  709. user_data,
  710. coin.2
  711. ]);
  712. }
  713. println!("{table}");
  714. return Ok(())
  715. }
  716. unreachable!()
  717. }
  718. Subcmd::Spend => {
  719. let tx = parse_tx_from_stdin().await?;
  720. let drk = Drk::new(
  721. blockchain_config.wallet_path,
  722. blockchain_config.wallet_pass,
  723. None,
  724. ex,
  725. args.fun,
  726. )
  727. .await?;
  728. if let Err(e) = drk.mark_tx_spend(&tx).await {
  729. eprintln!("Failed to mark transaction coins as spent: {e:?}");
  730. exit(2);
  731. };
  732. Ok(())
  733. }
  734. Subcmd::Unspend { coin } => {
  735. let bytes: [u8; 32] = match bs58::decode(&coin).into_vec()?.try_into() {
  736. Ok(b) => b,
  737. Err(e) => {
  738. eprintln!("Invalid coin: {e:?}");
  739. exit(2);
  740. }
  741. };
  742. let elem: pallas::Base = match pallas::Base::from_repr(bytes).into() {
  743. Some(v) => v,
  744. None => {
  745. eprintln!("Invalid coin");
  746. exit(2);
  747. }
  748. };
  749. let coin = Coin::from(elem);
  750. let drk = Drk::new(
  751. blockchain_config.wallet_path,
  752. blockchain_config.wallet_pass,
  753. None,
  754. ex,
  755. args.fun,
  756. )
  757. .await?;
  758. if let Err(e) = drk.unspend_coin(&coin).await {
  759. eprintln!("Failed to mark coin as unspent: {e:?}");
  760. exit(2);
  761. }
  762. Ok(())
  763. }
  764. Subcmd::Transfer { amount, token, recipient, spend_hook, user_data, half_split } => {
  765. let drk = Drk::new(
  766. blockchain_config.wallet_path,
  767. blockchain_config.wallet_pass,
  768. Some(blockchain_config.endpoint),
  769. ex,
  770. args.fun,
  771. )
  772. .await?;
  773. if let Err(e) = f64::from_str(&amount) {
  774. eprintln!("Invalid amount: {e:?}");
  775. exit(2);
  776. }
  777. let rcpt = match PublicKey::from_str(&recipient) {
  778. Ok(r) => r,
  779. Err(e) => {
  780. eprintln!("Invalid recipient: {e:?}");
  781. exit(2);
  782. }
  783. };
  784. let token_id = match drk.get_token(token).await {
  785. Ok(t) => t,
  786. Err(e) => {
  787. eprintln!("Invalid token alias: {e:?}");
  788. exit(2);
  789. }
  790. };
  791. let spend_hook = match spend_hook {
  792. Some(s) => match FuncId::from_str(&s) {
  793. Ok(s) => Some(s),
  794. Err(e) => {
  795. eprintln!("Invalid spend hook: {e:?}");
  796. exit(2);
  797. }
  798. },
  799. None => None,
  800. };
  801. let user_data = match user_data {
  802. Some(u) => {
  803. let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
  804. Ok(b) => b,
  805. Err(e) => {
  806. eprintln!("Invalid user data: {e:?}");
  807. exit(2);
  808. }
  809. };
  810. match pallas::Base::from_repr(bytes).into() {
  811. Some(v) => Some(v),
  812. None => {
  813. eprintln!("Invalid user data");
  814. exit(2);
  815. }
  816. }
  817. }
  818. None => None,
  819. };
  820. let tx = match drk
  821. .transfer(&amount, token_id, rcpt, spend_hook, user_data, half_split)
  822. .await
  823. {
  824. Ok(t) => t,
  825. Err(e) => {
  826. eprintln!("Failed to create payment transaction: {e:?}");
  827. exit(2);
  828. }
  829. };
  830. println!("{}", base64::encode(&serialize_async(&tx).await));
  831. drk.stop_rpc_client().await
  832. }
  833. Subcmd::Otc { command } => match command {
  834. OtcSubcmd::Init { value_pair, token_pair } => {
  835. let drk = Drk::new(
  836. blockchain_config.wallet_path,
  837. blockchain_config.wallet_pass,
  838. Some(blockchain_config.endpoint),
  839. ex,
  840. args.fun,
  841. )
  842. .await?;
  843. let value_pair = parse_value_pair(&value_pair)?;
  844. let token_pair = parse_token_pair(&drk, &token_pair).await?;
  845. let half = match drk.init_swap(value_pair, token_pair, None, None, None).await {
  846. Ok(h) => h,
  847. Err(e) => {
  848. eprintln!("Failed to create swap transaction half: {e:?}");
  849. exit(2);
  850. }
  851. };
  852. println!("{}", base64::encode(&serialize_async(&half).await));
  853. drk.stop_rpc_client().await
  854. }
  855. OtcSubcmd::Join => {
  856. let mut buf = String::new();
  857. stdin().read_to_string(&mut buf)?;
  858. let Some(bytes) = base64::decode(buf.trim()) else {
  859. eprintln!("Failed to decode partial swap data");
  860. exit(2);
  861. };
  862. let partial: PartialSwapData = deserialize_async(&bytes).await?;
  863. let drk = Drk::new(
  864. blockchain_config.wallet_path,
  865. blockchain_config.wallet_pass,
  866. Some(blockchain_config.endpoint),
  867. ex,
  868. args.fun,
  869. )
  870. .await?;
  871. let tx = match drk.join_swap(partial, None, None, None).await {
  872. Ok(tx) => tx,
  873. Err(e) => {
  874. eprintln!("Failed to create a join swap transaction: {e:?}");
  875. exit(2);
  876. }
  877. };
  878. println!("{}", base64::encode(&serialize_async(&tx).await));
  879. drk.stop_rpc_client().await
  880. }
  881. OtcSubcmd::Inspect => {
  882. let mut buf = String::new();
  883. stdin().read_to_string(&mut buf)?;
  884. let Some(bytes) = base64::decode(buf.trim()) else {
  885. eprintln!("Failed to decode swap transaction");
  886. exit(2);
  887. };
  888. let drk = Drk::new(
  889. blockchain_config.wallet_path,
  890. blockchain_config.wallet_pass,
  891. None,
  892. ex,
  893. args.fun,
  894. )
  895. .await?;
  896. if let Err(e) = drk.inspect_swap(bytes).await {
  897. eprintln!("Failed to inspect swap: {e:?}");
  898. exit(2);
  899. };
  900. Ok(())
  901. }
  902. OtcSubcmd::Sign => {
  903. let mut tx = parse_tx_from_stdin().await?;
  904. let drk = Drk::new(
  905. blockchain_config.wallet_path,
  906. blockchain_config.wallet_pass,
  907. None,
  908. ex,
  909. args.fun,
  910. )
  911. .await?;
  912. if let Err(e) = drk.sign_swap(&mut tx).await {
  913. eprintln!("Failed to sign joined swap transaction: {e:?}");
  914. exit(2);
  915. };
  916. println!("{}", base64::encode(&serialize_async(&tx).await));
  917. Ok(())
  918. }
  919. },
  920. Subcmd::Dao { command } => match command {
  921. DaoSubcmd::Create { proposer_limit, quorum, approval_ratio, gov_token_id } => {
  922. if let Err(e) = f64::from_str(&proposer_limit) {
  923. eprintln!("Invalid proposer limit: {e:?}");
  924. exit(2);
  925. }
  926. if let Err(e) = f64::from_str(&quorum) {
  927. eprintln!("Invalid quorum: {e:?}");
  928. exit(2);
  929. }
  930. let proposer_limit = decode_base10(&proposer_limit, BALANCE_BASE10_DECIMALS, true)?;
  931. let quorum = decode_base10(&quorum, BALANCE_BASE10_DECIMALS, true)?;
  932. if approval_ratio > 1.0 {
  933. eprintln!("Error: Approval ratio cannot be >1.0");
  934. exit(2);
  935. }
  936. let approval_ratio_base = 100_u64;
  937. let approval_ratio_quot = (approval_ratio * approval_ratio_base as f64) as u64;
  938. let drk = Drk::new(
  939. blockchain_config.wallet_path,
  940. blockchain_config.wallet_pass,
  941. None,
  942. ex,
  943. args.fun,
  944. )
  945. .await?;
  946. let gov_token_id = match drk.get_token(gov_token_id).await {
  947. Ok(g) => g,
  948. Err(e) => {
  949. eprintln!("Invalid Token ID: {e:?}");
  950. exit(2);
  951. }
  952. };
  953. let secret_key = SecretKey::random(&mut OsRng);
  954. let bulla_blind = BaseBlind::random(&mut OsRng);
  955. let params = DaoParams::new(
  956. proposer_limit,
  957. quorum,
  958. approval_ratio_base,
  959. approval_ratio_quot,
  960. gov_token_id,
  961. secret_key,
  962. bulla_blind,
  963. );
  964. let encoded = bs58::encode(&serialize_async(&params).await).into_string();
  965. println!("{encoded}");
  966. Ok(())
  967. }
  968. DaoSubcmd::View => {
  969. let mut buf = String::new();
  970. stdin().read_to_string(&mut buf)?;
  971. let bytes = bs58::decode(&buf.trim()).into_vec()?;
  972. let dao_params: DaoParams = deserialize_async(&bytes).await?;
  973. println!("{dao_params}");
  974. Ok(())
  975. }
  976. DaoSubcmd::Import { name } => {
  977. let mut buf = String::new();
  978. stdin().read_to_string(&mut buf)?;
  979. let bytes = bs58::decode(&buf.trim()).into_vec()?;
  980. let params: DaoParams = deserialize_async(&bytes).await?;
  981. let drk = Drk::new(
  982. blockchain_config.wallet_path,
  983. blockchain_config.wallet_pass,
  984. None,
  985. ex,
  986. args.fun,
  987. )
  988. .await?;
  989. if let Err(e) = drk.import_dao(&name, params).await {
  990. eprintln!("Failed to import DAO: {e:?}");
  991. exit(2);
  992. }
  993. Ok(())
  994. }
  995. DaoSubcmd::List { name } => {
  996. let drk = Drk::new(
  997. blockchain_config.wallet_path,
  998. blockchain_config.wallet_pass,
  999. None,
  1000. ex,
  1001. args.fun,
  1002. )
  1003. .await?;
  1004. if let Err(e) = drk.dao_list(&name).await {
  1005. eprintln!("Failed to list DAO: {e:?}");
  1006. exit(2);
  1007. }
  1008. Ok(())
  1009. }
  1010. DaoSubcmd::Balance { name } => {
  1011. let drk = Drk::new(
  1012. blockchain_config.wallet_path,
  1013. blockchain_config.wallet_pass,
  1014. None,
  1015. ex,
  1016. args.fun,
  1017. )
  1018. .await?;
  1019. let balmap = match drk.dao_balance(&name).await {
  1020. Ok(b) => b,
  1021. Err(e) => {
  1022. eprintln!("Failed to fetch DAO balance: {e:?}");
  1023. exit(2);
  1024. }
  1025. };
  1026. let aliases_map = match drk.get_aliases_mapped_by_token().await {
  1027. Ok(a) => a,
  1028. Err(e) => {
  1029. eprintln!("Failed to fetch wallet aliases: {e:?}");
  1030. exit(2);
  1031. }
  1032. };
  1033. // Create a prettytable with the new data:
  1034. let mut table = Table::new();
  1035. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  1036. table.set_titles(row!["Token ID", "Aliases", "Balance"]);
  1037. for (token_id, balance) in balmap.iter() {
  1038. let aliases = match aliases_map.get(token_id) {
  1039. Some(a) => a,
  1040. None => "-",
  1041. };
  1042. table.add_row(row![
  1043. token_id,
  1044. aliases,
  1045. encode_base10(*balance, BALANCE_BASE10_DECIMALS)
  1046. ]);
  1047. }
  1048. if table.is_empty() {
  1049. println!("No unspent balances found");
  1050. } else {
  1051. println!("{table}");
  1052. }
  1053. Ok(())
  1054. }
  1055. DaoSubcmd::Mint { name } => {
  1056. let drk = Drk::new(
  1057. blockchain_config.wallet_path,
  1058. blockchain_config.wallet_pass,
  1059. Some(blockchain_config.endpoint),
  1060. ex,
  1061. args.fun,
  1062. )
  1063. .await?;
  1064. let tx = match drk.dao_mint(&name).await {
  1065. Ok(tx) => tx,
  1066. Err(e) => {
  1067. eprintln!("Failed to mint DAO: {e:?}");
  1068. exit(2);
  1069. }
  1070. };
  1071. println!("{}", base64::encode(&serialize_async(&tx).await));
  1072. drk.stop_rpc_client().await
  1073. }
  1074. DaoSubcmd::ProposeTransfer {
  1075. name,
  1076. duration,
  1077. amount,
  1078. token,
  1079. recipient,
  1080. spend_hook,
  1081. user_data,
  1082. } => {
  1083. let drk = Drk::new(
  1084. blockchain_config.wallet_path,
  1085. blockchain_config.wallet_pass,
  1086. Some(blockchain_config.endpoint),
  1087. ex,
  1088. args.fun,
  1089. )
  1090. .await?;
  1091. if let Err(e) = f64::from_str(&amount) {
  1092. eprintln!("Invalid amount: {e:?}");
  1093. exit(2);
  1094. }
  1095. let rcpt = match PublicKey::from_str(&recipient) {
  1096. Ok(r) => r,
  1097. Err(e) => {
  1098. eprintln!("Invalid recipient: {e:?}");
  1099. exit(2);
  1100. }
  1101. };
  1102. let token_id = match drk.get_token(token).await {
  1103. Ok(t) => t,
  1104. Err(e) => {
  1105. eprintln!("Invalid token alias: {e:?}");
  1106. exit(2);
  1107. }
  1108. };
  1109. let spend_hook = match spend_hook {
  1110. Some(s) => match FuncId::from_str(&s) {
  1111. Ok(s) => Some(s),
  1112. Err(e) => {
  1113. eprintln!("Invalid spend hook: {e:?}");
  1114. exit(2);
  1115. }
  1116. },
  1117. None => None,
  1118. };
  1119. let user_data = match user_data {
  1120. Some(u) => {
  1121. let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
  1122. Ok(b) => b,
  1123. Err(e) => {
  1124. eprintln!("Invalid user data: {e:?}");
  1125. exit(2);
  1126. }
  1127. };
  1128. match pallas::Base::from_repr(bytes).into() {
  1129. Some(v) => Some(v),
  1130. None => {
  1131. eprintln!("Invalid user data");
  1132. exit(2);
  1133. }
  1134. }
  1135. }
  1136. None => None,
  1137. };
  1138. let proposal = match drk
  1139. .dao_propose_transfer(
  1140. &name, duration, &amount, token_id, rcpt, spend_hook, user_data,
  1141. )
  1142. .await
  1143. {
  1144. Ok(p) => p,
  1145. Err(e) => {
  1146. eprintln!("Failed to create DAO transfer proposal: {e:?}");
  1147. exit(2);
  1148. }
  1149. };
  1150. println!("Generated proposal: {}", proposal.bulla());
  1151. drk.stop_rpc_client().await
  1152. }
  1153. DaoSubcmd::Proposals { name } => {
  1154. let drk = Drk::new(
  1155. blockchain_config.wallet_path,
  1156. blockchain_config.wallet_pass,
  1157. None,
  1158. ex,
  1159. args.fun,
  1160. )
  1161. .await?;
  1162. let proposals = drk.get_dao_proposals(&name).await?;
  1163. for (i, proposal) in proposals.iter().enumerate() {
  1164. println!("{i}. {}", proposal.bulla());
  1165. }
  1166. Ok(())
  1167. }
  1168. DaoSubcmd::Proposal { bulla, export, mint_proposal } => {
  1169. let bulla = match DaoProposalBulla::from_str(&bulla) {
  1170. Ok(b) => b,
  1171. Err(e) => {
  1172. eprintln!("Invalid proposal bulla: {e:?}");
  1173. exit(2);
  1174. }
  1175. };
  1176. let drk = Drk::new(
  1177. blockchain_config.wallet_path,
  1178. blockchain_config.wallet_pass,
  1179. Some(blockchain_config.endpoint),
  1180. ex,
  1181. args.fun,
  1182. )
  1183. .await?;
  1184. let proposal = drk.get_dao_proposal_by_bulla(&bulla).await?;
  1185. if export {
  1186. // Retrieve the DAO
  1187. let dao = drk.get_dao_by_bulla(&proposal.proposal.dao_bulla).await?;
  1188. // Encypt the proposal
  1189. let enc_note = AeadEncryptedNote::encrypt(
  1190. &proposal,
  1191. &dao.params.dao.public_key,
  1192. &mut OsRng,
  1193. )
  1194. .unwrap();
  1195. // Export it to base64
  1196. println!("{}", base64::encode(&serialize_async(&enc_note).await));
  1197. return drk.stop_rpc_client().await
  1198. }
  1199. if mint_proposal {
  1200. for call in &proposal.proposal.auth_calls {
  1201. if call.function_code == DaoFunction::AuthMoneyTransfer as u8 {
  1202. let tx = match drk.dao_transfer_proposal_tx(&proposal).await {
  1203. Ok(tx) => tx,
  1204. Err(e) => {
  1205. eprintln!("Failed to create DAO transfer proposal: {e:?}");
  1206. exit(2);
  1207. }
  1208. };
  1209. println!("{}", base64::encode(&serialize_async(&tx).await));
  1210. return drk.stop_rpc_client().await
  1211. }
  1212. }
  1213. eprintln!("Unsuported DAO proposal");
  1214. exit(2);
  1215. }
  1216. println!("{proposal}");
  1217. let mut contract_calls = "\nInvoked contracts:\n".to_string();
  1218. for call in proposal.proposal.auth_calls {
  1219. contract_calls.push_str(&format!(
  1220. "\tContract: {}\n\tFunction: {}\n\tData: ",
  1221. call.contract_id, call.function_code
  1222. ));
  1223. if call.auth_data.is_empty() {
  1224. contract_calls.push_str("-\n");
  1225. continue;
  1226. }
  1227. if call.function_code == DaoFunction::AuthMoneyTransfer as u8 {
  1228. // We know that the plaintext data live in the data plaintext vec
  1229. if proposal.data.is_none() {
  1230. contract_calls.push_str("-\n");
  1231. continue;
  1232. }
  1233. let coin: CoinAttributes =
  1234. deserialize_async(proposal.data.as_ref().unwrap()).await?;
  1235. let spend_hook = if coin.spend_hook == FuncId::none() {
  1236. "-".to_string()
  1237. } else {
  1238. format!("{}", coin.spend_hook)
  1239. };
  1240. let user_data = if coin.user_data == pallas::Base::ZERO {
  1241. "-".to_string()
  1242. } else {
  1243. format!("{:?}", coin.user_data)
  1244. };
  1245. contract_calls.push_str(&format!("\n\t\t{}: {}\n\t\t{}: {} ({})\n\t\t{}: {}\n\t\t{}: {}\n\t\t{}: {}\n\t\t{}: {}\n\n",
  1246. "Recipient",
  1247. coin.public_key,
  1248. "Amount",
  1249. coin.value,
  1250. encode_base10(coin.value, BALANCE_BASE10_DECIMALS),
  1251. "Token",
  1252. coin.token_id,
  1253. "Spend hook",
  1254. spend_hook,
  1255. "User data",
  1256. user_data,
  1257. "Blind",
  1258. coin.blind));
  1259. }
  1260. }
  1261. println!("{contract_calls}");
  1262. let votes = drk.get_dao_proposal_votes(&bulla).await?;
  1263. let mut total_yes_vote_value = 0;
  1264. let mut total_no_vote_value = 0;
  1265. let mut total_all_vote_value = 0;
  1266. let mut table = Table::new();
  1267. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  1268. table.set_titles(row!["Transaction", "Tokens", "Vote"]);
  1269. for vote in votes {
  1270. let vote_option = if vote.vote_option {
  1271. total_yes_vote_value += vote.all_vote_value;
  1272. "Yes"
  1273. } else {
  1274. total_no_vote_value += vote.all_vote_value;
  1275. "No"
  1276. };
  1277. total_all_vote_value += vote.all_vote_value;
  1278. table.add_row(row![
  1279. vote.tx_hash,
  1280. encode_base10(vote.all_vote_value, BALANCE_BASE10_DECIMALS),
  1281. vote_option
  1282. ]);
  1283. }
  1284. let outcome = if table.is_empty() {
  1285. println!("Votes: No votes found");
  1286. "Rejected"
  1287. } else {
  1288. println!("Votes:");
  1289. println!("{table}");
  1290. println!(
  1291. "Total tokens votes: {}",
  1292. encode_base10(total_all_vote_value, BALANCE_BASE10_DECIMALS)
  1293. );
  1294. let approval_ratio =
  1295. (total_yes_vote_value as f64 * 100.0) / total_all_vote_value as f64;
  1296. println!(
  1297. "Total tokens Yes votes: {} ({approval_ratio:.2}%)",
  1298. encode_base10(total_yes_vote_value, BALANCE_BASE10_DECIMALS)
  1299. );
  1300. println!(
  1301. "Total tokens No votes: {} ({:.2}%)",
  1302. encode_base10(total_no_vote_value, BALANCE_BASE10_DECIMALS),
  1303. (total_no_vote_value as f64 * 100.0) / total_all_vote_value as f64
  1304. );
  1305. let dao = drk.get_dao_by_bulla(&proposal.proposal.dao_bulla).await?;
  1306. if total_all_vote_value >= dao.params.dao.quorum &&
  1307. approval_ratio >=
  1308. (dao.params.dao.approval_ratio_quot /
  1309. dao.params.dao.approval_ratio_base)
  1310. as f64
  1311. {
  1312. "Approved"
  1313. } else {
  1314. "Rejected"
  1315. }
  1316. };
  1317. if let Some(exec_tx_hash) = proposal.exec_tx_hash {
  1318. println!("Proposal was executed on transaction: {exec_tx_hash}");
  1319. return drk.stop_rpc_client().await
  1320. }
  1321. // Retrieve next block height and current block time target,
  1322. // to compute their window.
  1323. let next_block_height = drk.get_next_block_height().await?;
  1324. let block_target = drk.get_block_target().await?;
  1325. let current_window = blockwindow(next_block_height, block_target);
  1326. let end_time = proposal.proposal.creation_blockwindow +
  1327. proposal.proposal.duration_blockwindows;
  1328. let (voting_status, proposal_status_message) = if current_window < end_time {
  1329. ("Ongoing", format!("Current proposal outcome: {outcome}"))
  1330. } else {
  1331. ("Concluded", format!("Proposal outcome: {outcome}"))
  1332. };
  1333. println!("Voting status: {voting_status}");
  1334. println!("{proposal_status_message}");
  1335. drk.stop_rpc_client().await
  1336. }
  1337. DaoSubcmd::ProposalImport {} => {
  1338. let mut buf = String::new();
  1339. stdin().read_to_string(&mut buf)?;
  1340. let Some(bytes) = base64::decode(buf.trim()) else {
  1341. eprintln!("Failed to decode encrypted proposal data");
  1342. exit(2);
  1343. };
  1344. let encrypted_proposal: AeadEncryptedNote = deserialize_async(&bytes).await?;
  1345. let drk = Drk::new(
  1346. blockchain_config.wallet_path,
  1347. blockchain_config.wallet_pass,
  1348. None,
  1349. ex,
  1350. args.fun,
  1351. )
  1352. .await?;
  1353. // Retrieve all DAOs to try to decrypt the proposal
  1354. let daos = drk.get_daos().await?;
  1355. for dao in &daos {
  1356. if let Ok(proposal) =
  1357. encrypted_proposal.decrypt::<ProposalRecord>(&dao.params.secret_key)
  1358. {
  1359. let proposal = match drk.get_dao_proposal_by_bulla(&proposal.bulla()).await
  1360. {
  1361. Ok(p) => {
  1362. let mut our_proposal = p;
  1363. our_proposal.data = proposal.data;
  1364. our_proposal
  1365. }
  1366. Err(_) => proposal,
  1367. };
  1368. return drk.put_dao_proposal(&proposal).await
  1369. }
  1370. }
  1371. eprintln!("Couldn't decrypt the proposal with out DAO keys");
  1372. exit(2);
  1373. }
  1374. DaoSubcmd::Vote { bulla, vote, vote_weight } => {
  1375. let bulla = match DaoProposalBulla::from_str(&bulla) {
  1376. Ok(b) => b,
  1377. Err(e) => {
  1378. eprintln!("Invalid proposal bulla: {e:?}");
  1379. exit(2);
  1380. }
  1381. };
  1382. if vote > 1 {
  1383. eprintln!("Vote can be either 0 (NO) or 1 (YES)");
  1384. exit(2);
  1385. }
  1386. let vote = vote != 0;
  1387. let weight = match vote_weight {
  1388. Some(w) => {
  1389. if let Err(e) = f64::from_str(&w) {
  1390. eprintln!("Invalid vote weight: {e:?}");
  1391. exit(2);
  1392. }
  1393. Some(decode_base10(&w, BALANCE_BASE10_DECIMALS, true)?)
  1394. }
  1395. None => None,
  1396. };
  1397. let drk = Drk::new(
  1398. blockchain_config.wallet_path,
  1399. blockchain_config.wallet_pass,
  1400. Some(blockchain_config.endpoint),
  1401. ex,
  1402. args.fun,
  1403. )
  1404. .await?;
  1405. let tx = match drk.dao_vote(&bulla, vote, weight).await {
  1406. Ok(tx) => tx,
  1407. Err(e) => {
  1408. eprintln!("Failed to create DAO Vote transaction: {e:?}");
  1409. exit(2);
  1410. }
  1411. };
  1412. println!("{}", base64::encode(&serialize_async(&tx).await));
  1413. drk.stop_rpc_client().await
  1414. }
  1415. DaoSubcmd::Exec { bulla } => {
  1416. let bulla = match DaoProposalBulla::from_str(&bulla) {
  1417. Ok(b) => b,
  1418. Err(e) => {
  1419. eprintln!("Invalid proposal bulla: {e:?}");
  1420. exit(2);
  1421. }
  1422. };
  1423. let drk = Drk::new(
  1424. blockchain_config.wallet_path,
  1425. blockchain_config.wallet_pass,
  1426. Some(blockchain_config.endpoint),
  1427. ex,
  1428. args.fun,
  1429. )
  1430. .await?;
  1431. let proposal = drk.get_dao_proposal_by_bulla(&bulla).await?;
  1432. for call in &proposal.proposal.auth_calls {
  1433. if call.function_code == DaoFunction::AuthMoneyTransfer as u8 {
  1434. let tx = match drk.dao_exec_transfer(&proposal).await {
  1435. Ok(tx) => tx,
  1436. Err(e) => {
  1437. eprintln!("Failed to execute DAO transfer proposal: {e:?}");
  1438. exit(2);
  1439. }
  1440. };
  1441. println!("{}", base64::encode(&serialize_async(&tx).await));
  1442. return drk.stop_rpc_client().await
  1443. }
  1444. }
  1445. eprintln!("Unsuported DAO proposal");
  1446. exit(2);
  1447. }
  1448. DaoSubcmd::SpendHook => {
  1449. let spend_hook =
  1450. FuncRef { contract_id: *DAO_CONTRACT_ID, func_code: DaoFunction::Exec as u8 }
  1451. .to_func_id();
  1452. println!("{spend_hook}");
  1453. Ok(())
  1454. }
  1455. },
  1456. Subcmd::AttachFee => {
  1457. let mut tx = parse_tx_from_stdin().await?;
  1458. let drk = Drk::new(
  1459. blockchain_config.wallet_path,
  1460. blockchain_config.wallet_pass,
  1461. Some(blockchain_config.endpoint),
  1462. ex,
  1463. args.fun,
  1464. )
  1465. .await?;
  1466. if let Err(e) = drk.attach_fee(&mut tx).await {
  1467. eprintln!("Failed to attach the fee call to the transaction: {e:?}");
  1468. exit(2);
  1469. };
  1470. println!("{}", base64::encode(&serialize_async(&tx).await));
  1471. drk.stop_rpc_client().await
  1472. }
  1473. Subcmd::Inspect => {
  1474. let tx = parse_tx_from_stdin().await?;
  1475. println!("{tx:#?}");
  1476. Ok(())
  1477. }
  1478. Subcmd::Broadcast => {
  1479. let tx = parse_tx_from_stdin().await?;
  1480. let drk = Drk::new(
  1481. blockchain_config.wallet_path,
  1482. blockchain_config.wallet_pass,
  1483. Some(blockchain_config.endpoint),
  1484. ex,
  1485. args.fun,
  1486. )
  1487. .await?;
  1488. if let Err(e) = drk.simulate_tx(&tx).await {
  1489. eprintln!("Failed to simulate tx: {e:?}");
  1490. exit(2);
  1491. };
  1492. if let Err(e) = drk.mark_tx_spend(&tx).await {
  1493. eprintln!("Failed to mark transaction coins as spent: {e:?}");
  1494. exit(2);
  1495. };
  1496. let txid = match drk.broadcast_tx(&tx).await {
  1497. Ok(t) => t,
  1498. Err(e) => {
  1499. eprintln!("Failed to broadcast transaction: {e:?}");
  1500. exit(2);
  1501. }
  1502. };
  1503. println!("Transaction ID: {txid}");
  1504. drk.stop_rpc_client().await
  1505. }
  1506. Subcmd::Subscribe => {
  1507. let drk = Drk::new(
  1508. blockchain_config.wallet_path,
  1509. blockchain_config.wallet_pass,
  1510. Some(blockchain_config.endpoint.clone()),
  1511. ex.clone(),
  1512. args.fun,
  1513. )
  1514. .await?;
  1515. if let Err(e) = drk.subscribe_blocks(blockchain_config.endpoint, ex).await {
  1516. eprintln!("Block subscription failed: {e:?}");
  1517. exit(2);
  1518. }
  1519. drk.stop_rpc_client().await
  1520. }
  1521. Subcmd::Scan { reset } => {
  1522. let drk = Drk::new(
  1523. blockchain_config.wallet_path,
  1524. blockchain_config.wallet_pass,
  1525. Some(blockchain_config.endpoint),
  1526. ex,
  1527. args.fun,
  1528. )
  1529. .await?;
  1530. if reset {
  1531. println!("Reset requested.");
  1532. if let Err(e) = drk.scan_blocks(true).await {
  1533. eprintln!("Failed during scanning: {e:?}");
  1534. exit(2);
  1535. }
  1536. println!("Finished scanning blockchain");
  1537. return drk.stop_rpc_client().await
  1538. }
  1539. if let Err(e) = drk.scan_blocks(false).await {
  1540. eprintln!("Failed during scanning: {e:?}");
  1541. exit(2);
  1542. }
  1543. println!("Finished scanning blockchain");
  1544. drk.stop_rpc_client().await
  1545. }
  1546. Subcmd::Explorer { command } => match command {
  1547. ExplorerSubcmd::FetchTx { tx_hash, full, encode } => {
  1548. let tx_hash = TransactionHash(*blake3::Hash::from_hex(&tx_hash)?.as_bytes());
  1549. let drk = Drk::new(
  1550. blockchain_config.wallet_path,
  1551. blockchain_config.wallet_pass,
  1552. Some(blockchain_config.endpoint),
  1553. ex,
  1554. args.fun,
  1555. )
  1556. .await?;
  1557. let tx = match drk.get_tx(&tx_hash).await {
  1558. Ok(tx) => tx,
  1559. Err(e) => {
  1560. eprintln!("Failed to fetch transaction: {e:?}");
  1561. exit(2);
  1562. }
  1563. };
  1564. let Some(tx) = tx else {
  1565. println!("Transaction was not found");
  1566. exit(1);
  1567. };
  1568. // Make sure the tx is correct
  1569. assert_eq!(tx.hash(), tx_hash);
  1570. if encode {
  1571. println!("{}", base64::encode(&serialize_async(&tx).await));
  1572. exit(1)
  1573. }
  1574. println!("Transaction ID: {tx_hash}");
  1575. if full {
  1576. println!("{tx:?}");
  1577. }
  1578. drk.stop_rpc_client().await
  1579. }
  1580. ExplorerSubcmd::SimulateTx => {
  1581. let tx = parse_tx_from_stdin().await?;
  1582. let drk = Drk::new(
  1583. blockchain_config.wallet_path,
  1584. blockchain_config.wallet_pass,
  1585. Some(blockchain_config.endpoint),
  1586. ex,
  1587. args.fun,
  1588. )
  1589. .await?;
  1590. let is_valid = match drk.simulate_tx(&tx).await {
  1591. Ok(b) => b,
  1592. Err(e) => {
  1593. eprintln!("Failed to simulate tx: {e:?}");
  1594. exit(2);
  1595. }
  1596. };
  1597. println!("Transaction ID: {}", tx.hash());
  1598. println!("State: {}", if is_valid { "valid" } else { "invalid" });
  1599. drk.stop_rpc_client().await
  1600. }
  1601. ExplorerSubcmd::TxsHistory { tx_hash, encode } => {
  1602. let drk = Drk::new(
  1603. blockchain_config.wallet_path,
  1604. blockchain_config.wallet_pass,
  1605. None,
  1606. ex,
  1607. args.fun,
  1608. )
  1609. .await?;
  1610. if let Some(c) = tx_hash {
  1611. let (tx_hash, status, tx) = drk.get_tx_history_record(&c).await?;
  1612. if encode {
  1613. println!("{}", base64::encode(&serialize_async(&tx).await));
  1614. exit(1)
  1615. }
  1616. println!("Transaction ID: {tx_hash}");
  1617. println!("Status: {status}");
  1618. println!("{tx:?}");
  1619. return Ok(())
  1620. }
  1621. let map = match drk.get_txs_history() {
  1622. Ok(m) => m,
  1623. Err(e) => {
  1624. eprintln!("Failed to retrieve transactions history records: {e:?}");
  1625. exit(2);
  1626. }
  1627. };
  1628. // Create a prettytable with the new data:
  1629. let mut table = Table::new();
  1630. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  1631. table.set_titles(row!["Transaction Hash", "Status"]);
  1632. for (txs_hash, status) in map.iter() {
  1633. table.add_row(row![txs_hash, status]);
  1634. }
  1635. if table.is_empty() {
  1636. println!("No transactions found");
  1637. } else {
  1638. println!("{table}");
  1639. }
  1640. Ok(())
  1641. }
  1642. },
  1643. Subcmd::Alias { command } => match command {
  1644. AliasSubcmd::Add { alias, token } => {
  1645. if alias.chars().count() > 5 {
  1646. eprintln!("Error: Alias exceeds 5 characters");
  1647. exit(2);
  1648. }
  1649. let token_id = match TokenId::from_str(token.as_str()) {
  1650. Ok(t) => t,
  1651. Err(e) => {
  1652. eprintln!("Invalid Token ID: {e:?}");
  1653. exit(2);
  1654. }
  1655. };
  1656. let drk = Drk::new(
  1657. blockchain_config.wallet_path,
  1658. blockchain_config.wallet_pass,
  1659. None,
  1660. ex,
  1661. args.fun,
  1662. )
  1663. .await?;
  1664. if let Err(e) = drk.add_alias(alias, token_id).await {
  1665. eprintln!("Failed to add alias: {e:?}");
  1666. exit(2);
  1667. }
  1668. Ok(())
  1669. }
  1670. AliasSubcmd::Show { alias, token } => {
  1671. let token_id = match token {
  1672. Some(t) => match TokenId::from_str(t.as_str()) {
  1673. Ok(t) => Some(t),
  1674. Err(e) => {
  1675. eprintln!("Invalid Token ID: {e:?}");
  1676. exit(2);
  1677. }
  1678. },
  1679. None => None,
  1680. };
  1681. let drk = Drk::new(
  1682. blockchain_config.wallet_path,
  1683. blockchain_config.wallet_pass,
  1684. None,
  1685. ex,
  1686. args.fun,
  1687. )
  1688. .await?;
  1689. let map = drk.get_aliases(alias, token_id).await?;
  1690. // Create a prettytable with the new data:
  1691. let mut table = Table::new();
  1692. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  1693. table.set_titles(row!["Alias", "Token ID"]);
  1694. for (alias, token_id) in map.iter() {
  1695. table.add_row(row![alias, token_id]);
  1696. }
  1697. if table.is_empty() {
  1698. println!("No aliases found");
  1699. } else {
  1700. println!("{table}");
  1701. }
  1702. Ok(())
  1703. }
  1704. AliasSubcmd::Remove { alias } => {
  1705. let drk = Drk::new(
  1706. blockchain_config.wallet_path,
  1707. blockchain_config.wallet_pass,
  1708. None,
  1709. ex,
  1710. args.fun,
  1711. )
  1712. .await?;
  1713. if let Err(e) = drk.remove_alias(alias).await {
  1714. eprintln!("Failed to remove alias: {e:?}");
  1715. exit(2);
  1716. }
  1717. Ok(())
  1718. }
  1719. },
  1720. Subcmd::Token { command } => match command {
  1721. TokenSubcmd::Import { secret_key, token_blind } => {
  1722. let mint_authority = match SecretKey::from_str(&secret_key) {
  1723. Ok(ma) => ma,
  1724. Err(e) => {
  1725. eprintln!("Invalid mint authority: {e:?}");
  1726. exit(2);
  1727. }
  1728. };
  1729. let token_blind = match BaseBlind::from_str(&token_blind) {
  1730. Ok(tb) => tb,
  1731. Err(e) => {
  1732. eprintln!("Invalid token blind: {e:?}");
  1733. exit(2);
  1734. }
  1735. };
  1736. let drk = Drk::new(
  1737. blockchain_config.wallet_path,
  1738. blockchain_config.wallet_pass,
  1739. None,
  1740. ex,
  1741. args.fun,
  1742. )
  1743. .await?;
  1744. let token_id = drk.import_mint_authority(mint_authority, token_blind).await?;
  1745. println!("Successfully imported mint authority for token ID: {token_id}");
  1746. Ok(())
  1747. }
  1748. TokenSubcmd::GenerateMint => {
  1749. let drk = Drk::new(
  1750. blockchain_config.wallet_path,
  1751. blockchain_config.wallet_pass,
  1752. None,
  1753. ex,
  1754. args.fun,
  1755. )
  1756. .await?;
  1757. let mint_authority = SecretKey::random(&mut OsRng);
  1758. let token_blind = BaseBlind::random(&mut OsRng);
  1759. let token_id = drk.import_mint_authority(mint_authority, token_blind).await?;
  1760. println!("Successfully imported mint authority for token ID: {token_id}");
  1761. Ok(())
  1762. }
  1763. TokenSubcmd::List => {
  1764. let drk = Drk::new(
  1765. blockchain_config.wallet_path,
  1766. blockchain_config.wallet_pass,
  1767. None,
  1768. ex,
  1769. args.fun,
  1770. )
  1771. .await?;
  1772. let tokens = drk.get_mint_authorities().await?;
  1773. let aliases_map = match drk.get_aliases_mapped_by_token().await {
  1774. Ok(map) => map,
  1775. Err(e) => {
  1776. eprintln!("Failed to fetch wallet aliases: {e:?}");
  1777. exit(2);
  1778. }
  1779. };
  1780. let mut table = Table::new();
  1781. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  1782. table.set_titles(row![
  1783. "Token ID",
  1784. "Aliases",
  1785. "Mint Authority",
  1786. "Token Blind",
  1787. "Frozen"
  1788. ]);
  1789. for (token_id, authority, blind, frozen) in tokens {
  1790. let aliases = match aliases_map.get(&token_id.to_string()) {
  1791. Some(a) => a,
  1792. None => "-",
  1793. };
  1794. table.add_row(row![token_id, aliases, authority, blind, frozen]);
  1795. }
  1796. if table.is_empty() {
  1797. println!("No tokens found");
  1798. } else {
  1799. println!("{table}");
  1800. }
  1801. Ok(())
  1802. }
  1803. TokenSubcmd::Mint { token, amount, recipient, spend_hook, user_data } => {
  1804. let drk = Drk::new(
  1805. blockchain_config.wallet_path,
  1806. blockchain_config.wallet_pass,
  1807. Some(blockchain_config.endpoint),
  1808. ex,
  1809. args.fun,
  1810. )
  1811. .await?;
  1812. if let Err(e) = f64::from_str(&amount) {
  1813. eprintln!("Invalid amount: {e:?}");
  1814. exit(2);
  1815. }
  1816. let rcpt = match PublicKey::from_str(&recipient) {
  1817. Ok(r) => r,
  1818. Err(e) => {
  1819. eprintln!("Invalid recipient: {e:?}");
  1820. exit(2);
  1821. }
  1822. };
  1823. let token_id = match drk.get_token(token).await {
  1824. Ok(t) => t,
  1825. Err(e) => {
  1826. eprintln!("Invalid Token ID: {e:?}");
  1827. exit(2);
  1828. }
  1829. };
  1830. let spend_hook = match spend_hook {
  1831. Some(s) => match FuncId::from_str(&s) {
  1832. Ok(s) => Some(s),
  1833. Err(e) => {
  1834. eprintln!("Invalid spend hook: {e:?}");
  1835. exit(2);
  1836. }
  1837. },
  1838. None => None,
  1839. };
  1840. let user_data = match user_data {
  1841. Some(u) => {
  1842. let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
  1843. Ok(b) => b,
  1844. Err(e) => {
  1845. eprintln!("Invalid user data: {e:?}");
  1846. exit(2);
  1847. }
  1848. };
  1849. match pallas::Base::from_repr(bytes).into() {
  1850. Some(v) => Some(v),
  1851. None => {
  1852. eprintln!("Invalid user data");
  1853. exit(2);
  1854. }
  1855. }
  1856. }
  1857. None => None,
  1858. };
  1859. let tx = match drk.mint_token(&amount, rcpt, token_id, spend_hook, user_data).await
  1860. {
  1861. Ok(tx) => tx,
  1862. Err(e) => {
  1863. eprintln!("Failed to create token mint transaction: {e:?}");
  1864. exit(2);
  1865. }
  1866. };
  1867. println!("{}", base64::encode(&serialize_async(&tx).await));
  1868. drk.stop_rpc_client().await
  1869. }
  1870. TokenSubcmd::Freeze { token } => {
  1871. let drk = Drk::new(
  1872. blockchain_config.wallet_path,
  1873. blockchain_config.wallet_pass,
  1874. Some(blockchain_config.endpoint),
  1875. ex,
  1876. args.fun,
  1877. )
  1878. .await?;
  1879. let token_id = match drk.get_token(token).await {
  1880. Ok(t) => t,
  1881. Err(e) => {
  1882. eprintln!("Invalid Token ID: {e:?}");
  1883. exit(2);
  1884. }
  1885. };
  1886. let tx = match drk.freeze_token(token_id).await {
  1887. Ok(tx) => tx,
  1888. Err(e) => {
  1889. eprintln!("Failed to create token freeze transaction: {e:?}");
  1890. exit(2);
  1891. }
  1892. };
  1893. println!("{}", base64::encode(&serialize_async(&tx).await));
  1894. drk.stop_rpc_client().await
  1895. }
  1896. },
  1897. Subcmd::Contract { command } => match command {
  1898. ContractSubcmd::GenerateDeploy => {
  1899. let drk = Drk::new(
  1900. blockchain_config.wallet_path,
  1901. blockchain_config.wallet_pass,
  1902. None,
  1903. ex,
  1904. args.fun,
  1905. )
  1906. .await?;
  1907. if let Err(e) = drk.deploy_auth_keygen().await {
  1908. eprintln!("Error creating deploy auth keypair: {:?}", e);
  1909. exit(2);
  1910. }
  1911. Ok(())
  1912. }
  1913. ContractSubcmd::List => {
  1914. let drk = Drk::new(
  1915. blockchain_config.wallet_path,
  1916. blockchain_config.wallet_pass,
  1917. None,
  1918. ex,
  1919. args.fun,
  1920. )
  1921. .await?;
  1922. let auths = drk.list_deploy_auth().await?;
  1923. let mut table = Table::new();
  1924. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  1925. table.set_titles(row!["Index", "Contract ID", "Frozen"]);
  1926. for (idx, contract_id, frozen) in auths {
  1927. table.add_row(row![idx, contract_id, frozen]);
  1928. }
  1929. if table.is_empty() {
  1930. println!("No deploy authorities found");
  1931. } else {
  1932. println!("{table}");
  1933. }
  1934. Ok(())
  1935. }
  1936. ContractSubcmd::Deploy { deploy_auth, wasm_path, deploy_ix } => {
  1937. // Read the wasm bincode and deploy instruction
  1938. let wasm_bin = smol::fs::read(expand_path(&wasm_path)?).await?;
  1939. let deploy_ix = smol::fs::read(expand_path(&deploy_ix)?).await?;
  1940. let drk = Drk::new(
  1941. blockchain_config.wallet_path,
  1942. blockchain_config.wallet_pass,
  1943. Some(blockchain_config.endpoint),
  1944. ex,
  1945. args.fun,
  1946. )
  1947. .await?;
  1948. let mut tx = match drk.deploy_contract(deploy_auth, wasm_bin, deploy_ix).await {
  1949. Ok(v) => v,
  1950. Err(e) => {
  1951. eprintln!("Error creating contract deployment tx: {}", e);
  1952. exit(2);
  1953. }
  1954. };
  1955. if let Err(e) = drk.attach_fee(&mut tx).await {
  1956. eprintln!("Failed to attach the fee call to the transaction: {e:?}");
  1957. exit(2);
  1958. };
  1959. println!("{}", base64::encode(&serialize_async(&tx).await));
  1960. drk.stop_rpc_client().await
  1961. }
  1962. ContractSubcmd::Lock { deploy_auth } => {
  1963. let drk = Drk::new(
  1964. blockchain_config.wallet_path,
  1965. blockchain_config.wallet_pass,
  1966. Some(blockchain_config.endpoint),
  1967. ex,
  1968. args.fun,
  1969. )
  1970. .await?;
  1971. let mut tx = match drk.lock_contract(deploy_auth).await {
  1972. Ok(v) => v,
  1973. Err(e) => {
  1974. eprintln!("Error creating contract lock tx: {}", e);
  1975. exit(2);
  1976. }
  1977. };
  1978. if let Err(e) = drk.attach_fee(&mut tx).await {
  1979. eprintln!("Failed to attach the fee call to the transaction: {e:?}");
  1980. exit(2);
  1981. };
  1982. println!("{}", base64::encode(&serialize_async(&tx).await));
  1983. drk.stop_rpc_client().await
  1984. }
  1985. },
  1986. }
  1987. }