Require chain ID field in payment options of ethereum type
This commit is contained in:
parent
690a03946e
commit
c5c3911de6
7 changed files with 65 additions and 17 deletions
|
@ -0,0 +1,9 @@
|
||||||
|
UPDATE actor_profile
|
||||||
|
SET payment_options = (
|
||||||
|
-- remove all payment options except links
|
||||||
|
SELECT COALESCE (
|
||||||
|
jsonb_agg(opt) FILTER (WHERE opt ->> 'payment_type' = '1'),
|
||||||
|
'[]'
|
||||||
|
)
|
||||||
|
FROM jsonb_array_elements(actor_profile.payment_options) AS opt
|
||||||
|
);
|
|
@ -68,8 +68,9 @@ pub fn attach_payment_option(
|
||||||
payment_option: PaymentOption,
|
payment_option: PaymentOption,
|
||||||
) -> ActorAttachment {
|
) -> ActorAttachment {
|
||||||
match payment_option {
|
match payment_option {
|
||||||
|
// Local actors can't have payment links
|
||||||
PaymentOption::Link(_) => unimplemented!(),
|
PaymentOption::Link(_) => unimplemented!(),
|
||||||
PaymentOption::EthereumSubscription => {
|
PaymentOption::EthereumSubscription(_) => {
|
||||||
let name = "EthereumSubscription".to_string();
|
let name = "EthereumSubscription".to_string();
|
||||||
let subscription_page_url =
|
let subscription_page_url =
|
||||||
get_subscription_page_url(instance_url, user_id);
|
get_subscription_page_url(instance_url, user_id);
|
||||||
|
@ -132,6 +133,7 @@ pub fn parse_extra_field(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::utils::caip2::ChainId;
|
||||||
use crate::utils::id::new_uuid;
|
use crate::utils::id::new_uuid;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -155,7 +157,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_payment_option() {
|
fn test_payment_option() {
|
||||||
let user_id = new_uuid();
|
let user_id = new_uuid();
|
||||||
let payment_option = PaymentOption::EthereumSubscription;
|
let payment_option =
|
||||||
|
PaymentOption::ethereum_subscription(ChainId::ethereum_mainnet());
|
||||||
let subscription_page_url =
|
let subscription_page_url =
|
||||||
format!("https://example.com/profile/{}/subscription", user_id);
|
format!("https://example.com/profile/{}/subscription", user_id);
|
||||||
let attachment = attach_payment_option(
|
let attachment = attach_payment_option(
|
||||||
|
|
|
@ -12,9 +12,9 @@ pub async fn apply_migrations(db_client: &mut Client) {
|
||||||
|
|
||||||
for migration in migration_report.applied_migrations() {
|
for migration in migration_report.applied_migrations() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Migration Applied - Name: {}, Version: {}",
|
"migration applied: version {} ({})",
|
||||||
migration.name(),
|
|
||||||
migration.version(),
|
migration.version(),
|
||||||
|
migration.name(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ impl Account {
|
||||||
.map(|option| {
|
.map(|option| {
|
||||||
match option {
|
match option {
|
||||||
PaymentOption::Link(link) => link.href,
|
PaymentOption::Link(link) => link.href,
|
||||||
PaymentOption::EthereumSubscription => {
|
PaymentOption::EthereumSubscription(_) => {
|
||||||
get_subscription_page_url(instance_url, &profile.id)
|
get_subscription_page_url(instance_url, &profile.id)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,9 @@ pub async fn subscriptions_enabled(
|
||||||
let mut maybe_payment_option = None;
|
let mut maybe_payment_option = None;
|
||||||
match subscription_settings.into_inner() {
|
match subscription_settings.into_inner() {
|
||||||
SubscriptionSettings::Ethereum => {
|
SubscriptionSettings::Ethereum => {
|
||||||
|
let ethereum_config = config.blockchain.as_ref()
|
||||||
|
.and_then(|conf| conf.ethereum_config())
|
||||||
|
.ok_or(HttpError::NotSupported)?;
|
||||||
let contract_set = maybe_blockchain.as_ref().as_ref()
|
let contract_set = maybe_blockchain.as_ref().as_ref()
|
||||||
.ok_or(HttpError::NotSupported)?;
|
.ok_or(HttpError::NotSupported)?;
|
||||||
let wallet_address = current_user
|
let wallet_address = current_user
|
||||||
|
@ -78,7 +81,9 @@ pub async fn subscriptions_enabled(
|
||||||
if !is_registered {
|
if !is_registered {
|
||||||
return Err(ValidationError("recipient is not registered").into());
|
return Err(ValidationError("recipient is not registered").into());
|
||||||
};
|
};
|
||||||
maybe_payment_option = Some(PaymentOption::EthereumSubscription);
|
maybe_payment_option = Some(PaymentOption::ethereum_subscription(
|
||||||
|
ethereum_config.chain_id.clone(),
|
||||||
|
));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
SubscriptionSettings::Monero { } => {
|
SubscriptionSettings::Monero { } => {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::activitypub::identifiers::local_actor_id;
|
||||||
use crate::database::json_macro::{json_from_sql, json_to_sql};
|
use crate::database::json_macro::{json_from_sql, json_to_sql};
|
||||||
use crate::errors::{ConversionError, ValidationError};
|
use crate::errors::{ConversionError, ValidationError};
|
||||||
use crate::ethereum::identity::DidPkh;
|
use crate::ethereum::identity::DidPkh;
|
||||||
|
use crate::utils::caip2::ChainId;
|
||||||
use super::validators::{
|
use super::validators::{
|
||||||
validate_username,
|
validate_username,
|
||||||
validate_display_name,
|
validate_display_name,
|
||||||
|
@ -76,17 +77,26 @@ pub struct PaymentLink {
|
||||||
pub href: String,
|
pub href: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct EthereumSubscription {
|
||||||
|
chain_id: ChainId,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PaymentOption {
|
pub enum PaymentOption {
|
||||||
Link(PaymentLink),
|
Link(PaymentLink),
|
||||||
EthereumSubscription,
|
EthereumSubscription(EthereumSubscription),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaymentOption {
|
impl PaymentOption {
|
||||||
|
pub fn ethereum_subscription(chain_id: ChainId) -> Self {
|
||||||
|
Self::EthereumSubscription(EthereumSubscription { chain_id })
|
||||||
|
}
|
||||||
|
|
||||||
fn payment_type(&self) -> PaymentType {
|
fn payment_type(&self) -> PaymentType {
|
||||||
match self {
|
match self {
|
||||||
Self::Link(_) => PaymentType::Link,
|
Self::Link(_) => PaymentType::Link,
|
||||||
Self::EthereumSubscription => PaymentType::EthereumSubscription,
|
Self::EthereumSubscription(_) => PaymentType::EthereumSubscription,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +119,11 @@ impl<'de> Deserialize<'de> for PaymentOption {
|
||||||
.map_err(DeserializerError::custom)?;
|
.map_err(DeserializerError::custom)?;
|
||||||
Self::Link(link)
|
Self::Link(link)
|
||||||
},
|
},
|
||||||
PaymentType::EthereumSubscription => Self::EthereumSubscription,
|
PaymentType::EthereumSubscription => {
|
||||||
|
let payment_info = EthereumSubscription::deserialize(value)
|
||||||
|
.map_err(DeserializerError::custom)?;
|
||||||
|
Self::EthereumSubscription(payment_info)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Ok(payment_option)
|
Ok(payment_option)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +139,9 @@ impl Serialize for PaymentOption {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Link(link) => link.serialize(FlatMapSerializer(&mut map))?,
|
Self::Link(link) => link.serialize(FlatMapSerializer(&mut map))?,
|
||||||
Self::EthereumSubscription => (),
|
Self::EthereumSubscription(payment_info) => {
|
||||||
|
payment_info.serialize(FlatMapSerializer(&mut map))?
|
||||||
|
},
|
||||||
};
|
};
|
||||||
map.end()
|
map.end()
|
||||||
}
|
}
|
||||||
|
@ -384,14 +400,15 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_payment_option_ethereum_subscription_serialization() {
|
fn test_payment_option_ethereum_subscription_serialization() {
|
||||||
let json_data = r#"{"payment_type":2,"name":null,"href":null}"#;
|
let json_data = r#"{"payment_type":2,"chain_id":"eip155:1","name":null}"#;
|
||||||
let payment_option: PaymentOption = serde_json::from_str(json_data).unwrap();
|
let payment_option: PaymentOption = serde_json::from_str(json_data).unwrap();
|
||||||
assert!(matches!(
|
let payment_info = match payment_option {
|
||||||
payment_option,
|
PaymentOption::EthereumSubscription(ref payment_info) => payment_info,
|
||||||
PaymentOption::EthereumSubscription,
|
_ => panic!("wrong option"),
|
||||||
));
|
};
|
||||||
|
assert_eq!(payment_info.chain_id, ChainId::ethereum_mainnet());
|
||||||
let serialized = serde_json::to_string(&payment_option).unwrap();
|
let serialized = serde_json::to_string(&payment_option).unwrap();
|
||||||
assert_eq!(serialized, r#"{"payment_type":2}"#);
|
assert_eq!(serialized, r#"{"payment_type":2,"chain_id":"eip155:1"}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -2,7 +2,13 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Deserializer, de::Error as DeserializerError};
|
use serde::{
|
||||||
|
Deserialize,
|
||||||
|
Deserializer,
|
||||||
|
Serialize,
|
||||||
|
Serializer,
|
||||||
|
de::Error as DeserializerError,
|
||||||
|
};
|
||||||
|
|
||||||
const CAIP2_RE: &str = r"(?P<namespace>[-a-z0-9]{3,8}):(?P<reference>[-a-zA-Z0-9]{1,32})";
|
const CAIP2_RE: &str = r"(?P<namespace>[-a-z0-9]{3,8}):(?P<reference>[-a-zA-Z0-9]{1,32})";
|
||||||
const CAIP2_ETHEREUM_NAMESPACE: &str = "eip155";
|
const CAIP2_ETHEREUM_NAMESPACE: &str = "eip155";
|
||||||
|
@ -47,6 +53,14 @@ impl ToString for ChainId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for ChainId {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for ChainId {
|
impl<'de> Deserialize<'de> for ChainId {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where D: Deserializer<'de>
|
where D: Deserializer<'de>
|
||||||
|
|
Loading…
Reference in a new issue