lucia-apis 0.2.0

A set of API collections based on the lucia framework
Documentation
use crate::blockchain::solana::{
  AccountEncoding, AccountSubscribeConfig, Commitment, DataSlice, Filter, GetAccountInfoConfig,
  GetBlockConfig, GetProgramAccountsConfig, GetTokenAccountsByOwnerConfig, GetTransactionConfig,
  Memcmp, MemcmpEncodedBytes, MessageInput, MintOrProgramId, Solana, SolanaAddressHash,
  TransactionDetails, TransactionEncoding, TransactionInput,
};
use alloc::vec::Vec;
use ed25519_dalek::Keypair;
use lucia::{
  dnsn::SerdeJson,
  network::{transport::Transport, HttpParams, WsParams},
};

const ALICE_PUBLIC_KEY: SolanaAddressHash = [
  72, 221, 15, 10, 15, 203, 187, 109, 166, 124, 138, 38, 199, 74, 146, 72, 63, 245, 197, 247, 201,
  170, 164, 254, 147, 227, 243, 91, 101, 49, 105, 158,
];
const ALICE_SECRET_KEY: SolanaAddressHash = [
  30, 33, 76, 185, 72, 178, 196, 11, 231, 116, 8, 208, 127, 141, 89, 39, 217, 222, 1, 167, 32, 61,
  224, 135, 200, 132, 58, 174, 231, 165, 32, 132,
];
const BOB_PUBLIC_KEY: SolanaAddressHash = [
  24, 147, 209, 196, 197, 185, 156, 48, 170, 96, 192, 119, 193, 150, 129, 12, 221, 102, 119, 84,
  33, 221, 67, 224, 185, 107, 130, 157, 207, 85, 161, 30,
];
const TO_NORMAL_ACCOUNT: &str = "FiuQrMbFUYka1Goec4wdhoiNq3Ms99cxGrW8JWsWfPnJ";
const TO_SOL_TOKEN_ACCOUNT: &str = "CDqKzghiixHryqny9r8RPJzYfg3hiiF7e8JecsF6fuJw";
const TO_SOL_TOKEN_MINT: &str = "So11111111111111111111111111111111111111112";
const TOKEN_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
const WS_URL: &str = "ws://localhost:8900";

