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

feat(behaviour): multisend. #7

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions packages/packages.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"dev": {
"contract/valory/erc20/0.1.0": "bafybeibyhuxozkkvsymqz45vxixr3w3bs4vsvxg2nsx5jlyi3f3hhn7ezi",
"skill/valory/learning_abci/0.1.0": "bafybeih5m372dqrdfy7w6iah4m5qfgf3j5t7ihsgorzyzhax5srm32wmim",
"skill/valory/learning_chained_abci/0.1.0": "bafybeihu6uvvdnfntgluhrbxqus5malsit36xqsyxv46wjqsmuodadarpm",
"agent/valory/learning_agent/0.1.0": "bafybeiatizepnj42umexk3p5rnop67cdshbgrflvnysaagm3t4xqo74kem",
"service/valory/learning_service/0.1.0": "bafybeig34lg3wztyj7ecsbyb6qj7ryf34gnpr2cuoz3akjvgdsh3pejboq"
"skill/valory/learning_abci/0.1.0": "bafybeibzpjp2ib3bw2ee6nmvmgcv3hcoeohiutn43xa7m3edzqj5u3pgrq",
"skill/valory/learning_chained_abci/0.1.0": "bafybeihgij3dbus4bujhrbyw2hurtmj634u6pt7pyxaigcd6ziu2khcnda",
"agent/valory/learning_agent/0.1.0": "bafybeibci4xjzacxokypqn4fa2ujvay74la2zunzw2ex63yzqdsevqloie",
"service/valory/learning_service/0.1.0": "bafybeicjheswjgwvmvpzkyl5ed3jd5h6hlyngeb5trcxdt7tnkjpm2agsi"
},
"third_party": {
"protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi",
Expand Down
20 changes: 17 additions & 3 deletions packages/valory/agents/learning_agent/aea-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contracts:
- valory/gnosis_safe_proxy_factory:0.1.0:bafybeicpcpyurm7gxir2gnlsgzeirzomkhcbnzr5txk67zdf4mmg737rtu
- valory/multisend:0.1.0:bafybeig5byt5urg2d2bsecufxe5ql7f4mezg3mekfleeh32nmuusx66p4y
- valory/service_registry:0.1.0:bafybeihafe524ilngwzavkhwz4er56p7nyar26lfm7lrksfiqvvzo3kdcq
- valory/erc20:0.1.0:bafybeibyhuxozkkvsymqz45vxixr3w3bs4vsvxg2nsx5jlyi3f3hhn7ezi
- valory/erc20:0.1.0:bafybeif45jxexpcncirooh7gtzqg4g5lxunsdq7opzzw2maojv435rwzxi
protocols:
- open_aea/signing:1.0.0:bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi
- valory/abci:0.1.0:bafybeiaqmp7kocbfdboksayeqhkbrynvlfzsx4uy4x6nohywnmaig4an7u
Expand All @@ -32,8 +32,8 @@ protocols:
skills:
- valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu
- valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm
- valory/learning_abci:0.1.0:bafybeih5m372dqrdfy7w6iah4m5qfgf3j5t7ihsgorzyzhax5srm32wmim
- valory/learning_chained_abci:0.1.0:bafybeihu6uvvdnfntgluhrbxqus5malsit36xqsyxv46wjqsmuodadarpm
- valory/learning_abci:0.1.0:bafybeibzpjp2ib3bw2ee6nmvmgcv3hcoeohiutn43xa7m3edzqj5u3pgrq
- valory/learning_chained_abci:0.1.0:bafybeihgij3dbus4bujhrbyw2hurtmj634u6pt7pyxaigcd6ziu2khcnda
- valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey
- valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq
- valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi
Expand Down Expand Up @@ -219,3 +219,17 @@ models:
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
olas_eth_pair_price_specs:
args:
api_id: coingecko
headers:
Accept: application/json
method: GET
parameters:
ids: autonolas
vs_currencies: eth
x_cg_demo_api_key: ${str:null}
response_key: autonolas
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
12 changes: 12 additions & 0 deletions packages/valory/contracts/erc20/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ def check_balance(
wallet_balance = ledger_api.api.eth.get_balance(account)
return dict(token=token_balance, wallet=wallet_balance)

@classmethod
def total_supply(
cls,
ledger_api: EthereumApi,
contract_address: str,
) -> JSONLike:
"""Check the total supply of the OLAS token."""
contract_instance = cls.get_instance(ledger_api, contract_address)
total_supply = getattr(contract_instance.functions, "totalSupply") # noqa
token_total_supply = total_supply().call()
return dict(total_supply=token_total_supply)

@classmethod
def get_allowance(
cls,
Expand Down
2 changes: 1 addition & 1 deletion packages/valory/contracts/erc20/contract.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fingerprint:
README.md: bafybeifmfma6rglvpa22odtozyosnp5mwljum64utxip2wgmezuhnjjjyi
__init__.py: bafybeif5vpc3dfrlxlch7brbhmdwksabyzddpfqgm56vdbbkek3t3br6ke
build/ERC20.json: bafybeiemn5b5nszuss7xj6lmvmjuendltp6wz7ubihdvd7c6wqw4bohbpa
contract.py: bafybeidewckxjj6vaz7sphkpnjgmsyhi7v7hijaz44yu7vbsnv3znqj3wm
contract.py: bafybeieuyig4vjnpxli4ir36x765zat57lanjboaw72ylzwobsyvwnjjva
fingerprint_ignore_patterns: []
contracts: []
class_name: ERC20
Expand Down
58 changes: 57 additions & 1 deletion packages/valory/services/learning_service/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: bafybeid42pdrf6qrohedylj4ijrss236ai6geqgf3he44huowiuf7pl464
fingerprint_ignore_patterns: []
agent: valory/learning_agent:0.1.0:bafybeiatizepnj42umexk3p5rnop67cdshbgrflvnysaagm3t4xqo74kem
agent: valory/learning_agent:0.1.0:bafybeibci4xjzacxokypqn4fa2ujvay74la2zunzw2ex63yzqdsevqloie
number_of_agents: 4
deployment:
agent:
Expand Down Expand Up @@ -108,6 +108,20 @@ extra:
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
olas_eth_pair_price_specs:
args:
api_id: coingecko
headers:
Accept: application/json
method: GET
parameters:
ids: autonolas
vs_currencies: eth
x_cg_demo_api_key: ${str:null}
response_key: autonolas
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
1:
models:
benchmark_tool:
Expand Down Expand Up @@ -169,6 +183,20 @@ extra:
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
olas_eth_pair_price_specs:
args:
api_id: coingecko
headers:
Accept: application/json
method: GET
parameters:
ids: autonolas
vs_currencies: eth
x_cg_demo_api_key: ${str:null}
response_key: autonolas
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
2:
models:
benchmark_tool:
Expand Down Expand Up @@ -230,6 +258,20 @@ extra:
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
olas_eth_pair_price_specs:
args:
api_id: coingecko
headers:
Accept: application/json
method: GET
parameters:
ids: autonolas
vs_currencies: eth
x_cg_demo_api_key: ${str:null}
response_key: autonolas
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
3:
models:
benchmark_tool:
Expand Down Expand Up @@ -291,6 +333,20 @@ extra:
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
olas_eth_pair_price_specs:
args:
api_id: coingecko
headers:
Accept: application/json
method: GET
parameters:
ids: autonolas
vs_currencies: eth
x_cg_demo_api_key: ${str:null}
response_key: autonolas
response_type: dict
retries: 5
url: https://api.coingecko.com/api/v3/simple/price
---
public_id: valory/ledger:0.19.0
type: connection
Expand Down
132 changes: 129 additions & 3 deletions packages/valory/skills/learning_abci/behaviours.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
)
from packages.valory.skills.abstract_round_abci.io_.store import SupportedFiletype
from packages.valory.skills.learning_abci.models import (
CoingeckoETHSpecs,
CoingeckoSpecs,
Params,
SharedState,
Expand All @@ -53,6 +54,7 @@
TxPreparationPayload,
)
from packages.valory.skills.learning_abci.rounds import (
DataPullOlasEthPriceRound,
DataPullRound,
DecisionMakingRound,
Event,
Expand Down Expand Up @@ -100,6 +102,11 @@ def coingecko_specs(self) -> CoingeckoSpecs:
"""Get the Coingecko api specs."""
return self.context.coingecko_specs

@property
def olas_eth_pair_price_specs(self) -> CoingeckoETHSpecs:
"""Get the Coingecko api specs."""
return self.context.olas_eth_pair_price_specs

@property
def metadata_filepath(self) -> str:
"""Get the temporary filepath to the metadata."""
Expand Down Expand Up @@ -141,14 +148,18 @@ def async_act(self) -> Generator:
# Get the token balance
erc20_balance = yield from self.get_erc20_balance()

# Get the token total supply
total_supply = yield from self.get_erc20_total_supply()

# Prepare the payload to be shared with other agents
# After consensus, all the agents will have the same price, price_ipfs_hash and balance variables in their synchronized data
# After consensus, all the agents will have the same price, price_ipfs_hash, balance and total_supply variables in their synchronized data
payload = DataPullPayload(
sender=sender,
price=price,
price_ipfs_hash=price_ipfs_hash,
native_balance=native_balance,
erc20_balance=erc20_balance,
erc20_total_supply=total_supply,
)

# Send the payload to all agents and mark the behaviour as done
Expand Down Expand Up @@ -252,6 +263,44 @@ def get_erc20_balance(self) -> Generator[None, None, Optional[float]]:
)
return balance

def get_erc20_total_supply(self) -> Generator[None, None, Optional[float]]:
"""Get ERC20 total supply"""
self.context.logger.info(
f"Getting Olas total supply {self.params.olas_token_address}"
)

# Use the contract api to interact with the ERC20 contract
response_msg = yield from self.get_contract_api_response(
performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, # type: ignore
contract_address=self.params.olas_token_address,
contract_id=str(ERC20.contract_id),
contract_callable="total_supply",
chain_id=GNOSIS_CHAIN_ID,
)

# Check that the response is what we expect
if response_msg.performative != ContractApiMessage.Performative.RAW_TRANSACTION:
self.context.logger.error(
f"Error while retrieving the balance: {response_msg}"
)
return None

total_supply = response_msg.raw_transaction.body.get("total_supply", None)

# Ensure that the balance is not None
if total_supply is None:
self.context.logger.error(
f"Error while retrieving the total supply: {response_msg}"
)
return None

total_supply = total_supply / 10**18 # from wei

self.context.logger.info(
f"OLAS {self.params.olas_token_address} has total supply of {total_supply} token"
)
return total_supply

def get_native_balance(self) -> Generator[None, None, Optional[float]]:
"""Get the native balance"""
self.context.logger.info(
Expand Down Expand Up @@ -279,6 +328,80 @@ def get_native_balance(self) -> Generator[None, None, Optional[float]]:
return balance


class DataPull2Behaviour(LearningBaseBehaviour): # pylint: disable=too-many-ancestors
"""This behaviours pulls OLAS/ETH pair price from API endpoints"""

matching_round: Type[AbstractRound] = DataPullOlasEthPriceRound

def async_act(self) -> Generator:

with self.context.benchmark_tool.measure(self.behaviour_id).local():
sender = self.context.agent_address

# Get OLAS/ETH Price from API
price_from_api = yield from self.get_token_price_specs()
self.context.logger.info(f"price_from_api: {price_from_api}")

# Store the OLAS/ETH price in IPFS
eth_price_ipfs_hash = yield from self.send_price_to_ipfs(price_from_api)
self.context.logger.info(f"price_ipfs_hash: {eth_price_ipfs_hash}")

# Read the OLAS/ETH price from IPFS
price_read_from_ipfs = yield from self.get_price_from_ipfs(eth_price_ipfs_hash)
self.context.logger.info(f"price_read_from_ipfs: {price_read_from_ipfs}")

payload = DataPullPayload(
sender=sender,
price=price_read_from_ipfs.get('olas_eth_price'),
price_ipfs_hash=eth_price_ipfs_hash,
native_balance=0,
erc20_balance=0,
erc20_total_supply=0
)

with self.context.benchmark_tool.measure(self.behaviour_id).consensus():
yield from self.send_a2a_transaction(payload)
yield from self.wait_until_round_end()

self.set_done()

def get_token_price_specs(self) -> Generator[None, None, Optional[float]]:
"""Get token price from Coingecko using ApiSpecs"""

# Get the specs
specs = self.olas_eth_pair_price_specs.get_spec()

# Make the call
raw_response = yield from self.get_http_response(**specs)

# Process the response
response = self.olas_eth_pair_price_specs.process_response(raw_response)

# Get the price
price = response.get("eth", None)
self.context.logger.info(f"Got token eth price from Coingecko: {price}")
return price

def send_price_to_ipfs(self, price) -> Generator[None, None, Optional[str]]:
"""Store the olas/eth price in IPFS"""
data = {"olas_eth_price": price}
price_ipfs_hash = yield from self.send_to_ipfs(
filename=self.metadata_filepath, obj=data, filetype=SupportedFiletype.JSON
)
self.context.logger.info(
f"OLAS/ETH Price data stored in IPFS: https://gateway.autonolas.tech/ipfs/{price_ipfs_hash}"
)
return price_ipfs_hash

def get_price_from_ipfs(self, ipfs_hash : str) -> Generator[None, None, Optional[dict]]:
"""Load the price data from IPFS"""
price = yield from self.get_from_ipfs(
ipfs_hash=ipfs_hash, filetype=SupportedFiletype.JSON
)
self.context.logger.error(f"Got price from IPFS: {price}")
return price


class DecisionMakingBehaviour(
LearningBaseBehaviour
): # pylint: disable=too-many-ancestors
Expand Down Expand Up @@ -337,6 +460,8 @@ def get_next_event(self) -> Generator[None, None, str]:
)
return Event.ERROR.value

return Event.TRANSACT.value

# Make a decision based on the balance's last number
last_number = int(str(native_balance)[-1])

Expand Down Expand Up @@ -399,7 +524,7 @@ def async_act(self) -> Generator:
sender = self.context.agent_address

# Get the transaction hash
tx_hash = yield from self.get_tx_hash()
tx_hash = yield from self.get_multisend_safe_tx_hash()

payload = TxPreparationPayload(
sender=sender, tx_submitter=self.auto_behaviour_id(), tx_hash=tx_hash
Expand Down Expand Up @@ -493,7 +618,7 @@ def get_erc20_transfer_data(self) -> Generator[None, None, Optional[str]]:
contract_id=str(ERC20.contract_id),
contract_callable="build_transfer_tx",
recipient=self.params.transfer_target_address,
amount=1,
amount=10000000000000000000,
chain_id=GNOSIS_CHAIN_ID,
)

Expand Down Expand Up @@ -658,6 +783,7 @@ class LearningRoundBehaviour(AbstractRoundBehaviour):
abci_app_cls = LearningAbciApp # type: ignore
behaviours: Set[Type[BaseBehaviour]] = [ # type: ignore
DataPullBehaviour,
DataPull2Behaviour,
DecisionMakingBehaviour,
TxPreparationBehaviour,
]
Loading