Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test/decision_maker #328

Merged
merged 37 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b910578
tests:state
Ravleen-Solulab Sep 19, 2024
9c1f1f6
tests:state
Ravleen-Solulab Sep 20, 2024
1c1e079
tests:state
Ravleen-Solulab Sep 23, 2024
6adb7c5
tests:state
Ravleen-Solulab Sep 25, 2024
bd286d5
test:state
Ravleen-Solulab Sep 26, 2024
fe58225
test:state
Ravleen-Solulab Sep 27, 2024
c3640b5
test:state
Ravleen-Solulab Sep 30, 2024
8d90854
test:state
Ravleen-Solulab Sep 30, 2024
29eb1b6
test:state
Ravleen-Solulab Oct 3, 2024
2ec1d2e
test:state
Ravleen-Solulab Oct 4, 2024
519b83c
test:state
Ravleen-Solulab Oct 8, 2024
082be4e
test:state
Ravleen-Solulab Oct 10, 2024
4b49fca
fix:dependency
Ravleen-Solulab Oct 11, 2024
631ab68
test:state
Ravleen-Solulab Oct 11, 2024
e7f700d
check runs
Ravleen-Solulab Oct 14, 2024
a351372
check runs
Ravleen-Solulab Oct 15, 2024
43e5c15
check runs
Ravleen-Solulab Oct 16, 2024
8b051a9
fix:check runs error
Ravleen-Solulab Oct 17, 2024
5934b36
fix:check runs error
Ravleen-Solulab Oct 18, 2024
c9c0467
fix:check runs error
Ravleen-Solulab Oct 22, 2024
f2b6621
fix:dependency
Ravleen-Solulab Oct 22, 2024
822b3fc
fix:check runs error
Ravleen-Solulab Oct 25, 2024
5a4fd75
fix:dependency
Ravleen-Solulab Oct 25, 2024
3bdd74c
Merge branch 'valory-xyz:main' into Test-decision
Ravleen-Solulab Oct 25, 2024
7656661
Merge branch 'valory-xyz:main' into Test-decision
Ravleen-Solulab Oct 28, 2024
5f0b9f6
fix:test error
Ravleen-Solulab Oct 28, 2024
dcab18f
chore:geneators
Ravleen-Solulab Oct 28, 2024
70145a3
lock error
Ravleen-Solulab Oct 28, 2024
2065b70
fix:rounds test
Ravleen-Solulab Oct 28, 2024
5166936
fix:type error
Ravleen-Solulab Oct 28, 2024
b2ec6ea
fix:coverage
Ravleen-Solulab Oct 29, 2024
6a03794
fix:coverage
Ravleen-Solulab Oct 30, 2024
97e2f30
fix:coverage
Ravleen-Solulab Oct 30, 2024
d32b1b7
fix:coverage
Ravleen-Solulab Oct 30, 2024
253b6c3
fix: `gitleaks` command
Adamantios Nov 15, 2024
462e414
Merge branch 'main' into Test-decision
Adamantios Nov 15, 2024
ea766ab
chore: run generators
Adamantios Nov 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
"contract/valory/staking_token/0.1.0": "bafybeiep4r6qyilbfgzdvx6t7zvpgaioxqktmxm7puwtnbpb2ftlib43gy",
"contract/valory/relayer/0.1.0": "bafybeicawmds6czx7db2lcktvexwrp245jpekgulndtos5s5zdid3ilvq4",
"skill/valory/market_manager_abci/0.1.0": "bafybeicia7itulcrxlwbmhfzxppgo5i33tb2nmcycwizftw7gps4dareka",
"skill/valory/decision_maker_abci/0.1.0": "bafybeiaizbnlz46lba6tcwmajpo2z37cpxuobeqmlgdqbdxnt65jr6rely",
"skill/valory/trader_abci/0.1.0": "bafybeido4dpfcvpyrobs2veoliuwaz2ltuzvo2gqfbsqxgd5tdr7zllyru",
"skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeieszb6lxyy5gt4m4f75fjicuzf3ewsxgxcc5dh4l2tjjau7wcrgxe",
"skill/valory/decision_maker_abci/0.1.0": "bafybeia5qzqac2g444myog3tlwagthcknmanqunnytbjfsuk2msep2rnna",
"skill/valory/trader_abci/0.1.0": "bafybeigz6mm7xzhe334iuyy6nw4tdymxfjp6fp37firbzznxg7mowtvcoe",
"skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeigt42cdisy2z53u5pqq3qmjk5bb32cm5oegftptv56m7yq5dbdoyi",
"skill/valory/staking_abci/0.1.0": "bafybeictd5pxhscuhqntvctb7l5lfjausxt2m22rg5mkaiuj4cwwcxpvne",
"skill/valory/check_stop_trading_abci/0.1.0": "bafybeifmi64g4ki6zwbcncb35ovhd4sllw4xrszrkturpeqdhgf5bkiini",
"agent/valory/trader/0.1.0": "bafybeifaw3dk5uklcgj5iftcdhj4p2ymkidoli3uxibq4olddd3eboey7e",
"service/valory/trader/0.1.0": "bafybeiaqqlwqjiofrezpnk4hq6doiccbquzgmd6pcgtfx7akmfhhvyoipi",
"service/valory/trader_pearl/0.1.0": "bafybeidzv544ive2oozjk2szmengrcszea7ai54qts7ehdjdkwevwtwfba"
"agent/valory/trader/0.1.0": "bafybeidcn54f67yhqaa7aycsmurm4tvmx2dj2e2zir6keft3ubxchcu6hu",
"service/valory/trader/0.1.0": "bafybeig6jsofnzrst72r3qannxrpbrbfd7c4nvdcvacsplcx7gnyroqeuu",
"service/valory/trader_pearl/0.1.0": "bafybeih456qaxmfhx7bhhk2wdzjq4s3mbxmkfdveyucgwdh7ilqio4ruvm"
},
"third_party": {
"protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi",
Expand Down
6 changes: 3 additions & 3 deletions packages/valory/agents/trader/aea-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ skills:
- valory/reset_pause_abci:0.1.0:bafybeigrdlxed3xlsnxtjhnsbl3cojruihxcqx4jxhgivkd5i2fkjncgba
- valory/termination_abci:0.1.0:bafybeib5l7jhew5ic6iq24dd23nidcoimzqkrk556gqywhoziatj33zvwm
- valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae
- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeieszb6lxyy5gt4m4f75fjicuzf3ewsxgxcc5dh4l2tjjau7wcrgxe
- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeigt42cdisy2z53u5pqq3qmjk5bb32cm5oegftptv56m7yq5dbdoyi
- valory/market_manager_abci:0.1.0:bafybeicia7itulcrxlwbmhfzxppgo5i33tb2nmcycwizftw7gps4dareka
- valory/decision_maker_abci:0.1.0:bafybeiaizbnlz46lba6tcwmajpo2z37cpxuobeqmlgdqbdxnt65jr6rely
- valory/trader_abci:0.1.0:bafybeido4dpfcvpyrobs2veoliuwaz2ltuzvo2gqfbsqxgd5tdr7zllyru
- valory/decision_maker_abci:0.1.0:bafybeia5qzqac2g444myog3tlwagthcknmanqunnytbjfsuk2msep2rnna
- valory/trader_abci:0.1.0:bafybeigz6mm7xzhe334iuyy6nw4tdymxfjp6fp37firbzznxg7mowtvcoe
- valory/staking_abci:0.1.0:bafybeictd5pxhscuhqntvctb7l5lfjausxt2m22rg5mkaiuj4cwwcxpvne
- valory/check_stop_trading_abci:0.1.0:bafybeifmi64g4ki6zwbcncb35ovhd4sllw4xrszrkturpeqdhgf5bkiini
- valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm
Expand Down
2 changes: 1 addition & 1 deletion packages/valory/services/trader/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license: Apache-2.0
fingerprint:
README.md: bafybeigtuothskwyvrhfosps2bu6suauycolj67dpuxqvnicdrdu7yhtvq
fingerprint_ignore_patterns: []
agent: valory/trader:0.1.0:bafybeifaw3dk5uklcgj5iftcdhj4p2ymkidoli3uxibq4olddd3eboey7e
agent: valory/trader:0.1.0:bafybeidcn54f67yhqaa7aycsmurm4tvmx2dj2e2zir6keft3ubxchcu6hu
number_of_agents: 4
deployment:
agent:
Expand Down
2 changes: 1 addition & 1 deletion packages/valory/services/trader_pearl/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license: Apache-2.0
fingerprint:
README.md: bafybeibg7bdqpioh4lmvknw3ygnllfku32oca4eq5pqtvdrdsgw6buko7e
fingerprint_ignore_patterns: []
agent: valory/trader:0.1.0:bafybeifaw3dk5uklcgj5iftcdhj4p2ymkidoli3uxibq4olddd3eboey7e
agent: valory/trader:0.1.0:bafybeidcn54f67yhqaa7aycsmurm4tvmx2dj2e2zir6keft3ubxchcu6hu
number_of_agents: 1
deployment:
agent:
Expand Down
16 changes: 16 additions & 0 deletions packages/valory/skills/decision_maker_abci/skill.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,24 @@ fingerprint:
tests/behaviours/dummy_strategy/dummy_strategy.py: bafybeig5e3xfr7gxsakfj4stbxqcwdiljl7klvgahkuwe3obzxgkg3qt2e
tests/behaviours/test_base.py: bafybeif6pglmr7pvojylatfzaxtlk65igx6a2omyrbxfihnnft6o7p75p4
tests/conftest.py: bafybeidy5hw56kw5mxudnfbhvogofn6k4rqb4ux2bd45baedrrhmgyrude
tests/states/test_base.py: bafybeigiuctxda3npkbvx7nsq4jvqpckvbzgqlj76hdpk2ntc52ppc4vnm
tests/states/test_bet_placement.py: bafybeibvc37n2cluep4tasvgmvwxwne2deais6ptirducpogk67v4gj4ga
tests/states/test_blacklising.py: bafybeihm2ex6l7fhorgi3mjj2epztu2r7bqbg56unpgpzfzymghshchqzy
tests/states/test_check_benchmarking.py: bafybeifwpi5f4fhreqptfxdsnyv3nptkqytkwbukfuqkrjo4eww7cv3sxy
tests/states/test_claim_subscription.py: bafybeiclkxjhceb3ehgmg6klt4uywew5drk5b3w6no7mwxetpubxqrejfy
tests/states/test_decision_receive.py: bafybeifj7rwqyzfcvnqqhbo25pl2jppgjqnqldadwku5wl2tpskfj2zwxq
tests/states/test_decision_request.py: bafybeigqbakm2olkwvcngertjplhnmu6on6tp6hxn7lxygi2gf5a5eurbe
tests/states/test_final_states.py: bafybeiftfd3ovaqpfe7t5ry7maiziavk74wl66d6zo6ikhgodznormd2nm
tests/states/test_handle_failed_tx.py: bafybeibuepj6fko7ba3bef6nybzetilni2iwgkxd5xeazqskadbad3l2zq
tests/states/test_order_subscription.py: bafybeiag37uk5mitjm4yadcpr67icfmmzeucdlgvqoct4y4jeyhnebyyoi
tests/states/test_randomness.py: bafybeib3eqjv6mhlprzda7d4viddn5alrfqteq6juyg3ccejseoywcsbey
tests/states/test_redeem.py: bafybeiezdnfrxukb2xpwffrr357g2anmdkwy7wo3nphvlggipq5xrdzr7a
tests/states/test_sampling.py: bafybeibyglipxdl6f25qfxf36v2n3kckrpmwyuqcenfeqzhjujpwon6o2u
tests/states/test_tool_selection.py: bafybeib7js3dj7647t33o5ybfqftwytxktwrvhbri5yuyymg6znj6y7xxa
tests/test_dialogues.py: bafybeibulo64tgfrq4e5qbcqnmifrlehkqciwuavublints353zaj2mlpa
tests/test_handlers.py: bafybeihpkgtjjm3uegpup6zkznpoaxqpu6kmp3ujiggrzbe73p5fzlq7im
tests/test_payloads.py: bafybeifc2os3orozmsxbrcfp4c4vrweojo6g4ebxinr5ilescraw6qm6sa
tests/test_rounds.py: bafybeigifftusd4ew42tyvyrr55o2uehhcik2gdq3atkpjwwlqdeskedty
utils/__init__.py: bafybeiazrfg3kwfdl5q45azwz6b6mobqxngxpf4hazmrnkhinpk4qhbbf4
utils/nevermined.py: bafybeigallaqxhqopznhjhefr6bukh4ojkz5vdtqyzod5dksshrf24fjgi
utils/scaling.py: bafybeialr3z4zogp4k3l2bzcjfi4igvxzjexmlpgze2bai2ufc3plaow4y
Expand Down
252 changes: 252 additions & 0 deletions packages/valory/skills/decision_maker_abci/tests/states/test_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023-2024 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This package contains the tests for Decision Maker"""

import json
from unittest.mock import MagicMock, patch

import pytest

from packages.valory.skills.decision_maker_abci.policy import (
AccuracyInfo,
EGreedyPolicy,
)
from packages.valory.skills.decision_maker_abci.states.base import (
Event,
SynchronizedData,
TxPreparationRound,
)


class MechMetadata:
"""The class for test of Mech Data"""

def __init__(self, request_id: str, data: str) -> None:
"""Initialize MechMetadata with request ID and data."""
self.request_id = request_id
self.data = data


@pytest.fixture
def mocked_db() -> MagicMock:
"""Fixture to mock the database."""
return MagicMock()


@pytest.fixture
def sync_data(mocked_db: MagicMock) -> SynchronizedData:
"""Fixture for SynchronizedData."""
return SynchronizedData(db=mocked_db)


def test_sampled_bet_index(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the sampled_bet_index property."""
mocked_db.get_strict.return_value = "5"
assert sync_data.sampled_bet_index == 5
mocked_db.get_strict.assert_called_once_with("sampled_bet_index")


def test_is_mech_price_set(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the is_mech_price_set property."""
mocked_db.get.return_value = True
assert sync_data.is_mech_price_set is True
mocked_db.get.assert_called_once_with("mech_price", False)


def test_available_mech_tools(
sync_data: SynchronizedData, mocked_db: MagicMock
) -> None:
"""Test the available_mech_tools property."""
mocked_db.get_strict.return_value = '["tool1", "tool2"]'
assert sync_data.available_mech_tools == ["tool1", "tool2"]
mocked_db.get_strict.assert_called_once_with("available_mech_tools")


def test_is_policy_set(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the is_policy_set property."""
mocked_db.get.return_value = True
assert sync_data.is_policy_set is True
mocked_db.get.assert_called_once_with("policy", False)


def test_has_tool_selection_run(
sync_data: SynchronizedData, mocked_db: MagicMock
) -> None:
"""Test the has_tool_selection_run property."""
mocked_db.get.return_value = "tool1"
assert sync_data.has_tool_selection_run is True
mocked_db.get.assert_called_once_with("mech_tool", None)


def test_mech_tool(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the mech_tool property."""
mocked_db.get_strict.return_value = "tool1"
assert sync_data.mech_tool == "tool1"
mocked_db.get_strict.assert_called_once_with("mech_tool")


def test_utilized_tools(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the utilized_tools property."""
mocked_db.get_strict.return_value = '{"tx1": "tool1"}'
assert sync_data.utilized_tools == {"tx1": "tool1"}
mocked_db.get_strict.assert_called_once_with("utilized_tools")


def test_redeemed_condition_ids(
sync_data: SynchronizedData, mocked_db: MagicMock
) -> None:
"""Test the redeemed_condition_ids property."""
mocked_db.get.return_value = '["cond1", "cond2"]'
assert sync_data.redeemed_condition_ids == {"cond1", "cond2"}
mocked_db.get.assert_called_once_with("redeemed_condition_ids", None)


def test_payout_so_far(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the payout_so_far property."""
mocked_db.get.return_value = "100"
assert sync_data.payout_so_far == 100
mocked_db.get.assert_called_once_with("payout_so_far", None)


def test_vote(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the vote property."""
mocked_db.get_strict.return_value = "1"
assert sync_data.vote == 1
mocked_db.get_strict.assert_called_once_with("vote")


def test_confidence(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the confidence property."""
mocked_db.get_strict.return_value = "0.9"
assert sync_data.confidence == 0.9
mocked_db.get_strict.assert_called_once_with("confidence")


def test_bet_amount(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the bet_amount property."""
mocked_db.get_strict.return_value = "50"
assert sync_data.bet_amount == 50
mocked_db.get_strict.assert_called_once_with("bet_amount")


def test_is_profitable(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the is_profitable property."""
mocked_db.get_strict.return_value = True
assert sync_data.is_profitable is True
mocked_db.get_strict.assert_called_once_with("is_profitable")


def test_tx_submitter(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the tx_submitter property."""
mocked_db.get_strict.return_value = "submitter1"
assert sync_data.tx_submitter == "submitter1"
mocked_db.get_strict.assert_called_once_with("tx_submitter")


@patch("packages.valory.skills.decision_maker_abci.policy.EGreedyPolicy.deserialize")
def test_policy_property(
mock_deserialize: MagicMock, sync_data: SynchronizedData, mocked_db: MagicMock
) -> None:
"""Test for policy property"""
mock_policy_serialized = "serialized_policy_string"
mocked_db.get_strict.return_value = mock_policy_serialized

expected_policy = EGreedyPolicy(eps=0.1)
mock_deserialize.return_value = expected_policy

result = sync_data.policy

mocked_db.get_strict.assert_called_once_with("policy")
mock_deserialize.assert_called_once_with(mock_policy_serialized)
assert result == expected_policy


def test_mech_requests(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the mech_requests property."""
mocked_db.get.return_value = '[{"request_id": "1", "data": "request_data"}]'
requests = json.loads(mocked_db.get.return_value)

mech_requests = [
MechMetadata(request_id=item["request_id"], data=item["data"])
for item in requests
]

assert len(mech_requests) == 1
assert isinstance(mech_requests[0], MechMetadata)
assert mech_requests[0].request_id == "1"


def test_weighted_accuracy(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the weighted_accuracy property."""
selected_mech_tool = "tool1"
policy_db_name = "policy"
policy_mock = EGreedyPolicy(
eps=0.1, accuracy_store={selected_mech_tool: AccuracyInfo(requests=1)}
).serialize()
mocked_db.get_strict = lambda name: (
policy_mock if name == policy_db_name else selected_mech_tool
)
policy = EGreedyPolicy.deserialize(policy_mock)
assert selected_mech_tool in policy.weighted_accuracy
assert sync_data.weighted_accuracy == policy.weighted_accuracy[selected_mech_tool]


def test_mech_responses(sync_data: SynchronizedData, mocked_db: MagicMock) -> None:
"""Test the mech_responses property."""

# Mock the response with empty dictionaries to avoid field mismatches
mocked_db.get.return_value = "[{}, {}]"

# Access the mech_responses property
responses = sync_data.mech_responses

# Validate the responses length
assert len(responses) == 2

# Test when db.get() returns None
mocked_db.get.return_value = None
responses = sync_data.mech_responses
assert responses == []

# Test when db.get() returns an empty list
mocked_db.get.return_value = "[]"
responses = sync_data.mech_responses
assert responses == []


def test_end_block(mocked_db: MagicMock) -> None:
"""Test the end_block logic in TxPreparationRound."""
mocked_sync_data = MagicMock(spec=SynchronizedData)
mock_context = MagicMock()
round_instance = TxPreparationRound(
synchronized_data=mocked_sync_data, context=mock_context
)

with patch.object(
TxPreparationRound, "end_block", return_value=(mocked_sync_data, Event.DONE)
):
result = round_instance.end_block()
assert result == (mocked_sync_data, Event.DONE)

with patch.object(
TxPreparationRound, "end_block", return_value=(mocked_sync_data, Event.NONE)
):
result = round_instance.end_block()
assert result == (mocked_sync_data, Event.NONE)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2023-2024 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This package contains the tests for Decision Maker"""

from typing import Any, Dict

import pytest

from packages.valory.skills.decision_maker_abci.states.base import Event
from packages.valory.skills.decision_maker_abci.states.bet_placement import (
BetPlacementRound,
)


@pytest.fixture
def bet_placement_round() -> BetPlacementRound:
"""Fixture to set up a BetPlacementRound instance for testing."""
synchronized_data: Dict[str, Any] = {} # Added type annotation
context: Dict[str, Any] = {} # Added type annotation
return BetPlacementRound(synchronized_data, context)


def test_initial_event(bet_placement_round: BetPlacementRound) -> None:
"""Test that the initial event is set correctly."""
assert bet_placement_round.none_event == Event.INSUFFICIENT_BALANCE
Loading
Loading