Skip to content

Commit

Permalink
11 oct update fds_tx done. tests done till test_pay
Browse files Browse the repository at this point in the history
  • Loading branch information
Aviksaikat committed Oct 12, 2023
1 parent 41a7f0d commit 868582a
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 24 deletions.
20 changes: 10 additions & 10 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ readme = "README.md"
packages = [{ include = "fds", from = "src" }]

[tool.poetry.dependencies]
python = "^3.9" # ">=3.8,<3.12"
eth-ape = "*"
python = "^3.9" # ">=3.9,<3.12"
eth-ape = "^0.6.22"
ape-alchemy = "*"
ape-solidity = "*"
ape-foundry = "*"
Expand Down
4 changes: 2 additions & 2 deletions src/fds/fds_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ def at(self, address: AddressType, abi: AbiType = None) -> ContractInstance:
return self.contract

def deploy(
self, contract: ContractContainer, *args, publish: bool = False, **kwargs
self, _contract: ContractContainer, *args, publish: bool = False, **kwargs
) -> ContractInstance:
if not self.account:
raise AccountNotFoundException("Account has not been set up yet.")

self.contract = self.account.deploy(contract, *args, publish=publish, **kwargs)
self.contract = self.account.deploy(_contract, *args, publish=publish, **kwargs)
self.contract_address = self.contract.address # type: ignore
return self.contract

Expand Down
123 changes: 113 additions & 10 deletions src/fds/fds_tx.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
from typing import Optional, Tuple, Union

from ape import chain, networks
from ape.api.accounts import AccountAPI
from ape.api.transactions import ReceiptAPI
from ape.contracts.base import ContractContainer, ContractInstance
from ape.types import AddressType
from ape.types import AddressType, MessageSignature
from eth_account import Account
from eth_account.messages import encode_defunct

# * fds libraries
from fds.fds_contract import FDSContract
from fds.utils.types import AbiType
from fds.utils.Exceptions import InaccessibleGatewayException
from fds.utils.types import VRS, AbiType


class Tx:
def __init__(self, account: AccountAPI):
self.account = account
self.contract_address = None

def getContract(
self, account: AccountAPI, address: AddressType, abi: AbiType
) -> ContractInstance:
self.account = account
def getContract(self, address: AddressType, abi: AbiType) -> ContractInstance:
# self.account = account
self.address = address
self.abi = abi

