Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions contract/contracts/subscription/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ impl MyfansContract {
token_client.transfer(&fan, &fee_recipient, &fee);
}

let expiry = env.ledger().timestamp() + (plan.interval_days as u64 * 86400);
let expiry = env.ledger().sequence() + (plan.interval_days * 17280);
let sub = Subscription {
fan: fan.clone(),
plan_id,
expiry,
expiry: expiry as u64,
};
env.storage()
.instance()
Expand All @@ -114,12 +114,68 @@ impl MyfansContract {
.instance()
.get::<DataKey, Subscription>(&DataKey::Sub(fan, creator))
{
sub.expiry > env.ledger().timestamp()
env.ledger().sequence() <= sub.expiry as u32
} else {
false
}
}

pub fn extend_subscription(
env: Env,
fan: Address,
creator: Address,
extra_ledgers: u32,
token: Address,
) {
fan.require_auth();

let sub: Subscription = env
.storage()
.instance()
.get(&DataKey::Sub(fan.clone(), creator.clone()))
.expect("subscription not found");

if env.ledger().sequence() > sub.expiry as u32 {
panic!("subscription expired");
}

let plan: Plan = env
.storage()
.instance()
.get(&DataKey::Plan(sub.plan_id))
.unwrap();

let fee_bps: u32 = env.storage().instance().get(&DataKey::FeeBps).unwrap_or(0);
let fee_recipient: Address = env
.storage()
.instance()
.get(&DataKey::FeeRecipient)
.unwrap();

let fee = (plan.amount * fee_bps as i128) / 10000;
let creator_amount = plan.amount - fee;

let token_client = token::Client::new(&env, &token);
token_client.transfer(&fan, &creator, &creator_amount);
if fee > 0 {
token_client.transfer(&fan, &fee_recipient, &fee);
}

let new_expiry = sub.expiry + extra_ledgers as u64;
let updated_sub = Subscription {
fan: fan.clone(),
plan_id: sub.plan_id,
expiry: new_expiry,
};

env.storage()
.instance()
.set(&DataKey::Sub(fan.clone(), creator), &updated_sub);

env.events()
.publish((Symbol::new(&env, "extended"), sub.plan_id), fan);
}

pub fn cancel(env: Env, fan: Address, creator: Address) {
fan.require_auth();
env.storage()
Expand Down
103 changes: 99 additions & 4 deletions contract/contracts/subscription/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn test_subscribe_full_flow() {
assert_eq!(plan_id, 1);

// Subscribe calls token transfer, so it will deduct from fan
client.subscribe(&fan, &plan_id);
client.subscribe(&fan, &plan_id, &token.address);

// Check balances
// Fan paid 1000, should have 9000
Expand Down Expand Up @@ -74,7 +74,7 @@ fn test_subscribe_insufficient_balance_reverts() {
let plan_id = client.create_plan(&creator, &token.address, &1000, &30);

// This should panic due to token transfer failure automatically mapped inside Soroban
client.subscribe(&fan, &plan_id);
client.subscribe(&fan, &plan_id, &token.address);
}

#[test]
Expand All @@ -92,7 +92,7 @@ fn test_platform_fee_zero() {
token_admin.mint(&fan, &10000);

let plan_id = client.create_plan(&creator, &token.address, &1000, &30);
client.subscribe(&fan, &plan_id);
client.subscribe(&fan, &plan_id, &token.address);

// Fee is 0%. Creator gets all 1000.
assert_eq!(token.balance(&fee_recipient), 0);
Expand All @@ -110,10 +110,105 @@ fn test_cancel_subscription() {

token_admin.mint(&fan, &10000);
let plan_id = client.create_plan(&creator, &token.address, &1000, &30);
client.subscribe(&fan, &plan_id);
client.subscribe(&fan, &plan_id, &token.address);

assert!(client.is_subscriber(&fan, &creator));

client.cancel(&fan, &creator);
assert!(!client.is_subscriber(&fan, &creator));
}

#[test]
fn test_is_subscribed_false_after_expiry() {
let (env, client, admin, token, token_admin) = setup_test();
let fee_recipient = Address::generate(&env);
client.init(&admin, &0, &fee_recipient);

let creator = Address::generate(&env);
let fan = Address::generate(&env);

token_admin.mint(&fan, &10000);
let plan_id = client.create_plan(&creator, &token.address, &1000, &1);
client.subscribe(&fan, &plan_id, &token.address);

assert!(client.is_subscriber(&fan, &creator));

env.ledger().with_mut(|li| {
li.sequence_number += 17281;
});

assert!(!client.is_subscriber(&fan, &creator));
}

#[test]
fn test_extend_updates_expiry() {
let (env, client, admin, token, token_admin) = setup_test();
let fee_recipient = Address::generate(&env);
client.init(&admin, &0, &fee_recipient);

let creator = Address::generate(&env);
let fan = Address::generate(&env);

token_admin.mint(&fan, &20000);
let plan_id = client.create_plan(&creator, &token.address, &1000, &1);
client.subscribe(&fan, &plan_id, &token.address);

let initial_ledger = env.ledger().sequence();
let expected_expiry = initial_ledger + 17280;

env.ledger().with_mut(|li| {
li.sequence_number += 10000;
});

assert!(client.is_subscriber(&fan, &creator));

client.extend_subscription(&fan, &creator, &17280, &token.address);

env.ledger().with_mut(|li| {
li.sequence_number = expected_expiry + 1;
});

assert!(client.is_subscriber(&fan, &creator));
}

#[test]
fn test_extend_requires_payment() {
let (env, client, admin, token, token_admin) = setup_test();
let fee_recipient = Address::generate(&env);
client.init(&admin, &0, &fee_recipient);

let creator = Address::generate(&env);
let fan = Address::generate(&env);

token_admin.mint(&fan, &20000);
let plan_id = client.create_plan(&creator, &token.address, &1000, &1);
client.subscribe(&fan, &plan_id, &token.address);

assert_eq!(token.balance(&creator), 1000);

client.extend_subscription(&fan, &creator, &17280, &token.address);

assert_eq!(token.balance(&creator), 2000);
assert_eq!(token.balance(&fan), 18000);
}

#[test]
#[should_panic(expected = "subscription expired")]
fn test_extend_fails_if_expired() {
let (env, client, admin, token, token_admin) = setup_test();
let fee_recipient = Address::generate(&env);
client.init(&admin, &0, &fee_recipient);

let creator = Address::generate(&env);
let fan = Address::generate(&env);

token_admin.mint(&fan, &20000);
let plan_id = client.create_plan(&creator, &token.address, &1000, &1);
client.subscribe(&fan, &plan_id, &token.address);

env.ledger().with_mut(|li| {
li.sequence_number += 17281;
});

client.extend_subscription(&fan, &creator, &17280, &token.address);
}