From 8736157ca3197834fb0dd615c68b2cc238ed7c27 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:23:37 -0700 Subject: [PATCH 01/17] Add splits to purchases --- .../migrations/0093_splits_in_purchases.sql | 62 +++++++++++++++++++ .../discovery-provider/src/api/v1/helpers.py | 3 + .../src/api/v1/models/users.py | 6 ++ .../src/models/users/usdc_purchase.py | 2 + .../src/queries/get_extended_purchase_gate.py | 7 +-- .../src/tasks/index_payment_router.py | 20 +++--- 6 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql diff --git a/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql b/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql new file mode 100644 index 00000000000..8073ea69d5a --- /dev/null +++ b/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql @@ -0,0 +1,62 @@ +-- Recreates the extended purchase splits from the information in the purchases table +-- Required to populate purchase details modal +BEGIN; + +ALTER TABLE + usdc_purchases +ADD + COLUMN IF NOT EXISTS splits JSONB; + +WITH relevant_timestamps AS ( + SELECT + user_id, + MAX(block_timestamp) AS block_timestamp, + signature + FROM + user_payout_wallet_history + JOIN usdc_purchases ON seller_user_id = user_id + WHERE + block_timestamp < usdc_purchases.created_at + GROUP BY + user_id, + signature +) +UPDATE + usdc_purchases +SET + splits = jsonb_build_array( + jsonb_build_object( + 'user_id', + usdc_purchases.seller_user_id, + 'amount', + usdc_purchases.amount, + 'percentage', + 100.0, + 'eth_wallet', + users.wallet, + 'payout_wallet', + COALESCE( + user_payout_wallet_history.spl_usdc_payout_wallet, + usdc_user_bank_accounts.bank_account + ) + ) + ) +FROM + usdc_purchases AS up + JOIN users ON users.user_id = up.seller_user_id + JOIN usdc_user_bank_accounts ON users.wallet = usdc_user_bank_accounts.ethereum_address + LEFT JOIN relevant_timestamps ON relevant_timestamps.user_id = users.user_id + AND relevant_timestamps.signature = up.signature + LEFT JOIN user_payout_wallet_history ON user_payout_wallet_history.user_id = users.user_id + AND user_payout_wallet_history.block_timestamp = relevant_timestamps.block_timestamp +WHERE + up.signature = usdc_purchases.signature; + +ALTER TABLE + usdc_purchases +ALTER COLUMN + splits +SET + NOT NULL; + +COMMIT; \ No newline at end of file diff --git a/packages/discovery-provider/src/api/v1/helpers.py b/packages/discovery-provider/src/api/v1/helpers.py index bffefe4b42c..659532b6442 100644 --- a/packages/discovery-provider/src/api/v1/helpers.py +++ b/packages/discovery-provider/src/api/v1/helpers.py @@ -588,6 +588,9 @@ def extend_purchase(purchase): new_purchase["seller_user_id"] = encode_int_id(purchase["seller_user_id"]) new_purchase["content_id"] = encode_int_id(purchase["content_id"]) new_purchase["access"] = purchase["access"] + for split in new_purchase["splits"]: + if "user_id" in split: + split["user_id"] = encode_int_id(split["user_id"]) return new_purchase diff --git a/packages/discovery-provider/src/api/v1/models/users.py b/packages/discovery-provider/src/api/v1/models/users.py index a1e11be5af0..67116c9aa92 100644 --- a/packages/discovery-provider/src/api/v1/models/users.py +++ b/packages/discovery-provider/src/api/v1/models/users.py @@ -155,6 +155,11 @@ }, ) +split = ns.model( + "purchase_split", + {"user_id": fields.String(required=True), "amount": fields.String(required=True)}, +) + purchase = ns.model( "purchase", { @@ -169,5 +174,6 @@ "created_at": fields.String(required=True), "updated_at": fields.String(required=True), "access": StringEnumToLower(required=True), + "splits": fields.List(fields.Nested(split, requird=True), required=True), }, ) diff --git a/packages/discovery-provider/src/models/users/usdc_purchase.py b/packages/discovery-provider/src/models/users/usdc_purchase.py index 9d93fe7d0bf..e45a2fed1c4 100644 --- a/packages/discovery-provider/src/models/users/usdc_purchase.py +++ b/packages/discovery-provider/src/models/users/usdc_purchase.py @@ -1,6 +1,7 @@ import enum from sqlalchemy import BigInteger, Column, DateTime, Enum, Integer, String, text +from sqlalchemy.dialects.postgresql import JSONB from src.models.base import Base from src.models.model_utils import RepresentableMixin @@ -38,6 +39,7 @@ class USDCPurchase(Base, RepresentableMixin): region = Column(String, nullable=True) country = Column(String, nullable=True) vendor = Column(Enum(PurchaseVendor), nullable=True) + splits = Column(JSONB, nullable=False) created_at = Column( DateTime, nullable=False, index=True, server_default=text("CURRENT_TIMESTAMP") diff --git a/packages/discovery-provider/src/queries/get_extended_purchase_gate.py b/packages/discovery-provider/src/queries/get_extended_purchase_gate.py index 30cd90b8240..d237e49c19c 100644 --- a/packages/discovery-provider/src/queries/get_extended_purchase_gate.py +++ b/packages/discovery-provider/src/queries/get_extended_purchase_gate.py @@ -104,7 +104,7 @@ class NFTGate(TypedDict): def calculate_split_amounts( price: int | None, splits: List[Split], include_network_cut=False -): +) -> List[ExtendedSplit]: """ Deterministically calculates the USDC amounts to pay to each person, adjusting for rounding errors and ensuring the total matches the price. @@ -164,7 +164,7 @@ def calculate_split_amounts( "payout_wallet": staking_bridge_usdc_payout_wallet, } ) - return new_splits + return [cast(ExtendedSplit, split) for split in new_splits] def add_wallet_info_to_splits( @@ -237,8 +237,7 @@ def _get_extended_purchase_gate( original_splits = gate.get("usdc_purchase", {}).get("splits", []) splits = [orig.copy() for orig in original_splits] splits = add_wallet_info_to_splits(session, splits, datetime.now()) - splits = calculate_split_amounts(price, splits, include_network_cut) - extended_splits = [cast(ExtendedSplit, split) for split in splits] + extended_splits = calculate_split_amounts(price, splits, include_network_cut) extended_gate: ExtendedPurchaseGate = { "usdc_purchase": {"price": price, "splits": extended_splits} } diff --git a/packages/discovery-provider/src/tasks/index_payment_router.py b/packages/discovery-provider/src/tasks/index_payment_router.py index 96e92d2789a..6df879ffec3 100644 --- a/packages/discovery-provider/src/tasks/index_payment_router.py +++ b/packages/discovery-provider/src/tasks/index_payment_router.py @@ -38,6 +38,7 @@ from src.models.users.user import User from src.models.users.user_bank import USDCUserBankAccount from src.queries.get_extended_purchase_gate import ( + ExtendedSplit, add_wallet_info_to_splits, calculate_split_amounts, to_wallet_amount_map, @@ -136,7 +137,7 @@ class UserIdBankAccount(TypedDict): class PurchaseMetadataDict(TypedDict): price: int - splits: dict[str, int] + splits: List[ExtendedSplit] type: PurchaseType id: int purchaser_user_id: int @@ -364,8 +365,6 @@ def parse_route_transaction_memos( else: logger.error(f"index_payment_router.py | Unknown content type {type}") - # Convert the new splits format to the old splits format for - # maximal backwards compatibility if ( price is not None and splits is not None @@ -373,23 +372,16 @@ def parse_route_transaction_memos( and content_owner_id is not None ): wallet_splits = add_wallet_info_to_splits(session, splits, timestamp) - amount_splits = calculate_split_amounts( + extended_splits = calculate_split_amounts( price, wallet_splits, include_network_cut=include_network_cut ) - splits = to_wallet_amount_map(amount_splits) - if ( - price is not None - and splits is not None - and isinstance(splits, dict) - and content_owner_id is not None - ): route_transaction_memo = RouteTransactionMemo( type=RouteTransactionMemoType.purchase, metadata={ "type": type, "id": id, "price": price * USDC_PER_USD_CENT, - "splits": splits, + "splits": extended_splits, "purchaser_user_id": purchaser_user_id, "content_owner_id": content_owner_id, "access": access, @@ -418,7 +410,8 @@ def validate_purchase( ): """Validates the user has correctly constructed the transaction in order to create the purchase, including validating they paid the full price at the time of the purchase, and that payments were appropriately split""" # Check that the recipients all got the correct split - for account, split in purchase_metadata["splits"].items(): + splits = to_wallet_amount_map(purchase_metadata["splits"]) + for account, split in splits.items(): if account not in balance_changes: logger.error( f"index_payment_router.py | No split given to account={account}, expected={split}" @@ -465,6 +458,7 @@ def index_purchase( region=geo_metadata.get("region") if geo_metadata else None, country=geo_metadata.get("country") if geo_metadata else None, vendor=vendor, + splits=purchase_metadata["splits"], ) logger.debug( f"index_payment_router.py | tx: {tx_sig} | Creating usdc_purchase for purchase {usdc_purchase}" From af5aad25566f05cf25e78927ab7b82f89d0744ec Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:24:38 -0700 Subject: [PATCH 02/17] Generate SDK --- .../generated/full/.openapi-generator/FILES | 1 + .../sdk/api/generated/full/models/Purchase.ts | 16 ++++ .../generated/full/models/PurchaseSplit.ts | 76 +++++++++++++++++++ .../sdk/api/generated/full/models/index.ts | 1 + 4 files changed, 94 insertions(+) create mode 100644 packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts diff --git a/packages/libs/src/sdk/api/generated/full/.openapi-generator/FILES b/packages/libs/src/sdk/api/generated/full/.openapi-generator/FILES index c8326aa0678..5af19e2cd02 100644 --- a/packages/libs/src/sdk/api/generated/full/.openapi-generator/FILES +++ b/packages/libs/src/sdk/api/generated/full/.openapi-generator/FILES @@ -97,6 +97,7 @@ models/PlaylistMilestoneNotificationActionData.ts models/ProfilePicture.ts models/Purchase.ts models/PurchaseGate.ts +models/PurchaseSplit.ts models/PurchasersCountResponse.ts models/PurchasesCountResponse.ts models/PurchasesResponse.ts diff --git a/packages/libs/src/sdk/api/generated/full/models/Purchase.ts b/packages/libs/src/sdk/api/generated/full/models/Purchase.ts index a7e92ee75f5..82fe68ab157 100644 --- a/packages/libs/src/sdk/api/generated/full/models/Purchase.ts +++ b/packages/libs/src/sdk/api/generated/full/models/Purchase.ts @@ -14,6 +14,13 @@ */ import { exists, mapValues } from '../runtime'; +import type { PurchaseSplit } from './PurchaseSplit'; +import { + PurchaseSplitFromJSON, + PurchaseSplitFromJSONTyped, + PurchaseSplitToJSON, +} from './PurchaseSplit'; + import { } from './'; @@ -89,6 +96,12 @@ export interface Purchase { * @memberof Purchase */ access: string; + /** + * + * @type {Array} + * @memberof Purchase + */ + splits: Array; } /** @@ -107,6 +120,7 @@ export function instanceOfPurchase(value: object): value is Purchase { isInstance = isInstance && "createdAt" in value && value["createdAt"] !== undefined; isInstance = isInstance && "updatedAt" in value && value["updatedAt"] !== undefined; isInstance = isInstance && "access" in value && value["access"] !== undefined; + isInstance = isInstance && "splits" in value && value["splits"] !== undefined; return isInstance; } @@ -134,6 +148,7 @@ export function PurchaseFromJSONTyped(json: any, ignoreDiscriminator: boolean): 'createdAt': json['created_at'], 'updatedAt': json['updated_at'], 'access': json['access'], + 'splits': ((json['splits'] as Array).map(PurchaseSplitFromJSON)), }; } @@ -157,6 +172,7 @@ export function PurchaseToJSON(value?: Purchase | null): any { 'created_at': value.createdAt, 'updated_at': value.updatedAt, 'access': value.access, + 'splits': ((value.splits as Array).map(PurchaseSplitToJSON)), }; } diff --git a/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts b/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts new file mode 100644 index 00000000000..a472bcde7a8 --- /dev/null +++ b/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface PurchaseSplit + */ +export interface PurchaseSplit { + /** + * + * @type {string} + * @memberof PurchaseSplit + */ + userId: string; + /** + * + * @type {string} + * @memberof PurchaseSplit + */ + amount: string; +} + +/** + * Check if a given object implements the PurchaseSplit interface. + */ +export function instanceOfPurchaseSplit(value: object): value is PurchaseSplit { + let isInstance = true; + isInstance = isInstance && "userId" in value && value["userId"] !== undefined; + isInstance = isInstance && "amount" in value && value["amount"] !== undefined; + + return isInstance; +} + +export function PurchaseSplitFromJSON(json: any): PurchaseSplit { + return PurchaseSplitFromJSONTyped(json, false); +} + +export function PurchaseSplitFromJSONTyped(json: any, ignoreDiscriminator: boolean): PurchaseSplit { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'userId': json['user_id'], + 'amount': json['amount'], + }; +} + +export function PurchaseSplitToJSON(value?: PurchaseSplit | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'user_id': value.userId, + 'amount': value.amount, + }; +} + diff --git a/packages/libs/src/sdk/api/generated/full/models/index.ts b/packages/libs/src/sdk/api/generated/full/models/index.ts index bd526a7b402..8e8414686f1 100644 --- a/packages/libs/src/sdk/api/generated/full/models/index.ts +++ b/packages/libs/src/sdk/api/generated/full/models/index.ts @@ -87,6 +87,7 @@ export * from './PlaylistMilestoneNotificationActionData'; export * from './ProfilePicture'; export * from './Purchase'; export * from './PurchaseGate'; +export * from './PurchaseSplit'; export * from './PurchasersCountResponse'; export * from './PurchasesCountResponse'; export * from './PurchasesResponse'; From 42c7d03fa714f949e3982b35efcac1fa7a590711 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:29:38 -0700 Subject: [PATCH 03/17] Rename to get_purchase_gate to disambiguate extend_access_gate() --- ...hase_gate.py => test_get_purchase_gate.py} | 8 ++--- .../discovery-provider/src/api/v1/helpers.py | 29 +++++++++++++++---- .../src/api/v1/models/access_gate.py | 1 + .../src/api/v1/playlists.py | 4 +-- .../discovery-provider/src/api/v1/tracks.py | 11 +++---- ..._purchase_gate.py => get_purchase_gate.py} | 20 +++++-------- ...test.py => get_purchase_gate_unit_test.py} | 0 .../discovery-provider/src/queries/queries.py | 2 +- .../src/tasks/index_payment_router.py | 2 +- .../src/tasks/index_user_bank.py | 2 +- 10 files changed, 48 insertions(+), 31 deletions(-) rename packages/discovery-provider/integration_tests/queries/{test_get_extended_purchase_gate.py => test_get_purchase_gate.py} (98%) rename packages/discovery-provider/src/queries/{get_extended_purchase_gate.py => get_purchase_gate.py} (93%) rename packages/discovery-provider/src/queries/{get_extended_purchase_gate_unit_test.py => get_purchase_gate_unit_test.py} (100%) diff --git a/packages/discovery-provider/integration_tests/queries/test_get_extended_purchase_gate.py b/packages/discovery-provider/integration_tests/queries/test_get_purchase_gate.py similarity index 98% rename from packages/discovery-provider/integration_tests/queries/test_get_extended_purchase_gate.py rename to packages/discovery-provider/integration_tests/queries/test_get_purchase_gate.py index 082b4b72b32..e6750a15718 100644 --- a/packages/discovery-provider/integration_tests/queries/test_get_extended_purchase_gate.py +++ b/packages/discovery-provider/integration_tests/queries/test_get_purchase_gate.py @@ -1,10 +1,10 @@ import datetime from integration_tests.utils import populate_mock_db -from src.queries.get_extended_purchase_gate import ( +from src.queries.get_purchase_gate import ( add_wallet_info_to_splits, calculate_split_amounts, - get_extended_purchase_gate, + get_new_purchase_gate, to_wallet_amount_map, ) from src.utils.db_session import get_db @@ -65,7 +65,7 @@ def test_get_extended_splits(app): with db.scoped_session() as session: # Gets most recent payout wallet by default - res = get_extended_purchase_gate( + res = get_new_purchase_gate( { "usdc_purchase": { "price": 100, @@ -169,7 +169,7 @@ def test_get_extended_splits_with_network_cut(app): with db.scoped_session() as session: # Gets most recent payout wallet by default - res = get_extended_purchase_gate( + res = get_new_purchase_gate( { "usdc_purchase": { "price": 100, diff --git a/packages/discovery-provider/src/api/v1/helpers.py b/packages/discovery-provider/src/api/v1/helpers.py index 659532b6442..92463632070 100644 --- a/packages/discovery-provider/src/api/v1/helpers.py +++ b/packages/discovery-provider/src/api/v1/helpers.py @@ -11,11 +11,15 @@ from src.api.v1.models.common import full_response from src.models.rewards.challenge import ChallengeType from src.queries.get_challenges import ChallengeResponse -from src.queries.get_extended_purchase_gate import get_legacy_purchase_gate from src.queries.get_notifications import ( NotificationType, default_valid_notification_types, ) +from src.queries.get_purchase_gate import ( + AccessGate, + PurchaseGate, + get_legacy_purchase_gate, +) from src.queries.get_support_for_user import SupportResponse from src.queries.get_undisbursed_challenges import UndisbursedChallengeResponse from src.queries.query_helpers import ( @@ -399,17 +403,32 @@ def extend_track(track, session=None): # Transform new format of splits to legacy format for client compatibility if "stream_conditions" in track: - track["stream_conditions"] = get_legacy_purchase_gate( - track["stream_conditions"], session + track["stream_conditions"] = extend_access_gate( + track["stream_conditions"], session, use_legacy_purchase_gate=True ) if "download_conditions" in track: - track["download_conditions"] = get_legacy_purchase_gate( - track["download_conditions"], session + track["download_conditions"] = extend_access_gate( + track["download_conditions"], session, use_legacy_purchase_gate=True ) return track +def extend_access_gate(gate: AccessGate, session=None, use_legacy_purchase_gate=False): + if "usdc_purchase" in gate: + if use_legacy_purchase_gate: + return get_legacy_purchase_gate(gate, session) + # help mypy + gate = cast(PurchaseGate, gate) + for split in gate["usdc_purchase"]["splits"]: + if split.get("user_id") is not None: + # help mypy + user_id = cast(int, split["user_id"]) + split["user_id"] = encode_int_id(user_id) + return gate + return gate + + def get_encoded_track_id(track): return {"id": encode_int_id(track["track_id"])} diff --git a/packages/discovery-provider/src/api/v1/models/access_gate.py b/packages/discovery-provider/src/api/v1/models/access_gate.py index f56bf7f3559..e6625e32919 100644 --- a/packages/discovery-provider/src/api/v1/models/access_gate.py +++ b/packages/discovery-provider/src/api/v1/models/access_gate.py @@ -92,6 +92,7 @@ "extended_payment_split", payment_split, { + "user_id": fields.String(), "eth_wallet": fields.String(), "payout_wallet": fields.String(required=True), "amount": fields.Integer(required=True), diff --git a/packages/discovery-provider/src/api/v1/playlists.py b/packages/discovery-provider/src/api/v1/playlists.py index 8f05f1cf740..2e24da64205 100644 --- a/packages/discovery-provider/src/api/v1/playlists.py +++ b/packages/discovery-provider/src/api/v1/playlists.py @@ -32,9 +32,9 @@ playlist_model, ) from src.api.v1.models.users import user_model_full -from src.queries.get_extended_purchase_gate import get_extended_purchase_gate from src.queries.get_playlist_tracks import get_playlist_tracks from src.queries.get_playlists import get_playlists +from src.queries.get_purchase_gate import get_new_purchase_gate from src.queries.get_reposters_for_playlist import get_reposters_for_playlist from src.queries.get_savers_for_playlist import get_savers_for_playlist from src.queries.get_top_playlists import get_top_playlists # pylint: disable=C0302 @@ -720,7 +720,7 @@ def get(self, playlist_id: str): if not playlists: abort_not_found(playlist_id, ns) raw = playlists[0] - stream_conditions = get_extended_purchase_gate( + stream_conditions = get_new_purchase_gate( gate=raw["stream_conditions"], include_network_cut=include_network_cut ) playlist = extend_playlist(raw) diff --git a/packages/discovery-provider/src/api/v1/tracks.py b/packages/discovery-provider/src/api/v1/tracks.py index 005c985fbe6..78a5c5d7967 100644 --- a/packages/discovery-provider/src/api/v1/tracks.py +++ b/packages/discovery-provider/src/api/v1/tracks.py @@ -18,6 +18,7 @@ current_user_parser, decode_ids_array, decode_with_abort, + extend_access_gate, extend_blob_info, extend_track, extend_user, @@ -45,11 +46,11 @@ TRENDING_TRACKS_TTL_SEC, ) from src.queries.get_comments import get_track_comments -from src.queries.get_extended_purchase_gate import get_extended_purchase_gate from src.queries.get_feed import get_feed from src.queries.get_latest_entities import get_latest_entities from src.queries.get_nft_gated_track_signatures import get_nft_gated_track_signatures from src.queries.get_premium_tracks import get_usdc_purchase_tracks +from src.queries.get_purchase_gate import get_new_purchase_gate from src.queries.get_random_tracks import get_random_tracks from src.queries.get_recommended_tracks import ( DEFAULT_RECOMMENDED_LIMIT, @@ -1874,13 +1875,13 @@ def get(self, track_id: str): if not tracks: abort_not_found(track_id, ns) raw = tracks[0] - stream_conditions = get_extended_purchase_gate( + stream_conditions = get_new_purchase_gate( gate=raw["stream_conditions"], include_network_cut=include_network_cut ) - download_conditions = get_extended_purchase_gate( + download_conditions = get_new_purchase_gate( gate=raw["download_conditions"], include_network_cut=include_network_cut ) track = extend_track(raw) - track["stream_conditions"] = stream_conditions - track["download_conditions"] = download_conditions + track["stream_conditions"] = extend_access_gate(stream_conditions) + track["download_conditions"] = extend_access_gate(download_conditions) return success_response(track) diff --git a/packages/discovery-provider/src/queries/get_extended_purchase_gate.py b/packages/discovery-provider/src/queries/get_purchase_gate.py similarity index 93% rename from packages/discovery-provider/src/queries/get_extended_purchase_gate.py rename to packages/discovery-provider/src/queries/get_purchase_gate.py index d237e49c19c..9a2810c8fab 100644 --- a/packages/discovery-provider/src/queries/get_extended_purchase_gate.py +++ b/packages/discovery-provider/src/queries/get_purchase_gate.py @@ -170,7 +170,7 @@ def calculate_split_amounts( def add_wallet_info_to_splits( session: Session, splits: List[Split], timestamp: Optional[datetime] ): - user_ids = [split["user_id"] for split in splits] + user_ids = [split.get("user_id") for split in splits] max_block_timestamps = ( session.query( @@ -230,7 +230,7 @@ def to_wallet_amount_map(splits: List[ExtendedSplit]): } -def _get_extended_purchase_gate( +def _get_new_purchase_gate( session: Session, gate: PurchaseGate, include_network_cut=False ): price = gate.get("usdc_purchase", {}).get("price", None) @@ -239,23 +239,21 @@ def _get_extended_purchase_gate( splits = add_wallet_info_to_splits(session, splits, datetime.now()) extended_splits = calculate_split_amounts(price, splits, include_network_cut) extended_gate: ExtendedPurchaseGate = { - "usdc_purchase": {"price": price, "splits": extended_splits} + "usdc_purchase": {"price": price, "splits": extended_splits}, } return extended_gate -def get_extended_purchase_gate( - gate: AccessGate, session=None, include_network_cut=False -): +def get_new_purchase_gate(gate: AccessGate, session=None, include_network_cut=False): if gate and "usdc_purchase" in gate: # mypy gets confused.... gate = cast(PurchaseGate, gate) if session: - return _get_extended_purchase_gate(session, gate, include_network_cut) + return _get_new_purchase_gate(session, gate, include_network_cut) else: db: SessionManager = get_db_read_replica() with db.scoped_session() as session: - return _get_extended_purchase_gate(session, gate, include_network_cut) + return _get_new_purchase_gate(session, gate, include_network_cut) def get_legacy_purchase_gate(gate: AccessGate, session=None, include_network_cut=False): @@ -263,13 +261,11 @@ def get_legacy_purchase_gate(gate: AccessGate, session=None, include_network_cut # mypy gets confused.... gate = cast(PurchaseGate, gate) if session: - new_gate = _get_extended_purchase_gate(session, gate, include_network_cut) + new_gate = _get_new_purchase_gate(session, gate, include_network_cut) else: db: SessionManager = get_db_read_replica() with db.scoped_session() as session: - new_gate = _get_extended_purchase_gate( - session, gate, include_network_cut - ) + new_gate = _get_new_purchase_gate(session, gate, include_network_cut) extended_splits = new_gate["usdc_purchase"]["splits"] splits = to_wallet_amount_map(extended_splits) new_gate["usdc_purchase"]["splits"] = splits diff --git a/packages/discovery-provider/src/queries/get_extended_purchase_gate_unit_test.py b/packages/discovery-provider/src/queries/get_purchase_gate_unit_test.py similarity index 100% rename from packages/discovery-provider/src/queries/get_extended_purchase_gate_unit_test.py rename to packages/discovery-provider/src/queries/get_purchase_gate_unit_test.py diff --git a/packages/discovery-provider/src/queries/queries.py b/packages/discovery-provider/src/queries/queries.py index 1072328fdd8..9f7947de38e 100644 --- a/packages/discovery-provider/src/queries/queries.py +++ b/packages/discovery-provider/src/queries/queries.py @@ -4,7 +4,6 @@ from src import api_helpers, exceptions from src.queries.get_cid_source import get_cid_source -from src.queries.get_extended_purchase_gate import get_legacy_purchase_gate from src.queries.get_feed import get_feed from src.queries.get_follow_intersection_users import get_follow_intersection_users from src.queries.get_followees_for_user import get_followees_for_user @@ -18,6 +17,7 @@ get_previously_private_playlists, ) from src.queries.get_previously_unlisted_tracks import get_previously_unlisted_tracks +from src.queries.get_purchase_gate import get_legacy_purchase_gate from src.queries.get_remix_track_parents import get_remix_track_parents from src.queries.get_remixes_of import get_remixes_of from src.queries.get_repost_feed_for_user import get_repost_feed_for_user diff --git a/packages/discovery-provider/src/tasks/index_payment_router.py b/packages/discovery-provider/src/tasks/index_payment_router.py index 6df879ffec3..e6bfa411cc7 100644 --- a/packages/discovery-provider/src/tasks/index_payment_router.py +++ b/packages/discovery-provider/src/tasks/index_payment_router.py @@ -37,7 +37,7 @@ ) from src.models.users.user import User from src.models.users.user_bank import USDCUserBankAccount -from src.queries.get_extended_purchase_gate import ( +from src.queries.get_purchase_gate import ( ExtendedSplit, add_wallet_info_to_splits, calculate_split_amounts, diff --git a/packages/discovery-provider/src/tasks/index_user_bank.py b/packages/discovery-provider/src/tasks/index_user_bank.py index 68100e2b9fd..cf1a9b07f01 100644 --- a/packages/discovery-provider/src/tasks/index_user_bank.py +++ b/packages/discovery-provider/src/tasks/index_user_bank.py @@ -41,7 +41,7 @@ from src.models.users.user_bank import USDCUserBankAccount, UserBankAccount, UserBankTx from src.models.users.user_tip import UserTip from src.queries.get_balances import enqueue_immediate_balance_refresh -from src.queries.get_extended_purchase_gate import ( +from src.queries.get_purchase_gate import ( add_wallet_info_to_splits, calculate_split_amounts, to_wallet_amount_map, From 18c5819e66bcab80e46f384401fc8b4293745f23 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:30:31 -0700 Subject: [PATCH 04/17] Generate SDK (userId is optional now) --- .../api/generated/default/models/ExtendedPaymentSplit.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts b/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts index eaabe36fe53..f899881e888 100644 --- a/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts +++ b/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts @@ -22,10 +22,10 @@ import { exists, mapValues } from '../runtime'; export interface ExtendedPaymentSplit { /** * - * @type {number} + * @type {string} * @memberof ExtendedPaymentSplit */ - userId: number; + userId?: string; /** * * @type {number} @@ -57,7 +57,6 @@ export interface ExtendedPaymentSplit { */ export function instanceOfExtendedPaymentSplit(value: object): value is ExtendedPaymentSplit { let isInstance = true; - isInstance = isInstance && "userId" in value && value["userId"] !== undefined; isInstance = isInstance && "percentage" in value && value["percentage"] !== undefined; isInstance = isInstance && "payoutWallet" in value && value["payoutWallet"] !== undefined; isInstance = isInstance && "amount" in value && value["amount"] !== undefined; @@ -75,7 +74,7 @@ export function ExtendedPaymentSplitFromJSONTyped(json: any, ignoreDiscriminator } return { - 'userId': json['user_id'], + 'userId': !exists(json, 'user_id') ? undefined : json['user_id'], 'percentage': json['percentage'], 'ethWallet': !exists(json, 'eth_wallet') ? undefined : json['eth_wallet'], 'payoutWallet': json['payout_wallet'], From 9c7468d66d16589a4628914f806a7e878fea2f0a Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:35:17 -0700 Subject: [PATCH 05/17] Revert "Rename to get_purchase_gate to disambiguate extend_access_gate()" This reverts commit 42c7d03fa714f949e3982b35efcac1fa7a590711. --- ....py => test_get_extended_purchase_gate.py} | 8 ++--- .../discovery-provider/src/api/v1/helpers.py | 29 ++++--------------- .../src/api/v1/models/access_gate.py | 1 - .../src/api/v1/playlists.py | 4 +-- .../discovery-provider/src/api/v1/tracks.py | 11 ++++--- ..._gate.py => get_extended_purchase_gate.py} | 20 ++++++++----- ...> get_extended_purchase_gate_unit_test.py} | 0 .../discovery-provider/src/queries/queries.py | 2 +- .../src/tasks/index_payment_router.py | 2 +- .../src/tasks/index_user_bank.py | 2 +- 10 files changed, 31 insertions(+), 48 deletions(-) rename packages/discovery-provider/integration_tests/queries/{test_get_purchase_gate.py => test_get_extended_purchase_gate.py} (98%) rename packages/discovery-provider/src/queries/{get_purchase_gate.py => get_extended_purchase_gate.py} (93%) rename packages/discovery-provider/src/queries/{get_purchase_gate_unit_test.py => get_extended_purchase_gate_unit_test.py} (100%) diff --git a/packages/discovery-provider/integration_tests/queries/test_get_purchase_gate.py b/packages/discovery-provider/integration_tests/queries/test_get_extended_purchase_gate.py similarity index 98% rename from packages/discovery-provider/integration_tests/queries/test_get_purchase_gate.py rename to packages/discovery-provider/integration_tests/queries/test_get_extended_purchase_gate.py index e6750a15718..082b4b72b32 100644 --- a/packages/discovery-provider/integration_tests/queries/test_get_purchase_gate.py +++ b/packages/discovery-provider/integration_tests/queries/test_get_extended_purchase_gate.py @@ -1,10 +1,10 @@ import datetime from integration_tests.utils import populate_mock_db -from src.queries.get_purchase_gate import ( +from src.queries.get_extended_purchase_gate import ( add_wallet_info_to_splits, calculate_split_amounts, - get_new_purchase_gate, + get_extended_purchase_gate, to_wallet_amount_map, ) from src.utils.db_session import get_db @@ -65,7 +65,7 @@ def test_get_extended_splits(app): with db.scoped_session() as session: # Gets most recent payout wallet by default - res = get_new_purchase_gate( + res = get_extended_purchase_gate( { "usdc_purchase": { "price": 100, @@ -169,7 +169,7 @@ def test_get_extended_splits_with_network_cut(app): with db.scoped_session() as session: # Gets most recent payout wallet by default - res = get_new_purchase_gate( + res = get_extended_purchase_gate( { "usdc_purchase": { "price": 100, diff --git a/packages/discovery-provider/src/api/v1/helpers.py b/packages/discovery-provider/src/api/v1/helpers.py index 92463632070..659532b6442 100644 --- a/packages/discovery-provider/src/api/v1/helpers.py +++ b/packages/discovery-provider/src/api/v1/helpers.py @@ -11,15 +11,11 @@ from src.api.v1.models.common import full_response from src.models.rewards.challenge import ChallengeType from src.queries.get_challenges import ChallengeResponse +from src.queries.get_extended_purchase_gate import get_legacy_purchase_gate from src.queries.get_notifications import ( NotificationType, default_valid_notification_types, ) -from src.queries.get_purchase_gate import ( - AccessGate, - PurchaseGate, - get_legacy_purchase_gate, -) from src.queries.get_support_for_user import SupportResponse from src.queries.get_undisbursed_challenges import UndisbursedChallengeResponse from src.queries.query_helpers import ( @@ -403,32 +399,17 @@ def extend_track(track, session=None): # Transform new format of splits to legacy format for client compatibility if "stream_conditions" in track: - track["stream_conditions"] = extend_access_gate( - track["stream_conditions"], session, use_legacy_purchase_gate=True + track["stream_conditions"] = get_legacy_purchase_gate( + track["stream_conditions"], session ) if "download_conditions" in track: - track["download_conditions"] = extend_access_gate( - track["download_conditions"], session, use_legacy_purchase_gate=True + track["download_conditions"] = get_legacy_purchase_gate( + track["download_conditions"], session ) return track -def extend_access_gate(gate: AccessGate, session=None, use_legacy_purchase_gate=False): - if "usdc_purchase" in gate: - if use_legacy_purchase_gate: - return get_legacy_purchase_gate(gate, session) - # help mypy - gate = cast(PurchaseGate, gate) - for split in gate["usdc_purchase"]["splits"]: - if split.get("user_id") is not None: - # help mypy - user_id = cast(int, split["user_id"]) - split["user_id"] = encode_int_id(user_id) - return gate - return gate - - def get_encoded_track_id(track): return {"id": encode_int_id(track["track_id"])} diff --git a/packages/discovery-provider/src/api/v1/models/access_gate.py b/packages/discovery-provider/src/api/v1/models/access_gate.py index e6625e32919..f56bf7f3559 100644 --- a/packages/discovery-provider/src/api/v1/models/access_gate.py +++ b/packages/discovery-provider/src/api/v1/models/access_gate.py @@ -92,7 +92,6 @@ "extended_payment_split", payment_split, { - "user_id": fields.String(), "eth_wallet": fields.String(), "payout_wallet": fields.String(required=True), "amount": fields.Integer(required=True), diff --git a/packages/discovery-provider/src/api/v1/playlists.py b/packages/discovery-provider/src/api/v1/playlists.py index 2e24da64205..8f05f1cf740 100644 --- a/packages/discovery-provider/src/api/v1/playlists.py +++ b/packages/discovery-provider/src/api/v1/playlists.py @@ -32,9 +32,9 @@ playlist_model, ) from src.api.v1.models.users import user_model_full +from src.queries.get_extended_purchase_gate import get_extended_purchase_gate from src.queries.get_playlist_tracks import get_playlist_tracks from src.queries.get_playlists import get_playlists -from src.queries.get_purchase_gate import get_new_purchase_gate from src.queries.get_reposters_for_playlist import get_reposters_for_playlist from src.queries.get_savers_for_playlist import get_savers_for_playlist from src.queries.get_top_playlists import get_top_playlists # pylint: disable=C0302 @@ -720,7 +720,7 @@ def get(self, playlist_id: str): if not playlists: abort_not_found(playlist_id, ns) raw = playlists[0] - stream_conditions = get_new_purchase_gate( + stream_conditions = get_extended_purchase_gate( gate=raw["stream_conditions"], include_network_cut=include_network_cut ) playlist = extend_playlist(raw) diff --git a/packages/discovery-provider/src/api/v1/tracks.py b/packages/discovery-provider/src/api/v1/tracks.py index 78a5c5d7967..005c985fbe6 100644 --- a/packages/discovery-provider/src/api/v1/tracks.py +++ b/packages/discovery-provider/src/api/v1/tracks.py @@ -18,7 +18,6 @@ current_user_parser, decode_ids_array, decode_with_abort, - extend_access_gate, extend_blob_info, extend_track, extend_user, @@ -46,11 +45,11 @@ TRENDING_TRACKS_TTL_SEC, ) from src.queries.get_comments import get_track_comments +from src.queries.get_extended_purchase_gate import get_extended_purchase_gate from src.queries.get_feed import get_feed from src.queries.get_latest_entities import get_latest_entities from src.queries.get_nft_gated_track_signatures import get_nft_gated_track_signatures from src.queries.get_premium_tracks import get_usdc_purchase_tracks -from src.queries.get_purchase_gate import get_new_purchase_gate from src.queries.get_random_tracks import get_random_tracks from src.queries.get_recommended_tracks import ( DEFAULT_RECOMMENDED_LIMIT, @@ -1875,13 +1874,13 @@ def get(self, track_id: str): if not tracks: abort_not_found(track_id, ns) raw = tracks[0] - stream_conditions = get_new_purchase_gate( + stream_conditions = get_extended_purchase_gate( gate=raw["stream_conditions"], include_network_cut=include_network_cut ) - download_conditions = get_new_purchase_gate( + download_conditions = get_extended_purchase_gate( gate=raw["download_conditions"], include_network_cut=include_network_cut ) track = extend_track(raw) - track["stream_conditions"] = extend_access_gate(stream_conditions) - track["download_conditions"] = extend_access_gate(download_conditions) + track["stream_conditions"] = stream_conditions + track["download_conditions"] = download_conditions return success_response(track) diff --git a/packages/discovery-provider/src/queries/get_purchase_gate.py b/packages/discovery-provider/src/queries/get_extended_purchase_gate.py similarity index 93% rename from packages/discovery-provider/src/queries/get_purchase_gate.py rename to packages/discovery-provider/src/queries/get_extended_purchase_gate.py index 9a2810c8fab..d237e49c19c 100644 --- a/packages/discovery-provider/src/queries/get_purchase_gate.py +++ b/packages/discovery-provider/src/queries/get_extended_purchase_gate.py @@ -170,7 +170,7 @@ def calculate_split_amounts( def add_wallet_info_to_splits( session: Session, splits: List[Split], timestamp: Optional[datetime] ): - user_ids = [split.get("user_id") for split in splits] + user_ids = [split["user_id"] for split in splits] max_block_timestamps = ( session.query( @@ -230,7 +230,7 @@ def to_wallet_amount_map(splits: List[ExtendedSplit]): } -def _get_new_purchase_gate( +def _get_extended_purchase_gate( session: Session, gate: PurchaseGate, include_network_cut=False ): price = gate.get("usdc_purchase", {}).get("price", None) @@ -239,21 +239,23 @@ def _get_new_purchase_gate( splits = add_wallet_info_to_splits(session, splits, datetime.now()) extended_splits = calculate_split_amounts(price, splits, include_network_cut) extended_gate: ExtendedPurchaseGate = { - "usdc_purchase": {"price": price, "splits": extended_splits}, + "usdc_purchase": {"price": price, "splits": extended_splits} } return extended_gate -def get_new_purchase_gate(gate: AccessGate, session=None, include_network_cut=False): +def get_extended_purchase_gate( + gate: AccessGate, session=None, include_network_cut=False +): if gate and "usdc_purchase" in gate: # mypy gets confused.... gate = cast(PurchaseGate, gate) if session: - return _get_new_purchase_gate(session, gate, include_network_cut) + return _get_extended_purchase_gate(session, gate, include_network_cut) else: db: SessionManager = get_db_read_replica() with db.scoped_session() as session: - return _get_new_purchase_gate(session, gate, include_network_cut) + return _get_extended_purchase_gate(session, gate, include_network_cut) def get_legacy_purchase_gate(gate: AccessGate, session=None, include_network_cut=False): @@ -261,11 +263,13 @@ def get_legacy_purchase_gate(gate: AccessGate, session=None, include_network_cut # mypy gets confused.... gate = cast(PurchaseGate, gate) if session: - new_gate = _get_new_purchase_gate(session, gate, include_network_cut) + new_gate = _get_extended_purchase_gate(session, gate, include_network_cut) else: db: SessionManager = get_db_read_replica() with db.scoped_session() as session: - new_gate = _get_new_purchase_gate(session, gate, include_network_cut) + new_gate = _get_extended_purchase_gate( + session, gate, include_network_cut + ) extended_splits = new_gate["usdc_purchase"]["splits"] splits = to_wallet_amount_map(extended_splits) new_gate["usdc_purchase"]["splits"] = splits diff --git a/packages/discovery-provider/src/queries/get_purchase_gate_unit_test.py b/packages/discovery-provider/src/queries/get_extended_purchase_gate_unit_test.py similarity index 100% rename from packages/discovery-provider/src/queries/get_purchase_gate_unit_test.py rename to packages/discovery-provider/src/queries/get_extended_purchase_gate_unit_test.py diff --git a/packages/discovery-provider/src/queries/queries.py b/packages/discovery-provider/src/queries/queries.py index 9f7947de38e..1072328fdd8 100644 --- a/packages/discovery-provider/src/queries/queries.py +++ b/packages/discovery-provider/src/queries/queries.py @@ -4,6 +4,7 @@ from src import api_helpers, exceptions from src.queries.get_cid_source import get_cid_source +from src.queries.get_extended_purchase_gate import get_legacy_purchase_gate from src.queries.get_feed import get_feed from src.queries.get_follow_intersection_users import get_follow_intersection_users from src.queries.get_followees_for_user import get_followees_for_user @@ -17,7 +18,6 @@ get_previously_private_playlists, ) from src.queries.get_previously_unlisted_tracks import get_previously_unlisted_tracks -from src.queries.get_purchase_gate import get_legacy_purchase_gate from src.queries.get_remix_track_parents import get_remix_track_parents from src.queries.get_remixes_of import get_remixes_of from src.queries.get_repost_feed_for_user import get_repost_feed_for_user diff --git a/packages/discovery-provider/src/tasks/index_payment_router.py b/packages/discovery-provider/src/tasks/index_payment_router.py index e6bfa411cc7..6df879ffec3 100644 --- a/packages/discovery-provider/src/tasks/index_payment_router.py +++ b/packages/discovery-provider/src/tasks/index_payment_router.py @@ -37,7 +37,7 @@ ) from src.models.users.user import User from src.models.users.user_bank import USDCUserBankAccount -from src.queries.get_purchase_gate import ( +from src.queries.get_extended_purchase_gate import ( ExtendedSplit, add_wallet_info_to_splits, calculate_split_amounts, diff --git a/packages/discovery-provider/src/tasks/index_user_bank.py b/packages/discovery-provider/src/tasks/index_user_bank.py index cf1a9b07f01..68100e2b9fd 100644 --- a/packages/discovery-provider/src/tasks/index_user_bank.py +++ b/packages/discovery-provider/src/tasks/index_user_bank.py @@ -41,7 +41,7 @@ from src.models.users.user_bank import USDCUserBankAccount, UserBankAccount, UserBankTx from src.models.users.user_tip import UserTip from src.queries.get_balances import enqueue_immediate_balance_refresh -from src.queries.get_purchase_gate import ( +from src.queries.get_extended_purchase_gate import ( add_wallet_info_to_splits, calculate_split_amounts, to_wallet_amount_map, From 607241d46fe050237bd8796f71ab7657d41e996d Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:38:59 -0700 Subject: [PATCH 06/17] userId as optional number --- packages/discovery-provider/src/api/v1/models/access_gate.py | 1 + .../sdk/api/generated/default/models/ExtendedPaymentSplit.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/discovery-provider/src/api/v1/models/access_gate.py b/packages/discovery-provider/src/api/v1/models/access_gate.py index f56bf7f3559..46e87ec3098 100644 --- a/packages/discovery-provider/src/api/v1/models/access_gate.py +++ b/packages/discovery-provider/src/api/v1/models/access_gate.py @@ -92,6 +92,7 @@ "extended_payment_split", payment_split, { + "user_id": fields.Integer(required=False), "eth_wallet": fields.String(), "payout_wallet": fields.String(required=True), "amount": fields.Integer(required=True), diff --git a/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts b/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts index f899881e888..42d41dd09f4 100644 --- a/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts +++ b/packages/libs/src/sdk/api/generated/default/models/ExtendedPaymentSplit.ts @@ -22,10 +22,10 @@ import { exists, mapValues } from '../runtime'; export interface ExtendedPaymentSplit { /** * - * @type {string} + * @type {number} * @memberof ExtendedPaymentSplit */ - userId?: string; + userId?: number; /** * * @type {number} From e044e3c80d41464e8d0cb70856702a2caf54e1dd Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:42:47 -0700 Subject: [PATCH 07/17] remove hashid'ing purchase splits to be consistent --- packages/discovery-provider/src/api/v1/helpers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/discovery-provider/src/api/v1/helpers.py b/packages/discovery-provider/src/api/v1/helpers.py index 659532b6442..bffefe4b42c 100644 --- a/packages/discovery-provider/src/api/v1/helpers.py +++ b/packages/discovery-provider/src/api/v1/helpers.py @@ -588,9 +588,6 @@ def extend_purchase(purchase): new_purchase["seller_user_id"] = encode_int_id(purchase["seller_user_id"]) new_purchase["content_id"] = encode_int_id(purchase["content_id"]) new_purchase["access"] = purchase["access"] - for split in new_purchase["splits"]: - if "user_id" in split: - split["user_id"] = encode_int_id(split["user_id"]) return new_purchase From 0ba4dc8f4e8b856f898b1539acf131ee66d43794 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:59:12 -0700 Subject: [PATCH 08/17] idempotency on the update --- .../ddl/migrations/0093_splits_in_purchases.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql b/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql index 8073ea69d5a..4a8f64e2216 100644 --- a/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql +++ b/packages/discovery-provider/ddl/migrations/0093_splits_in_purchases.sql @@ -50,7 +50,8 @@ FROM LEFT JOIN user_payout_wallet_history ON user_payout_wallet_history.user_id = users.user_id AND user_payout_wallet_history.block_timestamp = relevant_timestamps.block_timestamp WHERE - up.signature = usdc_purchases.signature; + up.splits IS NULL + AND up.signature = usdc_purchases.signature; ALTER TABLE usdc_purchases From aa666d47735b80d5f817d33f2d678a88df1fbbed Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:00:27 -0700 Subject: [PATCH 09/17] correct type to integer, since no longer hash IDing it --- packages/discovery-provider/src/api/v1/models/users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/discovery-provider/src/api/v1/models/users.py b/packages/discovery-provider/src/api/v1/models/users.py index 67116c9aa92..e46feb6a64b 100644 --- a/packages/discovery-provider/src/api/v1/models/users.py +++ b/packages/discovery-provider/src/api/v1/models/users.py @@ -157,7 +157,7 @@ split = ns.model( "purchase_split", - {"user_id": fields.String(required=True), "amount": fields.String(required=True)}, + {"user_id": fields.Integer(required=True), "amount": fields.String(required=True)}, ) purchase = ns.model( From fdc3368df5e781709239e0dabf0f6edb1d140224 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:02:27 -0700 Subject: [PATCH 10/17] remove typo --- packages/discovery-provider/src/api/v1/models/users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/discovery-provider/src/api/v1/models/users.py b/packages/discovery-provider/src/api/v1/models/users.py index e46feb6a64b..49e5d221f27 100644 --- a/packages/discovery-provider/src/api/v1/models/users.py +++ b/packages/discovery-provider/src/api/v1/models/users.py @@ -174,6 +174,6 @@ "created_at": fields.String(required=True), "updated_at": fields.String(required=True), "access": StringEnumToLower(required=True), - "splits": fields.List(fields.Nested(split, requird=True), required=True), + "splits": fields.List(fields.Nested(split), required=True), }, ) From 14003a0063360f2f891ce1a920309098a2d8bbad Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:13:48 -0700 Subject: [PATCH 11/17] Remove user bank purchase indexing and fix tests --- .../gated_content/test_access.py | 8 +- .../queries/test_get_usdc_purchases.py | 8 +- .../tasks/test_index_payment_router.py | 14 +- .../tasks/test_index_user_bank.py | 513 +--- .../tasks/user_bank_mock_transactions.py | 2139 +++-------------- .../integration_tests/utils.py | 12 + .../src/tasks/index_user_bank.py | 353 +-- 7 files changed, 408 insertions(+), 2639 deletions(-) diff --git a/packages/discovery-provider/integration_tests/gated_content/test_access.py b/packages/discovery-provider/integration_tests/gated_content/test_access.py index 74a0f9e653b..7c0ff1f3030 100644 --- a/packages/discovery-provider/integration_tests/gated_content/test_access.py +++ b/packages/discovery-provider/integration_tests/gated_content/test_access.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from typing import Any, Dict, List from integration_tests.utils import populate_mock_db @@ -217,19 +217,19 @@ "buyer_user_id": 3, "content_id": 2, "content_type": "album", - "created_at": datetime.utcfromtimestamp(1711485198), + "created_at": datetime.fromtimestamp(1711485198, timezone.utc), }, { "buyer_user_id": 5, "content_id": 2, "content_type": "album", - "created_at": datetime.utcfromtimestamp(1711485200), + "created_at": datetime.fromtimestamp(1711485200, timezone.utc), }, { "buyer_user_id": 6, "content_id": usdc_stream_gated_track["track_id"], "content_type": "track", - "created_at": datetime.utcfromtimestamp(1711485200), + "created_at": datetime.fromtimestamp(1711485200, timezone.utc), }, ] user_1 = {"user_id": 1} diff --git a/packages/discovery-provider/integration_tests/queries/test_get_usdc_purchases.py b/packages/discovery-provider/integration_tests/queries/test_get_usdc_purchases.py index ae53607bebe..8728ae7dbf2 100644 --- a/packages/discovery-provider/integration_tests/queries/test_get_usdc_purchases.py +++ b/packages/discovery-provider/integration_tests/queries/test_get_usdc_purchases.py @@ -96,6 +96,7 @@ "content_type": PurchaseType.track, "content_id": 4, "created_at": datetime(2023, 8, 11), + "splits": [{"user_id": 10, "amount": 3500000, "percentage": 100}], }, { "slot": 4, @@ -106,6 +107,7 @@ "content_type": PurchaseType.track, "content_id": 3, "created_at": datetime(2023, 8, 10), + "splits": [{"user_id": 10, "amount": 1990000, "percentage": 100}], }, { "slot": 8, @@ -116,6 +118,7 @@ "content_type": PurchaseType.track, "content_id": 5, "created_at": datetime(2023, 8, 14), + "splits": [{"user_id": 20, "amount": 1000000, "percentage": 100}], }, { "slot": 7, @@ -125,7 +128,8 @@ "extra_amount": 0, "content_type": PurchaseType.track, "content_id": 3, - "created_at": datetime(2023, 8, 13), + "created_at": datetime(2023, 8, 13, 12), + "splits": [{"user_id": 10, "amount": 1990000, "percentage": 100}], }, { "slot": 6, @@ -136,6 +140,7 @@ "content_type": PurchaseType.track, "content_id": 2, "created_at": datetime(2023, 8, 12), + "splits": [{"user_id": 10, "amount": 2990000, "percentage": 100}], }, { "slot": 9, @@ -146,6 +151,7 @@ "content_type": PurchaseType.track, "content_id": 2, "created_at": datetime(2023, 8, 13), + "splits": [{"user_id": 10, "amount": 2990000, "percentage": 100}], }, ], } diff --git a/packages/discovery-provider/integration_tests/tasks/test_index_payment_router.py b/packages/discovery-provider/integration_tests/tasks/test_index_payment_router.py index df35597bec2..f98dc1ac6c2 100644 --- a/packages/discovery-provider/integration_tests/tasks/test_index_payment_router.py +++ b/packages/discovery-provider/integration_tests/tasks/test_index_payment_router.py @@ -93,21 +93,21 @@ "track_price_history": [ { # pay full price to trackOwner "track_id": 1, - "splits": {"7gfRGGdp89N9g3mCsZjaGmDDRdcTnZh9u3vYyBab2tRy": 1000000}, + "splits": [{"user_id": 1, "amount": 1000000, "percentage": 100}], "total_price_cents": 100, "access": PurchaseAccessType.stream, }, { # pay $1 each to track owner and third party "track_id": 2, - "splits": { - "7gfRGGdp89N9g3mCsZjaGmDDRdcTnZh9u3vYyBab2tRy": 1000000, - "7dw7W4Yv7F1uWb9dVH1CFPm39mePyypuCji2zxcFA556": 1000000, - }, + "splits": [ + {"user_id": 1, "amount": 1000000, "percentage": 50}, + {"user_id": 3, "amount": 1000000, "percentage": 50}, + ], "total_price_cents": 200, }, { # download access type "track_id": 3, - "splits": {"7gfRGGdp89N9g3mCsZjaGmDDRdcTnZh9u3vYyBab2tRy": 1000000}, + "splits": [{"user_id": 1, "amount": 1000000, "percentage": 100}], "total_price_cents": 100, "access": PurchaseAccessType.download, }, @@ -115,7 +115,7 @@ "album_price_history": [ { # pay full price to albumOwner "playlist_id": 1, - "splits": {"7gfRGGdp89N9g3mCsZjaGmDDRdcTnZh9u3vYyBab2tRy": 1000000}, + "splits": [{"user_id": 1, "amount": 1000000, "percentage": 100}], "total_price_cents": 100, }, ], diff --git a/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py b/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py index 75ba679fdbf..5e7163ce55a 100644 --- a/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py +++ b/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py @@ -12,17 +12,9 @@ SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, SENDER_USDC_USER_BANK_ADDRESS, mock_failed_track_purchase_tx, - mock_invalid_track_purchase_bad_splits_tx, - mock_invalid_track_purchase_missing_splits_tx, - mock_invalid_track_purchase_unknown_pda_tx, mock_unknown_instruction_tx, - mock_valid_album_purchase_tx, mock_valid_create_audio_token_account_tx, mock_valid_create_usdc_token_account_tx, - mock_valid_track_purchase_pay_extra_tx, - mock_valid_track_purchase_tx, - mock_valid_track_purchase_tx_download_access, - mock_valid_track_purchase_tx_stream_access, mock_valid_transfer_prepare_withdrawal_tx, mock_valid_transfer_withdrawal_tx, mock_valid_transfer_without_purchase_tx, @@ -37,11 +29,7 @@ TransactionMethod, TransactionType, ) -from src.models.users.usdc_purchase import ( - PurchaseAccessType, - PurchaseType, - USDCPurchase, -) +from src.models.users.usdc_purchase import PurchaseAccessType, USDCPurchase from src.models.users.usdc_transactions_history import ( USDCTransactionMethod, USDCTransactionsHistory, @@ -155,150 +143,6 @@ } -def test_process_user_bank_tx_details_valid_purchase(app): - tx_response = mock_valid_track_purchase_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - session=session, - redis=redis, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is not None - assert purchase.seller_user_id == track_owner_id - assert purchase.buyer_user_id == track_buyer_id - assert purchase.amount == 1000000 - assert purchase.extra_amount == 0 - assert purchase.content_type == PurchaseType.track - assert purchase.content_id == 1 - assert purchase.access == PurchaseAccessType.stream - - owner_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_owner_usdc_user_bank) - .first() - ) - assert owner_transaction_record is not None - assert ( - owner_transaction_record.transaction_type - == USDCTransactionType.purchase_content - ) - assert owner_transaction_record.method == USDCTransactionMethod.receive - assert owner_transaction_record.change == 1000000 - assert owner_transaction_record.tx_metadata == str(track_buyer_id) - - buyer_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_buyer_user_bank) - .first() - ) - assert buyer_transaction_record is not None - assert ( - buyer_transaction_record.transaction_type - == USDCTransactionType.purchase_content - ) - assert buyer_transaction_record.method == USDCTransactionMethod.send - assert buyer_transaction_record.change == -1000000 - assert buyer_transaction_record.tx_metadata == str(track_owner_id) - - -def test_process_user_bank_tx_details_valid_purchase_album(app): - tx_response = mock_valid_album_purchase_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - session=session, - redis=redis, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is not None - assert purchase.seller_user_id == track_owner_id - assert purchase.buyer_user_id == track_buyer_id - assert purchase.amount == 1000000 - assert purchase.extra_amount == 0 - assert purchase.content_type == PurchaseType.album - assert purchase.content_id == 1 - assert purchase.access == PurchaseAccessType.stream - - owner_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_owner_usdc_user_bank) - .first() - ) - assert owner_transaction_record is not None - assert ( - owner_transaction_record.transaction_type - == USDCTransactionType.purchase_content - ) - assert owner_transaction_record.method == USDCTransactionMethod.receive - assert owner_transaction_record.change == 1000000 - assert owner_transaction_record.tx_metadata == str(track_buyer_id) - - buyer_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_buyer_user_bank) - .first() - ) - assert buyer_transaction_record is not None - assert ( - buyer_transaction_record.transaction_type - == USDCTransactionType.purchase_content - ) - assert buyer_transaction_record.method == USDCTransactionMethod.send - assert buyer_transaction_record.change == -1000000 - assert buyer_transaction_record.tx_metadata == str(track_owner_id) - - def test_process_user_bank_tx_details_transfer_without_purchase( app, ): @@ -361,249 +205,6 @@ def test_process_user_bank_tx_details_transfer_without_purchase( assert buyer_transaction_record.tx_metadata == track_owner_usdc_user_bank -def test_process_user_bank_tx_details_valid_purchase_with_pay_extra(app): - tx_response = mock_valid_track_purchase_pay_extra_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - redis=redis, - session=session, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is not None - assert purchase.seller_user_id == track_owner_id - assert purchase.buyer_user_id == track_buyer_id - assert purchase.amount == 1000000 - assert purchase.extra_amount == 1000000 - assert purchase.content_type == PurchaseType.track - assert purchase.content_id == 1 - - owner_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_owner_usdc_user_bank) - .first() - ) - assert owner_transaction_record is not None - assert ( - owner_transaction_record.transaction_type - == USDCTransactionType.purchase_content - ) - assert owner_transaction_record.method == USDCTransactionMethod.receive - assert owner_transaction_record.change == 2000000 - assert owner_transaction_record.tx_metadata == str(track_buyer_id) - - buyer_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_buyer_user_bank) - .first() - ) - assert buyer_transaction_record is not None - assert ( - buyer_transaction_record.transaction_type - == USDCTransactionType.purchase_content - ) - assert buyer_transaction_record.method == USDCTransactionMethod.send - assert buyer_transaction_record.change == -2000000 - assert buyer_transaction_record.tx_metadata == str(track_owner_id) - - -# Simulates buying a track for the correct price ($2) and allocating all of the -# amount to one address, with a missing split for the other -def test_process_user_bank_tx_details_invalid_purchase_missing_splits(app): - tx_response = mock_invalid_track_purchase_missing_splits_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - redis=redis, - session=session, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - # Expect no purchase record - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is None - - # We do still expect the transfers to get indexed, but as regular transfers - owner_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_owner_usdc_user_bank) - .first() - ) - assert owner_transaction_record is not None - # Regular transfer, not a purchase - assert owner_transaction_record.transaction_type == USDCTransactionType.transfer - assert owner_transaction_record.method == USDCTransactionMethod.receive - assert owner_transaction_record.change == 2000000 - # For transfers, the metadata is the source address - assert owner_transaction_record.tx_metadata == track_buyer_user_bank - - -# Simulates buying a track for the correct price ($2) but giving one of the recipients -# an insufficient amount. track owner receives $1.50 and second split receives $0.50 when -# they should both receive $1 -def test_process_user_bank_tx_details_invalid_purchase_bad_splits(app): - tx_response = mock_invalid_track_purchase_bad_splits_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - redis=redis, - session=session, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - # Expect no purchase record - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is None - - # We do still expect the transfers to get indexed, but as regular transfers - owner_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_owner_usdc_user_bank) - .first() - ) - assert owner_transaction_record is not None - # Regular transfer, not a purchase - assert owner_transaction_record.transaction_type == USDCTransactionType.transfer - assert owner_transaction_record.method == USDCTransactionMethod.receive - assert owner_transaction_record.change == 1500000 - # For transfers, the metadata is the source address - assert owner_transaction_record.tx_metadata == track_buyer_user_bank - - buyer_transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .filter(USDCTransactionsHistory.user_bank == track_buyer_user_bank) - .first() - ) - assert buyer_transaction_record is not None - # Regular transfer, not a purchase - # This is a little odd in that we index one transfer of $2 out of the sender - # account with just the track owner user bank as a destination. Technically - # since it's just metadata, this should be fine. But we eventually should include - # all addresses, or register a separate transaction for each transfer. - assert buyer_transaction_record.transaction_type == USDCTransactionType.transfer - assert buyer_transaction_record.method == USDCTransactionMethod.send - assert buyer_transaction_record.change == -2000000 - # For transfers, the metadata is the dest address - assert buyer_transaction_record.tx_metadata == track_owner_usdc_user_bank - - -def test_process_user_bank_txs_details_create_challenge_events_for_purchase(app): - tx_response = mock_valid_track_purchase_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - redis=redis, - session=session, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.fromtimestamp(tx_response.value.block_time), - challenge_event_bus=challenge_event_bus, - ) - # Note: Challenge amounts are 1 per dollar of USDC - calls = [ - call( - ChallengeEvent.audio_matching_buyer, - tx_response.value.slot, - datetime.fromtimestamp(tx_response.value.block_time), - track_buyer_id, - {"track_id": 1, "amount": 1}, - ), - call( - ChallengeEvent.audio_matching_seller, - tx_response.value.slot, - datetime.fromtimestamp(tx_response.value.block_time), - track_owner_id, - {"track_id": 1, "sender_user_id": track_buyer_id, "amount": 1}, - ), - ] - challenge_event_bus.dispatch.assert_has_calls( - calls, - any_order=True, - ) - - def test_process_user_bank_txs_details_create_usdc_user_bank(app): tx_response = mock_valid_create_usdc_token_account_tx with app.app_context(): @@ -687,52 +288,6 @@ def test_process_user_bank_tx_details_skip_errors(app): assert transaction_record is None -# Transfers must reference accounts belonging to the Claimable Tokens PDA -def test_process_user_bank_txs_details_skip_unknown_PDA_ATAs(app): - # This transaction does everything a valid transaction would for - # a purchase, but uses an ATA that we don't recognize as the source. - tx_response = mock_invalid_track_purchase_unknown_pda_tx - with app.app_context(): - db = get_db() - redis = get_redis() - - solana_client_manager_mock = create_autospec(SolanaClientManager) - - transaction = tx_response.value.transaction.transaction - - tx_sig_str = str(transaction.signatures[0]) - - challenge_event_bus = create_autospec(ChallengeEventBus) - - populate_mock_db(db, test_entries) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - redis=redis, - session=session, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - # Expect no purchase record - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is None - - # Expect no transfer record - transaction_record = ( - session.query(USDCTransactionsHistory) - .filter(USDCTransactionsHistory.signature == tx_sig_str) - .first() - ) - assert transaction_record is None - - # Don't process any transactions that aren't CreateAccount or Transfer instructions def test_process_user_bank_txs_details_skip_unknown_instructions(app): # This transaction results in a balance change but doesn't reference an @@ -1096,69 +651,3 @@ def test_process_user_bank_txs_details_transfer_audio_tip_challenge_event(app): ) ] challenge_event_bus.dispatch.assert_has_calls(calls) - - -# Index tx with stream access in memo correctly -def test_process_user_bank_txs_details_stream_access(app): - with app.app_context(): - db = get_db() - redis = get_redis() - solana_client_manager_mock = create_autospec(SolanaClientManager) - challenge_event_bus = create_autospec(ChallengeEventBus) - populate_mock_db(db, test_entries) - - tx_response = mock_valid_track_purchase_tx_stream_access - transaction = tx_response.value.transaction.transaction - tx_sig_str = str(transaction.signatures[0]) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - session=session, - redis=redis, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is not None - assert purchase.access == PurchaseAccessType.stream - - -# Index tx with download access in memo correctly -def test_process_user_bank_txs_details_download_access(app): - with app.app_context(): - db = get_db() - redis = get_redis() - solana_client_manager_mock = create_autospec(SolanaClientManager) - challenge_event_bus = create_autospec(ChallengeEventBus) - populate_mock_db(db, test_entries) - - tx_response = mock_valid_track_purchase_tx_download_access - transaction = tx_response.value.transaction.transaction - tx_sig_str = str(transaction.signatures[0]) - - with db.scoped_session() as session: - process_user_bank_tx_details( - solana_client_manager=solana_client_manager_mock, - session=session, - redis=redis, - tx_info=tx_response, - tx_sig=tx_sig_str, - timestamp=datetime.now(), - challenge_event_bus=challenge_event_bus, - ) - - purchase = ( - session.query(USDCPurchase) - .filter(USDCPurchase.signature == tx_sig_str) - .first() - ) - assert purchase is not None - assert purchase.access == PurchaseAccessType.download diff --git a/packages/discovery-provider/integration_tests/tasks/user_bank_mock_transactions.py b/packages/discovery-provider/integration_tests/tasks/user_bank_mock_transactions.py index c670e5b9e50..e583c2f4c29 100644 --- a/packages/discovery-provider/integration_tests/tasks/user_bank_mock_transactions.py +++ b/packages/discovery-provider/integration_tests/tasks/user_bank_mock_transactions.py @@ -55,27 +55,14 @@ # (backwards compatibility to old 3 or 4 field format) # base58.b58encode("track:1:10").decode("utf-8") PURCHASE_TRACK1_MEMO_DATA = "7YSwHDhdZsHu6X" -# base58.b58encode("track:2:10").decode("utf-8") -PURCHASE_TRACK2_MEMO_DATA = "7YSwHDhdZtmtNs" -# base58.b58encode("track:1:10:2:stream").decode("utf-8") -PURCHASE_TRACK_STREAM_ACCESS_MEMO_DATA = "NKSrfbiivt2H3Rc3uQd2JbGcAY" -# base58.b58encode("track:3:10:2:download").decode("utf-8") -PURCHASE_TRACK_DOWNLOAD_MEMO_DATA = "8AJuUqamV55ZcUtzu1FNWfHj9WwVm" -# base58.b58encode("album:1:10:2:stream").decode("utf-8") -PURCHASE_ALBUM1_MEMO_DATA = "JqTGUU6bYmRE1wDmu6zqRtzpKe" # base58.b58encode("Prepare Withdrawal").decode("utf-8") PREPARE_WITHDRAWAL_MEMO = "4LXeTxmZydvvx9jk2DnmBAwcX" # base58.b58encode("Withdrawal").decode("utf-8") WITHDRAWAL_MEMO = "5uqUSXZpY6AMST" -# contentType:contentId:blockNumber:purchaserUserId -# base58.b58encode("track:1:10:2").decode("utf-8") -PURCHASE_TRACK1_MEMO_DATA_WITH_PURCHASER_ID = "3CTE8jFKicpYknyXo" - - -# Purchase of track id 1 for $1 USDC -mock_valid_track_purchase_tx = GetTransactionResp.from_json( +# Transfer $1 USDC between two user banks without a purchase +mock_valid_transfer_without_purchase_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", @@ -117,12 +104,6 @@ "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", "stackHeight": None, }, - { - "programIdIndex": 8, - "accounts": [0], - "data": PURCHASE_TRACK1_MEMO_DATA, - "stackHeight": None, - }, ], }, }, @@ -180,9 +161,6 @@ "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", ], "preTokenBalances": [ { @@ -247,8 +225,9 @@ ) ) -# Purchase of album for $1 USDC -mock_valid_album_purchase_tx = GetTransactionResp.from_json( + +# Transfer $1 to a non user bank account as preparation for withdrawal +mock_valid_transfer_prepare_withdrawal_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", @@ -266,7 +245,7 @@ FEE_PAYER, SENDER_USDC_USER_BANK_ADDRESS, NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, + SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, "11111111111111111111111111111111", CLAIMABLE_TOKENS_PDA, USDC_PDA, @@ -293,7 +272,7 @@ { "programIdIndex": 8, "accounts": [0], - "data": PURCHASE_ALBUM1_MEMO_DATA, + "data": PREPARE_WITHDRAWAL_MEMO, "stackHeight": None, }, ], @@ -379,7 +358,7 @@ "amount": "0", "uiAmountString": "0", }, - "owner": USDC_PDA, + "owner": SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, ], @@ -405,7 +384,7 @@ "amount": "1000000", "uiAmountString": "1", }, - "owner": USDC_PDA, + "owner": SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, ], @@ -420,53 +399,70 @@ ) ) -# Purchase of track id 1 for $2 USDC, assumes track is priced at $1 -mock_valid_track_purchase_pay_extra_tx = GetTransactionResp.from_json( + +# Transfer $1 to a user bank and then out to an external address with a withdrawal memo +# Flow is root wallet usdc acct -> user bank -> external address +mock_valid_transfer_withdrawal_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", "result": { - "slot": 227246439, + "slot": 245096140, "transaction": { "signatures": [MOCK_SIGNATURE], "message": { "header": { "numRequiredSignatures": 1, "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, + "numReadonlyUnsignedAccounts": 9, }, "accountKeys": [ FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, + "3nmLmEzwBBjERiV9UU1a4JXzESwDjrKZdSjP1KG4M9Mc", # Destination + SENDER_USDC_USER_BANK_ADDRESS, # User Bank Sender + NONCE_ACCOUNT_ADDRESS, # Nonce + SENDER_ROOT_WALLET_USDC_ACCOUNT, # Root wallet usdc account "11111111111111111111111111111111", + USDC_MINT, CLAIMABLE_TOKENS_PDA, USDC_PDA, "KeccakSecp256k11111111111111111111111111111", "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", "Sysvar1nstructions1111111111111111111111111", "SysvarRent111111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", + "recentBlockhash": "B1jNkfQheX2m3NosedANVueUCw9sekihw5KNAYACwKWg", "instructions": [ { - "programIdIndex": 7, + "programIdIndex": 13, + "accounts": [4, 6, 2, 0], + "data": "gvPShZQhKrzGM", + "stackHeight": None, + }, + { + "programIdIndex": 9, "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", + "data": "H4eCsH1NV1T5Rrwb1xTxXJaqbsXvM53nh9nFhr4guwnTgJUjzbiPWBg4vTg44xzSgQCs1uEz1MYyFnynuyWtVKqMyUUvTNMU3giPhXtdfJLMrPhtaPz1sYN6qcd7YGwyk6cMvEZPPjGjMceYxumjPN4aYeFsrazCisbpp8FYiN4bVsV1HV5BkEqfH1wgDaezzBmFd", "stackHeight": None, }, { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], + "programIdIndex": 7, + "accounts": [0, 2, 1, 3, 8, 13, 12, 5, 14], "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", "stackHeight": None, }, { - "programIdIndex": 8, + "programIdIndex": 10, "accounts": [0], - "data": PURCHASE_TRACK1_MEMO_DATA, + "data": WITHDRAWAL_MEMO, + "stackHeight": None, + }, + { + "programIdIndex": 11, + "accounts": [0], + "data": "4sHBbAWcJZWyJ5TcmKVbb1xNF1kEk9KKgfjJ1pB8aiYY6b3jYCg3NRpegK2XUEdUdtZtEhiT9sxDTanxBg7QNo74XiqnQ6rmtsSP8Ky42NQ9oZQbMhptMz5hmVxzEfYUYFyQCDToEjfQT2daE3CtsXSdgt3BnmZnpck", "stackHeight": None, }, ], @@ -477,124 +473,162 @@ "status": {"Ok": None}, "fee": 10000, "preBalances": [ - 1689358166, + 49083651, + 2039280, 2039280, 953520, 2039280, 1, + 227469696505, 1141440, 0, 1, - 121159680, + 521498880, + 521498880, 0, 1009200, 934087680, ], "postBalances": [ - 1689348166, + 49073651, + 2039280, 2039280, 953520, 2039280, 1, + 227469696505, 1141440, 0, 1, - 121159680, + 521498880, + 521498880, 0, 1009200, 934087680, ], "innerInstructions": [ { - "index": 1, + "index": 2, "instructions": [ { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", + "programIdIndex": 14, + "accounts": [2, 1, 8, 8], + "data": "3QCwqmHZ4mdq", "stackHeight": 2, } ], } ], "logMessages": [ + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]", + "Program log: Instruction: TransferChecked", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6199 of 800000 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", "Program log: Instruction: Transfer", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 774885 compute units", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", + f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 793801 compute units", f"Program {CLAIMABLE_TOKENS_PDA} success", "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", + "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [1]", + "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr consumed 56953 of 769652 compute units", + "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success", ], "preTokenBalances": [ { "accountIndex": 1, "mint": USDC_MINT, "uiTokenAmount": { - "uiAmount": 2.0, + "uiAmount": None, "decimals": 6, - "amount": "2000000", - "uiAmountString": "2", + "amount": "0", + "uiAmountString": "0", }, - "owner": USDC_PDA, + "owner": "41zCUJsKk6cMB94DDtm99qWmyMZfp4GkAhhuz4xTwePu", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, { - "accountIndex": 3, + "accountIndex": 2, "mint": USDC_MINT, "uiTokenAmount": { - "uiAmount": None, + "uiAmount": 12.61479, "decimals": 6, - "amount": "0", - "uiAmountString": "0", + "amount": "12614790", + "uiAmountString": "12.61479", }, "owner": USDC_PDA, "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, + { + "accountIndex": 4, + "mint": USDC_MINT, + "uiTokenAmount": { + "uiAmount": 1.0, + "decimals": 6, + "amount": "1000000", + "uiAmountString": "1", + }, + "owner": SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + }, ], "postTokenBalances": [ { "accountIndex": 1, "mint": USDC_MINT, "uiTokenAmount": { - "uiAmount": None, + "uiAmount": 1.0, "decimals": 6, - "amount": "0", - "uiAmountString": "0", + "amount": "1000000", + "uiAmountString": "1", }, - "owner": USDC_PDA, + "owner": "41zCUJsKk6cMB94DDtm99qWmyMZfp4GkAhhuz4xTwePu", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, { - "accountIndex": 3, + "accountIndex": 2, "mint": USDC_MINT, "uiTokenAmount": { - "uiAmount": 2.0, + "uiAmount": 12.61479, "decimals": 6, - "amount": "2000000", - "uiAmountString": "2", + "amount": "12614790", + "uiAmountString": "12.61479", }, "owner": USDC_PDA, "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, + { + "accountIndex": 4, + "mint": USDC_MINT, + "uiTokenAmount": { + "uiAmount": None, + "decimals": 6, + "amount": "0", + "uiAmountString": "0", + }, + "owner": "61DEuBcQzLWLgsr8F8XqJo5NeULfdTdVThKiYz4AtdB7", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + }, ], "rewards": [], "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, + "computeUnitsConsumed": 87301, }, - "blockTime": 1698802811, + "blockTime": 1706633649, }, "id": 0, } ) ) -# Transfer $1 USDC between two user banks without a purchase -mock_valid_transfer_without_purchase_tx = GetTransactionResp.from_json( +# Mock purchase that fails with an instruction error. +# Used to make sure that indexing can handle errors gracefully. +mock_failed_track_purchase_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", @@ -636,12 +670,18 @@ "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", "stackHeight": None, }, + { + "programIdIndex": 8, + "accounts": [0], + "data": PURCHASE_TRACK1_MEMO_DATA, + "stackHeight": None, + }, ], }, }, "meta": { - "err": None, - "status": {"Ok": None}, + "err": {"InstructionError": [0, {"Custom": 1}]}, + "status": {"Err": {"InstructionError": [0, {"Custom": 1}]}}, "fee": 10000, "preBalances": [ 1689358166, @@ -693,6 +733,9 @@ "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", f"Program {CLAIMABLE_TOKENS_PDA} success", + "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", + "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", + "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", ], "preTokenBalances": [ { @@ -758,1746 +801,276 @@ ) -# Transfer $1 to a non user bank account as preparation for withdrawal -mock_valid_transfer_prepare_withdrawal_tx = GetTransactionResp.from_json( +# Create token account for userbank address 7G1angvMtUZLFMyrMDGj7bxsduB4bjLD7VXRR7N4FXqe +# and eth address 0xe66402f9a6714a874a539fb1689b870dd271dfb2 +mock_valid_create_usdc_token_account_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", "result": { - "slot": 227246439, + "slot": 1039, "transaction": { - "signatures": [MOCK_SIGNATURE], + "signatures": [ + "61h4M3EjVAZ9caw37ygLKpiAdGpsbjTESmrT2zSDBAd19hM3i49DWbQRza5PG3coX2raaaqPKckd5LrRS7h5BiZp" + ], "message": { "header": { "numRequiredSignatures": 1, "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, + "numReadonlyUnsignedAccounts": 6, }, "accountKeys": [ FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, + RECIPIENT_USDC_USER_BANK_ADDRESS, "11111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, + USDC_MINT, USDC_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", - "Sysvar1nstructions1111111111111111111111111", "SysvarRent111111111111111111111111111111111", + CLAIMABLE_TOKENS_PDA, "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", + "recentBlockhash": "G49zpD1xEJvjeWNDFwRbCrwYp8rH3dKgfvkcZnpubYMW", "instructions": [ { - "programIdIndex": 7, - "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", - "stackHeight": None, - }, - { - "programIdIndex": 8, - "accounts": [0], - "data": PREPARE_WITHDRAWAL_MEMO, + "programIdIndex": 6, + "accounts": [0, 3, 4, 1, 5, 7, 2], + "data": "14DAXhVVokSE25ZP5P4DToK4ts3zZ", "stackHeight": None, - }, + } ], }, }, "meta": { "err": None, "status": {"Ok": None}, - "fee": 10000, - "preBalances": [ - 1689358166, - 2039280, - 953520, + "fee": 5000, + "preBalances": [19998586040, 0, 1, 1461600, 0, 1009200, 1141440, 1], + "postBalances": [ + 19996541760, 2039280, 1, - 1141440, - 0, - 1, - 121159680, + 1461600, 0, 1009200, - 934087680, - ], - "postBalances": [ - 1689348166, - 2039280, - 953520, - 2039280, - 1, 1141440, - 0, 1, - 121159680, - 0, - 1009200, - 934087680, ], "innerInstructions": [ { - "index": 1, + "index": 0, "instructions": [ { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", - "stackHeight": 2, - } - ], - } - ], - "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", - ], - "preTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, - }, - "blockTime": 1698802811, - }, - "id": 0, - } - ) -) - - -# Transfer $1 to a user bank and then out to an external address with a withdrawal memo -# Flow is root wallet usdc acct -> user bank -> external address -mock_valid_transfer_withdrawal_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 245096140, - "transaction": { - "signatures": [MOCK_SIGNATURE], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 9, - }, - "accountKeys": [ - FEE_PAYER, - "3nmLmEzwBBjERiV9UU1a4JXzESwDjrKZdSjP1KG4M9Mc", # Destination - SENDER_USDC_USER_BANK_ADDRESS, # User Bank Sender - NONCE_ACCOUNT_ADDRESS, # Nonce - SENDER_ROOT_WALLET_USDC_ACCOUNT, # Root wallet usdc account - "11111111111111111111111111111111", - USDC_MINT, - CLAIMABLE_TOKENS_PDA, - USDC_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", - "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", - "Sysvar1nstructions1111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "B1jNkfQheX2m3NosedANVueUCw9sekihw5KNAYACwKWg", - "instructions": [ - { - "programIdIndex": 13, - "accounts": [4, 6, 2, 0], - "data": "gvPShZQhKrzGM", - "stackHeight": None, - }, - { - "programIdIndex": 9, - "accounts": [], - "data": "H4eCsH1NV1T5Rrwb1xTxXJaqbsXvM53nh9nFhr4guwnTgJUjzbiPWBg4vTg44xzSgQCs1uEz1MYyFnynuyWtVKqMyUUvTNMU3giPhXtdfJLMrPhtaPz1sYN6qcd7YGwyk6cMvEZPPjGjMceYxumjPN4aYeFsrazCisbpp8FYiN4bVsV1HV5BkEqfH1wgDaezzBmFd", - "stackHeight": None, - }, - { - "programIdIndex": 7, - "accounts": [0, 2, 1, 3, 8, 13, 12, 5, 14], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", - "stackHeight": None, - }, - { - "programIdIndex": 10, - "accounts": [0], - "data": WITHDRAWAL_MEMO, - "stackHeight": None, - }, - { - "programIdIndex": 11, - "accounts": [0], - "data": "4sHBbAWcJZWyJ5TcmKVbb1xNF1kEk9KKgfjJ1pB8aiYY6b3jYCg3NRpegK2XUEdUdtZtEhiT9sxDTanxBg7QNo74XiqnQ6rmtsSP8Ky42NQ9oZQbMhptMz5hmVxzEfYUYFyQCDToEjfQT2daE3CtsXSdgt3BnmZnpck", - "stackHeight": None, - }, - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 10000, - "preBalances": [ - 49083651, - 2039280, - 2039280, - 953520, - 2039280, - 1, - 227469696505, - 1141440, - 0, - 1, - 521498880, - 521498880, - 0, - 1009200, - 934087680, - ], - "postBalances": [ - 49073651, - 2039280, - 2039280, - 953520, - 2039280, - 1, - 227469696505, - 1141440, - 0, - 1, - 521498880, - 521498880, - 0, - 1009200, - 934087680, - ], - "innerInstructions": [ - { - "index": 2, - "instructions": [ - { - "programIdIndex": 14, - "accounts": [2, 1, 8, 8], - "data": "3QCwqmHZ4mdq", - "stackHeight": 2, - } - ], - } - ], - "logMessages": [ - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]", - "Program log: Instruction: TransferChecked", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6199 of 800000 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 774885 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 793801 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", - "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [1]", - "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr consumed 56953 of 769652 compute units", - "Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success", - ], - "preTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": "41zCUJsKk6cMB94DDtm99qWmyMZfp4GkAhhuz4xTwePu", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 2, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 12.61479, - "decimals": 6, - "amount": "12614790", - "uiAmountString": "12.61479", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 4, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": "41zCUJsKk6cMB94DDtm99qWmyMZfp4GkAhhuz4xTwePu", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 2, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 12.61479, - "decimals": 6, - "amount": "12614790", - "uiAmountString": "12.61479", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 4, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": "61DEuBcQzLWLgsr8F8XqJo5NeULfdTdVThKiYz4AtdB7", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 87301, - }, - "blockTime": 1706633649, - }, - "id": 0, - } - ) -) - -# Purchase of track id 2 for $2 USDC with single recipient. -# It is assumed track id 2 has multiple splits so this will be an invalid purchase -mock_invalid_track_purchase_missing_splits_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 227246439, - "transaction": { - "signatures": [MOCK_SIGNATURE], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, - }, - "accountKeys": [ - FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, - "11111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, - USDC_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", - "Sysvar1nstructions1111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", - "instructions": [ - { - "programIdIndex": 7, - "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", - "stackHeight": None, - }, - { - "programIdIndex": 8, - "accounts": [0], - "data": PURCHASE_TRACK2_MEMO_DATA, - "stackHeight": None, - }, - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 10000, - "preBalances": [ - 1689358166, - 2039280, - 953520, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "postBalances": [ - 1689348166, - 2039280, - 953520, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "innerInstructions": [ - { - "index": 1, - "instructions": [ - { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", - "stackHeight": 2, - } - ], - } - ], - "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", - ], - "preTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 2.0, - "decimals": 6, - "amount": "2000000", - "uiAmountString": "2", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 2.0, - "decimals": 6, - "amount": "2000000", - "uiAmountString": "2", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, - }, - "blockTime": 1698802811, - }, - "id": 0, - } - ) -) - -# Purchases Track id 2 for $2 USDC. Transfers 1.50 to one recipient and 0.50 to another -# Assumes that it should be an even split. -mock_invalid_track_purchase_bad_splits_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 227246439, - "transaction": { - "signatures": [MOCK_SIGNATURE], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, - }, - "accountKeys": [ - FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, - EXTERNAL_ACCOUNT_ADDRESS, - "11111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, - USDC_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", - "Sysvar1nstructions1111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", - "instructions": [ - { - "programIdIndex": 8, - "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 6, - "accounts": [0, 1, 3, 2, 7, 11, 10, 5, 12], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", - "stackHeight": None, - }, - { - "programIdIndex": 9, - "accounts": [0], - "data": PURCHASE_TRACK2_MEMO_DATA, - "stackHeight": None, - }, - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 10000, - "preBalances": [ - 1689358166, - 2039280, - 953520, - 2039280, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "postBalances": [ - 1689348166, - 2039280, - 953520, - 2039280, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "innerInstructions": [ - { - "index": 1, - "instructions": [ - { - "programIdIndex": 12, - "accounts": [1, 3, 7, 7], - "data": "3mhiKuxuaKy1", - "stackHeight": 2, - } - ], - }, - { - "index": 2, - "instructions": [ - { - "programIdIndex": 12, - "accounts": [1, 4, 7, 7], - "data": "3mhiKuxuaKy1", - "stackHeight": 2, - } - ], - }, - ], - "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", - ], - "preTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 2.0, - "decimals": 6, - "amount": "2000000", - "uiAmountString": "2", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 4, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": EXTERNAL_ACCOUNT_ADDRESS_OWNER, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.5, - "decimals": 6, - "amount": "1500000", - "uiAmountString": "1.5", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 4, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 0.5, - "decimals": 6, - "amount": "500000", - "uiAmountString": "0.5", - }, - "owner": EXTERNAL_ACCOUNT_ADDRESS_OWNER, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, - }, - "blockTime": 1698802811, - }, - "id": 0, - } - ) -) - -# Mock purchase that fails with an instruction error. -# Used to make sure that indexing can handle errors gracefully. -mock_failed_track_purchase_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 227246439, - "transaction": { - "signatures": [MOCK_SIGNATURE], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, - }, - "accountKeys": [ - FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, - "11111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, - USDC_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", - "Sysvar1nstructions1111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", - "instructions": [ - { - "programIdIndex": 7, - "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", - "stackHeight": None, - }, - { - "programIdIndex": 8, - "accounts": [0], - "data": PURCHASE_TRACK1_MEMO_DATA, - "stackHeight": None, - }, - ], - }, - }, - "meta": { - "err": {"InstructionError": [0, {"Custom": 1}]}, - "status": {"Err": {"InstructionError": [0, {"Custom": 1}]}}, - "fee": 10000, - "preBalances": [ - 1689358166, - 2039280, - 953520, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "postBalances": [ - 1689348166, - 2039280, - 953520, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "innerInstructions": [ - { - "index": 1, - "instructions": [ - { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", - "stackHeight": 2, - } - ], - } - ], - "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", - ], - "preTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": USDC_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, - }, - "blockTime": 1698802811, - }, - "id": 0, - } - ) -) - -# Purchase of track id 1 for $1 USDC using an unknown PDA -mock_invalid_track_purchase_unknown_pda_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 227246439, - "transaction": { - "signatures": [MOCK_SIGNATURE], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, - }, - "accountKeys": [ - FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, - "11111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, - UNKNOWN_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", - "Sysvar1nstructions1111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", - "instructions": [ - { - "programIdIndex": 7, - "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", - "stackHeight": None, - }, - { - "programIdIndex": 8, - "accounts": [0], - "data": PURCHASE_TRACK1_MEMO_DATA, - "stackHeight": None, - }, - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 10000, - "preBalances": [ - 1689358166, - 2039280, - 953520, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "postBalances": [ - 1689348166, - 2039280, - 953520, - 2039280, - 1, - 1141440, - 0, - 1, - 121159680, - 0, - 1009200, - 934087680, - ], - "innerInstructions": [ - { - "index": 1, - "instructions": [ - { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", - "stackHeight": 2, - } - ], - } - ], - "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", - ], - "preTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": UNKNOWN_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": UNKNOWN_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": UNKNOWN_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", - }, - "owner": UNKNOWN_PDA, - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, - }, - "blockTime": 1698802811, - }, - "id": 0, - } - ) -) - -# Create token account for userbank address 7G1angvMtUZLFMyrMDGj7bxsduB4bjLD7VXRR7N4FXqe -# and eth address 0xe66402f9a6714a874a539fb1689b870dd271dfb2 -mock_valid_create_usdc_token_account_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 1039, - "transaction": { - "signatures": [ - "61h4M3EjVAZ9caw37ygLKpiAdGpsbjTESmrT2zSDBAd19hM3i49DWbQRza5PG3coX2raaaqPKckd5LrRS7h5BiZp" - ], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 6, - }, - "accountKeys": [ - FEE_PAYER, - RECIPIENT_USDC_USER_BANK_ADDRESS, - "11111111111111111111111111111111", - USDC_MINT, - USDC_PDA, - "SysvarRent111111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "G49zpD1xEJvjeWNDFwRbCrwYp8rH3dKgfvkcZnpubYMW", - "instructions": [ - { - "programIdIndex": 6, - "accounts": [0, 3, 4, 1, 5, 7, 2], - "data": "14DAXhVVokSE25ZP5P4DToK4ts3zZ", - "stackHeight": None, - } - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 5000, - "preBalances": [19998586040, 0, 1, 1461600, 0, 1009200, 1141440, 1], - "postBalances": [ - 19996541760, - 2039280, - 1, - 1461600, - 0, - 1009200, - 1141440, - 1, - ], - "innerInstructions": [ - { - "index": 0, - "instructions": [ - { - "programIdIndex": 2, - "accounts": [0, 1, 4], - "data": "R7r7mzYUyce18QDjGswhPZMQ5y7Q58eHcPYaiaAZsGS68TQJgcprBhcjbzoKQiTs4cCNsbjYTrxanQ32T8WPN2am1EN3hNK4VamjnXvqQUezUzTxYFDCkA3tuXbHyBKmooGc7sdQpNfhgWmQhcCL1wvCuBoqiQQo6tg", - "stackHeight": None, - }, - { - "programIdIndex": 7, - "accounts": [1, 3, 4, 5], - "data": "2", - "stackHeight": None, - }, - ], - } - ], - "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: CreateTokenAccount", - "Program 11111111111111111111111111111111 invoke [2]", - "Program 11111111111111111111111111111111 success", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: InitializeAccount", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3602 of 1382448 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 21499 of 1400000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - ], - "preTokenBalances": [], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": USDC_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", - }, - "owner": USDC_PDA, - } - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - }, - "blockTime": 1689354934, - }, - "id": 0, - } - ) -) - - -# Create token account for userbank address -mock_valid_create_audio_token_account_tx = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 119348547, - "transaction": { - "signatures": [ - "5vfrb8GUSSjXR3xMzKh7NREFtnnZRD3eZskbyryAtWoRUtac8mDxrc2D9RWt96sYLV1hsgnnhbnm8o6gJ4p7AyMx" - ], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 6, - }, - "accountKeys": [ - FEE_PAYER, - SENDER_ACCOUNT_WAUDIO_ADDRESS, - WAUDIO_MINT, - WAUDIO_PDA, - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "11111111111111111111111111111111", - CLAIMABLE_TOKENS_PDA, - ], - "recentBlockhash": "D2F72MHmv38pnzEZjJfienL72WLvWj7a5i4F82SSu1RH", - "instructions": [ - { - "programIdIndex": 7, - "accounts": [0, 2, 3, 1, 4, 5, 6], - "data": "12k8SxknneEqdTrNyE17w1fLa2Lr3", - "stackHeight": None, - } - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 5000, - "preBalances": [ - 23307213200, - 0, - 1461600, - 0, - 1009200, - 953185920, - 1, - 1141440, - ], - "postBalances": [ - 23305168920, - 2039280, - 1461600, - 0, - 1009200, - 953185920, - 1, - 1141440, - ], - "innerInstructions": [ - { - "index": 0, - "instructions": [ - { - "programIdIndex": 6, - "accounts": [0, 1, 3], - "data": "R7r7mFYpVnfogxGmgKgoouv7CPupJtRDwQVv3vdrK5Jc6WMMKQSCJRVvFjiJTnjDsSdhQknmHiuv3mTonKg92uBjwC9jzpaHzmZ3MtdyZEQ5ThmZ7DHSxrqKFhkdj5A7VGKeVhxp9ooWpZQMUQaD7oVU1EgZpvj5SmE", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [1, 2, 3, 4], - "data": "2", - "stackHeight": None, - }, - ], - } - ], - "logMessages": [ - "Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: CreateTokenAccount", - "Program 11111111111111111111111111111111 invoke [2]", - "Program 11111111111111111111111111111111 success", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: InitializeAccount", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3272 of 178721 compute units", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - "Program {CLAIMABLE_TOKENS_PDA} consumed 25212 of 200000 compute units", - "Program {CLAIMABLE_TOKENS_PDA} success", - ], - "preTokenBalances": [], - "postTokenBalances": [ - { - "accountIndex": 1, - "mint": WAUDIO_MINT, - "uiTokenAmount": { - "uiAmount": None, - "decimals": 8, - "amount": "0", - "uiAmountString": "0", - }, - "owner": WAUDIO_PDA, - } - ], - "rewards": [], - "loadedAddresses": {"writable": [], "readonly": []}, - }, - "version": "legacy", - "blockTime": 1644007841, - }, - "id": 0, - } - ) -) - -# 100 wAudio transfer between user banks (tipping) -mock_valid_waudio_transfer_between_user_banks = GetTransactionResp.from_json( - json.dumps( - { - "jsonrpc": "2.0", - "result": { - "slot": 242838213, - "transaction": { - "signatures": [ - "4RMmBXdRvE9bomvFSEhKcv53ffwwyHfaU33hqVipbynprqgF673dSJJJAZV6kwqW58Ge3Dd926Fg941kJUvFEBwb" - ], - "message": { - "header": { - "numRequiredSignatures": 1, - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 7, - }, - "accountKeys": [ - "HXqdXhJiRe2reQVWmWq13V8gjGtVP7rSh27va5gC3M3P", - "DQJe1p8CJukkiGc7y4XXDub1ZThiy14k29yhC5rmPZSM", - RECEIVER_ACCOUNT_WAUDIO_ADDRESS, - SENDER_ACCOUNT_WAUDIO_ADDRESS, - "11111111111111111111111111111111", - WAUDIO_PDA, - CLAIMABLE_TOKENS_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Sysvar1nstructions1111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - ], - "recentBlockhash": "8crN1sya8UYoBDFp2DjDDdFn7vKoLS1NoYoEj1E1JVJ6", - "instructions": [ - { - "programIdIndex": 7, - "accounts": [], - "data": "H4eCheRWTZDTCFYTTmUz3quundBipFSVGxiji3qqK7CEhSLCEvVCUPVSPKEgHvmV1XCtNrtzuL34yKX4kMz9hxBPRyh96LiZqYEdbgGD5qo45KdcFgt7tRioh7P1He5PSLzjTNdoRS2466UajSRXAjChoAfQcKmh6m89fmAy1GqJjxCUJVPKvuc4PM2NzXu9Ls5sV", - "stackHeight": None, - }, - { - "programIdIndex": 6, - "accounts": [0, 3, 2, 1, 5, 9, 8, 4, 10], - "data": "6JzBv4aFeZziRJMpjMVqY8G1Zfnw", - "stackHeight": None, - }, - ], - }, - }, - "meta": { - "err": None, - "status": {"Ok": None}, - "fee": 10000, - "preBalances": [ - 1679954583, - 953520, - 2039280, - 2039280, - 1, - 11030000, - 1141440, - 1, - 0, - 1009200, - 934087680, - ], - "postBalances": [ - 1679944583, - 953520, - 2039280, - 2039280, - 1, - 11030000, - 1141440, - 1, - 0, - 1009200, - 934087680, - ], - "innerInstructions": [ - { - "index": 1, - "instructions": [ + "programIdIndex": 2, + "accounts": [0, 1, 4], + "data": "R7r7mzYUyce18QDjGswhPZMQ5y7Q58eHcPYaiaAZsGS68TQJgcprBhcjbzoKQiTs4cCNsbjYTrxanQ32T8WPN2am1EN3hNK4VamjnXvqQUezUzTxYFDCkA3tuXbHyBKmooGc7sdQpNfhgWmQhcCL1wvCuBoqiQQo6tg", + "stackHeight": None, + }, { - "programIdIndex": 10, - "accounts": [3, 2, 5, 5], - "data": "3DcCptZte3oM", - "stackHeight": 2, - } + "programIdIndex": 7, + "accounts": [1, 3, 4, 5], + "data": "2", + "stackHeight": None, + }, ], } ], "logMessages": [ - "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ invoke [1]", - "Program log: Instruction: Transfer", + f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", + "Program log: Instruction: CreateTokenAccount", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 378084 compute units", + "Program log: Instruction: InitializeAccount", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3602 of 1382448 compute units", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ consumed 27149 of 400000 compute units", - "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ success", - ], - "preTokenBalances": [ - { - "accountIndex": 2, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", - "uiTokenAmount": { - "uiAmount": 476.19608924, - "decimals": 8, - "amount": "47619608924", - "uiAmountString": "476.19608924", - }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", - "uiTokenAmount": { - "uiAmount": 339.66380431, - "decimals": 8, - "amount": "33966380431", - "uiAmountString": "339.66380431", - }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, + f"Program {CLAIMABLE_TOKENS_PDA} consumed 21499 of 1400000 compute units", + f"Program {CLAIMABLE_TOKENS_PDA} success", ], + "preTokenBalances": [], "postTokenBalances": [ { - "accountIndex": 2, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", - "uiTokenAmount": { - "uiAmount": 576.19608924, - "decimals": 8, - "amount": "57619608924", - "uiAmountString": "576.19608924", - }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", + "accountIndex": 1, + "mint": USDC_MINT, "uiTokenAmount": { - "uiAmount": 239.66380431, - "decimals": 8, - "amount": "23966380431", - "uiAmountString": "239.66380431", + "uiAmount": None, + "decimals": 6, + "amount": "0", + "uiAmountString": "0", }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, + "owner": USDC_PDA, + } ], "rewards": [], "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 27149, }, - "version": "legacy", - "blockTime": 1705694686, + "blockTime": 1689354934, }, "id": 0, } ) ) -# Transfer of 100 wAudio user bank to an external source. -# Counts as audio_transaction_history, but not as a tip. -mock_valid_waudio_transfer_from_user_bank_to_external_address = GetTransactionResp.from_json( + +# Create token account for userbank address +mock_valid_create_audio_token_account_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", "result": { - "slot": 242838213, + "slot": 119348547, "transaction": { "signatures": [ - "4RMmBXdRvE9bomvFSEhKcv53ffwwyHfaU33hqVipbynprqgF673dSJJJAZV6kwqW58Ge3Dd926Fg941kJUvFEBwb" + "5vfrb8GUSSjXR3xMzKh7NREFtnnZRD3eZskbyryAtWoRUtac8mDxrc2D9RWt96sYLV1hsgnnhbnm8o6gJ4p7AyMx" ], "message": { "header": { "numRequiredSignatures": 1, "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 7, + "numReadonlyUnsignedAccounts": 6, }, "accountKeys": [ - "HXqdXhJiRe2reQVWmWq13V8gjGtVP7rSh27va5gC3M3P", - "DQJe1p8CJukkiGc7y4XXDub1ZThiy14k29yhC5rmPZSM", - EXTERNAL_ACCOUNT_ADDRESS, + FEE_PAYER, SENDER_ACCOUNT_WAUDIO_ADDRESS, - "11111111111111111111111111111111", + WAUDIO_MINT, WAUDIO_PDA, - CLAIMABLE_TOKENS_PDA, - "KeccakSecp256k11111111111111111111111111111", - "Sysvar1nstructions1111111111111111111111111", "SysvarRent111111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "11111111111111111111111111111111", + CLAIMABLE_TOKENS_PDA, ], - "recentBlockhash": "8crN1sya8UYoBDFp2DjDDdFn7vKoLS1NoYoEj1E1JVJ6", + "recentBlockhash": "D2F72MHmv38pnzEZjJfienL72WLvWj7a5i4F82SSu1RH", "instructions": [ { "programIdIndex": 7, - "accounts": [], - "data": "H4eCheRWTZDTCFYTTmUz3quundBipFSVGxiji3qqK7CEhSLCEvVCUPVSPKEgHvmV1XCtNrtzuL34yKX4kMz9hxBPRyh96LiZqYEdbgGD5qo45KdcFgt7tRioh7P1He5PSLzjTNdoRS2466UajSRXAjChoAfQcKmh6m89fmAy1GqJjxCUJVPKvuc4PM2NzXu9Ls5sV", - "stackHeight": None, - }, - { - "programIdIndex": 6, - "accounts": [0, 3, 2, 1, 5, 9, 8, 4, 10], - "data": "6JzBv4aFeZziRJMpjMVqY8G1Zfnw", + "accounts": [0, 2, 3, 1, 4, 5, 6], + "data": "12k8SxknneEqdTrNyE17w1fLa2Lr3", "stackHeight": None, - }, + } ], }, }, "meta": { "err": None, "status": {"Ok": None}, - "fee": 10000, + "fee": 5000, "preBalances": [ - 1679954583, - 953520, - 2039280, - 2039280, - 1, - 11030000, - 1141440, - 1, + 23307213200, + 0, + 1461600, 0, 1009200, - 934087680, + 953185920, + 1, + 1141440, ], "postBalances": [ - 1679944583, - 953520, - 2039280, + 23305168920, 2039280, - 1, - 11030000, - 1141440, - 1, + 1461600, 0, 1009200, - 934087680, + 953185920, + 1, + 1141440, ], "innerInstructions": [ { - "index": 1, + "index": 0, "instructions": [ { - "programIdIndex": 10, - "accounts": [3, 2, 5, 5], - "data": "3DcCptZte3oM", - "stackHeight": 2, - } + "programIdIndex": 6, + "accounts": [0, 1, 3], + "data": "R7r7mFYpVnfogxGmgKgoouv7CPupJtRDwQVv3vdrK5Jc6WMMKQSCJRVvFjiJTnjDsSdhQknmHiuv3mTonKg92uBjwC9jzpaHzmZ3MtdyZEQ5ThmZ7DHSxrqKFhkdj5A7VGKeVhxp9ooWpZQMUQaD7oVU1EgZpvj5SmE", + "stackHeight": None, + }, + { + "programIdIndex": 5, + "accounts": [1, 2, 3, 4], + "data": "2", + "stackHeight": None, + }, ], } ], "logMessages": [ - "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ invoke [1]", - "Program log: Instruction: Transfer", + "Program {CLAIMABLE_TOKENS_PDA} invoke [1]", + "Program log: Instruction: CreateTokenAccount", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 378084 compute units", + "Program log: Instruction: InitializeAccount", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3272 of 178721 compute units", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ consumed 27149 of 400000 compute units", - "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ success", - ], - "preTokenBalances": [ - { - "accountIndex": 2, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", - "uiTokenAmount": { - "uiAmount": 476.19608924, - "decimals": 8, - "amount": "47619608924", - "uiAmountString": "476.19608924", - }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", - "uiTokenAmount": { - "uiAmount": 339.66380431, - "decimals": 8, - "amount": "33966380431", - "uiAmountString": "339.66380431", - }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, + "Program {CLAIMABLE_TOKENS_PDA} consumed 25212 of 200000 compute units", + "Program {CLAIMABLE_TOKENS_PDA} success", ], + "preTokenBalances": [], "postTokenBalances": [ { - "accountIndex": 2, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", - "uiTokenAmount": { - "uiAmount": 576.19608924, - "decimals": 8, - "amount": "57619608924", - "uiAmountString": "576.19608924", - }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, - { - "accountIndex": 3, - "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", + "accountIndex": 1, + "mint": WAUDIO_MINT, "uiTokenAmount": { - "uiAmount": 239.66380431, + "uiAmount": None, "decimals": 8, - "amount": "23966380431", - "uiAmountString": "239.66380431", + "amount": "0", + "uiAmountString": "0", }, - "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - }, + "owner": WAUDIO_PDA, + } ], "rewards": [], "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 27149, }, "version": "legacy", - "blockTime": 1705694686, + "blockTime": 1644007841, }, "id": 0, } ) ) -# Appears to be a purchase, but doesn't use a recognized instruction -mock_unknown_instruction_tx = GetTransactionResp.from_json( +# 100 wAudio transfer between user banks (tipping) +mock_valid_waudio_transfer_between_user_banks = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", "result": { - "slot": 227246439, + "slot": 242838213, "transaction": { - "signatures": [MOCK_SIGNATURE], + "signatures": [ + "4RMmBXdRvE9bomvFSEhKcv53ffwwyHfaU33hqVipbynprqgF673dSJJJAZV6kwqW58Ge3Dd926Fg941kJUvFEBwb" + ], "message": { "header": { "numRequiredSignatures": 1, "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, + "numReadonlyUnsignedAccounts": 7, }, "accountKeys": [ - FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, + "HXqdXhJiRe2reQVWmWq13V8gjGtVP7rSh27va5gC3M3P", + "DQJe1p8CJukkiGc7y4XXDub1ZThiy14k29yhC5rmPZSM", + RECEIVER_ACCOUNT_WAUDIO_ADDRESS, + SENDER_ACCOUNT_WAUDIO_ADDRESS, "11111111111111111111111111111111", + WAUDIO_PDA, CLAIMABLE_TOKENS_PDA, - USDC_PDA, "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", "Sysvar1nstructions1111111111111111111111111", "SysvarRent111111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", + "recentBlockhash": "8crN1sya8UYoBDFp2DjDDdFn7vKoLS1NoYoEj1E1JVJ6", "instructions": [ { "programIdIndex": 7, "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", + "data": "H4eCheRWTZDTCFYTTmUz3quundBipFSVGxiji3qqK7CEhSLCEvVCUPVSPKEgHvmV1XCtNrtzuL34yKX4kMz9hxBPRyh96LiZqYEdbgGD5qo45KdcFgt7tRioh7P1He5PSLzjTNdoRS2466UajSRXAjChoAfQcKmh6m89fmAy1GqJjxCUJVPKvuc4PM2NzXu9Ls5sV", "stackHeight": None, }, { - "programIdIndex": 8, - "accounts": [0], - "data": PURCHASE_TRACK1_MEMO_DATA, + "programIdIndex": 6, + "accounts": [0, 3, 2, 1, 5, 9, 8, 4, 10], + "data": "6JzBv4aFeZziRJMpjMVqY8G1Zfnw", "stackHeight": None, }, ], @@ -2508,29 +1081,27 @@ "status": {"Ok": None}, "fee": 10000, "preBalances": [ - 1689358166, - 2039280, + 1679954583, 953520, 2039280, + 2039280, 1, + 11030000, 1141440, - 0, 1, - 121159680, 0, 1009200, 934087680, ], "postBalances": [ - 1689348166, - 2039280, + 1679944583, 953520, 2039280, + 2039280, 1, + 11030000, 1141440, - 0, 1, - 121159680, 0, 1009200, 934087680, @@ -2540,137 +1111,131 @@ "index": 1, "instructions": [ { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", + "programIdIndex": 10, + "accounts": [3, 2, 5, 5], + "data": "3DcCptZte3oM", "stackHeight": 2, } ], } ], "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: RandomThing", + "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ invoke [1]", + "Program log: Instruction: Transfer", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: RandomThing", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", + "Program log: Instruction: Transfer", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 378084 compute units", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", + "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ consumed 27149 of 400000 compute units", + "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ success", ], "preTokenBalances": [ { - "accountIndex": 1, - "mint": USDC_MINT, + "accountIndex": 2, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", + "uiAmount": 476.19608924, + "decimals": 8, + "amount": "47619608924", + "uiAmountString": "476.19608924", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, { "accountIndex": 3, - "mint": USDC_MINT, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", + "uiAmount": 339.66380431, + "decimals": 8, + "amount": "33966380431", + "uiAmountString": "339.66380431", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, ], "postTokenBalances": [ { - "accountIndex": 1, - "mint": USDC_MINT, + "accountIndex": 2, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", + "uiAmount": 576.19608924, + "decimals": 8, + "amount": "57619608924", + "uiAmountString": "576.19608924", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, { "accountIndex": 3, - "mint": USDC_MINT, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", + "uiAmount": 239.66380431, + "decimals": 8, + "amount": "23966380431", + "uiAmountString": "239.66380431", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, ], "rewards": [], "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, + "computeUnitsConsumed": 27149, }, - "blockTime": 1698802811, + "version": "legacy", + "blockTime": 1705694686, }, "id": 0, } ) ) -# Valid purchase transaction with "stream" access in memo -mock_valid_track_purchase_tx_stream_access = GetTransactionResp.from_json( +# Transfer of 100 wAudio user bank to an external source. +# Counts as audio_transaction_history, but not as a tip. +mock_valid_waudio_transfer_from_user_bank_to_external_address = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", "result": { - "slot": 227246439, + "slot": 242838213, "transaction": { - "signatures": [MOCK_SIGNATURE], + "signatures": [ + "4RMmBXdRvE9bomvFSEhKcv53ffwwyHfaU33hqVipbynprqgF673dSJJJAZV6kwqW58Ge3Dd926Fg941kJUvFEBwb" + ], "message": { "header": { "numRequiredSignatures": 1, "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 8, + "numReadonlyUnsignedAccounts": 7, }, "accountKeys": [ - FEE_PAYER, - SENDER_USDC_USER_BANK_ADDRESS, - NONCE_ACCOUNT_ADDRESS, - RECIPIENT_USDC_USER_BANK_ADDRESS, + "HXqdXhJiRe2reQVWmWq13V8gjGtVP7rSh27va5gC3M3P", + "DQJe1p8CJukkiGc7y4XXDub1ZThiy14k29yhC5rmPZSM", + EXTERNAL_ACCOUNT_ADDRESS, + SENDER_ACCOUNT_WAUDIO_ADDRESS, "11111111111111111111111111111111", + WAUDIO_PDA, CLAIMABLE_TOKENS_PDA, - USDC_PDA, "KeccakSecp256k11111111111111111111111111111", - "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", "Sysvar1nstructions1111111111111111111111111", "SysvarRent111111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", ], - "recentBlockhash": "5H434VMiHgK7RaJZaBKKcriu4eky8erb9QGfcHJSZquU", + "recentBlockhash": "8crN1sya8UYoBDFp2DjDDdFn7vKoLS1NoYoEj1E1JVJ6", "instructions": [ { "programIdIndex": 7, "accounts": [], - "data": "H4eCheRWTZDTCFYUcyMzE6EhQMZvvvLKJ9g6YaUpbZeoLLgVj1uvwCTdzcb2MzbKHsRjN8DjLYdqxuQEZe2TjUKCuBMrFtpnnLd4RcvBnr4ieHCdH8ZU1N6XDfiqyKB4zenQ9S4viza4ob4gbtmiRS6o6KGEtL3fJQRvaA3tdtSx1rfFogZzwMXAxHrkuxHrpAqfm", - "stackHeight": None, - }, - { - "programIdIndex": 5, - "accounts": [0, 1, 3, 2, 6, 10, 9, 4, 11], - "data": "6dMrrkPeSzw2r5huQ6RToaJCaVuu", + "data": "H4eCheRWTZDTCFYTTmUz3quundBipFSVGxiji3qqK7CEhSLCEvVCUPVSPKEgHvmV1XCtNrtzuL34yKX4kMz9hxBPRyh96LiZqYEdbgGD5qo45KdcFgt7tRioh7P1He5PSLzjTNdoRS2466UajSRXAjChoAfQcKmh6m89fmAy1GqJjxCUJVPKvuc4PM2NzXu9Ls5sV", "stackHeight": None, }, { - "programIdIndex": 8, - "accounts": [0], - "data": PURCHASE_TRACK_STREAM_ACCESS_MEMO_DATA, + "programIdIndex": 6, + "accounts": [0, 3, 2, 1, 5, 9, 8, 4, 10], + "data": "6JzBv4aFeZziRJMpjMVqY8G1Zfnw", "stackHeight": None, }, ], @@ -2681,29 +1246,27 @@ "status": {"Ok": None}, "fee": 10000, "preBalances": [ - 1689358166, - 2039280, + 1679954583, 953520, 2039280, + 2039280, 1, + 11030000, 1141440, - 0, 1, - 121159680, 0, 1009200, 934087680, ], "postBalances": [ - 1689348166, - 2039280, + 1679944583, 953520, 2039280, + 2039280, 1, + 11030000, 1141440, - 0, 1, - 121159680, 0, 1009200, 934087680, @@ -2713,92 +1276,90 @@ "index": 1, "instructions": [ { - "programIdIndex": 11, - "accounts": [1, 3, 6, 6], - "data": "3mhiKuxuaKy1", + "programIdIndex": 10, + "accounts": [3, 2, 5, 5], + "data": "3DcCptZte3oM", "stackHeight": 2, } ], } ], "logMessages": [ - f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", + "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ invoke [1]", "Program log: Instruction: Transfer", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", "Program log: Instruction: Transfer", - "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 378084 compute units", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", - f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", - f"Program {CLAIMABLE_TOKENS_PDA} success", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo invoke [1]", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo consumed 588 of 575851 compute units", - "Program Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo success", + "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ consumed 27149 of 400000 compute units", + "Program Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ success", ], "preTokenBalances": [ { - "accountIndex": 1, - "mint": USDC_MINT, + "accountIndex": 2, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", + "uiAmount": 476.19608924, + "decimals": 8, + "amount": "47619608924", + "uiAmountString": "476.19608924", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, { "accountIndex": 3, - "mint": USDC_MINT, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", + "uiAmount": 339.66380431, + "decimals": 8, + "amount": "33966380431", + "uiAmountString": "339.66380431", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, ], "postTokenBalances": [ { - "accountIndex": 1, - "mint": USDC_MINT, + "accountIndex": 2, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": None, - "decimals": 6, - "amount": "0", - "uiAmountString": "0", + "uiAmount": 576.19608924, + "decimals": 8, + "amount": "57619608924", + "uiAmountString": "576.19608924", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, { "accountIndex": 3, - "mint": USDC_MINT, + "mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM", "uiTokenAmount": { - "uiAmount": 1.0, - "decimals": 6, - "amount": "1000000", - "uiAmountString": "1", + "uiAmount": 239.66380431, + "decimals": 8, + "amount": "23966380431", + "uiAmountString": "239.66380431", }, - "owner": USDC_PDA, + "owner": "5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", }, ], "rewards": [], "loadedAddresses": {"writable": [], "readonly": []}, - "computeUnitsConsumed": 24737, + "computeUnitsConsumed": 27149, }, - "blockTime": 1698802811, + "version": "legacy", + "blockTime": 1705694686, }, "id": 0, } ) ) -# Valid purchase transaction with "download" access in memo -mock_valid_track_purchase_tx_download_access = GetTransactionResp.from_json( +# Appears to be a purchase, but doesn't use a recognized instruction +mock_unknown_instruction_tx = GetTransactionResp.from_json( json.dumps( { "jsonrpc": "2.0", @@ -2843,7 +1404,7 @@ { "programIdIndex": 8, "accounts": [0], - "data": PURCHASE_TRACK_DOWNLOAD_MEMO_DATA, + "data": PURCHASE_TRACK1_MEMO_DATA, "stackHeight": None, }, ], @@ -2896,9 +1457,9 @@ ], "logMessages": [ f"Program {CLAIMABLE_TOKENS_PDA} invoke [1]", - "Program log: Instruction: Transfer", + "Program log: Instruction: RandomThing", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", - "Program log: Instruction: Transfer", + "Program log: Instruction: RandomThing", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4728 of 581084 compute units", "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", f"Program {CLAIMABLE_TOKENS_PDA} consumed 24149 of 600000 compute units", diff --git a/packages/discovery-provider/integration_tests/utils.py b/packages/discovery-provider/integration_tests/utils.py index fa7a82eb63c..236fadea777 100644 --- a/packages/discovery-provider/integration_tests/utils.py +++ b/packages/discovery-provider/integration_tests/utils.py @@ -742,6 +742,18 @@ def populate_mock_db(db, entities, block_offset=None): created_at=usdc_purchase.get("created_at", datetime.now()), updated_at=usdc_purchase.get("updated_at", datetime.now()), access=usdc_purchase.get("access", PurchaseAccessType.stream), + splits=usdc_purchase.get( + "splits", + [ + { + "user_id": 2, + "amount": 1000000, + "percentage": 100.0, + "eth_wallet": "", + "payout_wallet": "", + } + ], + ), ) session.add(purchase) for i, cid_data in enumerate(cid_datas): diff --git a/packages/discovery-provider/src/tasks/index_user_bank.py b/packages/discovery-provider/src/tasks/index_user_bank.py index 68100e2b9fd..6c203072db2 100644 --- a/packages/discovery-provider/src/tasks/index_user_bank.py +++ b/packages/discovery-provider/src/tasks/index_user_bank.py @@ -3,7 +3,7 @@ import time from datetime import datetime from decimal import Decimal -from typing import List, Optional, Tuple, TypedDict, Union, cast +from typing import List, Optional, Tuple, TypedDict, cast import base58 from redis import Redis @@ -19,19 +19,11 @@ from src.challenges.challenge_event import ChallengeEvent from src.challenges.challenge_event_bus import ChallengeEventBus from src.exceptions import SolanaTransactionFetchError -from src.models.playlists.album_price_history import AlbumPriceHistory -from src.models.tracks.track_price_history import TrackPriceHistory from src.models.users.audio_transactions_history import ( AudioTransactionsHistory, TransactionMethod, TransactionType, ) -from src.models.users.usdc_purchase import ( - PurchaseAccessType, - PurchaseType, - PurchaseVendor, - USDCPurchase, -) from src.models.users.usdc_transactions_history import ( USDCTransactionMethod, USDCTransactionsHistory, @@ -41,16 +33,10 @@ from src.models.users.user_bank import USDCUserBankAccount, UserBankAccount, UserBankTx from src.models.users.user_tip import UserTip from src.queries.get_balances import enqueue_immediate_balance_refresh -from src.queries.get_extended_purchase_gate import ( - add_wallet_info_to_splits, - calculate_split_amounts, - to_wallet_amount_map, -) from src.solana.constants import ( FETCH_TX_SIGNATURES_BATCH_SIZE, TX_SIGNATURES_MAX_BATCHES, TX_SIGNATURES_RESIZE_LENGTH, - USDC_DECIMALS, ) from src.solana.solana_client_manager import SolanaClientManager from src.solana.solana_helpers import ( @@ -286,280 +272,6 @@ def get_transfer_type_from_memo(memos: List[str]) -> USDCTransactionType: return USDCTransactionType.transfer -class PurchaseMetadataDict(TypedDict): - price: int - splits: dict[str, int] - type: PurchaseType - id: int - purchaser_user_id: Optional[int] - access: PurchaseAccessType - - -def get_purchase_metadata_from_memo( - session: Session, memos: List[str], timestamp: datetime -) -> Union[PurchaseMetadataDict, None]: - """Checks the list of memos for one matching the format of a purchase's content_metadata, and then uses that content_metadata to find the stream_conditions associated with that content to get the price""" - for memo in memos: - try: - content_metadata = memo.split(":") - if len(content_metadata) == 3: - type_str, id_str, blocknumber_str = content_metadata - purchaser_user_id_str = None - access_str = "stream" # default to stream access - elif len(content_metadata) == 4: - ( - type_str, - id_str, - blocknumber_str, - purchaser_user_id_str, - ) = content_metadata - access_str = "stream" # default to stream access - elif len(content_metadata) == 5: - ( - type_str, - id_str, - blocknumber_str, - purchaser_user_id_str, - access_str, - ) = content_metadata - else: - logger.debug( - f"index_user_bank.py | Ignoring memo, no content metadata found: {memo}" - ) - continue - - type = PurchaseType[type_str.lower()] - id = int(id_str) - blocknumber = int(blocknumber_str) - purchaser_user_id = ( - int(purchaser_user_id_str) if purchaser_user_id_str else None - ) - access = PurchaseAccessType[access_str.lower()] - - # TODO: Wait for blocknumber to be indexed by ACDC - logger.debug( - f"index_user_bank.py | Found content_metadata in memo: type={type}, id={id}, blocknumber={blocknumber}, purchaser_user_id={purchaser_user_id}, access={access}" - ) - - price = None - splits = None - if type == PurchaseType.track: - env = shared_config["discprov"]["env"] - query = session.query(TrackPriceHistory) - if env != "dev": - # In local stack, the blocktime of solana-test-validator is offset. - # The start time of the validator is baked into the prebuilt container. - # So if the container was built on 7/15, but you upped the container on 7/22, the blocktimes will still say 7/15 and be way behind. - # To remedy this locally would require getting the start time of the solana-test-validator container and getting its offset compared to when - # the the validator thinks the beginning of time is, and that's just too much work so I'm just not adding the blocktime filter in local dev - query.filter(TrackPriceHistory.block_timestamp < timestamp) - result = ( - query.filter( - TrackPriceHistory.track_id == id, - TrackPriceHistory.access == access, - ) - .order_by(desc(TrackPriceHistory.block_timestamp)) - .first() - ) - if result is not None: - price = result.total_price_cents - splits = result.splits - elif type == PurchaseType.album: - env = shared_config["discprov"]["env"] - query = session.query(AlbumPriceHistory) - if env != "dev": - # See above comment - query.filter(AlbumPriceHistory.block_timestamp < timestamp) - result = ( - query.filter(AlbumPriceHistory.playlist_id == id) - .order_by(desc(AlbumPriceHistory.block_timestamp)) - .first() - ) - if result is not None: - price = result.total_price_cents - splits = result.splits - else: - logger.error(f"index_user_bank.py | Unknown content type {type}") - continue - - # Convert the new splits format to the old splits format for - # maximal backwards compatibility - if price is not None and splits is not None and isinstance(splits, list): - wallet_splits = add_wallet_info_to_splits(session, splits, timestamp) - amount_splits = calculate_split_amounts(price, wallet_splits) - splits = to_wallet_amount_map(amount_splits) - if price is not None and splits is not None and isinstance(splits, dict): - purchase_metadata: PurchaseMetadataDict = { - "type": type, - "id": id, - "price": price * USDC_PER_USD_CENT, - "splits": splits, - "purchaser_user_id": purchaser_user_id, - "access": access, - } - logger.debug( - f"index_user_bank.py | Got purchase metadata {content_metadata}" - ) - return purchase_metadata - else: - logger.error( - f"index_user_bank.py | Couldn't find relevant price for {content_metadata}" - ) - continue - - except (ValueError, KeyError) as e: - logger.debug( - f"index_user_bank.py | Ignoring memo, failed to parse content metadata: {memo}, Error: {e}" - ) - logger.error("index_user_bank.py | Failed to find any content metadata") - return None - - -def validate_purchase( - purchase_metadata: PurchaseMetadataDict, balance_changes: dict[str, BalanceChange] -): - """Validates the user has correctly constructed the transaction in order to create the purchase, including validating they paid the full price at the time of the purchase, and that payments were appropriately split""" - # Check that the recipients all got the correct split - for account, split in purchase_metadata["splits"].items(): - if account not in balance_changes: - logger.error( - f"index_payment_router.py | No split given to account={account}, expected={split}" - ) - return False - if balance_changes[account]["change"] < split: - logger.error( - f"index_payment_router.py | Incorrect split given to account={account} amount={balance_changes[account]['change']} expected={split}" - ) - return False - return True - - -def index_purchase( - session: Session, - receiver_user_id: int, - receiver_account: str, - sender_user_id: int, - sender_account: str, - balance_changes: dict[str, BalanceChange], - purchase_metadata: PurchaseMetadataDict, - slot: int, - timestamp: datetime, - tx_sig: str, -): - # Detect "pay extra" amount (difference between sender's balance change and price - # of the content) - extra_amount = max( - 0, -(balance_changes[sender_account]["change"]) - purchase_metadata["price"] - ) - usdc_purchase = USDCPurchase( - slot=slot, - signature=tx_sig, - seller_user_id=receiver_user_id, - buyer_user_id=sender_user_id, - amount=purchase_metadata["price"], - extra_amount=extra_amount, - content_type=purchase_metadata["type"], - content_id=purchase_metadata["id"], - access=purchase_metadata["access"], - vendor=PurchaseVendor.user_bank, - ) - logger.debug( - f"index_user_bank.py | Creating usdc_purchase for purchase {usdc_purchase}" - ) - session.add(usdc_purchase) - - usdc_tx_sent = USDCTransactionsHistory( - user_bank=sender_account, - slot=slot, - signature=tx_sig, - transaction_type=USDCTransactionType.purchase_content, - method=USDCTransactionMethod.send, - transaction_created_at=timestamp, - change=Decimal(balance_changes[sender_account]["change"]), - balance=Decimal(balance_changes[sender_account]["post_balance"]), - tx_metadata=str(receiver_user_id), - ) - logger.debug( - f"index_user_bank.py | Creating usdc_tx_history send tx for purchase {usdc_tx_sent}" - ) - session.add(usdc_tx_sent) - usdc_tx_received = USDCTransactionsHistory( - user_bank=receiver_account, - slot=slot, - signature=tx_sig, - transaction_type=USDCTransactionType.purchase_content, - method=USDCTransactionMethod.receive, - transaction_created_at=timestamp, - change=Decimal(balance_changes[receiver_account]["change"]), - balance=Decimal(balance_changes[receiver_account]["post_balance"]), - tx_metadata=str(sender_user_id), - ) - session.add(usdc_tx_received) - logger.debug( - f"index_user_bank.py | Creating usdc_tx_history received tx for purchase {usdc_tx_received}" - ) - - -def validate_and_index_purchase( - session: Session, - receiver_user_id: int, - receiver_account: str, - sender_user_id: int, - sender_account: str, - balance_changes: dict[str, BalanceChange], - purchase_metadata: Union[PurchaseMetadataDict, None], - slot: int, - timestamp: datetime, - tx_sig: str, -): - """Checks if the transaction is a valid purchase and if so creates the purchase record. Otherwise, indexes a transfer.""" - if purchase_metadata is not None and validate_purchase( - purchase_metadata=purchase_metadata, balance_changes=balance_changes - ): - index_purchase( - session=session, - receiver_user_id=receiver_user_id, - receiver_account=receiver_account, - sender_user_id=sender_user_id, - sender_account=sender_account, - balance_changes=balance_changes, - purchase_metadata=purchase_metadata, - slot=slot, - timestamp=timestamp, - tx_sig=tx_sig, - ) - else: - # Both non-purchases and invalid purchases will be treated as transfers - usdc_tx_sent = USDCTransactionsHistory( - user_bank=sender_account, - slot=slot, - signature=tx_sig, - transaction_type=USDCTransactionType.transfer, - method=USDCTransactionMethod.send, - transaction_created_at=timestamp, - change=Decimal(balance_changes[sender_account]["change"]), - balance=Decimal(balance_changes[sender_account]["post_balance"]), - tx_metadata=receiver_account, - ) - logger.debug(f"index_user_bank.py | Creating transfer sent tx {usdc_tx_sent}") - session.add(usdc_tx_sent) - usdc_tx_received = USDCTransactionsHistory( - user_bank=receiver_account, - slot=slot, - signature=tx_sig, - transaction_type=USDCTransactionType.transfer, - method=USDCTransactionMethod.receive, - transaction_created_at=timestamp, - change=Decimal(balance_changes[receiver_account]["change"]), - balance=Decimal(balance_changes[receiver_account]["post_balance"]), - tx_metadata=sender_account, - ) - session.add(usdc_tx_received) - logger.debug( - f"index_user_bank.py | Creating transfer received tx {usdc_tx_received}" - ) - - def index_user_tip( session: Session, receiver_user_id: int, @@ -762,46 +474,35 @@ def process_transfer_instruction( ChallengeEvent.send_tip, slot, timestamp, sender_user_id ) elif is_usdc: - # Index as a purchase of some content - purchase_metadata = get_purchase_metadata_from_memo( - session=session, memos=memos, timestamp=timestamp - ) - validate_and_index_purchase( - session=session, - receiver_user_id=receiver_user_id, - receiver_account=receiver_account, - sender_user_id=sender_user_id, - sender_account=sender_account, - balance_changes=balance_changes, - purchase_metadata=purchase_metadata, + usdc_tx_sent = USDCTransactionsHistory( + user_bank=sender_account, slot=slot, - timestamp=timestamp, - tx_sig=tx_sig, + signature=tx_sig, + transaction_type=USDCTransactionType.transfer, + method=USDCTransactionMethod.send, + transaction_created_at=timestamp, + change=Decimal(balance_changes[sender_account]["change"]), + balance=Decimal(balance_changes[sender_account]["post_balance"]), + tx_metadata=receiver_account, ) - if purchase_metadata is None: - logger.error( - "index_user_bank.py | Found purchase event but purchase_metadata is None" - ) - return - - amount = int(round(purchase_metadata["price"]) / 10**USDC_DECIMALS) - challenge_event_bus.dispatch( - ChallengeEvent.audio_matching_buyer, - slot, - timestamp, - sender_user_id, - {"track_id": purchase_metadata["id"], "amount": amount}, + logger.debug( + f"index_user_bank.py | Creating USDC transfer sent tx {usdc_tx_sent}" ) - challenge_event_bus.dispatch( - ChallengeEvent.audio_matching_seller, - slot, - timestamp, - receiver_user_id, - { - "track_id": purchase_metadata["id"], - "sender_user_id": sender_user_id, - "amount": amount, - }, + session.add(usdc_tx_sent) + usdc_tx_received = USDCTransactionsHistory( + user_bank=receiver_account, + slot=slot, + signature=tx_sig, + transaction_type=USDCTransactionType.transfer, + method=USDCTransactionMethod.receive, + transaction_created_at=timestamp, + change=Decimal(balance_changes[receiver_account]["change"]), + balance=Decimal(balance_changes[receiver_account]["post_balance"]), + tx_metadata=sender_account, + ) + session.add(usdc_tx_received) + logger.debug( + f"index_user_bank.py | Creating USDC transfer received tx {usdc_tx_received}" ) From 1a3a3daf876385b4aac9ccb20671c08a7c5ded0b Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:39:25 -0700 Subject: [PATCH 12/17] fix notifs tests --- .../mappings/usdcPurchaseBuyer.test.ts | 15 +++++-- .../mappings/usdcPurchaseSeller.test.ts | 45 ++++++++++++++++--- .../plugins/notifications/src/types/dn.ts | 1 + 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts index 4b4e729d0e6..e211ab41e6d 100644 --- a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts +++ b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts @@ -58,7 +58,14 @@ describe('USDC Purchase Buyer', () => { content_type: usdc_purchase_content_type.track, content_id: 10, amount: '1000', - extra_amount: '0' + extra_amount: '0', + splits: [ + { + user_id: 1, + amount: 10000000, + percentage: 100 + } + ] } ]) await insertMobileSettings(processor.identityDB, [{ userId: 2 }]) @@ -130,7 +137,8 @@ describe('USDC Purchase Buyer', () => { content_type: usdc_purchase_content_type.track, content_id: 10, amount: '1000000', - extra_amount: '0' + extra_amount: '0', + splits: [{ user_id: 1, percentage: 100, amount: 1000000 }] }, { seller_user_id: 1, @@ -138,7 +146,8 @@ describe('USDC Purchase Buyer', () => { content_type: usdc_purchase_content_type.album, content_id: 15, amount: '1000000', - extra_amount: '0' + extra_amount: '0', + splits: [{ user_id: 1, percentage: 100, amount: 1000000 }] } ]) diff --git a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts index 1d0b9396754..ed18ed9ab6b 100644 --- a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts +++ b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts @@ -58,7 +58,14 @@ describe('USDC Purchase Seller', () => { content_type: usdc_purchase_content_type.track, content_id: 10, amount: '1000000', - extra_amount: '0' + extra_amount: '0', + splits: [ + { + user_id: 1, + amount: 1000000, + percentage: 100 + } + ] } ]) await insertMobileSettings(processor.identityDB, [{ userId: 1 }]) @@ -131,7 +138,14 @@ describe('USDC Purchase Seller', () => { content_type: usdc_purchase_content_type.track, content_id: 10, amount: '1000000', - extra_amount: '0' + extra_amount: '0', + splits: [ + { + user_id: 1, + amount: 1000000, + percentage: 100 + } + ] }, { seller_user_id: 1, @@ -139,7 +153,14 @@ describe('USDC Purchase Seller', () => { content_type: usdc_purchase_content_type.album, content_id: 15, amount: '1000000', - extra_amount: '0' + extra_amount: '0', + splits: [ + { + user_id: 1, + amount: 1000000, + percentage: 100 + } + ] } ]) @@ -156,7 +177,14 @@ describe('USDC Purchase Seller', () => { amount: 1000000, extra_amount: 0, content_id: 10, - content_type: 'track' + content_type: 'track', + splits: [ + { + user_id: 1, + amount: 1000000, + percentage: 100 + } + ] }, user_ids: [1], receiver_user_id: 1 @@ -185,7 +213,14 @@ describe('USDC Purchase Seller', () => { amount: 1000000, extra_amount: 0, content_id: 15, - content_type: 'album' + content_type: 'album', + splits: [ + { + user_id: 1, + amount: 1000000, + percentage: 100 + } + ] }, user_ids: [1], receiver_user_id: 1 diff --git a/packages/discovery-provider/plugins/notifications/src/types/dn.ts b/packages/discovery-provider/plugins/notifications/src/types/dn.ts index 2a867f11690..497108ed43b 100644 --- a/packages/discovery-provider/plugins/notifications/src/types/dn.ts +++ b/packages/discovery-provider/plugins/notifications/src/types/dn.ts @@ -695,6 +695,7 @@ export interface UsdcPurchaseRow { signature: string slot: number updated_at?: Date + splits: any } export interface UsdcTransactionsHistoryRow { balance: string From eca21dc9b6dc0c97140280275368039cd2d7a530 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:19:00 -0700 Subject: [PATCH 13/17] it's gotta be stringified I guess --- .../mappings/usdcPurchaseBuyer.test.ts | 18 ++++++++++++------ .../mappings/usdcPurchaseSeller.test.ts | 12 ++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts index e211ab41e6d..a81792f9946 100644 --- a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts +++ b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseBuyer.test.ts @@ -59,13 +59,13 @@ describe('USDC Purchase Buyer', () => { content_id: 10, amount: '1000', extra_amount: '0', - splits: [ + splits: JSON.stringify([ { user_id: 1, amount: 10000000, percentage: 100 } - ] + ]) } ]) await insertMobileSettings(processor.identityDB, [{ userId: 2 }]) @@ -138,7 +138,9 @@ describe('USDC Purchase Buyer', () => { content_id: 10, amount: '1000000', extra_amount: '0', - splits: [{ user_id: 1, percentage: 100, amount: 1000000 }] + splits: JSON.stringify([ + { user_id: 1, percentage: 100, amount: 1000000 } + ]) }, { seller_user_id: 1, @@ -147,7 +149,9 @@ describe('USDC Purchase Buyer', () => { content_id: 15, amount: '1000000', extra_amount: '0', - splits: [{ user_id: 1, percentage: 100, amount: 1000000 }] + splits: JSON.stringify([ + { user_id: 1, percentage: 100, amount: 1000000 } + ]) } ]) @@ -164,7 +168,8 @@ describe('USDC Purchase Buyer', () => { amount: 1000000, extra_amount: 0, content_id: 10, - content_type: 'track' + content_type: 'track', + splits: [{ user_id: 1, percentage: 100, amount: 1000000 }] }, user_ids: [2], receiver_user_id: 2 @@ -193,7 +198,8 @@ describe('USDC Purchase Buyer', () => { amount: 1000000, extra_amount: 0, content_id: 15, - content_type: 'album' + content_type: 'album', + splits: [{ user_id: 1, percentage: 100, amount: 1000000 }] }, user_ids: [2], receiver_user_id: 2 diff --git a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts index ed18ed9ab6b..725d1434c5e 100644 --- a/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts +++ b/packages/discovery-provider/plugins/notifications/src/__tests__/mappings/usdcPurchaseSeller.test.ts @@ -59,13 +59,13 @@ describe('USDC Purchase Seller', () => { content_id: 10, amount: '1000000', extra_amount: '0', - splits: [ + splits: JSON.stringify([ { user_id: 1, amount: 1000000, percentage: 100 } - ] + ]) } ]) await insertMobileSettings(processor.identityDB, [{ userId: 1 }]) @@ -139,13 +139,13 @@ describe('USDC Purchase Seller', () => { content_id: 10, amount: '1000000', extra_amount: '0', - splits: [ + splits: JSON.stringify([ { user_id: 1, amount: 1000000, percentage: 100 } - ] + ]) }, { seller_user_id: 1, @@ -154,13 +154,13 @@ describe('USDC Purchase Seller', () => { content_id: 15, amount: '1000000', extra_amount: '0', - splits: [ + splits: JSON.stringify([ { user_id: 1, amount: 1000000, percentage: 100 } - ] + ]) } ]) From a7ca952b7230cb648f7443f3f9fde31eae523fd5 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 30 Aug 2024 03:02:46 -0700 Subject: [PATCH 14/17] Generate SDK --- .../libs/src/sdk/api/generated/full/models/PurchaseSplit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts b/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts index a472bcde7a8..cbcbf7164aa 100644 --- a/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts +++ b/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts @@ -22,10 +22,10 @@ import { exists, mapValues } from '../runtime'; export interface PurchaseSplit { /** * - * @type {string} + * @type {number} * @memberof PurchaseSplit */ - userId: string; + userId: number; /** * * @type {string} From 0c8635f2ce0e41f1acc2eabc7ed831bd0527867c Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 30 Aug 2024 03:10:03 -0700 Subject: [PATCH 15/17] update purchasesplit add payoutwallet, optional userid --- .../discovery-provider/src/api/v1/models/users.py | 6 +++++- .../sdk/api/generated/full/models/PurchaseSplit.ts | 14 +++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/discovery-provider/src/api/v1/models/users.py b/packages/discovery-provider/src/api/v1/models/users.py index 49e5d221f27..499fb2a12a0 100644 --- a/packages/discovery-provider/src/api/v1/models/users.py +++ b/packages/discovery-provider/src/api/v1/models/users.py @@ -157,7 +157,11 @@ split = ns.model( "purchase_split", - {"user_id": fields.Integer(required=True), "amount": fields.String(required=True)}, + { + "user_id": fields.Integer(), + "payout_wallet": fields.String(required=True), + "amount": fields.String(required=True), + }, ) purchase = ns.model( diff --git a/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts b/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts index cbcbf7164aa..4f5ed3ddf91 100644 --- a/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts +++ b/packages/libs/src/sdk/api/generated/full/models/PurchaseSplit.ts @@ -25,7 +25,13 @@ export interface PurchaseSplit { * @type {number} * @memberof PurchaseSplit */ - userId: number; + userId?: number; + /** + * + * @type {string} + * @memberof PurchaseSplit + */ + payoutWallet: string; /** * * @type {string} @@ -39,7 +45,7 @@ export interface PurchaseSplit { */ export function instanceOfPurchaseSplit(value: object): value is PurchaseSplit { let isInstance = true; - isInstance = isInstance && "userId" in value && value["userId"] !== undefined; + isInstance = isInstance && "payoutWallet" in value && value["payoutWallet"] !== undefined; isInstance = isInstance && "amount" in value && value["amount"] !== undefined; return isInstance; @@ -55,7 +61,8 @@ export function PurchaseSplitFromJSONTyped(json: any, ignoreDiscriminator: boole } return { - 'userId': json['user_id'], + 'userId': !exists(json, 'user_id') ? undefined : json['user_id'], + 'payoutWallet': json['payout_wallet'], 'amount': json['amount'], }; } @@ -70,6 +77,7 @@ export function PurchaseSplitToJSON(value?: PurchaseSplit | null): any { return { 'user_id': value.userId, + 'payout_wallet': value.payoutWallet, 'amount': value.amount, }; } From d53a3e08237a0ba0e0d67628001ff435ec624963 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 30 Aug 2024 03:10:21 -0700 Subject: [PATCH 16/17] changeset --- .changeset/strong-rice-reflect.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/strong-rice-reflect.md diff --git a/.changeset/strong-rice-reflect.md b/.changeset/strong-rice-reflect.md new file mode 100644 index 00000000000..e87f2d6f3fc --- /dev/null +++ b/.changeset/strong-rice-reflect.md @@ -0,0 +1,5 @@ +--- +'@audius/sdk': minor +--- + +Add splits to Purchase From d78919c5e9ab7e32097235366fa09811d4acd85f Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 30 Aug 2024 03:12:35 -0700 Subject: [PATCH 17/17] Update changeset --- .changeset/strong-rice-reflect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/strong-rice-reflect.md b/.changeset/strong-rice-reflect.md index e87f2d6f3fc..6425435b692 100644 --- a/.changeset/strong-rice-reflect.md +++ b/.changeset/strong-rice-reflect.md @@ -2,4 +2,4 @@ '@audius/sdk': minor --- -Add splits to Purchase +Add splits to Purchase model when retrieving Sales/Purchases