Skip to content

Commit

Permalink
feat: P-693 add MCRT token holding amount VC, refactored test cases a…
Browse files Browse the repository at this point in the history
…nd supported customized assertion ranges (#2673)

Co-authored-by: higherordertech <higherordertech>
  • Loading branch information
higherordertech committed Apr 24, 2024
1 parent 5188d35 commit 22f3547
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 60 deletions.
5 changes: 4 additions & 1 deletion primitives/core/src/assertion/web3_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub enum Web3TokenType {
Nfp,
#[codec(index = 23)]
Sol,
#[codec(index = 24)]
Mcrt,
}

impl Web3TokenType {
Expand All @@ -84,7 +86,8 @@ impl Web3TokenType {
Web3Network::Litmus,
],
Self::Nfp => vec![Web3Network::Bsc],
Self::Sol => vec![Web3Network::Bsc, Web3Network::Ethereum, Web3Network::Solana],
Self::Sol | Self::Mcrt =>
vec![Web3Network::Bsc, Web3Network::Ethereum, Web3Network::Solana],
_ => vec![Web3Network::Ethereum],
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ pub enum TokenHoldingAmountCommand {
Trx,
Nfp,
Sol,
Mcrt,
}

#[derive(Subcommand, Debug)]
Expand Down Expand Up @@ -604,6 +605,7 @@ impl Command {
TokenHoldingAmountCommand::Trx => TokenHoldingAmount(Web3TokenType::Trx),
TokenHoldingAmountCommand::Nfp => TokenHoldingAmount(Web3TokenType::Nfp),
TokenHoldingAmountCommand::Sol => TokenHoldingAmount(Web3TokenType::Sol),
TokenHoldingAmountCommand::Mcrt => TokenHoldingAmount(Web3TokenType::Mcrt),
},
Command::PlatformUser(arg) => match arg {
PlatformUserCommand::KaratDaoUser => PlatformUser(PlatformUserType::KaratDaoUser),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export default {
"Trx",
"Nfp",
"Sol",
"Mcrt",
],
},
// PlatformUserType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,4 +564,48 @@ mod tests {
},
};
}

#[test]
fn build_mcrt_holding_amount_works() {
let data_provider_config = init();
let address = "EJpLyTeE8XHG9CeREeHd6pr6hNhaRnTRJx4Z5DPhEJJ6"
.from_base58()
.unwrap()
.as_slice()
.try_into()
.unwrap();
let identities: Vec<IdentityNetworkTuple> =
vec![(Identity::Solana(address), vec![Web3Network::Solana])];

let req = crate_assertion_build_request(Web3TokenType::Mcrt, identities);

match build(&req, Web3TokenType::Mcrt, &data_provider_config) {
Ok(credential) => {
log::info!("build mcrt TokenHoldingAmount done");
assert_eq!(
*(credential.credential_subject.assertions.first().unwrap()),
AssertionLogic::And {
items: vec![
create_token_assertion_logic(Web3TokenType::Mcrt),
create_network_address_assertion_logics(Web3TokenType::Mcrt),
Box::new(AssertionLogic::Item {
src: "$holding_amount".into(),
op: Op::GreaterEq,
dst: "150000".into()
}),
Box::new(AssertionLogic::Item {
src: "$holding_amount".into(),
op: Op::LessThan,
dst: "500000".into()
})
]
}
);
assert_eq!(*(credential.credential_subject.values.first().unwrap()), true);
},
Err(e) => {
panic!("build mcrt TokenHoldingAmount failed with error {:?}", e);
},
}
}
}
30 changes: 28 additions & 2 deletions tee-worker/litentry/core/common/src/web3_token/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam
#[cfg(all(not(feature = "std"), feature = "sgx"))]
extern crate sgx_tstd as std;

use std::{vec, vec::Vec};

use litentry_primitives::Web3TokenType;

