Skip to content

Commit

Permalink
Merge pull request #49 from TaggrNetwork/release
Browse files Browse the repository at this point in the history
Release: reaction cost reductions, feature closing functionality and bugfixes
  • Loading branch information
taggrx authored Apr 26, 2024
2 parents 7bd956c + d4601a0 commit 2065327
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 99 deletions.
6 changes: 3 additions & 3 deletions docs/WHITEPAPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ Below is a breakdown of costs.
| Hashtags | `T * followers(T)` | Each unique hashtag `T` is charged with the number of credits corresponding to the number of its followers |
| On-chain pictures | `B * $blob_cost` | For `B` pictures in a post or comment |
| Poll | `$poll_cost` | For adding a poll to a post or comment |
| Reacting with ❤️ , 👍, 😢 | `3` | Gives `2` reward points, burns the rest as a fee. |
| Reacting with 🔥, 😂, 🚀, 💯 | `12` | Gives `10` rewards points, burns the rest as a fee. |
| Reacting with ⭐️, 🏴‍☠️ | `23` | Gives `20` reward points, burns the rest as a fee. |
| Reacting with ❤️ , 👍, 😢 | `2` | Gives `1` reward points, burns the rest as a fee. |
| Reacting with 🔥, 😂, 🚀, 💯 | `6` | Gives `5` rewards points, burns the rest as a fee. |
| Reacting with ⭐️, 🏴‍☠️ | `11` | Gives `10` reward points, burns the rest as a fee. |
| Reacting with 👎 | `3` | Burns `3` credits and rewards of post's author and burns 3 credits of the user. |
| New realm creation | `$realm_cost` | Burns `$realm_cost` credits |

