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

[PAY-2947] Implement spl_usdc_payout_wallet user entity manager actions #8479

Merged
merged 5 commits into from
May 22, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
begin;

alter table users add column if not exists spl_usdc_payout_wallet varchar;

commit;
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ def __init__(
redis=None,
eth_manager=None,
entity_manager_contract=None,
solana_client_manager=None,
):
self.web3 = web3
self.challenge_event_bus: ChallengeEventBus = challenge_event_bus
self.redis = redis
self.eth_manager = eth_manager
self.entity_manager_contract = entity_manager_contract
self.solana_client_manager = solana_client_manager
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
from datetime import datetime
from typing import List
from unittest import mock
from unittest.mock import create_autospec

from solders.account import AccountJSON
from sqlalchemy import asc
from web3 import Web3
from web3.datastructures import AttributeDict
Expand All @@ -12,13 +14,15 @@
from src.challenges.challenge_event import ChallengeEvent
from src.models.indexing.cid_data import CIDData
from src.models.users.user import User
from src.solana.solana_client_manager import SolanaClientManager
from src.tasks.entity_manager.entities.user import UserEventMetadata, update_user_events
from src.tasks.entity_manager.entity_manager import entity_manager_update
from src.tasks.entity_manager.utils import (
CHARACTER_LIMIT_USER_BIO,
TRACK_ID_OFFSET,
USER_ID_OFFSET,
)
from src.utils.config import shared_config
from src.utils.db_session import get_db
from src.utils.redis_connection import get_redis

Expand Down Expand Up @@ -1271,3 +1275,165 @@ def get_events_side_effect(_, tx_receipt):
assert user_3.name == "Isaac"
assert user_3.handle == "isaac"
assert user_3.bio == ""


def test_index_adding_spl_usdc_payout_wallet(app, mocker):
bus_mock = set_patches(mocker)

# setup db and mocked txs
with app.app_context():
db = get_db()
web3 = Web3()
solana_client_manager_mock = create_autospec(SolanaClientManager)
solana_client_manager_mock.get_account_info_json_parsed.return_value = AccountJSON.from_json(
json.dumps(
{
"lamports": 2039280,
"data": {
"program": "spl-token",
"parsed": {
"info": {
"isNative": False,
"mint": shared_config["solana"]["usdc_mint"],
"owner": "F1vVY6VtF5oLT2QYEqy6276JGGhgaLEDZMamoFsJWSYk",
"state": "initialized",
"tokenAmount": {
"amount": "3267791",
"decimals": 6,
"uiAmount": 3.267791,
"uiAmountString": "3.267791",
},
},
"type": "account",
},
"space": 165,
},
"owner": [
6,
221,
246,
225,
215,
101,
161,
147,
217,
203,
225,
70,
206,
235,
121,
172,
28,
180,
133,
237,
95,
91,
55,
145,
58,
140,
245,
133,
126,
255,
0,
169,
],
"executable": False,
"rentEpoch": 18446744073709551615,
}
)
)
update_task = UpdateTask(
web3, bus_mock, solana_client_manager=solana_client_manager_mock
)

test_metadata = {
"QmUpdateUser": {
"name": "Britney Spores",
"handle": "britneyspores",
"profile_picture_sizes": "QmNmzMoiLYSAgrLbAAnaPW9q3YZwZvHybbbs59QamzUQxg",
"cover_photo_sizes": "QmR2fSFvtpWg7nfdYtoJ3KgDNf4YgcuSzKjwZjansW9wcj",
"bio": "No but I'm like actually Toxic",
"location": "McComb, MS",
"spl_usdc_payout_wallet": "7MjEvdyxduYPYwikWovAJ9TjLkzgdW3DiihysYTTN4up",
"user_id": 1,
},
}

update_user_json = json.dumps(test_metadata["QmUpdateUser"])

tx_receipts = {
"UpdateUserTx": [
{
"args": AttributeDict(
{
"_entityId": 1,
"_entityType": "User",
"_userId": 1,
"_action": "Update",
"_metadata": f'{{"cid": "QmUpdateUser", "data": {update_user_json}}}',
"_signer": "wallet",
}
)
},
]
}

entity_manager_txs = [
AttributeDict({"transactionHash": update_task.web3.to_bytes(text=tx_receipt)})
for tx_receipt in tx_receipts
]

def get_events_side_effect(_, tx_receipt):
return tx_receipts[tx_receipt["transactionHash"].decode("utf-8")]

mocker.patch(
"src.tasks.entity_manager.entity_manager.get_entity_manager_events_tx",
side_effect=get_events_side_effect,
autospec=True,
)

entities = {
"users": [
{
"user_id": 1,
"handle": "britneyspores",
"wallet": "wallet",
},
]
}

populate_mock_db(db, entities)

with db.scoped_session() as session:
# index transactions
entity_manager_update(
update_task,
session,
entity_manager_txs,
block_number=0,
block_timestamp=BLOCK_DATETIME.timestamp(),
block_hash=hex(0),
)

with db.scoped_session() as session:
all_users: List[User] = session.query(User).all()
assert len(all_users) == 1

