예제 #1
0
    def test_markets_with_event_description(self):
        # test empty events response
        empty_markets_response = self.client.get(
            reverse('api:markets'), content_type='application/json')
        self.assertEquals(
            len(json.loads(empty_markets_response.content).get('results')), 0)

        # create markets
        oracle = CentralizedOracleFactory()
        event = CategoricalEventFactory(oracle=oracle)
        market = MarketFactory(event=event)

        market_response_data = self.client.get(reverse('api:markets'),
                                               content_type='application/json')
        self.assertEquals(market_response_data.status_code, status.HTTP_200_OK)
        results = json.loads(market_response_data.content).get('results')
        self.assertEquals(len(results), 1)

        self.assertIsNotNone(
            results[0]['event']['oracle'].get('eventDescription'))
        self.assertIsNotNone(
            results[0]['event']['oracle']['eventDescription'].get('ipfsHash'))
        self.assertEqual(
            results[0]['event']['oracle']['eventDescription']['ipfsHash'],
            oracle.event_description.ipfs_hash)
예제 #2
0
    def test_shares_by_account(self):
        account1 = '{:040d}'.format(13)
        account2 = '{:040d}'.format(14)

        url = reverse('api:shares-by-account', kwargs={'account_address': account1})
        empty_shares_response = self.client.get(url, content_type='application/json')
        self.assertEquals(len(json.loads(empty_shares_response.content).get('results')), 0)

        oracle = CentralizedOracleFactory()
        event = CategoricalEventFactory(oracle=oracle)
        market = MarketFactory(event=event, creator=account1)
        outcome_token = OutcomeTokenFactory(event=market.event)
        OutcomeTokenBalanceFactory(owner=market.creator, outcome_token=outcome_token)
        BuyOrderFactory(outcome_token=outcome_token, sender=account1, market=market)

        url = reverse('api:shares-by-account', kwargs={'account_address': account1})
        shares_response = self.client.get(url, content_type='application/json')
        decoded_response = json.loads(shares_response.content)
        self.assertEquals(len(decoded_response.get('results')), 1)
        self.assertEquals(
            decoded_response.get('results')[0].get('eventDescription').get('title'),
            oracle.event_description.title
        )
        self.assertEquals(
            decoded_response.get('results')[0].get('marginalPrice'),
            0.5
        )

        url = reverse('api:shares-by-account', kwargs={'account_address': account2})
        no_shares_response = self.client.get(url, content_type='application/json')
        self.assertEquals(len(json.loads(no_shares_response.content).get('results')), 0)
예제 #3
0
    def test_create_market(self):
        oracle = CentralizedOracleFactory()
        event = CategoricalEventFactory(oracle=oracle)
        market = MarketFactory()

        block = {
            'number': market.creation_block,
            'timestamp': mktime(market.creation_date_time.timetuple())
        }

        market_dict = {
            'address':
            market.factory,
            'params': [{
                'name': 'creator',
                'value': market.creator
            }, {
                'name': 'centralizedOracle',
                'value': oracle.address,
            }, {
                'name': 'marketMaker',
                'value': market.market_maker
            }, {
                'name': 'fee',
                'value': market.fee
            }, {
                'name': 'market',
                'value': market.address
            }]
        }
        market.delete()

        s = MarketSerializer(data=market_dict, block=block)
        self.assertFalse(s.is_valid(), s.errors)

        market_dict.get('params').append({
            'name': 'eventContract',
            'value': market.address
        })

        market_dict.get('params').append({'name': 'fee', 'value': market.fee})

        s = MarketSerializer(data=market_dict, block=block)
        self.assertFalse(s.is_valid(), s.errors)

        market_dict.get('params')[-2]['value'] = event.address

        s = MarketSerializer(data=market_dict, block=block)
        self.assertFalse(s.is_valid(), s.errors)

        marketMaker = [
            x for x in market_dict.get('params')
            if x.get('name') == 'marketMaker'
        ][0]
        marketMaker.update({'value': settings.LMSR_MARKET_MAKER})

        s = MarketSerializer(data=market_dict, block=block)
        self.assertTrue(s.is_valid(), s.errors)
        instance = s.save()
        self.assertIsNotNone(instance)
