예제 #1
0
def make_deposit_transfer(transfer_amount,
                          token,
                          receive_account,
                          transfer_mode=None,
                          automatically_resolve_complete=True,
                          uuid=None,
                          fiat_ramp=None):
    """
    This is used for a user depositing funds to their Sempo wallet. Only interacts with Sempo float.
    :param transfer_amount:
    :param token:
    :param receive_account:
    :param transfer_mode:
    :param automatically_resolve_complete:
    :param uuid:
    :param fiat_ramp: A FiatRamp Object to tie to credit transfer
    :return:
    """

    transfer = CreditTransfer(amount=transfer_amount,
                              token=token,
                              recipient_user=receive_account,
                              transfer_type=TransferTypeEnum.DEPOSIT,
                              transfer_mode=transfer_mode,
                              uuid=uuid,
                              fiat_ramp=fiat_ramp)

    if automatically_resolve_complete:
        transfer.resolve_as_completed()

    return transfer
예제 #2
0
    def migrate_balances(self, ge_address_to_user: dict):

        org = Organisation.query.get(self.sempo_organisation_id)
        token = org.token

        ge_address_to_user.pop(None, None)

        addresses = list(ge_address_to_user.keys())

        for user_address in addresses:

            try:
                balance_wei = 0

                for ge_token in GE_MIGRATION_TOKENS.keys():
                    contract_address = GE_MIGRATION_TOKENS[ge_token]

                    v = get_token_balance(user_address, contract_address)

                    if v != '':
                        balance_wei += int(v)

                user = ge_address_to_user[user_address]

                print(f'transfering {balance_wei} wei to {user}')

                if balance_wei != 0:

                    migration_transfer = CreditTransfer(
                        amount=balance_wei / 1e16,
                        token=token,
                        sender_transfer_account=org.
                        queried_org_level_transfer_account,
                        recipient_user=user,
                        transfer_type=TransferTypeEnum.PAYMENT,
                        transfer_subtype=TransferSubTypeEnum.DISBURSEMENT)

                    db.session.add(migration_transfer)

                    migration_transfer.resolve_as_completed()

            except Exception as e:
                print(e)
                sentry_sdk.capture_exception(e)
                pass
예제 #3
0
    def post(self):
        # Handle Parameters
        post_data = request.get_json()
        recipient_blockchain_address = post_data.get(
            'recipient_blockchain_address', None)
        transfer_amount = post_data.get('transfer_amount', None)
        master_wallet = g.active_organisation.org_level_transfer_account
        receive_transfer_account = create_transfer_account_if_required(
            recipient_blockchain_address, g.active_organisation.token,
            TransferAccountType.EXTERNAL)

        error_message = None
        if not recipient_blockchain_address:
            error_message = '"recipient_blockchain_address" parameter required'
        if not transfer_amount:
            error_message = '"transfer_amount" parameter required'
        if not error_message and not isinstance(transfer_amount, int):
            try:
                transfer_amount = int(transfer_amount)
            except:
                error_message = '"transfer_amount must be an integer'
        if not master_wallet:
            error_message = f'Organisation {g.active_organisation.id} has no master wallet'
        if error_message:
            return {'message': error_message}, 400

        transfer = CreditTransfer(
            transfer_amount,
            g.active_organisation.token,
            sender_transfer_account=master_wallet,
            recipient_transfer_account=receive_transfer_account,
            transfer_type=TransferTypeEnum.
            PAYMENT,  # PAYMENT, since WITHDRAWAL is to the float account by definition
            require_sufficient_balance=True)
        # Make the transfer!
        transfer.resolve_as_complete_and_trigger_blockchain()

        credit_transfer = credit_transfer_schema.dump(transfer)
        response_object = {
            'message': 'Transfer Successful',
            'data': {
                'credit_transfer': credit_transfer,
            }
        }
        return response_object, 201