use crate::Web3Network;
Expand Down Expand Up @@ -55,6 +57,7 @@ impl TokenName for Web3TokenType {
Self::Trx => "TRX",
Self::Nfp => "NFP",
Self::Sol => "SOL",
Self::Mcrt => "MCRT",
}
}
}
Expand Down Expand Up @@ -146,6 +149,12 @@ impl TokenAddress for Web3TokenType {
(Self::Sol, Web3Network::Bsc) => Some("0x570a5d26f7765ecb712c0924e4de545b89fd43df"),
(Self::Sol, Web3Network::Ethereum) =>
Some("0x5288738df1aeb0894713de903e1d0c001eeb7644"),
// Mcrt
(Self::Mcrt, Web3Network::Bsc) => Some("0x4b8285aB433D8f69CB48d5Ad62b415ed1a221e4f"),
(Self::Mcrt, Web3Network::Ethereum) =>
Some("0xde16ce60804a881e9f8c4ebb3824646edecd478d"),
(Self::Mcrt, Web3Network::Solana) =>
Some("FADm4QuSUF1K526LvTjvbJjKzeeipP6bj5bSzp3r6ipq"),
_ => None,
}
}
Expand Down Expand Up @@ -199,9 +208,13 @@ impl TokenDecimals for Web3TokenType {
// Sol
(Self::Sol, Web3Network::Bsc) | (Self::Sol, Web3Network::Ethereum) => 18,
// Ton
(Self::Ton, Web3Network::Bsc) | (Self::Ton, Web3Network::Ethereum) => 9,
(Self::Ton, Web3Network::Bsc) | (Self::Ton, Web3Network::Ethereum) |
// Mcrt
(Self::Mcrt, Web3Network::Bsc) | (Self::Mcrt, Web3Network::Ethereum) => 9,
// Wbtc
(Self::Wbtc, Web3Network::Bsc) | (Self::Wbtc, Web3Network::Ethereum) => 8,
(Self::Wbtc, Web3Network::Bsc) | (Self::Wbtc, Web3Network::Ethereum) |
// Mcrt
(Self::Mcrt, Web3Network::Solana) => 8,
// Usdc
(Self::Usdc, Web3Network::Ethereum) |
// Usdt
Expand All @@ -216,3 +229,16 @@ impl TokenDecimals for Web3TokenType {
10_u64.pow(decimals)
}
}

pub trait TokenHoldingAmountRange {
fn get_token_holding_amount_range(&self) -> Vec<f64>;
}