예제 #4
0
    def test_markets_by_resolution_date(self):
        # test empty events response
        empty_markets_response = self.client.get(
            reverse('api:markets'), content_type='application/json')
        self.assertEquals(
            len(json.loads(empty_markets_response.content).get('results')), 0)

        oracle = CentralizedOracleFactory()
        event = CategoricalEventFactory(oracle=oracle)
        market = MarketFactory(event=event)
        from_date = (datetime.now() -
                     timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')

        url = reverse('api:markets') + '?resolution_date_time_0=' + from_date
        correct_date_time_range_response = self.client.get(
            url, content_type='application/json')
        self.assertEquals(
            len(
                json.loads(
                    correct_date_time_range_response.content).get('results')),
            1)

        url = reverse(
            'api:markets'
        ) + '?resolution_date_time_0=' + from_date + '&resolution_date_time_1=' + from_date
        empty_date_time_range_response = self.client.get(
            url, content_type='application/json')
        self.assertEquals(
            len(
                json.loads(
                    empty_date_time_range_response.content).get('results')), 0)
예제 #5
0
    def test_winnings_redemption_rollback(self):
        event_factory = CategoricalEventFactory(redeemed_winnings=100)
        address = event_factory.address[0:-4] + 'user'
        block = {
            'number': 1,
            'timestamp': self.to_timestamp(datetime.now())
        }
        winnings_event = {
            'name': 'WinningsRedemption',
            'address': event_factory.address,
            'params': [
                {
                    'name': 'receiver',
                    'value': address
                },
                {
                    'name': 'winnings',
                    'value': 10
                }
            ]
        }

        EventInstanceReceiver().save(winnings_event, block)
        event_before_rollback = CategoricalEvent.objects.get(address=event_factory.address)
        self.assertEquals(event_before_rollback.redeemed_winnings, event_factory.redeemed_winnings+10)
        EventInstanceReceiver().rollback(winnings_event, block)
        event_after_rollback = CategoricalEvent.objects.get(address=event_factory.address)
        self.assertEquals(event_after_rollback.redeemed_winnings, event_factory.redeemed_winnings)
예제 #6
0
 def test_categorical_event_serializer(self):
     event = CategoricalEventFactory()
     events_response = self.client.get(reverse('api:events'),
                                       content_type='application/json')
     self.assertEquals(
         json.loads(events_response.content).get('results')[0]['type'],
         'CATEGORICAL')
예제 #7
0
    def test_events(self):
        # test empty events response
        empty_events_response = self.client.get(
            reverse('api:events'), content_type='application/json')
        self.assertEquals(
            len(json.loads(empty_events_response.content).get('results')), 0)

        # outcomes creation
        # outcomes = (OutcomeTokenFactory(), OutcomeTokenFactory(), OutcomeTokenFactory())
        # event creation
        event = CategoricalEventFactory()
        # self.assertEquals(event.outcome_tokens.count(), len(outcomes))
        events_response = self.client.get(reverse('api:events'),
                                          content_type='application/json')
        self.assertEquals(
            len(json.loads(events_response.content).get('results')), 1)

        event_filtered_response = self.client.get(
            reverse('api:events-by-address', kwargs={'addr': "abcdef0"}),
            content_type='application/json')
        self.assertEquals(event_filtered_response.status_code,
                          status.HTTP_404_NOT_FOUND)

        event_filtered_response = self.client.get(
            reverse('api:events-by-address', kwargs={'addr': event.address}),
            content_type='application/json')
        self.assertEquals(event_filtered_response.status_code,
                          status.HTTP_200_OK)
        self.assertEquals(
            json.loads(events_response.content).get('results')[0].get(
                'contract').get('address'), add_0x_prefix(event.address))
예제 #8
0
    def test_event_description_different_outcomes(self):

        oracle = CentralizedOracleFactory()

        oracle.event_description.outcomes = ['Yes', 'No', 'Third']
        oracle.event_description.save()

        event = CategoricalEventFactory(oracle=oracle)

        # Categorical Event with different outcomes

        block = {
            'number': event.creation_block,
            'timestamp': mktime(event.creation_date_time.timetuple())
        }

        categorical_event = {
            'address':
            event.factory,
            'params': [{
                'name': 'creator',
                'value': event.creator
            }, {
                'name': 'collateralToken',
                'value': event.creator
            }, {
                'name': 'oracle',
                'value': oracle.address
            }, {
                'name': 'outcomeCount',
                'value': 2
            }, {
                'name': 'categoricalEvent',
                'value': event.address,
            }]
        }

        event.delete()

        s = CategoricalEventSerializer(data=categorical_event, block=block)
        self.assertFalse(s.is_valid())
        categorical_event['params'][3]['value'] = 3
        s2 = CategoricalEventSerializer(data=categorical_event, block=block)
        self.assertTrue(s2.is_valid(), s.errors)
        instance = s2.save()
        self.assertIsNotNone(instance)
예제 #9
0
    def test_market_outcome_token_purchase_rollback(self):
        oracle_factory = CentralizedOracleFactory()
        event_factory = CategoricalEventFactory(oracle=oracle_factory)
        outcome_token = OutcomeTokenFactory(event=event_factory, index=0)
        market_without_rollback = MarketFactory(event=event_factory)
        buyer_address = '{:040d}'.format(100)
        self.assertIsNotNone(market_without_rollback.pk)

        block = {
            'number': oracle_factory.creation_block,
            'timestamp': self.to_timestamp(oracle_factory.creation_date_time)
        }

        outcome_token_purchase_event = {
            'name':
            'OutcomeTokenPurchase',
            'address':
            market_without_rollback.address,
            'params': [
                {
                    'name': 'outcomeTokenCost',
                    'value': 100
                },
                {
                    'name': 'marketFees',
                    'value': 10
                },
                {
                    'name': 'buyer',
                    'value': buyer_address
                },
                {
                    'name': 'outcomeTokenIndex',
                    'value': 0
                },
                {
                    'name': 'outcomeTokenCount',
                    'value': 10
                },
            ]
        }

        # Send outcome token purchase event
        MarketInstanceReceiver().save(outcome_token_purchase_event, block)
        orders_before_rollback = BuyOrder.objects.filter(
            creation_block=block.get('number'),
            sender=buyer_address,
            market=market_without_rollback)
        self.assertEquals(len(orders_before_rollback), 1)

        # Outcome token purchase rollback
        MarketInstanceReceiver().rollback(outcome_token_purchase_event, block)
        market_with_rollback = Market.objects.get(event=event_factory.address)
        orders_after_rollback = BuyOrder.objects.filter(
            creation_block=block.get('number'),
            sender=buyer_address,
            market=market_with_rollback)
        self.assertEquals(len(orders_after_rollback), 0)
예제 #10
0
    def test_market_receiver(self):
        oracle = CentralizedOracleFactory()
        oracle.event_description.outcomes = ['1', '2', '3']
        oracle.event_description.save()
        event = CategoricalEventFactory(oracle=oracle)
        market = MarketFactory()
        event_address = event.address

        block = {
            'number': market.creation_block,
            'timestamp': self.to_timestamp(market.creation_date_time)
        }

        market_dict = {
            'name':
            'StandardMarketCreation',
            'address':
            market.factory,
            'params': [{
                'name': 'creator',
                'value': market.creator
            }, {
                'name': 'centralizedOracle',
                'value': oracle.address,
            }, {
                'name': 'marketMaker',
                'value': market.market_maker
            }, {
                'name': 'fee',
                'value': market.fee
            }, {
                'name': 'eventContract',
                'value': event_address
            }, {
                'name': 'fee',
                'value': market.fee
            }, {
                'name': 'market',
                'value': market.address
            }]
        }

        market.delete()

        MarketFactoryReceiver().save(market_dict, block)
        with self.assertRaises(Market.DoesNotExist):
            Market.objects.get(event=event_address)

        market_dict.get('params')[2].update(
            {'value': settings.LMSR_MARKET_MAKER})
        MarketFactoryReceiver().save(market_dict, block)
        saved_market = Market.objects.get(event=event_address)
        self.assertIsNotNone(saved_market.pk)
        self.assertEquals(len(market.net_outcome_tokens_sold), 2)
        self.assertEquals(len(saved_market.net_outcome_tokens_sold), 3)
예제 #11
0
    def test_market_marginal_prices(self):
        oracle = CentralizedOracleFactory()
        categorical_event = CategoricalEventFactory(oracle=oracle)
        outcome_token = OutcomeTokenFactory(event=categorical_event)
        market = MarketFactory(event=categorical_event)
        sender_address = '{:040d}'.format(100)

        # Buy Order
        order_one = BuyOrderFactory(market=market, sender=sender_address)
        order_two = BuyOrderFactory(market=market, sender=sender_address)
        market_response = self.client.get(reverse('api:markets-by-name', kwargs={'market_address': market.address}), content_type='application/json')
        market_data = json.loads(market_response.content)
        self.assertEquals(market_data.get('marginalPrices'), order_two.marginal_prices)
예제 #12
0
    def test_create_categorical_event(self):
        event = CategoricalEventFactory()
        oracle = CentralizedOracleFactory()

        block = {
            'number': event.creation_block,
            'timestamp': mktime(event.creation_date_time.timetuple())
        }

        categorical_event = {
            'address':
            event.factory,
            'params': [{
                'name': 'creator',
                'value': event.creator
            }, {
                'name': 'collateralToken',
                'value': event.collateral_token
            }, {
                'name': 'oracle',
                'value': oracle.address
            }, {
                'name': 'outcomeCount',
                'value': 2
            }]
        }
        event.delete()
        s = CategoricalEventSerializer(data=categorical_event, block=block)
        self.assertFalse(s.is_valid(), s.errors)

        categorical_event.get('params').append({
            'name': 'categoricalEvent',
            'value': event.address
        })

        s = CategoricalEventSerializer(data=categorical_event, block=block)
        self.assertTrue(s.is_valid(), s.errors)
        instance = s.save()
        self.assertIsNotNone(instance)
예제 #13
0
    def test_event_instance_outcome_assignment_receiver(self):
        event = CategoricalEventFactory()
        assignment_event = {
            'name': 'OutcomeAssignment',
            'address': event.address,
            'params': [{
                'name': 'outcome',
                'value': 1,
            }]
        }

        EventInstanceReceiver().save(assignment_event)
        saved_event = Event.objects.get(address=event.address)
        self.assertTrue(saved_event.is_winning_outcome_set)
예제 #14
0
    def test_collected_fees(self):
        categorical_event = CategoricalEventFactory()
        outcome_token = OutcomeTokenFactory(event=categorical_event, index=0)
        market = MarketFactory(event=categorical_event)
        sender_address = '{:040d}'.format(100)
        fees = 10

        outcome_token_purchase_event = {
            'name':
            'OutcomeTokenPurchase',
            'address':
            market.address,
            'params': [
                {
                    'name': 'outcomeTokenCost',
                    'value': 100
                },
                {
                    'name': 'marketFees',
                    'value': fees
                },
                {
                    'name': 'buyer',
                    'value': sender_address
                },
                {
                    'name': 'outcomeTokenIndex',
                    'value': 0
                },
                {
                    'name': 'outcomeTokenCount',
                    'value': 10
                },
            ]
        }

        block = {'number': 1, 'timestamp': self.to_timestamp(datetime.now())}

        # Save event
        MarketInstanceReceiver().save(outcome_token_purchase_event, block)
        # Check that collected fees was incremented
        market_check = Market.objects.get(address=market.address)
        self.assertEquals(market_check.collected_fees,
                          market.collected_fees + fees)

        block.update({'number': 2})
        MarketInstanceReceiver().save(outcome_token_purchase_event, block)
        market_check = Market.objects.get(address=market.address)
        self.assertEquals(market_check.collected_fees,
                          market.collected_fees + fees + fees)
예제 #15
0
    def test_categorical_event_receiver(self):
        event = CategoricalEventFactory()
        oracle = OracleFactory()
        event_address = event.address

        block = {
            'number': event.creation_block,
            'timestamp': self.to_timestamp(event.creation_date_time)
        }

        categorical_event = {
            'address':
            event.factory,
            'name':
            'CategoricalEventCreation',
            'params': [{
                'name': 'creator',
                'value': event.creator
            }, {
                'name': 'collateralToken',
                'value': event.collateral_token
            }, {
                'name': 'oracle',
                'value': oracle.address
            }, {
                'name': 'outcomeCount',
                'value': 2
            }, {
                'name': 'categoricalEvent',
                'value': event_address
            }]
        }
        event.delete()

        EventFactoryReceiver().save(categorical_event, block)
        event = CategoricalEvent.objects.get(address=event_address)
        self.assertIsNotNone(event.pk)
예제 #16
0
    def test_market_factory_rollback(self):
        oracle = CentralizedOracleFactory()
        event = CategoricalEventFactory(oracle=oracle)
        market = MarketFactory()

        block = {
            'number': oracle.creation_block,
            'timestamp': self.to_timestamp(oracle.creation_date_time)
        }

        market_creation_event = {
            'name': 'StandardMarketCreation',
            'address': market.factory,
            'params': [
                {
                    'name': 'creator',
                    'value': market.creator
                },
                {
                    'name': 'centralizedOracle',
                    'value': oracle.address,
                },
                {
                    'name': 'marketMaker',
                    'value': settings.LMSR_MARKET_MAKER
                },
                {
                    'name': 'fee',
                    'value': market.fee
                },
                {
                    'name': 'eventContract',
                    'value': event.address
                },
                {
                    'name': 'market',
                    'value': market.address
                }
            ]
        }
        market.delete()

        MarketFactoryReceiver().save(market_creation_event, block)
        market_without_rollback = Market.objects.get(event=event.address)
        self.assertIsNotNone(market_without_rollback.pk)
        # Rollback
        MarketFactoryReceiver().rollback(market_creation_event, block)
        with self.assertRaises(Market.DoesNotExist):
            Market.objects.get(event=event.address)
예제 #17
0
    def test_outcome_token_purchase_marginal_price(self):
        categorical_event = CategoricalEventFactory()
        OutcomeTokenFactory(event=categorical_event, index=0)
        OutcomeTokenFactory(event=categorical_event, index=1)
        market = MarketFactory(event=categorical_event,
                               funding=1e18,
                               net_outcome_tokens_sold=[0, 0])
        sender_address = '{:040d}'.format(100)

        outcome_token_purchase_event = {
            'name':
            'OutcomeTokenPurchase',
            'address':
            market.address,
            'params': [
                {
                    'name': 'outcomeTokenCost',
                    'value': 1000000000000000000
                },
                {
                    'name': 'marketFees',
                    'value': 0
                },
                {
                    'name': 'buyer',
                    'value': sender_address
                },
                {
                    'name': 'outcomeTokenIndex',
                    'value': 0
                },
                {
                    'name': 'outcomeTokenCount',
                    'value': 1584900000000000000
                },
            ]
        }

        block = {'number': 1, 'timestamp': self.to_timestamp(datetime.now())}

        self.assertEquals(BuyOrder.objects.all().count(), 0)
        MarketInstanceReceiver().save(outcome_token_purchase_event, block)
        buy_orders = BuyOrder.objects.all()
        self.assertEquals(buy_orders.count(), 1)
        self.assertListEqual(
            buy_orders[0].marginal_prices,
            [Decimal(0.7500), Decimal(0.2500)])  # outcomeTokenCost+fee
예제 #18
0
    def test_market_outcome_token_sale_rollback(self):
        categorical_event = CategoricalEventFactory()
        outcome_token = OutcomeTokenFactory(event=categorical_event, index=0)
        market = MarketFactory(event=categorical_event)
        seller_address = '{:040d}'.format(100)

        block = {'number': 1, 'timestamp': self.to_timestamp(datetime.now())}
        outcome_token_sell_event = {
            'name':
            'OutcomeTokenSale',
            'address':
            market.address,
            'params': [
                {
                    'name': 'outcomeTokenProfit',
                    'value': 100
                },
                {
                    'name': 'marketFees',
                    'value': 10
                },
                {
                    'name': 'seller',
                    'value': seller_address
                },
                {
                    'name': 'outcomeTokenIndex',
                    'value': 0
                },
                {
                    'name': 'outcomeTokenCount',
                    'value': 10
                },
            ]
        }

        MarketInstanceReceiver().save(outcome_token_sell_event, block)
        orders_before_rollback = SellOrder.objects.filter(
            creation_block=block.get('number'), sender=seller_address)
        self.assertEquals(len(orders_before_rollback), 1)

        # Outcome token sell rollback
        MarketInstanceReceiver().rollback(outcome_token_sell_event, block)
        orders_before_rollback = SellOrder.objects.filter(
            creation_block=block.get('number'), sender=seller_address)
        self.assertEquals(len(orders_before_rollback), 0)
예제 #19
0
    def test_categorical_event_factory_rollback(self):
        event = CategoricalEventFactory()
        oracle = OracleFactory()
        event_address = event.address

        block = {
            'number': event.creation_block,
            'timestamp': self.to_timestamp(event.creation_date_time)
        }

        categorical_event = {
            'address': event.factory,
            'name': 'CategoricalEventCreation',
            'params': [
                {
                    'name': 'creator',
                    'value': event.creator
                },
                {
                    'name': 'collateralToken',
                    'value': event.collateral_token
                },
                {
                    'name': 'oracle',
                    'value': oracle.address
                },
                {
                    'name': 'outcomeCount',
                    'value': 2
                },
                {
                    'name': 'categoricalEvent',
                    'value': event_address
                }
            ]
        }

        EventFactoryReceiver().save(categorical_event, block)
        event = CategoricalEvent.objects.get(address=event_address)
        self.assertIsNotNone(event.pk)
        EventFactoryReceiver().rollback(categorical_event, block)
        with self.assertRaises(CategoricalEvent.DoesNotExist):
            CategoricalEvent.objects.get(address=event_address)

        # Rollback over an nonexistent event shoul fail
        self.assertRaises(Exception, EventFactoryReceiver().rollback, categorical_event, block)
예제 #20
0
    def test_outcome_token_purchase(self):
        categorical_event = CategoricalEventFactory()
        outcome_token = OutcomeTokenFactory(event=categorical_event, index=0)
        market = MarketFactory(event=categorical_event)
        sender_address = '{:040d}'.format(100)

        outcome_token_purchase_event = {
            'name':
            'OutcomeTokenPurchase',
            'address':
            market.address,
            'params': [
                {
                    'name': 'outcomeTokenCost',
                    'value': 100
                },
                {
                    'name': 'marketFees',
                    'value': 10
                },
                {
                    'name': 'buyer',
                    'value': sender_address
                },
                {
                    'name': 'outcomeTokenIndex',
                    'value': 0
                },
                {
                    'name': 'outcomeTokenCount',
                    'value': 10
                },
            ]
        }

        block = {'number': 1, 'timestamp': self.to_timestamp(datetime.now())}

        self.assertEquals(BuyOrder.objects.all().count(), 0)
        MarketInstanceReceiver().save(outcome_token_purchase_event, block)
        buy_orders = BuyOrder.objects.all()
        self.assertEquals(buy_orders.count(), 1)
        self.assertEquals(buy_orders[0].cost, 110)  # outcomeTokenCost+fee
예제 #21
0
    def test_outcome_token_sell(self):
        categorical_event = CategoricalEventFactory()
        outcome_token = OutcomeTokenFactory(event=categorical_event, index=0)
        market = MarketFactory(event=categorical_event)
        sender_address = '{:040d}'.format(100)

        outcome_token_sell_event = {
            'name':
            'OutcomeTokenSale',
            'address':
            market.address,
            'params': [
                {
                    'name': 'outcomeTokenProfit',
                    'value': 100
                },
                {
                    'name': 'marketFees',
                    'value': 10
                },
                {
                    'name': 'seller',
                    'value': sender_address
                },
                {
                    'name': 'outcomeTokenIndex',
                    'value': 0
                },
                {
                    'name': 'outcomeTokenCount',
                    'value': 10
                },
            ]
        }

        block = {'number': 1, 'timestamp': self.to_timestamp(datetime.now())}

        self.assertEquals(SellOrder.objects.all().count(), 0)
        MarketInstanceReceiver().save(outcome_token_sell_event, block)
        sell_orders = SellOrder.objects.all()
        self.assertEquals(sell_orders.count(), 1)
        self.assertEquals(sell_orders[0].profit, 90)  # outcomeTokenProfit-fee
예제 #22
0
    def test_event_instance_winnings_redemption_receiver(self):
        event = CategoricalEventFactory()
        redemption_event = {
            'name':
            'WinningsRedemption',
            'address':
            event.address,
            'params': [{
                'name': 'receiver',
                'value': event.creator,
            }, {
                'name': 'winnings',
                'value': 1
            }]
        }

        EventInstanceReceiver().save(redemption_event)
        saved_event = Event.objects.get(address=event.address)
        self.assertEquals(saved_event.redeemed_winnings,
                          event.redeemed_winnings + 1)