Expand All @@ -24,11 +30,11 @@ def getContract(
def deployContract(
self, contract: ContractContainer, *args, publish: bool = False, **kwargs
) -> ContractInstance:
contract = FDSContract(self.account) # type: ignore
fdscontract = FDSContract(self.account) # type: ignore

return contract.deploy(contract=contract, *args, publish=publish, **kwargs)
return fdscontract.deploy(contract=contract, *args, publish=publish, **kwargs)

def syncNonce(self, account: AccountAPI) -> int:
def syncNonce(self) -> int:
"""
Sync the nonce with the account. This is used to prevent
accidental re - authenticating a user when they change their account.
Expand All @@ -37,6 +43,103 @@ def syncNonce(self, account: AccountAPI) -> int:
@return The nonce
"""
self.account = account
# self.account = account

return self.account.nonce

def pay(
self, to_address: AddressType, amount: Union[str, int, None] = None, gas: int = 6000000
) -> ReceiptAPI:
"""
https://docs.apeworx.io/ape/stable/userguides/transactions.html
Another way to use a static-fee transaction (without having to
provide gas_price) is to set the key-value argument type equal to 0x00.
When declaring type="0x0" and not specifying a gas_price, the gas_price
gets set using the provider's estimation.
"""

self.web3 = networks.provider._web3
# self.account = account
self.to_address = to_address
self.gas = gas

# * convert the value to int using ape's converter
if not isinstance(amount, int):
amount = chain.conversion_manager.convert(amount, int)

# Estimate the gas required for the transaction
required_gas_price = self.web3.eth.estimate_gas(
{
"to": self.to_address,
"from": self.account.address,
"value": amount,
}
)

# Calculate the actual gas price
actual_gas_price = self.web3.eth.gas_price * required_gas_price
if self.gas < actual_gas_price:
self.gas = actual_gas_price

# Calculate the value to be sent subtracting the gas
value = int(amount) - self.gas # type: ignore

tx_params = {
"to": self.to_address,
"from": self.account.address,
# * letting ape to set the best gas price
# "gas": self.web3.eth.gas_price,
# "gasPrice": self.gas,
"value": value,
"data": "",
}

txn = networks.ecosystem.create_transaction(**tx_params) # type: ignore

receipt = self.account.call(txn)

return receipt

def getBalace(self, address: AddressType) -> int:
self.web3 = networks.provider._web3

try:
return self.web3.eth.get_balance(address)
except Exception as e:
raise InaccessibleGatewayException("Can't access gateway", e)

def getBlockNumber(self):
"""
Returns the latest block number of the ecosystem
"""
return networks.provider.get_block("latest")

def sign(
self, account: AddressType, message: str, key: Optional[str] = None
) -> MessageSignature:
"""
https://eth-account.readthedocs.io/en/stable/eth_account.html#eth_account.account.Account.sign_message
"""
self.account = account # type: ignore
self.key = key
message_hash = encode_defunct(text=message)
if key:
signed_message = self.account.sign_message(message_hash, key) # type: ignore
return signed_message.signature # type: ignore

signed_message = self.account.sign_message(message_hash) # type: ignore
return signed_message.signature # type: ignore

def recover(
self, message: str, signature: str, vrs: Optional[Tuple[VRS, VRS, VRS]] = None
) -> AddressType:
"""
https://eth-account.readthedocs.io/en/stable/eth_account.html#eth_account.account.Account.recover_message
"""
message_hash = encode_defunct(text=message)
if vrs:
signer = Account.recover_message(message_hash, vrs=vrs)
return signer

signer = Account.recover_message(message_hash, signature=signature)
return signer
5 changes: 5 additions & 0 deletions src/fds/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ def test_accounts(accounts):
return accounts.test_accounts


@pytest.fixture
def vitalik(accounts):
return accounts["0xab5801a7d398351b8be11c439e05c5b3259aec9b"]


@pytest.fixture(scope="session")
def owner(test_accounts):
return test_accounts[0]
Expand Down
60 changes: 60 additions & 0 deletions src/fds/tests/unit_tests/test_fds_tx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from fds.fds_tx import Tx


def test_get_contract(sender, owner, solidity_contract_instance, ABI):
contract_address = solidity_contract_instance.address

tx_object = Tx(owner)
deployed_contract = tx_object.getContract(sender, contract_address, ABI)

assert deployed_contract.address == contract_address
assert deployed_contract.balance == 0
assert deployed_contract.owner() == owner

# * test contract method
assert deployed_contract.getCount() == 0
deployed_contract.increment(sender=sender)
assert deployed_contract.count() == 1


def test_deploy_contract(owner, solidity_contract_container):
tx_object = Tx(owner)
contract = tx_object.deployContract(solidity_contract_container)

assert contract.balance == 0
assert contract.owner() == owner

# * test contract method
assert contract.getCount() == 0
contract.increment(sender=owner)
assert contract.count() == 1


def test_syncNonce(sender, receiver):
tx_object = Tx(sender)

nonce = tx_object.syncNonce()

assert isinstance(nonce, int)

assert nonce == 0

# if it's possible send some tx to increase eth balance
# print(sender.balance)
sender.transfer(receiver, "10 ether")

assert tx_object.syncNonce() > 0


def test_pay(sender, receiver):
tx_object = Tx(sender)

receipt = tx_object.pay(
to_address=receiver.address,
amount="0.1 ether",
)

assert receipt
assert receipt.data.hex() == "0x"
# assert receipt.to == receiver.address
assert receipt.gas_used > 0
4 changes: 4 additions & 0 deletions src/fds/utils/Exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ class NotImplementedException(Exception):

class ContractNotFoundException(Exception):
pass


class InaccessibleGatewayException(Exception):
pass
5 changes: 5 additions & 0 deletions src/fds/utils/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@
from ape.types import ABI

AbiType = Optional[Union[List[ABI], Dict, str, Path]]
# * vrs (tuple(v, r, s), each element is hex str, bytes or int) – the three
# * pieces generated by an elliptic curve signature
# * https://eth-account.readthedocs.io/en/stable/eth_account.html\
# * eth_account.account.Account.recover_message
VRS = Union[str, bytes, int]

0 comments on commit 868582a

Please sign in to comment.