_create_http_test!(Solana::new(None), http(), get_account_info, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux
        .get_account_info()
        .data(
          TO_NORMAL_ACCOUNT,
          Some(GetAccountInfoConfig {
            commitment: None,
            data_slice: None,
            encoding: Some(AccountEncoding::JsonParsed),
          }),
        )
        .build(),
      pkgs_aux,
    )
    .await
    .unwrap()
    .result
    .unwrap()
    .value
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_balance, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_balance().data(TO_NORMAL_ACCOUNT, None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_block, |pkgs_aux, trans| async {
  let slot = trans
    .send_retrieve_and_decode_contained(&mut pkgs_aux.get_slot().data(None).build(), pkgs_aux)
    .await
    .unwrap()
    .result
    .unwrap();
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux
        .get_block()
        .data(
          slot,
          Some(GetBlockConfig {
            commitment: Some(Commitment::Finalized),
            encoding: Some(TransactionEncoding::JsonParsed),
            rewards: Some(true),
            transaction_details: Some(TransactionDetails::Full),
          }),
        )
        .build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_blocks, |pkgs_aux, trans| async {
  let slot = trans
    .send_retrieve_and_decode_contained(&mut pkgs_aux.get_slot().data(None).build(), pkgs_aux)
    .await
    .unwrap()
    .result
    .unwrap();
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_blocks().data(slot, None, None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_blocks_with_limit, |pkgs_aux, trans| async {
  let slot = trans
    .send_retrieve_and_decode_contained(&mut pkgs_aux.get_slot().data(None).build(), pkgs_aux)
    .await
    .unwrap()
    .result
    .unwrap();
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_blocks_with_limit().data(slot, None, None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_block_height, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_block_height().data(None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_block_commitment, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_block_commitment().data(2).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_block_production, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_block_production().data(None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_cluster_nodes, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(&mut pkgs_aux.get_cluster_nodes().build(), pkgs_aux)
    .await
    .unwrap();
});

#[cfg(feature = "solana-program")]
_create_http_test!(Solana::new(None), http(), get_fee_for_message, |pkgs_aux, trans| async {
  let from_keypair = Keypair::from_bytes(&alice_keypair()[..]).unwrap();
  let from_public_key = from_keypair.public.to_bytes();
  let blockhash = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_latest_blockhash().data(None).build(),
      pkgs_aux,
    )
    .await
    .unwrap()
    .result
    .unwrap()
    .value
    .blockhash;
  assert_eq!(
    trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux
          .get_fee_for_message()
          .data(None, &transfer_message(blockhash, from_public_key))
          .unwrap()
          .build(),
        pkgs_aux
      )
      .await
      .unwrap()
      .result
      .unwrap()
      .value
      .unwrap(),
    5000
  );
});

_create_http_test!(
  Solana::new(None),
  http(),
  get_minimum_balance_for_rent_exemption,
  |pkgs_aux, trans| async {
    let _res = trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux.get_minimum_balance_for_rent_exemption().data(100, None).build(),
        pkgs_aux,
      )
      .await
      .unwrap();
  }
);

_create_http_test!(Solana::new(None), http(), get_multiple_accounts, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux
        .get_multiple_accounts()
        .data(&[TO_NORMAL_ACCOUNT, TO_SOL_TOKEN_ACCOUNT], None)
        .build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_program_accounts, |pkgs_aux, trans| async {
  assert_eq!(
    trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux
          .get_program_accounts()
          .data(
            TOKEN_PROGRAM,
            Some(GetProgramAccountsConfig {
              commitment: None,
              data_slice: Some(DataSlice { length: 32, offset: 0 }),
              encoding: Some(AccountEncoding::Base64),
              filters: Some(&[
                Filter::DataSize(165),
                Filter::Memcmp(Memcmp {
                  bytes: MemcmpEncodedBytes::Base58(TO_NORMAL_ACCOUNT),
                  offset: 32,
                }),
              ]),
              min_context_slot: Some(2),
            }),
          )
          .build(),
        pkgs_aux
      )
      .await
      .unwrap()
      .result
      .unwrap()
      .len(),
    1
  );
});

_create_http_test!(Solana::new(None), http(), get_slot, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(&mut pkgs_aux.get_slot().data(None).build(), pkgs_aux)
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_slot_leader, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_slot_leader().data(None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_slot_leaders, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_slot_leaders().data(1, 2).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(Solana::new(None), http(), get_token_account_balance, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(
      &mut pkgs_aux.get_token_account_balance().data(TO_SOL_TOKEN_ACCOUNT, None).build(),
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_http_test!(
  Solana::new(None),
  http(),
  get_token_accounts_by_owner,
  |pkgs_aux, trans| async {
    let _res = trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux
          .get_token_accounts_by_owner()
          .data(
            TO_NORMAL_ACCOUNT,
            MintOrProgramId::Mint(TO_SOL_TOKEN_MINT),
            Some(GetTokenAccountsByOwnerConfig {
              commitment: None,
              data_slice: None,
              encoding: Some(AccountEncoding::JsonParsed),
              min_context_slot: None,
            }),
          )
          .build(),
        pkgs_aux,
      )
      .await
      .unwrap()
      .result
      .unwrap()
      .value[0]
      .pubkey;
  }
);

_create_http_test!(Solana::new(None), http(), get_version, |pkgs_aux, trans| async {
  let _res = trans
    .send_retrieve_and_decode_contained(&mut pkgs_aux.get_version().build(), pkgs_aux)
    .await
    .unwrap()
    .result
    .unwrap()
    .feature_set;
});

#[cfg(feature = "solana-program")]
_create_http_test!(
  Solana::new(None),
  http(),
  http_get_latest_blockhash_send_transaction_and_get_transaction,
  |pkgs_aux, trans| async {
    let from_keypair = Keypair::from_bytes(&alice_keypair()[..]).unwrap();
    let blockhash = trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux.get_latest_blockhash().data(None).build(),
        pkgs_aux,
      )
      .await
      .unwrap()
      .result
      .unwrap()
      .value
      .blockhash;
    let tx = TransactionInput::new(
      &mut pkgs_aux.byte_buffer,
      blockhash,
      transfer_message(blockhash, from_keypair.public.to_bytes()),
      &[from_keypair],
    )
    .unwrap();
    let tx_hash = trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux.send_transaction().data(None, &tx).unwrap().build(),
        pkgs_aux,
      )
      .await
      .unwrap()
      .result
      .unwrap();
    assert!(Solana::confirm_transaction(<_>::default(), pkgs_aux, trans, &tx_hash).await.unwrap());

    let _res = trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux
          .get_transaction()
          .data(
            tx_hash.as_str(),
            Some(GetTransactionConfig {
              commitment: Some(Commitment::Finalized),
              encoding: Some(TransactionEncoding::Base64),
            }),
          )
          .build(),
        pkgs_aux,
      )
      .await
      .unwrap()
      .result
      .unwrap()
      .transaction;
  }
);

_create_http_test!(Solana::new(None), http(), http_reqs_with_array, |pkgs_aux, trans| async {
  let mut buffer = Vec::new();
  let _res = trans
    .send_retrieve_and_decode_batch(
      &mut buffer,
      &mut [
        &mut pkgs_aux.get_balance().data(TO_NORMAL_ACCOUNT, None).build(),
        &mut pkgs_aux.get_balance().data(TO_NORMAL_ACCOUNT, None).build(),
      ][..],
      pkgs_aux,
    )
    .await
    .unwrap();
});

_create_ws_test!(
  WS_URL,
  Solana::new(None),
  ws(),
  account_subscribe,
  (account_unsubscribe),
  |pkgs_aux, trans| async {
    [trans
      .send_retrieve_and_decode_contained(
        &mut pkgs_aux
          .account_subscribe()
          .data(
            TO_NORMAL_ACCOUNT,
            Some(AccountSubscribeConfig { commitment: None, encoding: None }),
          )
          .build(),
        pkgs_aux,
      )
      .await
      .unwrap()
      .result
      .unwrap()]
  }
);

_create_ws_test!(
  WS_URL,
  Solana::new(None),
  ws(),
  root_subscribe,
  (root_unsubscribe),
  |pkgs_aux, trans| async {
    [trans
      .send_retrieve_and_decode_contained(&mut pkgs_aux.root_subscribe().build(), pkgs_aux)
      .await
      .unwrap()
      .result
      .unwrap()]
  }
);

_create_ws_test!(
  WS_URL,
  Solana::new(None),
  ws(),
  slot_subscribe,
  (slot_unsubscribe),
  |pkgs_aux, trans| async {
    [trans
      .send_retrieve_and_decode_contained(&mut pkgs_aux.slot_subscribe().build(), pkgs_aux)
      .await
      .unwrap()
      .result
      .unwrap()]
  }
);

_create_ws_test!(
  WS_URL,
  Solana::new(None),
  ws(),
  slot_updates_subscribe,
  (slots_updates_unsubscribe),
  |pkgs_aux, trans| async {
    [trans
      .send_retrieve_and_decode_contained(&mut pkgs_aux.slots_updates_subscribe().build(), pkgs_aux)
      .await
      .unwrap()
      .result
      .unwrap()]
  }
);

_create_ws_test!(
  WS_URL,
  Solana::new(None),
  ws(),
  ws_reqs_with_array,
  (account_unsubscribe, account_unsubscribe),
  |pkgs_aux, trans| async {
    let mut buffer = Vec::new();
    trans
      .send_retrieve_and_decode_batch(
        &mut buffer,
        &mut [
          &mut pkgs_aux
            .account_subscribe()
            .data(
              TO_NORMAL_ACCOUNT,
              Some(AccountSubscribeConfig {
                commitment: Some(Commitment::Confirmed),
                encoding: Some(AccountEncoding::JsonParsed),
              }),
            )
            .build(),
          &mut pkgs_aux
            .account_subscribe()
            .data(
              TO_NORMAL_ACCOUNT,
              Some(AccountSubscribeConfig {
                commitment: Some(Commitment::Confirmed),
                encoding: Some(AccountEncoding::JsonParsed),
              }),
            )
            .build(),
        ],
        pkgs_aux,
      )
      .await
      .unwrap();
    [*buffer[0].result.as_ref().unwrap(), *buffer[1].result.as_ref().unwrap()]
  }
);

fn alice_keypair() -> [u8; 64] {
  let mut array = [0; 64];
  array[..32].copy_from_slice(&ALICE_SECRET_KEY);
  array[32..].copy_from_slice(&ALICE_PUBLIC_KEY);
  array
}

fn http() -> (SerdeJson, HttpParams) {
  (SerdeJson, HttpParams::from_url("http://localhost:8899").unwrap())
}

#[cfg(feature = "solana-program")]
fn transfer_message(blockhash: [u8; 32], from_public_key: [u8; 32]) -> MessageInput {
  let transfer = solana_program::system_instruction::transfer(
    &from_public_key.into(),
    &BOB_PUBLIC_KEY.into(),
    100000000,
  )
  .try_into()
  .unwrap();
  MessageInput::with_params(&[transfer], Some(from_public_key), blockhash).unwrap()
}

fn ws() -> (SerdeJson, WsParams) {
  (SerdeJson, WsParams::default())
}