예제 #4
0
def make_withdrawal_transfer(transfer_amount,
                             token,
                             send_account,
                             transfer_mode=None,
                             require_sender_approved=True,
                             require_sufficient_balance=True,
                             automatically_resolve_complete=True,
                             uuid=None):
    """
    This is used for a user withdrawing funds from their Sempo wallet. Only interacts with Sempo float.
    :param transfer_amount:
    :param token:
    :param send_account:
    :param transfer_mode:
    :param require_sender_approved:
    :param require_sufficient_balance:
    :param automatically_resolve_complete:
    :param uuid:
    :return:
    """

    transfer = CreditTransfer(transfer_amount,
                              token,
                              sender_user=send_account,
                              uuid=uuid,
                              transfer_type=TransferTypeEnum.WITHDRAWAL,
                              transfer_mode=transfer_mode)

    if require_sender_approved and not transfer.check_sender_is_approved():
        message = "Sender {} is not approved".format(send_account)
        transfer.resolve_as_rejected(message)
        raise AccountNotApprovedError(message, is_sender=True)

    if require_sufficient_balance and not transfer.check_sender_has_sufficient_balance(
    ):
        message = "Sender {} has insufficient balance".format(send_account)
        transfer.resolve_as_rejected(message)
        raise InsufficientBalanceError(message)

    if automatically_resolve_complete:
        transfer.resolve_as_completed()
        pusher.push_admin_credit_transfer(transfer)

    return transfer
예제 #5
0
def create_transfer(amount, sender_user, recipient_user, token, subtype=None):
    transfer = CreditTransfer(
        amount=amount,
        sender_user=sender_user,
        recipient_user=recipient_user,
        token=token,
        uuid=str(uuid4()))

    db.session.add(transfer)
    # Mimics before_request hook
    g.pending_transactions = []

    transfer.resolve_as_completed()
    
    # Mimics after_request hook
    for transaction, queue in g.pending_transactions:
        transaction.send_blockchain_payload_to_worker(queue=queue)

    transfer.transfer_type = TransferTypeEnum.PAYMENT
    transfer.transfer_subtype = subtype

    # Commit to prevent memory errors with large numbers of txns counts
    db.session.commit()

    return transfer
예제 #6
0
def new_credit_transfer(create_transfer_account_user, external_reserve_token):
    from server.models.credit_transfer import CreditTransfer
    from uuid import uuid4

    credit_transfer = CreditTransfer(
        amount=1000,
        token=external_reserve_token,
        sender_user=create_transfer_account_user,
        recipient_user=create_transfer_account_user,
        transfer_type=TransferTypeEnum.PAYMENT,
        transfer_subtype=TransferSubTypeEnum.STANDARD,
        uuid=str(uuid4()))
    return credit_transfer
예제 #7
0
def other_new_credit_transfer(create_transfer_account_user, external_reserve_token):
    # Janky copy paste job because of how pytest works
    from server.models.credit_transfer import CreditTransfer
    from uuid import uuid4

    credit_transfer = CreditTransfer(
        amount=1000,
        token=external_reserve_token,
        sender_user=create_transfer_account_user,
        recipient_user=create_transfer_account_user,
        transfer_type=TransferTypeEnum.PAYMENT,
        transfer_subtype=TransferSubTypeEnum.STANDARD,
        uuid=str(uuid4())
    )
    return credit_transfer
예제 #8
0
def create_transfer(amount,
                    sender_user,
                    recipient_user,
                    token,
                    subtype=TransferSubTypeEnum.STANDARD,
                    transfer_usages=None,
                    created_offset=0,
                    transfer_mode=None):
    transfer = CreditTransfer(amount=amount,
                              sender_user=sender_user,
                              recipient_user=recipient_user,
                              token=token,
                              uuid=str(uuid4()))

    db.session.add(transfer)
    # Mimics before_request hook
    g.pending_transactions = []

    if transfer_usages:
        transfer.transfer_usages = transfer_usages

    transfer.resolve_as_complete_and_trigger_blockchain()

    transfer.transfer_type = TransferTypeEnum.PAYMENT
    transfer.transfer_subtype = subtype
    transfer.transfer_mode = transfer_mode

    transfer.created = datetime.utcnow() - timedelta(days=created_offset)

    # Commit to prevent memory errors with large numbers of txns counts
    db.session.commit()

    # Mimic after request hook midway through process
    for transaction, queue in g.pending_transactions:
        transaction.send_blockchain_payload_to_worker(queue=queue)

    return transfer
예제 #9
0
def create_transfer(amount, sender_user, recipient_user, token, subtype=None):
    transfer = CreditTransfer(
        amount=amount,
        sender_user=sender_user,
        recipient_user=recipient_user,
        token=token,
        uuid=str(uuid4()))

    db.session.add(transfer)

    transfer.resolve_as_completed()

    transfer.transfer_type = TransferTypeEnum.PAYMENT
    transfer.transfer_subtype = subtype

    return transfer
