Skip to content

Commit

Permalink
Refund any prize message balance once it has ended
Browse files Browse the repository at this point in the history
  • Loading branch information
megrogan committed Oct 2, 2023
1 parent a6799b2 commit 28e891a
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 14 deletions.
1 change: 1 addition & 0 deletions backend/canisters/community/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Notifications for custom messages should use the sub-type ([#4465](https://github.com/open-chat-labs/open-chat/pull/4465))
- Join all community members to channels that are made public ([#4469](https://github.com/open-chat-labs/open-chat/pull/4469))
- Support prize messages in any token by getting fee from original transfer ([#4470](https://github.com/open-chat-labs/open-chat/pull/4470))
- Refund any prize message balance once it has ended ([#4476](https://github.com/open-chat-labs/open-chat/pull/4476))

## [[2.0.864](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.864-community)] - 2023-09-27

Expand Down
63 changes: 61 additions & 2 deletions backend/canisters/community/impl/src/timer_job_types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::jobs::import_groups::{finalize_group_import, mark_import_complete, process_channel_members};
use crate::mutate_state;
use crate::{mutate_state, read_state};
use canister_timer_jobs::Job;
use ledger_utils::process_transaction;
use serde::{Deserialize, Serialize};
use types::{BlobReference, ChannelId, ChatId, MessageId, MessageIndex};
use tracing::error;
use types::{BlobReference, CanisterId, ChannelId, ChatId, MessageId, MessageIndex, PendingCryptoTransaction};
use utils::time::HOUR_IN_MS;

#[derive(Serialize, Deserialize, Clone)]
pub enum TimerJob {
Expand All @@ -12,6 +15,8 @@ pub enum TimerJob {
FinalizeGroupImport(FinalizeGroupImportJob),
ProcessGroupImportChannelMembers(ProcessGroupImportChannelMembersJob),
MarkGroupImportComplete(MarkGroupImportCompleteJob),
ClosePrize(ClosePrizeJob),
MakeTransfer(MakeTransferJob),
}

#[derive(Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -51,6 +56,18 @@ pub struct MarkGroupImportCompleteJob {
pub channel_id: ChannelId,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct ClosePrizeJob {
pub channel_id: ChannelId,
pub thread_root_message_index: Option<MessageIndex>,
pub message_index: MessageIndex,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct MakeTransferJob {
pub pending_transaction: PendingCryptoTransaction,
}

impl Job for TimerJob {
fn execute(&self) {
match self {
Expand All @@ -60,6 +77,8 @@ impl Job for TimerJob {
TimerJob::FinalizeGroupImport(job) => job.execute(),
TimerJob::ProcessGroupImportChannelMembers(job) => job.execute(),
TimerJob::MarkGroupImportComplete(job) => job.execute(),
TimerJob::ClosePrize(job) => job.execute(),
TimerJob::MakeTransfer(job) => job.execute(),
}
}
}
Expand Down Expand Up @@ -130,3 +149,43 @@ impl Job for MarkGroupImportCompleteJob {
mark_import_complete(self.group_id, self.channel_id);
}
}

impl Job for ClosePrizeJob {
fn execute(&self) {
if let Some(pending_transaction) = mutate_state(|state| {
if let Some(channel) = state.data.channels.get_mut(&self.channel_id) {
channel
.chat
.events
.close_prize(self.thread_root_message_index, self.message_index, state.env.now_nanos())
} else {
None
}
}) {
let make_transfer_job = MakeTransferJob { pending_transaction };
make_transfer_job.execute();
}
}
}

impl Job for MakeTransferJob {
fn execute(&self) {
let sender = read_state(|state| state.env.canister_id());
let pending = self.pending_transaction.clone();
ic_cdk::spawn(make_transfer(pending, sender));

async fn make_transfer(pending_transaction: PendingCryptoTransaction, sender: CanisterId) {
if let Err(error) = process_transaction(pending_transaction.clone(), sender).await {
error!(?error, "Transaction failed");
mutate_state(|state| {
let now = state.env.now();
state.data.timer_jobs.enqueue_job(
TimerJob::MakeTransfer(MakeTransferJob { pending_transaction }),
now + HOUR_IN_MS,
now,
);
});
}
}
}
}
14 changes: 13 additions & 1 deletion backend/canisters/community/impl/src/updates/send_message.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::activity_notifications::handle_activity_notification;
use crate::model::members::CommunityMembers;
use crate::model::user_groups::UserGroup;
use crate::timer_job_types::{DeleteFileReferencesJob, EndPollJob, TimerJob};
use crate::timer_job_types::{ClosePrizeJob, DeleteFileReferencesJob, EndPollJob, TimerJob};
use crate::{mutate_state, run_regular_jobs, RuntimeState};
use canister_api_macros::update_candid_and_msgpack;
use canister_timer_jobs::TimerJobs;
Expand Down Expand Up @@ -173,6 +173,18 @@ fn register_timer_jobs(
timer_jobs.enqueue_job(TimerJob::DeleteFileReferences(DeleteFileReferencesJob { files }), expiry, now);
}
}

if let MessageContent::Prize(p) = &message_event.event.content {
timer_jobs.enqueue_job(
TimerJob::ClosePrize(ClosePrizeJob {
channel_id,
thread_root_message_index,
message_index: message_event.event.message_index,
}),
p.end_date,
now,
);
}
}

lazy_static! {
Expand Down
1 change: 1 addition & 0 deletions backend/canisters/group/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

- Notifications for custom messages should use the sub-type ([#4465](https://github.com/open-chat-labs/open-chat/pull/4465))
- Support prize messages in any token by getting fee from original transfer ([#4470](https://github.com/open-chat-labs/open-chat/pull/4470))
- Refund any prize message balance once it has ended ([#4476](https://github.com/open-chat-labs/open-chat/pull/4476))

## [[2.0.865](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.865-group)] - 2023-09-27

Expand Down
59 changes: 57 additions & 2 deletions backend/canisters/group/impl/src/timer_job_types.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use crate::activity_notifications::handle_activity_notification;
use crate::mutate_state;
use crate::{activity_notifications::handle_activity_notification, read_state};
use canister_timer_jobs::Job;
use ledger_utils::process_transaction;
use serde::{Deserialize, Serialize};
use types::{BlobReference, MessageId, MessageIndex};
use tracing::error;
use types::{BlobReference, CanisterId, MessageId, MessageIndex, PendingCryptoTransaction};
use utils::time::HOUR_IN_MS;

#[derive(Serialize, Deserialize, Clone)]
pub enum TimerJob {
HardDeleteMessageContent(HardDeleteMessageContentJob),
DeleteFileReferences(DeleteFileReferencesJob),
EndPoll(EndPollJob),
ClosePrize(ClosePrizeJob),
MakeTransfer(MakeTransferJob),
}

#[derive(Serialize, Deserialize, Clone)]
Expand All @@ -28,12 +33,25 @@ pub struct EndPollJob {
pub message_index: MessageIndex,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct ClosePrizeJob {
pub thread_root_message_index: Option<MessageIndex>,
pub message_index: MessageIndex,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct MakeTransferJob {
pub pending_transaction: PendingCryptoTransaction,
}

impl Job for TimerJob {
fn execute(&self) {
match self {
TimerJob::HardDeleteMessageContent(job) => job.execute(),
TimerJob::DeleteFileReferences(job) => job.execute(),
TimerJob::EndPoll(job) => job.execute(),
TimerJob::ClosePrize(job) => job.execute(),
TimerJob::MakeTransfer(job) => job.execute(),
}
}
}
Expand Down Expand Up @@ -87,3 +105,40 @@ impl Job for EndPollJob {
});
}
}

impl Job for ClosePrizeJob {
fn execute(&self) {
if let Some(pending_transaction) = mutate_state(|state| {
state
.data
.chat
.events
.close_prize(self.thread_root_message_index, self.message_index, state.env.now_nanos())
}) {
let make_transfer_job = MakeTransferJob { pending_transaction };
make_transfer_job.execute();
}
}
}

impl Job for MakeTransferJob {
fn execute(&self) {
let sender = read_state(|state| state.env.canister_id());
let pending = self.pending_transaction.clone();
ic_cdk::spawn(make_transfer(pending, sender));

async fn make_transfer(pending_transaction: PendingCryptoTransaction, sender: CanisterId) {
if let Err(error) = process_transaction(pending_transaction.clone(), sender).await {
error!(?error, "Transaction failed");
mutate_state(|state| {
let now = state.env.now();
state.data.timer_jobs.enqueue_job(
TimerJob::MakeTransfer(MakeTransferJob { pending_transaction }),
now + HOUR_IN_MS,
now,
);
});
}
}
}
}
14 changes: 11 additions & 3 deletions backend/canisters/group/impl/src/updates/send_message.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::activity_notifications::handle_activity_notification;
use crate::timer_job_types::{DeleteFileReferencesJob, EndPollJob};
use crate::timer_job_types::{ClosePrizeJob, DeleteFileReferencesJob, EndPollJob};
use crate::{mutate_state, run_regular_jobs, RuntimeState, TimerJob};
use canister_api_macros::update_candid_and_msgpack;
use canister_timer_jobs::TimerJobs;
Expand Down Expand Up @@ -117,6 +117,14 @@ fn register_timer_jobs(
}
}

// TODO: If this is a prize message then set a timer to transfer
// the balance of any remaining prizes to the original sender
if let MessageContent::Prize(p) = &message_event.event.content {
timer_jobs.enqueue_job(
TimerJob::ClosePrize(ClosePrizeJob {
thread_root_message_index,
message_index: message_event.event.message_index,
}),
p.end_date,
now,
);
}
}
44 changes: 38 additions & 6 deletions backend/libraries/chat_events/src/chat_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::*;
use candid::Principal;
use ic_ledger_types::Tokens;
use itertools::Itertools;
use ledger_utils::create_pending_transaction;
use rand::rngs::StdRng;
use rand::Rng;
use search::{Document, Query};
Expand All @@ -13,13 +14,13 @@ use std::cmp::{max, Reverse};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use types::{
CanisterId, Chat, CompletedCryptoTransaction, Cryptocurrency, DirectChatCreated, EventIndex, EventWrapper,
EventsTimeToLiveUpdated, GroupCanisterThreadDetails, GroupCreated, GroupFrozen, GroupUnfrozen, HydratedMention, Mention,
Message, MessageContentInitial, MessageId, MessageIndex, MessageMatch, Milliseconds, MultiUserChat, PollVotes,
PrizeWinnerContent, ProposalUpdate, PushEventResult, PushIfNotContains, RangeSet, Reaction, RegisterVoteResult,
TimestampMillis, Timestamped, Tips, UserId, VoteOperation,
CanisterId, Chat, CompletedCryptoTransaction, CryptoTransaction, Cryptocurrency, DirectChatCreated, EventIndex,
EventWrapper, EventsTimeToLiveUpdated, GroupCanisterThreadDetails, GroupCreated, GroupFrozen, GroupUnfrozen, Hash,
HydratedMention, Mention, Message, MessageContentInitial, MessageId, MessageIndex, MessageMatch, MessageReport,
Milliseconds, MultiUserChat, PendingCryptoTransaction, PollVotes, PrizeWinnerContent, ProposalUpdate, PushEventResult,
PushIfNotContains, RangeSet, Reaction, RegisterVoteResult, ReportedMessageInternal, TimestampMillis, TimestampNanos,
Timestamped, Tips, UserId, VoteOperation,
};
use types::{Hash, MessageReport, ReportedMessageInternal};

pub const OPENCHAT_BOT_USER_ID: UserId = UserId::new(Principal::from_slice(&[228, 104, 142, 9, 133, 211, 135, 217, 129, 1]));

Expand Down Expand Up @@ -407,6 +408,37 @@ impl ChatEvents {
EndPollResult::PollNotFound
}

pub fn close_prize(
&mut self,
thread_root_message_index: Option<MessageIndex>,
message_index: MessageIndex,
now_nanos: TimestampNanos,
) -> Option<PendingCryptoTransaction> {
let now_ms = now_nanos / 1_000_000;
if let Some((message, _)) =
self.message_internal_mut(EventIndex::default(), thread_root_message_index, message_index.into(), now_ms)
{
if let MessageContentInternal::Prize(p) = &mut message.content {
if let CryptoTransaction::Completed(t) = &p.transaction {
let unclaimed = p.prizes_remaining.iter().map(|t| t.e8s() as u128).sum::<u128>();
if unclaimed > 0 {
p.prizes_remaining = Vec::new();
return Some(create_pending_transaction(
t.token(),
t.ledger_canister_id(),
unclaimed,
t.fee(),
message.sender,
now_nanos,
));
}
}
}
}

None
}

pub fn record_proposal_vote(
&mut self,
user_id: UserId,
Expand Down

0 comments on commit 28e891a

Please sign in to comment.