Skip to content

Commit

Permalink
Conversion tool: added parser and merge parser for FTMScan explorer.
Browse files Browse the repository at this point in the history
  • Loading branch information
nanonano committed Jan 20, 2024
1 parent f50c3c8 commit da9b963
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
- Yoroi parser: added new export format.
- Coinbase parser: added "Staking Income" and "Advance Trade Buy/Sell" transaction types.
- Binance parser: added additional operation types for Statements. ([#319](https://github.com/BittyTax/BittyTax/issues/319))
- Conversion tool: added parser for FTMScan explorer. ([#262](https://github.com/BittyTax/BittyTax/issues/262))
- Conversion tool: added merge parser for FTMScan. ([#262](https://github.com/BittyTax/BittyTax/issues/262))
### Changed
- Conversion tool: openpyxl use read-only mode. ([#337](https://github.com/BittyTax/BittyTax/issues/337))
- Accounting tool: openpyxl use read-only mode. ([#337](https://github.com/BittyTax/BittyTax/issues/337))
Expand Down
2 changes: 1 addition & 1 deletion src/bittytax/conv/mergers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from . import bscscan, etherscan, hecoinfo, polygonscan, snowtrace
from . import bscscan, etherscan, ftmscan, hecoinfo, polygonscan, snowtrace
51 changes: 51 additions & 0 deletions src/bittytax/conv/mergers/ftmscan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# (c) Nano Nano Ltd 2024

from typing import TYPE_CHECKING, Dict, List

from ...bt_types import FileId
from ..datamerge import DataMerge, ParserRequired
from ..out_record import TransactionOutRecord
from ..parsers.etherscan import etherscan_nfts, etherscan_tokens
from ..parsers.ftmscan import WALLET, WORKSHEET_NAME, ftm_int, ftm_txns
from .etherscan import INTERNAL_TXNS, NFTS, TOKENS, TXNS, _do_merge_etherscan

STAKE_ADDRESSES: List[str] = []

if TYPE_CHECKING:
from ..datafile import DataFile


def merge_ftmscan(data_files: Dict[FileId, "DataFile"]) -> bool:
# Do same merge as Etherscan
merge = _do_merge_etherscan(data_files, STAKE_ADDRESSES)

if merge:
# Change Etherscan parsers to FTMScan
if TOKENS in data_files:
data_files[TOKENS].parser.worksheet_name = WORKSHEET_NAME
for data_row in data_files[TOKENS].data_rows:
if data_row.t_record:
address = data_row.t_record.wallet[-abs(TransactionOutRecord.WALLET_ADDR_LEN) :]
data_row.t_record.wallet = f"{WALLET}-{address}"

if NFTS in data_files:
data_files[NFTS].parser.worksheet_name = WORKSHEET_NAME
for data_row in data_files[NFTS].data_rows:
if data_row.t_record:
address = data_row.t_record.wallet[-abs(TransactionOutRecord.WALLET_ADDR_LEN) :]
data_row.t_record.wallet = f"{WALLET}-{address}"

return merge


DataMerge(
"FTMScan fees & multi-token transactions",
{
TXNS: {"req": ParserRequired.MANDATORY, "obj": ftm_txns},
TOKENS: {"req": ParserRequired.OPTIONAL, "obj": etherscan_tokens},
NFTS: {"req": ParserRequired.OPTIONAL, "obj": etherscan_nfts},
INTERNAL_TXNS: {"req": ParserRequired.OPTIONAL, "obj": ftm_int},
},
merge_ftmscan,
)
1 change: 1 addition & 0 deletions src/bittytax/conv/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
electrum,
etherscan,
exodus,
ftmscan,
ftx,
gatehub,
gemini,
Expand Down
198 changes: 198 additions & 0 deletions src/bittytax/conv/parsers/ftmscan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
# (c) Nano Nano Ltd 2024

from decimal import Decimal
from typing import TYPE_CHECKING

from typing_extensions import Unpack

from ...bt_types import TrType
from ..dataparser import DataParser, ParserArgs, ParserType
from ..out_record import TransactionOutRecord
from .etherscan import _get_note

if TYPE_CHECKING:
from ..datarow import DataRow

WALLET = "Fantom Chain"
WORKSHEET_NAME = "FTMScan"


def parse_ftmscan(data_row: "DataRow", _parser: DataParser, **_kwargs: Unpack[ParserArgs]) -> None:
row_dict = data_row.row_dict
data_row.timestamp = DataParser.parse_timestamp(int(row_dict["UnixTimestamp"]))

if row_dict["Status"] != "":
# Failed transactions should not have a Value_OUT
row_dict["Value_OUT(FTM)"] = "0"

if Decimal(row_dict["Value_IN(FTM)"]) > 0:
if row_dict["Status"] == "":
data_row.t_record = TransactionOutRecord(
TrType.DEPOSIT,
data_row.timestamp,
buy_quantity=Decimal(row_dict["Value_IN(FTM)"]),
buy_asset="FTM",
wallet=_get_wallet(row_dict["To"]),
note=_get_note(row_dict),
)
elif Decimal(row_dict["Value_OUT(FTM)"]) > 0:
data_row.t_record = TransactionOutRecord(
TrType.WITHDRAWAL,
data_row.timestamp,
sell_quantity=Decimal(row_dict["Value_OUT(FTM)"]),
sell_asset="FTM",
fee_quantity=Decimal(row_dict["TxnFee(FTM)"]),
fee_asset="FTM",
wallet=_get_wallet(row_dict["From"]),
note=_get_note(row_dict),
)
else:
data_row.t_record = TransactionOutRecord(
TrType.SPEND,
data_row.timestamp,
sell_quantity=Decimal(row_dict["Value_OUT(FTM)"]),
sell_asset="FTM",
fee_quantity=Decimal(row_dict["TxnFee(FTM)"]),
fee_asset="FTM",
wallet=_get_wallet(row_dict["From"]),
note=_get_note(row_dict),
)


def _get_wallet(address: str) -> str:
return f"{WALLET}-{address.lower()[0 : TransactionOutRecord.WALLET_ADDR_LEN]}"


def parse_ftmscan_internal(
data_row: "DataRow", _parser: DataParser, **_kwargs: Unpack[ParserArgs]
) -> None:
row_dict = data_row.row_dict
data_row.timestamp = DataParser.parse_timestamp(int(row_dict["UnixTimestamp"]))

# Failed internal transaction
if row_dict["Status"] != "0":
return

if Decimal(row_dict["Value_IN(FTM)"]) > 0:
data_row.t_record = TransactionOutRecord(
TrType.DEPOSIT,
data_row.timestamp,
buy_quantity=Decimal(row_dict["Value_IN(FTM)"]),
buy_asset="FTM",
wallet=_get_wallet(row_dict["TxTo"]),
)
elif Decimal(row_dict["Value_OUT(FTM)"]) > 0:
data_row.t_record = TransactionOutRecord(
TrType.WITHDRAWAL,
data_row.timestamp,
sell_quantity=Decimal(row_dict["Value_OUT(FTM)"]),
sell_asset="FTM",
wallet=_get_wallet(row_dict["From"]),
)


# Token and NFT transactions have the same header as Etherscan
ftm_txns = DataParser(
ParserType.EXPLORER,
"FTMScan (FTM Transactions)",
[
"Txhash",
"Blockno",
"UnixTimestamp",
"DateTime (UTC)",
"From",
"To",
"ContractAddress",
"Value_IN(FTM)",
"Value_OUT(FTM)",
None,
"TxnFee(FTM)",
"TxnFee(USD)",
"Historical $Price/FTM",
"Status",
"ErrCode",
"Method",
],
worksheet_name=WORKSHEET_NAME,
row_handler=parse_ftmscan,
)

DataParser(
ParserType.EXPLORER,
"FTMScan (FTM Transactions)",
[
"Txhash",
"Blockno",
"UnixTimestamp",
"DateTime (UTC)",
"From",
"To",
"ContractAddress",
"Value_IN(FTM)",
"Value_OUT(FTM)",
None,
"TxnFee(FTM)",
"TxnFee(USD)",
"Historical $Price/FTM",
"Status",
"ErrCode",
"Method",
"PrivateNote",
],
worksheet_name=WORKSHEET_NAME,
row_handler=parse_ftmscan,
)

ftm_int = DataParser(
ParserType.EXPLORER,
"FTMScan (FTM Internal Transactions)",
[
"Txhash",
"Blockno",
"UnixTimestamp",
"DateTime (UTC)",
"ParentTxFrom",
"ParentTxTo",
"ParentTxFTM_Value",
"From",
"TxTo",
"ContractAddress",
"Value_IN(FTM)",
"Value_OUT(FTM)",
None,
"Historical $Price/FTM",
"Status",
"ErrCode",
"Type",
],
worksheet_name=WORKSHEET_NAME,
row_handler=parse_ftmscan_internal,
)

DataParser(
ParserType.EXPLORER,
"FTMScan (FTM Internal Transactions)",
[
"Txhash",
"Blockno",
"UnixTimestamp",
"DateTime (UTC)",
"ParentTxFrom",
"ParentTxTo",
"ParentTxFTM_Value",
"From",
"TxTo",
"ContractAddress",
"Value_IN(FTM)",
"Value_OUT(FTM)",
None,
"Historical $Price/FTM",
"Status",
"ErrCode",
"Type",
"PrivateNote",
],
worksheet_name=WORKSHEET_NAME,
row_handler=parse_ftmscan_internal,
)

0 comments on commit da9b963

Please sign in to comment.