예제 #10
0
def create_transfer(amount, sender_user, recipient_user, token, subtype=None):
    transfer = CreditTransfer(
        amount=amount,
        sender_user=sender_user,
        recipient_user=recipient_user,
        token=token,
        uuid=str(uuid4()))

    db.session.add(transfer)

    transfer.resolve_as_completed()

    transfer.transfer_type = TransferTypeEnum.PAYMENT
    transfer.transfer_subtype = subtype

    # Commit to prevent memory errors with large numbers of txns counts
    db.session.commit()

    return transfer
예제 #11
0
def make_payment_transfer(
        transfer_amount,
        token=None,
        send_user=None,
        send_transfer_account=None,
        receive_user=None,
        receive_transfer_account=None,
        transfer_use=None,
        transfer_mode=None,
        require_sender_approved=True,
        require_recipient_approved=True,
        require_sufficient_balance=True,
        automatically_resolve_complete=True,
        uuid=None,
        transfer_subtype: TransferSubTypeEnum = TransferSubTypeEnum.STANDARD,
        is_ghost_transfer=False,
        queue='high-priority'):
    """
    This is used for internal transfers between Sempo wallets.
    :param transfer_amount:
    :param token:
    :param send_user:
    :param send_transfer_account:
    :param receive_user:
    :param receive_transfer_account:
    :param transfer_use:
    :param transfer_mode: TransferModeEnum
    :param require_sender_approved:
    :param require_recipient_approved:
    :param require_sufficient_balance:
    :param automatically_resolve_complete:
    :param uuid:
    :param transfer_subtype: accepts TransferSubType str.
    :param is_ghost_transfer: if an account is created for recipient just to exchange, it's not real
    :param enable_pusher:
    :param queue:
    :return:
    """

    if transfer_subtype is TransferSubTypeEnum.DISBURSEMENT:
        require_sender_approved = False
        require_recipient_approved = False
        require_sufficient_balance = False
        # primary NGO wallet to disburse from
        send_transfer_account = receive_user.default_organisation.queried_org_level_transfer_account

    if transfer_subtype is TransferSubTypeEnum.RECLAMATION:
        require_sender_approved = False
        # primary NGO wallet to reclaim to
        receive_transfer_account = send_user.default_organisation.queried_org_level_transfer_account

    if transfer_subtype is TransferSubTypeEnum.INCENTIVE:
        send_transfer_account = receive_transfer_account.get_float_transfer_account(
        )

    transfer = CreditTransfer(
        transfer_amount,
        token=token,
        sender_user=send_user,
        sender_transfer_account=send_transfer_account,
        recipient_user=receive_user,
        recipient_transfer_account=receive_transfer_account,
        uuid=uuid,
        transfer_type=TransferTypeEnum.PAYMENT,
        transfer_subtype=transfer_subtype,
        transfer_mode=transfer_mode,
        is_ghost_transfer=is_ghost_transfer)

    make_cashout_incentive_transaction = False

    if transfer_use is not None:
        usages = []
        try:
            use_ids = transfer_use.split(',')  # passed as '3,4' etc.
        except AttributeError:
            use_ids = transfer_use
        for use_id in use_ids:
            if use_id != 'null':
                use = TransferUsage.query.get(int(use_id))
                if use:
                    usages.append(use.name)
                    if use.is_cashout:
                        make_cashout_incentive_transaction = True
                else:
                    usages.append('Other')

        transfer.transfer_use = usages

    transfer.uuid = uuid

    if require_sender_approved and not transfer.check_sender_is_approved():
        message = "Sender {} is not approved".format(send_transfer_account)
        transfer.resolve_as_rejected(message)
        raise AccountNotApprovedError(message, is_sender=True)

    if require_recipient_approved and not transfer.check_recipient_is_approved(
    ):
        message = "Recipient {} is not approved".format(receive_user)
        transfer.resolve_as_rejected(message)
        raise AccountNotApprovedError(message, is_sender=False)

    if require_sufficient_balance and not transfer.check_sender_has_sufficient_balance(
    ):
        message = "Sender {} has insufficient balance".format(
            send_transfer_account)
        transfer.resolve_as_rejected(message)
        raise InsufficientBalanceError(message)

    if automatically_resolve_complete:
        transfer.resolve_as_completed(queue=queue)

    if make_cashout_incentive_transaction:
        try:
            # todo: check limits apply
            incentive_amount = round(
                transfer_amount *
                current_app.config['CASHOUT_INCENTIVE_PERCENT'] / 100)

            make_payment_transfer(
                incentive_amount,
                receive_user=receive_user,
                transfer_subtype=TransferSubTypeEnum.INCENTIVE,
                transfer_mode=TransferModeEnum.INTERNAL)

        except Exception as e:
            print(e)
            sentry_sdk.capture_exception(e)

    return transfer