impl TokenHoldingAmountRange for Web3TokenType {
fn get_token_holding_amount_range(&self) -> Vec<f64> {
match self {
Self::Mcrt => vec![0.0, 2000.0, 10000.0, 50000.0, 150000.0, 500000.0],
_ => vec![0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1600.0, 3000.0],
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extern crate sgx_tstd as std;

use lc_common::{
web3_network_to_chain,
web3_token::{TokenAddress, TokenName},
web3_token::{TokenAddress, TokenHoldingAmountRange, TokenName},
};
use litentry_primitives::{Web3Network, Web3TokenType};

Expand All @@ -33,9 +33,6 @@ use lc_credentials::{
Credential,
};

const TOKEN_HOLDING_AMOUNT_RANGE: [f64; 10] =
[0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1600.0, 3000.0];

const TYPE: &str = "Token Holding Amount";
const DESCRIPTION: &str = "The amount of a particular token you are holding";

Expand Down Expand Up @@ -82,11 +79,13 @@ fn update_assertion(token_type: Web3TokenType, balance: f64, credential: &mut Cr

assertion = assertion.add_item(network_assertion);

let index = BalanceRange::index(&TOKEN_HOLDING_AMOUNT_RANGE, balance);
let token_holding_amount_range_vec = token_type.get_token_holding_amount_range();
let token_holding_amount_range = token_holding_amount_range_vec.as_slice();
let index = BalanceRange::index(token_holding_amount_range, balance);
match index {
Some(index) => {
let min = format!("{}", &TOKEN_HOLDING_AMOUNT_RANGE[index]);
let max = format!("{}", &TOKEN_HOLDING_AMOUNT_RANGE[index + 1]);
let min = format!("{}", token_holding_amount_range[index]);
let max = format!("{}", token_holding_amount_range[index + 1]);
let min_item =
AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::GreaterEq, &min);
let max_item =
Expand All @@ -101,7 +100,7 @@ fn update_assertion(token_type: Web3TokenType, balance: f64, credential: &mut Cr
let min_item = AssertionLogic::new_item(
ASSERTION_KEYS.holding_amount,
Op::GreaterEq,
&format!("{}", &TOKEN_HOLDING_AMOUNT_RANGE.last().unwrap()),
&format!("{}", token_holding_amount_range.last().unwrap()),
);
assertion = assertion.add_item(min_item);

Expand Down
80 changes: 75 additions & 5 deletions tee-worker/litentry/core/data-providers/src/moralis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,25 @@ impl NftApiList for MoralisClient {
}

#[derive(Serialize, Deserialize, Debug)]
pub struct GetSolanaNativeBalanceBalanceByWalletResponse {
pub struct GetSolanaNativeBalanceByWalletResponse {
pub lamports: String,
pub solana: String,
}

impl<'a> RestPath<ReqPath<'a>> for GetSolanaNativeBalanceBalanceByWalletResponse {
impl<'a> RestPath<ReqPath<'a>> for GetSolanaNativeBalanceByWalletResponse {
fn get_path(path: ReqPath) -> Result<String, HttpError> {
Ok(path.path.into())
}
}

#[derive(Serialize, Deserialize, Debug)]
pub struct GetSolanaTokenBalanceByWalletResponse {
// token address
pub mint: String,
pub amount: String,
}

impl<'a> RestPath<ReqPath<'a>> for Vec<GetSolanaTokenBalanceByWalletResponse> {
fn get_path(path: ReqPath) -> Result<String, HttpError> {
Ok(path.path.into())
}
Expand All @@ -231,7 +244,13 @@ pub trait BalanceApiList {
&mut self,
address: String,
fast_fail: bool,
) -> Result<GetSolanaNativeBalanceBalanceByWalletResponse, Error>;
) -> Result<GetSolanaNativeBalanceByWalletResponse, Error>;

fn get_solana_tokens_balance_by_wallet(
&mut self,
address: String,
fast_fail: bool,
) -> Result<Vec<GetSolanaTokenBalanceByWalletResponse>, Error>;
}

impl BalanceApiList for MoralisClient {
Expand All @@ -240,13 +259,13 @@ impl BalanceApiList for MoralisClient {
&mut self,
address: String,
fast_fail: bool,
) -> Result<GetSolanaNativeBalanceBalanceByWalletResponse, Error> {
) -> Result<GetSolanaNativeBalanceByWalletResponse, Error> {
let params =
MoralisRequest { path: format!("account/mainnet/{}/balance", address), query: None };

debug!("get_solana_native_balance_by_wallet, address: {:?}", address);

match self.get::<GetSolanaNativeBalanceBalanceByWalletResponse>(
match self.get::<GetSolanaNativeBalanceByWalletResponse>(
ClientType::Solana,
params,
fast_fail,
Expand All @@ -261,6 +280,33 @@ impl BalanceApiList for MoralisClient {
},
}
}

// https://docs.moralis.io/web3-data-api/solana/reference/get-spl
fn get_solana_tokens_balance_by_wallet(
&mut self,
address: String,
fast_fail: bool,
) -> Result<Vec<GetSolanaTokenBalanceByWalletResponse>, Error> {
let params =
MoralisRequest { path: format!("account/mainnet/{}/tokens", address), query: None };

debug!("get_solana_tokens_balance_by_wallet, address: {:?}", address);

match self.get::<Vec<GetSolanaTokenBalanceByWalletResponse>>(
ClientType::Solana,
params,
fast_fail,
) {
Ok(resp) => {
debug!("get_solana_tokens_balance_by_wallet, response: {:?}", resp);
Ok(resp)
},
Err(e) => {
debug!("get_solana_tokens_balance_by_wallet, error: {:?}", e);
Err(e)
},
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -323,4 +369,28 @@ mod tests {
assert_eq!(result.lamports, "0");
assert_eq!(result.solana, "0");
}

#[test]
fn does_get_solana_tokens_balance_by_wallet_works() {
let config = init();
let mut client = MoralisClient::new(&config);
let mut result = client
.get_solana_tokens_balance_by_wallet(
"EJpLyTeE8XHG9CeREeHd6pr6hNhaRnTRJx4Z5DPhEJJ6".into(),
true,
)
.unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result[0].mint, "FADm4QuSUF1K526LvTjvbJjKzeeipP6bj5bSzp3r6ipq");
assert_eq!(result[0].amount, "405219.979008");
assert_eq!(result[1].mint, "BNrgKeLwMUwWQYovZpANYQNCC7Aw8FgvFL3GQut1gL6B");
assert_eq!(result[1].amount, "31");
result = client
.get_solana_tokens_balance_by_wallet(
"EJpLyTeE8XHG9CeREeHd6pr6hNhaRnTRJx4Z5DPhEJJ1".into(),
true,
)
.unwrap();
assert_eq!(result.len(), 0);
}
}
52 changes: 36 additions & 16 deletions tee-worker/litentry/core/mock-server/src/moralis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
use std::collections::HashMap;

use lc_data_providers::moralis::{
GetNftsByWalletResult, GetSolanaNativeBalanceBalanceByWalletResponse, MoralisPageResponse,
GetNftsByWalletResult, GetSolanaNativeBalanceByWalletResponse,
GetSolanaTokenBalanceByWalletResponse, MoralisPageResponse,
};

use warp::{http::Response, Filter};
Expand Down Expand Up @@ -59,20 +60,39 @@ pub(crate) fn query() -> impl Filter<Extract = impl warp::Reply, Error = warp::R
pub(crate) fn query_solana(
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::get()
.and(warp::path!("moralis_solana" / "account" / "mainnet" / String / "balance"))
.map(move |address| {
if address == "EJpLyTeE8XHG9CeREeHd6pr6hNhaRnTRJx4Z5DPhEJJ6" {
let body = GetSolanaNativeBalanceBalanceByWalletResponse {
lamports: "5903457912".into(),
solana: "5.903457912".into(),
};
Response::builder().body(serde_json::to_string(&body).unwrap())
} else {
let body = GetSolanaNativeBalanceBalanceByWalletResponse {
lamports: "0".into(),
solana: "0".into(),
};
Response::builder().body(serde_json::to_string(&body).unwrap())
}
.and(warp::path!("moralis_solana" / "account" / "mainnet" / String / String))
.map(move |address: String, api: String| match api.as_str() {
"balance" =>
if address == "EJpLyTeE8XHG9CeREeHd6pr6hNhaRnTRJx4Z5DPhEJJ6" {
let body = GetSolanaNativeBalanceByWalletResponse {
lamports: "5903457912".into(),
solana: "5.903457912".into(),
};
Response::builder().body(serde_json::to_string(&body).unwrap())
} else {
let body = GetSolanaNativeBalanceByWalletResponse {
lamports: "0".into(),
solana: "0".into(),
};
Response::builder().body(serde_json::to_string(&body).unwrap())
},
"tokens" =>
if address == "EJpLyTeE8XHG9CeREeHd6pr6hNhaRnTRJx4Z5DPhEJJ6" {
let body = vec![
GetSolanaTokenBalanceByWalletResponse {
mint: "FADm4QuSUF1K526LvTjvbJjKzeeipP6bj5bSzp3r6ipq".into(),
amount: "405219.979008".into(),
},
GetSolanaTokenBalanceByWalletResponse {
mint: "BNrgKeLwMUwWQYovZpANYQNCC7Aw8FgvFL3GQut1gL6B".into(),
amount: "31".into(),
},
];
Response::builder().body(serde_json::to_string(&body).unwrap())
} else {
let body: Vec<GetSolanaTokenBalanceByWalletResponse> = vec![];
Response::builder().body(serde_json::to_string(&body).unwrap())
},
_ => Response::builder().status(404).body(String::from("Error query")),
})
}
Loading

0 comments on commit 22f3547

Please sign in to comment.