user: User = (
session.query(User)
.filter(
User.user_id == 1,
)
.first()
)
assert user.name == "Britney Spores"
assert user.handle == "britneyspores"
assert (
user.spl_usdc_payout_wallet
== "7MjEvdyxduYPYwikWovAJ9TjLkzgdW3DiihysYTTN4up"
)
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def test_valid_parse_metadata(app):
"is_storage_v2": False,
"artist_pick_track_id": TRACK_ID_OFFSET,
"allow_ai_attribution": False,
"allowed_api_keys": False,
},
"QmCreateTrack1": {
"track_cid": "some-track-cid",
Expand Down
1 change: 1 addition & 0 deletions packages/discovery-provider/src/models/users/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class User(Base, RepresentableMixin):
user_storage_account = Column(String)
user_authority_account = Column(String)
allow_ai_attribution = Column(Boolean, nullable=False, server_default=text("false"))
spl_usdc_payout_wallet = Column(String)

block1 = relationship( # type: ignore
"Block", primaryjoin="User.blocknumber == Block.number"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from eth_account.messages import defunct_hash_message
from nacl.encoding import HexEncoder
from nacl.signing import VerifyKey
from solders.pubkey import Pubkey
from sqlalchemy.orm.session import Session

from src.challenges.challenge_event import ChallengeEvent
from src.challenges.challenge_event_bus import ChallengeEventBus
Expand All @@ -17,6 +19,8 @@
from src.models.users.user import User
from src.models.users.user_events import UserEvent
from src.queries.get_balances import enqueue_immediate_balance_refresh
from src.solana.solana_client_manager import SolanaClientManager
from src.solana.solana_helpers import SPL_TOKEN_ID
from src.tasks.entity_manager.utils import (
CHARACTER_LIMIT_USER_BIO,
USER_ID_OFFSET,
Expand All @@ -37,6 +41,9 @@

logger = logging.getLogger(__name__)

SPL_TOKEN_PUBKEY = Pubkey.from_string(SPL_TOKEN_ID)
USDC_MINT = shared_config["solana"]["usdc_mint"]


def get_verifier_address():
if "verified_address" in shared_config["contracts"]:
Expand Down Expand Up @@ -105,7 +112,12 @@ def validate_user_tx(params: ManageEntityParameters):
)


def validate_user_metadata(session, user_record: User, user_metadata: Dict):
def validate_user_metadata(
session: Session,
solana_client_manager: SolanaClientManager,
user_record: User,
user_metadata: Dict,
):
if not isinstance(user_metadata, dict):
raise IndexingValidationError("Invalid user metadata")
# If the user's handle is not set, validate that it is unique
Expand Down Expand Up @@ -142,6 +154,25 @@ def validate_user_metadata(session, user_record: User, user_metadata: Dict):
f"Cannot set artist pick. Track {user_metadata['artist_pick_track_id']} does not exist"
)

if user_metadata.get("spl_usdc_payout_wallet"):
wallet = user_metadata["spl_usdc_payout_wallet"]
pubkey = Pubkey.from_string(wallet)

if str(pubkey) != wallet:
raise IndexingValidationError(f"Invalid spl address {wallet}")

account_info = solana_client_manager.get_account_info_json_parsed(pubkey)

if account_info.owner != SPL_TOKEN_PUBKEY:
raise IndexingValidationError(
f"Spl address is not a token account {wallet}"
)

if account_info.data.parsed["info"]["mint"] != USDC_MINT:
raise IndexingValidationError(
f"Spl address is not a USDC token account {wallet}"
)


def validate_user_handle(handle: Union[str, None]):
if not handle:
Expand Down Expand Up @@ -196,6 +227,7 @@ def create_user(
if user_metadata is not None:
validate_user_metadata(
params.session,
params.solana_client_manager,
user_record,
user_metadata,
)
Expand Down Expand Up @@ -237,6 +269,7 @@ def update_user(

validate_user_metadata(
params.session,
params.solana_client_manager,
user_record,
params.metadata,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ def entity_manager_update(
pending_playlist_routes,
update_task.eth_manager,
update_task.web3,
update_task.solana_client_manager,
block_timestamp,
block_number,
block_hash,
Expand Down
3 changes: 3 additions & 0 deletions packages/discovery-provider/src/tasks/entity_manager/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from src.models.tracks.track import Track
from src.models.tracks.track_route import TrackRoute
from src.models.users.user import User
from src.solana.solana_client_manager import SolanaClientManager
from src.tasks.metadata import (
playlist_metadata_format,
track_metadata_format,
Expand Down Expand Up @@ -194,6 +195,7 @@ def __init__(
pending_playlist_routes: List[PlaylistRoute],
eth_manager: EthManager,
web3: Web3,
solana_client_manager: SolanaClientManager,
block_timestamp: int,
block_number: int,
event_blockhash: str,
Expand All @@ -213,6 +215,7 @@ def __init__(
self.challenge_bus = challenge_bus
self.web3 = web3
self.eth_manager = eth_manager
self.solana_client_manager = solana_client_manager
self.pending_track_routes = pending_track_routes
self.pending_playlist_routes = pending_playlist_routes

Expand Down