Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCT-2263: Community onboarding through Sablier #600

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
54 changes: 33 additions & 21 deletions backend/app/infrastructure/routes/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
post_user_terms_of_service_consent,
get_user_terms_of_service_consent_status,
)
from app.modules.user.winnings.controller import get_user_winnings
from app.modules.user.sablier_streams.controller import get_sablier_streams
from app.settings import config

ns = Namespace("user", description="Octant user settings")
Expand Down Expand Up @@ -98,27 +98,35 @@
},
)

user_winning_model = api.model(
"UserWinning",
user_stream_model = api.model(
"UserStream",
{
"amount": fields.String(
required=True,
description="Amount in WEI",
),
"dateAvailableForWithdrawal": fields.String(
required=True,
description="Date when winning is available for withdrawal as unix timestamp",
description="Date when stream is available for withdrawal as unix timestamp",
),
"isCancelled": fields.Boolean(
required=True,
description="Flag indicating whether stream is cancelled",
),
"remainingAmount": fields.String(
required=True,
description="Remaining amount in WEI",
),
},
)

user_winnings_model = api.model(
"UserWinnings",
user_streams_model = api.model(
"SablierStreams",
{
"winnings": fields.List(
fields.Nested(user_winning_model),
"sablierStreams": fields.List(
fields.Nested(user_stream_model),
required=True,
description="User winnings",
description="User's sablier streams",
),
},
)
Expand Down Expand Up @@ -344,29 +352,33 @@ def get(self, epoch: int):
}


@ns.route("/<string:user_address>/raffle/winnings")
@ns.route("/<string:user_address>/sablier-streams")
@ns.doc(
params={
"user_address": "User ethereum address in hexadecimal format (case-insensitive, prefixed with 0x)",
}
)
class UserWinnings(OctantResource):
class SablierStreams(OctantResource):
@ns.doc(
description="Returns an array of user's winnings with amounts and availability dates",
description="Returns an array of user's streams from Sablier with amounts, availability dates, remainingAmount and isCancelled flag.",
)
@ns.marshal_with(user_winnings_model)
@ns.response(200, "User's winnings retrieved successfully")
@ns.marshal_with(user_streams_model)
@ns.response(200, "User's streams from Sablier retrieved successfully")
def get(self, user_address: str):
app.logger.debug(f"Getting winnings for user {user_address}.")
winnings = get_user_winnings(user_address)
app.logger.debug(f"Retrieved {len(winnings)} winnings for user {user_address}.")
app.logger.debug(f"Getting sablier streams for user {user_address}.")
sablier_streams = get_sablier_streams(user_address)
app.logger.debug(
f"Retrieved {len(sablier_streams)} sablier streams for user {user_address}."
)

return {
"winnings": [
"sablierStreams": [
{
"amount": winning.amount,
"dateAvailableForWithdrawal": winning.date_available_for_withdrawal,
"amount": stream.amount,
"dateAvailableForWithdrawal": stream.date_available_for_withdrawal,
"isCancelled": stream.is_cancelled,
"remainingAmount": stream.remaining_amount,
}
for winning in winnings
for stream in sablier_streams
]
}
56 changes: 16 additions & 40 deletions backend/app/infrastructure/sablier/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@ class SablierStream(TypedDict):
id: str
actions: List[SablierAction]
intactAmount: str


class SablierStreamForTrackingWinner(TypedDict):
id: str
canceled: bool
endTime: str
depositAmount: str
intactAmount: str


def fetch_streams(query: str, variables: Dict) -> List[SablierStream]:
Expand All @@ -55,8 +51,18 @@ def fetch_streams(query: str, variables: Dict) -> List[SablierStream]:
for stream in streams:
actions = stream.get("actions", [])
final_intact_amount = stream.get("intactAmount", 0)
is_cancelled = stream["canceled"]
end_time = stream["endTime"]
deposit_amount = stream["depositAmount"]

all_streams.append(
SablierStream(actions=actions, intactAmount=final_intact_amount)
SablierStream(
actions=actions,
intactAmount=final_intact_amount,
canceled=is_cancelled,
endTime=end_time,
depositAmount=deposit_amount,
)
)

if len(streams) < limit:
Expand All @@ -70,6 +76,7 @@ def fetch_streams(query: str, variables: Dict) -> List[SablierStream]:
def get_user_events_history(user_address: str) -> List[SablierStream]:
"""
Get all the locks and unlocks for a user.
Query used for computing user's effective deposit and getting all sablier streams from an endpoint.
"""
query = """
query GetEvents($sender: String!, $recipient: String!, $tokenAddress: String!, $limit: Int!, $skip: Int!) {
Expand All @@ -86,6 +93,9 @@ def get_user_events_history(user_address: str) -> List[SablierStream]:
) {
id
intactAmount
canceled
endTime
depositAmount
actions(where: {category_in: [Cancel, Withdraw, Create]}, orderBy: timestamp) {
category
addressA
Expand Down Expand Up @@ -146,40 +156,6 @@ def get_all_streams_history() -> List[SablierStream]:
return fetch_streams(query, variables)


def get_streams_with_create_events_to_user(
user_address: str,
) -> List[SablierStreamForTrackingWinner]:
"""
Get all the create events for a user.
"""
query = """
query GetCreateEvents($sender: String!, $recipient: String!, $tokenAddress: String!) {
streams(
where: {
sender: $sender
recipient: $recipient
asset_: {address: $tokenAddress}
transferable: false
}
orderBy: timestamp
) {
id
intactAmount
endTime
depositAmount
}
}
"""
variables = {
"sender": _get_sender(),
"recipient": user_address,
"tokenAddress": _get_token_address(),
}

result = gql_sablier_factory.build().execute(gql(query), variable_values=variables)
return result.get("streams", [])


def _get_sender():
chain_id = app.config["CHAIN_ID"]
sender = (
Expand Down
10 changes: 6 additions & 4 deletions backend/app/modules/modules_factory/current.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from app.modules.history.service.full import FullHistory
from app.modules.modules_factory.protocols import (
OctantRewards,
WinningsService,
UserEffectiveDeposits,
TotalEffectiveDeposits,
HistoryService,
Expand All @@ -17,6 +16,7 @@
ScoreDelegation,
UniquenessQuotients,
ProjectsDetailsService,
SablierStreamsService,
)
from app.modules.modules_factory.protocols import SimulatePendingSnapshots
from app.modules.multisig_signatures.service.offchain import OffchainMultisigSignatures
Expand Down Expand Up @@ -55,7 +55,9 @@
from app.modules.projects.details.service.projects_details import (
StaticProjectsDetailsService,
)
from app.modules.user.winnings.service.raffle import RaffleWinningsService
from app.modules.user.sablier_streams.service.sablier_streams import (
UserSablierStreamsService,
)


class CurrentUserDeposits(UserEffectiveDeposits, TotalEffectiveDeposits, Protocol):
Expand All @@ -68,7 +70,7 @@ class CurrentServices(Model):
user_tos_service: UserTos
user_antisybil_service: GitcoinPassportAntisybil
octant_rewards_service: OctantRewards
user_winnings_service: WinningsService
sablier_streams_service: SablierStreamsService
history_service: HistoryService
simulated_pending_snapshot_service: SimulatePendingSnapshots
multisig_signatures_service: MultisigSignatures
Expand Down Expand Up @@ -160,7 +162,7 @@ def create(chain_id: int) -> "CurrentServices":
multisig_signatures_service=multisig_signatures,
user_tos_service=user_tos,
user_antisybil_service=user_antisybil_service,
user_winnings_service=RaffleWinningsService(),
sablier_streams_service=UserSablierStreamsService(),
projects_metadata_service=StaticProjectsMetadataService(),
projects_details_service=StaticProjectsDetailsService(),
user_budgets_service=user_budgets,
Expand Down
10 changes: 6 additions & 4 deletions backend/app/modules/modules_factory/pre_pending.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
AllUserEffectiveDeposits,
OctantRewards,
PendingSnapshots,
WinningsService,
SablierStreamsService,
UserEffectiveDeposits,
SavedProjectRewardsService,
ProjectsMetadataService,
Expand All @@ -29,7 +29,9 @@
from app.modules.projects.details.service.projects_details import (
StaticProjectsDetailsService,
)
from app.modules.user.winnings.service.raffle import RaffleWinningsService
from app.modules.user.sablier_streams.service.sablier_streams import (
UserSablierStreamsService,
)


class PrePendingUserDeposits(UserEffectiveDeposits, AllUserEffectiveDeposits, Protocol):
Expand All @@ -43,7 +45,7 @@ class PrePendingServices(Model):
project_rewards_service: SavedProjectRewardsService
projects_metadata_service: ProjectsMetadataService
projects_details_service: ProjectsDetailsService
user_winnings_service: WinningsService
user_winnings_service: SablierStreamsService

@staticmethod
def create(chain_id: int) -> "PrePendingServices":
Expand Down Expand Up @@ -72,5 +74,5 @@ def create(chain_id: int) -> "PrePendingServices":
project_rewards_service=SavedProjectRewards(),
projects_metadata_service=StaticProjectsMetadataService(),
projects_details_service=StaticProjectsDetailsService(),
user_winnings_service=RaffleWinningsService(),
user_winnings_service=UserSablierStreamsService(),
)
8 changes: 4 additions & 4 deletions backend/app/modules/modules_factory/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from app.modules.history.dto import UserHistoryDTO
from app.modules.multisig_signatures.dto import Signature
from app.modules.projects.details.service.projects_details import ProjectsDetailsDTO
from app.modules.user.winnings.service.raffle import UserWinningDTO
from app.modules.user.sablier_streams.service.sablier_streams import UserStreamsDTO


@runtime_checkable
Expand Down Expand Up @@ -127,10 +127,10 @@ def get_unused_rewards(self, context: Context) -> Dict[str, int]:


@runtime_checkable
class WinningsService(Protocol):
def get_user_winnings(
class SablierStreamsService(Protocol):
def get_sablier_streams(
self, context: Context, user_address: str
) -> List[UserWinningDTO]:
) -> List[UserStreamsDTO]:
...


Expand Down
16 changes: 16 additions & 0 deletions backend/app/modules/user/sablier_streams/controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import List

from app.context.manager import state_context
from app.modules.registry import get_services
from app.modules.modules_factory.protocols import SablierStreamsService
from app.modules.user.sablier_streams.service.sablier_streams import UserStreamsDTO
from app.context.epoch_state import EpochState


def get_sablier_streams(user_address: str) -> List[UserStreamsDTO]:
context = state_context(EpochState.CURRENT)
service: SablierStreamsService = get_services(
context.epoch_state
).sablier_streams_service

return service.get_sablier_streams(context, user_address)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from app.pydantic import Model
from typing import List
from dataclasses import dataclass

from app.infrastructure.sablier.events import get_user_events_history
from app.context.manager import Context


@dataclass
class UserStreamsDTO:
amount: str
date_available_for_withdrawal: str
is_cancelled: bool
remaining_amount: str


class UserSablierStreamsService(Model):
def get_sablier_streams(
self, _: Context, user_address: str
) -> List[UserStreamsDTO]:
user_streams = get_user_events_history(
user_address
) # in practice: we should assume a user only has always one stream (one create event)

user_streams_details = []
for user_stream in user_streams:
date_available_for_withdrawal = user_stream["endTime"]
amount = user_stream["depositAmount"]
is_cancelled = user_stream["canceled"]
remaining_amount = user_stream["intactAmount"]

user_streams_details.append(
UserStreamsDTO(
amount=amount,
date_available_for_withdrawal=date_available_for_withdrawal,
is_cancelled=is_cancelled,
remaining_amount=remaining_amount,
)
)

return user_streams_details
14 changes: 0 additions & 14 deletions backend/app/modules/user/winnings/controller.py

This file was deleted.

Loading
Loading