Expand Down
12 changes: 8 additions & 4 deletions e2e/test1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ test.describe("Upgrades & token transfer flow", () => {
.click();
// React with a star
await page
.locator('button[title="Reward points: 20"]')
.locator('button[title="Reward points: 10"]')
.first()
.click({ delay: 3000 });
await page.waitForTimeout(4500);
Expand All @@ -83,7 +83,7 @@ test.describe("Upgrades & token transfer flow", () => {
await page.goto("/");
await page.getByTestId("toggle-user-section").click();

await expect(page.getByTestId("token-balance")).toHaveText("20");
await expect(page.getByTestId("token-balance")).toHaveText("10");

const transferExecuted = new Promise((resolve, _reject) => {
page.on("dialog", async (dialog) => {
Expand All @@ -108,7 +108,7 @@ test.describe("Upgrades & token transfer flow", () => {

await transferExecuted;

await expect(page.getByTestId("token-balance")).toHaveText("14.75");
await expect(page.getByTestId("token-balance")).toHaveText("4.75");
await page.getByTestId("token-balance").click();
await page.getByRole("link", { name: "6qfxa" }).click();
await expect(
Expand Down Expand Up @@ -220,7 +220,11 @@ test.describe("Upgrades & token transfer flow", () => {
await fileChooser.setFiles([binaryPath]);
// Wait for async proposal validation
await page.waitForTimeout(2000);
await page.locator("input[type=text]").fill("coffeecoffeecoffee");
await page
.locator("div")
.filter({ hasText: /^GIT COMMIT$/ })
.getByRole("textbox")
.fill("coffeecoffeecoffee");
await page.getByRole("button", { name: "SUBMIT" }).click();
await expect(page.getByText(/STATUS.*OPEN/)).toBeVisible();
await expect(page.getByText("TYPE: RELEASE")).toBeVisible();
Expand Down
2 changes: 1 addition & 1 deletion e2e/test2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ test.describe("Regular users flow", () => {
// React with a star
await page
.locator(".feed_item", { hasText: /Hello world/ })
.locator('button[title="Reward points: 20"]')
.locator('button[title="Reward points: 10"]')
.first()
.click({ delay: 3000 });
// comment on the first post
Expand Down
4 changes: 2 additions & 2 deletions e2e/test4.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ test.describe("Report and transfer to user", () => {
});
await feedItem.getByTestId("reaction-picker").click();
await feedItem
.locator('button[title="Reward points: 20"]')
.locator('button[title="Reward points: 10"]')
.first()
.click({ delay: 3000 });

Expand Down Expand Up @@ -204,7 +204,7 @@ test.describe("Report and transfer to user", () => {
await page.waitForTimeout(1000);
await page.getByTestId("toggle-user-section").click();

await expect(page.getByTestId("token-balance")).toHaveText("20");
await expect(page.getByTestId("token-balance")).toHaveText("10");

const transferExecuted = new Promise((resolve, _reject) => {
page.on("dialog", async (dialog) => {
Expand Down
30 changes: 15 additions & 15 deletions src/backend/env/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,30 +317,30 @@ pub const CONFIG: &Config = &Config {

reactions: &[
// sad, thumb up, heart
(11, 2),
(10, 2),
(12, 2),
(11, 1),
(10, 1),
(12, 1),
// rocket, 100, joy, flame,
(53, 10),
(52, 10),
(51, 10),
(50, 10),
(53, 5),
(52, 5),
(51, 5),
(50, 5),
// thumb down
// star, pirate
(100, 20),
(101, 20),
(100, 10),
(101, 10),
(1, -3),
],

min_positive_reaction_id: 10,

reaction_fee: &[
(100, 3),
(101, 3),
(50, 2),
(51, 2),
(52, 2),
(53, 2),
(100, 1),
(101, 1),
(50, 1),
(51, 1),
(52, 1),
(53, 1),
(10, 1),
(11, 1),
(12, 1),
Expand Down
11 changes: 11 additions & 0 deletions src/backend/env/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,14 @@ pub fn create_feature(caller: Principal, post_id: PostId) -> Result<(), String>
Ok(())
})
}

pub fn close_feature(state: &mut State, post_id: PostId) -> Result<(), String> {
let mut feature = state.memory.features.remove(&post_id)?;
feature.status = 1;
state
.memory
.features
.insert(post_id, feature)
.expect("couldn't re-insert feature");
Ok(())
}
69 changes: 39 additions & 30 deletions src/backend/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::token::{Account, Token, Transaction};
use crate::{assets, id, mutate, read, time};
use candid::Principal;
use config::{CONFIG, ICP_CYCLES_PER_XDR};
use ic_cdk::api::performance_counter;
use ic_cdk::api::stable::stable64_size;
use ic_cdk::api::{self, canister_balance};
use ic_ledger_types::{AccountIdentifier, DEFAULT_SUBACCOUNT, MAINNET_LEDGER_CANISTER_ID};
Expand Down Expand Up @@ -202,10 +203,6 @@ pub struct State {

migrations: BTreeSet<UserId>,

// TODO: delete
#[serde(skip)]
pub posts_with_tags: Vec<PostId>,

#[serde(default)]
pub recent_tags: VecDeque<String>,

Expand Down Expand Up @@ -1615,12 +1612,13 @@ impl State {
)
});

let log_time = |state: &mut State, frequency, threshold_millis| {
let log = |state: &mut State, frequency, threshold_millis| {
let instructions = performance_counter(0) / 1000000000;
let millis = (time() - now) / MILLISECOND;
if millis > threshold_millis {
state.logger.debug(format!(
"{} routine finished after `{}` ms.",
frequency, millis
"{} routine finished after `{}` ms and used `{}B` instructions.",
frequency, millis, instructions
))
}
};
Expand All @@ -1629,7 +1627,7 @@ impl State {
State::weekly_chores(now).await;
mutate(|state| {
state.last_weekly_chores += WEEK;
log_time(state, "Weekly", 0);
log(state, "Weekly", 0);
});
}

Expand All @@ -1643,15 +1641,15 @@ impl State {
state.pending_polls.len(),
state.migrations.len(),
));
log_time(state, "Daily", 60000);
log(state, "Daily", 0);
});
}

if last_hourly_chores + HOUR < now {
State::hourly_chores(now).await;
mutate(|state| {
state.last_hourly_chores += HOUR;
log_time(state, "Hourly", 0);
log(state, "Hourly", 60000);
});
}
}
Expand Down Expand Up @@ -1733,10 +1731,21 @@ impl State {
self.distribution_reports.push(summary);
}

// Refresh tag costs, mark inactive users as inactive
fn clean_up(&mut self, now: Time) {
for tag in self.tag_indexes.values_mut() {
tag.subscribers = 0;
}
for user in self.users.values_mut() {
if user.active_within_weeks(now, 1) {
user.active_weeks += 1;

// Count this active user's subscriptions
for tag in user.feeds.iter().flat_map(|feed| feed.iter()) {
if let Some(index) = self.tag_indexes.get_mut(tag) {
index.subscribers += 1
}
}
} else {
user.deactivate();
}
Expand Down Expand Up @@ -3054,15 +3063,15 @@ pub(crate) mod tests {

let user = state.users.get(&user_0).unwrap();
assert_eq!(user.karma_donations.len(), 2);
assert_eq!(user.karma_donations.get(&user_1).unwrap(), &20);
assert_eq!(user.karma_donations.get(&user_2).unwrap(), &10);
assert_eq!(user.karma_donations.get(&user_1).unwrap(), &10);
assert_eq!(user.karma_donations.get(&user_2).unwrap(), &5);

let post_2 =
Post::create(state, "B".to_string(), &[], pr(2), 0, None, None, None).unwrap();
assert!(state.react(pr(0), post_2, 100, WEEK).is_ok());
let user = state.users.get(&user_0).unwrap();
assert_eq!(user.karma_donations.len(), 2);
assert_eq!(user.karma_donations.get(&user_2).unwrap(), &30);
assert_eq!(user.karma_donations.get(&user_2).unwrap(), &15);
})
}

Expand Down Expand Up @@ -3539,21 +3548,21 @@ pub(crate) mod tests {
}

assert_eq!(state.realms.values().next().unwrap().revenue, 200);
assert_eq!(state.principal_to_user(pr(0)).unwrap().rewards(), 1000);
assert_eq!(state.principal_to_user(pr(1)).unwrap().rewards(), 1000);
assert_eq!(state.principal_to_user(pr(0)).unwrap().rewards(), 500);
assert_eq!(state.principal_to_user(pr(1)).unwrap().rewards(), 500);
assert_eq!(state.principal_to_user(pr(2)).unwrap().rewards(), 0);
assert_eq!(state.burned_cycles, 500);
assert_eq!(state.burned_cycles, 300);
state.distribute_realm_revenue(WEEK + WEEK / 2);
assert_eq!(state.realms.values().next().unwrap().revenue, 0);
let expected_revenue = (200 / 100 * CONFIG.realm_revenue_percentage / 2) as i64;
assert_eq!(state.burned_cycles, 500 - 2 * expected_revenue);
assert_eq!(state.burned_cycles, 300 - 2 * expected_revenue);
assert_eq!(
state.principal_to_user(pr(0)).unwrap().rewards(),
1000 + expected_revenue
500 + expected_revenue
);
assert_eq!(
state.principal_to_user(pr(1)).unwrap().rewards(),
1000 + expected_revenue
500 + expected_revenue
);
assert_eq!(state.principal_to_user(pr(2)).unwrap().rewards(), 0);
})
Expand Down Expand Up @@ -3689,13 +3698,13 @@ pub(crate) mod tests {

assert_eq!(
state.users.get(&id).unwrap().rewards() as Credits,
20 + 10 + 2 * CONFIG.response_reward
10 + 5 + 2 * CONFIG.response_reward
);

assert_eq!(
state.users.get_mut(&upvoter_id).unwrap().credits(),
// reward + fee + post creation
upvoter_credits - 20 - 3 - 2
upvoter_credits - 10 - 1 - 2
);

let versions = vec!["a".into(), "b".into()];
Expand All @@ -3709,7 +3718,7 @@ pub(crate) mod tests {
.unwrap();
assert_eq!(
state.delete_post(pr(0), post_id, versions.clone()),
Err("not enough credits (this post requires 62 credits to be deleted)".into())
Err("not enough credits (this post requires 47 credits to be deleted)".into())
);

state
Expand All @@ -3727,7 +3736,7 @@ pub(crate) mod tests {
assert_eq!(
state.users.get(&upvoter_id).unwrap().credits(),
// reward received back
upvoter_credits - 20 - 3 - 2 + 20
upvoter_credits - 10 - 1 - 2 + 10
);
assert_eq!(state.users.get(&id).unwrap().rewards(), 0);

Expand Down Expand Up @@ -4457,9 +4466,9 @@ pub(crate) mod tests {
assert!(state.react(p, post_id, 50, 0).is_ok());
assert!(state.react(p, post_id, 100, 0).is_err());
assert!(state.react(p2, post_id, 100, 0).is_ok());
let reaction_costs_1 = 12;
let burned_credits_by_reactions = 2 + 3;
let mut rewards_from_reactions = 10 + 20;
let reaction_costs_1 = 6;
let burned_credits_by_reactions = 1 + 1;
let mut rewards_from_reactions = 5 + 10;

// try to self upvote (should be a no-op)
assert!(state.react(p0, post_id, 100, 0).is_err());
Expand Down Expand Up @@ -4536,21 +4545,21 @@ pub(crate) mod tests {
lurker.change_credits(100, CreditsDelta::Plus, "").unwrap();
let lurker_principal = lurker.principal;
assert!(state.react(lurker_principal, id, 50, 0).is_ok());
assert_eq!(state.users.get(&user_id111).unwrap().rewards(), 10);
assert_eq!(state.users.get(&user_id111).unwrap().rewards(), 5);

// another reaction on a new post
let id =
Post::create(state, "t".to_string(), &[], pr(55), 0, Some(0), None, None).unwrap();
assert!(state.react(lurker_principal, id, 50, 0).is_ok());

assert_eq!(state.users.get(&user_id111).unwrap().rewards(), 20);
assert_eq!(state.users.get(&user_id111).unwrap().rewards(), 10);

// another reaction on a new post
let id =
Post::create(state, "t".to_string(), &[], pr(55), 0, Some(0), None, None).unwrap();
assert!(state.react(lurker_principal, id, 50, 0).is_ok());

assert_eq!(state.users.get(&user_id111).unwrap().rewards(), 30);
assert_eq!(state.users.get(&user_id111).unwrap().rewards(), 15);
})
}

Expand All @@ -4562,7 +4571,7 @@ pub(crate) mod tests {
create_user(state, pr(2));
let c = CONFIG;

for (reaction, total_fee) in &[(10, 1), (50, 2), (101, 3)] {
for (reaction, total_fee) in &[(10, 1), (50, 1), (101, 1)] {
state.burned_cycles = 0;
let post_id =
Post::create(state, "test".to_string(), &[], pr(0), 0, None, None, None)
Expand Down
5 changes: 2 additions & 3 deletions src/backend/env/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::cmp::{Ordering, PartialOrd};
use super::reports::ReportState;
use super::*;
use super::{storage::Storage, user::UserId};
use crate::mutate;
use crate::reports::Report;
use crate::{mutate, performance_counter};
use ic_cdk::caller;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -769,10 +769,9 @@ pub fn archive_cold_posts(state: &mut State, max_posts_in_heap: usize) -> Result
state.backup_exists = false;

state.logger.debug(format!(
"`{}` posts archived (posts still in heap: `{}`, instructions used: `{}B`)",
"`{}` posts archived (posts still in heap: `{}`)",
archived_posts,
state.posts.len(),
performance_counter(0) / 1000000000
));
Ok(())
}
Expand Down
Loading

0 comments on commit 2065327

Please sign in to comment.