def test_mfee3(): """ Unit test for the fee calculation in the mfee3_only_imbalance_fees scenario """ amount = 500_000_000_000_000_000 deposit = TokenAmount(1_000_000_000_000_000_000) imbalance_penalty = calculate_imbalance_fees(deposit, ProportionalFeeAmount(10_000)) fee_schedule = FeeScheduleState(imbalance_penalty=imbalance_penalty, cap_fees=False) channels = make_channel_pair(fee_schedule, deposit) # How much do we need to send so that the target receives `amount`? PFS-like calculation. fee_calculation = get_initial_amount_for_amount_after_fees( amount_after_fees=PaymentAmount(amount), channels=[channels] ) assert fee_calculation amount_with_margin = calculate_safe_amount_with_fee( fee_calculation.amount_without_fees, FeeAmount(sum(fee_calculation.mediation_fees)) ) assert amount_with_margin == 480_850_038_799_922_400 # print values for scenario print("{:_} {:_}".format(deposit - amount_with_margin, amount_with_margin)) for med_fee in running_sum(fee_calculation.mediation_fees): print( "{:_} {:_}".format( deposit - amount_with_margin + med_fee, amount_with_margin - med_fee ) )
def test_mfee4(): """ Unit test for the fee calculation in the mfee4_combined_fees scenario """ amount = PaymentAmount(500_000_000_000_000_000) deposit = 1_000_000_000_000_000_000 prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(10_000)) imbalance_penalty = calculate_imbalance_fees( TokenAmount(deposit * 2), ProportionalFeeAmount(20_000) ) fee_schedule = FeeScheduleState( flat=FeeAmount(100 // 2), proportional=prop_fee, imbalance_penalty=imbalance_penalty, cap_fees=False, ) channels = make_channel_pair(fee_schedule, deposit, deposit) # How much do we need to send so that the target receives `amount`? PFS-like calculation. fee_calculation = get_initial_amount_for_amount_after_fees( amount_after_fees=PaymentAmount(amount), channels=[channels, channels] ) assert fee_calculation amount_with_margin = calculate_safe_amount_with_fee( amount, FeeAmount(sum(fee_calculation.mediation_fees)) ) # Calculate mediation fees for both mediators med_fees = [] incoming_amount = amount_with_margin for _ in range(2): outgoing_amount = get_amount_without_fees( amount_with_fees=incoming_amount, channel_in=channels[0], channel_out=channels[1] ) assert outgoing_amount med_fees.append(incoming_amount - outgoing_amount) incoming_amount = outgoing_amount assert amount_with_margin == 543_503_066_141_505_551 # print values for scenario print("{:_} {:_}".format(deposit - amount_with_margin, deposit + amount_with_margin)) for med_fee in running_sum(med_fees): print( "{:_} {:_}".format( deposit - amount_with_margin + med_fee, deposit + amount_with_margin - med_fee ) )
def test_get_initial_payment_for_final_target_amount( flat_fee: FeeAmount, prop_fee: ProportionalFeeAmount, balance: TokenAmount, final_amount: PaymentAmount, initial_amount: PaymentWithFeeAmount, expected_fees: List[FeeAmount], ): prop_fee = ppm_fee_per_channel(prop_fee) channel_set = make_channel_set([ NettingChannelStateProperties( canonical_identifier=factories.create( CanonicalIdentifierProperties( channel_identifier=ChannelID(1))), our_state=NettingChannelEndStateProperties(balance=TokenAmount(0)), partner_state=NettingChannelEndStateProperties(balance=balance), fee_schedule=FeeScheduleState(flat=flat_fee, proportional=prop_fee), ), NettingChannelStateProperties( canonical_identifier=factories.create( CanonicalIdentifierProperties( channel_identifier=ChannelID(2))), our_state=NettingChannelEndStateProperties(balance=balance), partner_state=NettingChannelEndStateProperties( balance=TokenAmount(0)), fee_schedule=FeeScheduleState(flat=flat_fee, proportional=prop_fee), ), ]) calculation = get_initial_amount_for_amount_after_fees( amount_after_fees=final_amount, channels=[(channel_set.channels[0], channel_set.channels[1])], ) assert calculation is not None assert calculation.total_amount == initial_amount assert calculation.mediation_fees == expected_fees
def test_mfee2(): """ Unit test for the fee calculation in the mfee2_proportional_fees scenario """ amount = 10_000 deposit = 100_000 prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(10_000)) fee_schedule = FeeScheduleState(proportional=ProportionalFeeAmount(prop_fee)) channels = make_channel_pair(fee_schedule, deposit) # How much do we need to send so that the target receives `amount`? PFS-like calculation. fee_calculation = get_initial_amount_for_amount_after_fees( amount_after_fees=PaymentAmount(amount), channels=[channels, channels] ) assert fee_calculation amount_with_margin = calculate_safe_amount_with_fee( fee_calculation.amount_without_fees, FeeAmount(sum(fee_calculation.mediation_fees)) ) assert amount_with_margin == 10_213 # print values for scenario print(deposit - amount_with_margin, amount_with_margin) for med_fee in running_sum(fee_calculation.mediation_fees): print(deposit - amount_with_margin + med_fee, amount_with_margin - med_fee)
def test_fee_round_trip(flat_fee, prop_fee, imbalance_fee, amount, balance1, balance2): """Tests mediation fee deduction. First we're doing a PFS-like calculation going backwards from the target amount to get the amount that the initiator has to send. Then we calculate the fees from a mediator's point of view and check if `amount_with_fees - fees = amount`. """ # Find examples where there is a reasonable chance of succeeding amount = int(min(amount, balance1 * 0.95 - 1, balance2 * 0.95 - 1)) assume(amount > 0) total_balance = TokenAmount(100_000_000_000_000_000_000) prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee)) imbalance_fee = calculate_imbalance_fees( channel_capacity=total_balance, proportional_imbalance_fee=ProportionalFeeAmount(imbalance_fee), ) channel_in = factories.create( NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=total_balance - balance1), partner_state=NettingChannelEndStateProperties(balance=balance1), fee_schedule=FeeScheduleState( cap_fees=False, flat=FeeAmount(flat_fee), proportional=prop_fee_per_channel, imbalance_penalty=imbalance_fee, ), ) ) channel_out = factories.create( NettingChannelStateProperties( our_state=NettingChannelEndStateProperties(balance=balance2), partner_state=NettingChannelEndStateProperties(balance=total_balance - balance2), fee_schedule=FeeScheduleState( cap_fees=False, flat=FeeAmount(flat_fee), proportional=prop_fee_per_channel, imbalance_penalty=imbalance_fee, ), ) ) # How much do we need to send so that the target receives `amount`? PFS-like calculation. fee_calculation = get_initial_amount_for_amount_after_fees( amount_after_fees=PaymentAmount(amount), channels=[(channel_in, channel_out)] ) assume(fee_calculation) # There is not enough capacity for the payment in all cases assert fee_calculation # How much would a mediator send to the target? Ideally exactly `amount`. amount_without_margin_after_fees = get_amount_without_fees( amount_with_fees=fee_calculation.total_amount, channel_in=channel_in, channel_out=channel_out, ) assume(amount_without_margin_after_fees) # We might lack capacity for the payment assert abs(amount - amount_without_margin_after_fees) <= 1 # Equal except for rounding errors # If we add the fee margin, the mediator must always send at least `amount` to the target! amount_with_fee_and_margin = calculate_safe_amount_with_fee( fee_calculation.amount_without_fees, FeeAmount(sum(fee_calculation.mediation_fees)) ) amount_with_margin_after_fees = get_amount_without_fees( amount_with_fees=amount_with_fee_and_margin, channel_in=channel_in, channel_out=channel_out ) assume(amount_with_margin_after_fees) # We might lack capacity to add margins assert amount_with_margin_after_fees >= amount