예제 #12
0
    def post(self):
        post_data = request.get_json()

        transfer_amount = post_data.get('transfer_amount')
        sender_blockchain_address = post_data.get('sender_blockchain_address')
        recipient_blockchain_address = post_data.get(
            'recipient_blockchain_address')
        blockchain_transaction_hash = post_data.get(
            'blockchain_transaction_hash')
        contract_address = post_data.get('contract_address')

        transfer = CreditTransfer.query.execution_options(
            show_all=True).filter_by(
                blockchain_hash=blockchain_transaction_hash).first()
        # Case 1: Transfer exists in the database already. Mark received_third_party_sync as true, fetch it, and return it in that case
        if transfer:
            transfer.received_third_party_sync = True
            credit_transfer = credit_transfer_schema.dump(transfer).data
            response_object = {
                'message': 'Transfer Successful',
                'data': {
                    'credit_transfer': credit_transfer,
                }
            }

        else:
            token = Token.query.filter_by(address=contract_address).first()
            maybe_sender_transfer_account = TransferAccount.query.execution_options(
                show_all=True).filter_by(
                    blockchain_address=sender_blockchain_address).first()
            maybe_sender_user = maybe_sender_transfer_account.users[
                0] if maybe_sender_transfer_account and len(
                    maybe_sender_transfer_account.users) == 1 else None

            maybe_recipient_transfer_account = TransferAccount.query.execution_options(
                show_all=True).filter_by(
                    blockchain_address=recipient_blockchain_address).first()
            maybe_recipient_user = maybe_recipient_transfer_account.users[
                0] if maybe_recipient_transfer_account and len(
                    maybe_recipient_transfer_account.users) == 1 else None

            # Case 2: Two non-sempo users making a trade on our token. We don't have to track this!
            if not maybe_recipient_transfer_account and not maybe_sender_transfer_account:
                response_object = {
                    'message': 'No existing users involved in this transfer',
                    'data': {}
                }
            # Case 3: Two non-Sempo users, at least one of whom has interacted with Sempo users before transacting with one another
            # We don't have to track this either!
            elif (
                    # The recipient is either an external transfer account we've seen before
                    # OR we haven't seen them before and so can infer they're external
                ((maybe_recipient_transfer_account
                  and maybe_recipient_transfer_account.account_type
                  == TransferAccountType.EXTERNAL)
                 or not maybe_recipient_transfer_account) and
                    #
                    # And the sender is either an external transfer account we've seen before
                    # OR we haven't seen them before and so can infer they're external
                ((maybe_sender_transfer_account
                  and maybe_sender_transfer_account.account_type
                  == TransferAccountType.EXTERNAL)
                 or not maybe_sender_transfer_account)):
                response_object = {
                    'message': 'Only external users involved in this transfer',
                    'data': {}
                }
            # Case 4: One or both of the transfer accounts are affiliated with Sempo accounts.
            # This is the only case where we want to generate a new CreditTransfer object.
            else:
                send_transfer_account = create_transfer_account_if_required(
                    sender_blockchain_address, token,
                    TransferAccountType.EXTERNAL)
                receive_transfer_account = create_transfer_account_if_required(
                    recipient_blockchain_address, token,
                    TransferAccountType.EXTERNAL)
                transfer = CreditTransfer(
                    transfer_amount,
                    token=token,
                    sender_transfer_account=send_transfer_account,
                    recipient_transfer_account=receive_transfer_account,
                    transfer_type=TransferTypeEnum.PAYMENT,
                    sender_user=maybe_sender_user,
                    recipient_user=maybe_recipient_user,
                    require_sufficient_balance=False,
                    received_third_party_sync=True)

                transfer.resolve_as_complete_with_existing_blockchain_transaction(
                    blockchain_transaction_hash)

                db.session.flush()

                credit_transfer = credit_transfer_schema.dump(transfer).data
                response_object = {
                    'message': 'Transfer Successful',
                    'data': {
                        'credit_transfer': credit_transfer,
                    }
                }
        return make_response(jsonify(response_object)), 201