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: add support pinata.cloud and infura.io IPFS storage #195

Merged
merged 3 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion tests/internal/test_ipfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def test_fetch_cid_status_from_ipfs():
status = fetch_cid_status_from_ipfs("bafkreigvk6oenx6mp4mca4at4znujzgljywcfghuvrcxxkhye5b7ghutbm")
assert status == 200
status = fetch_cid_status_from_ipfs("bafkreigdsodbw6dlajnk7xyudw52cutzioovt7r7mrdf3t3cx7xfzz3eou")
assert status == 404
assert status == 404 or status == 504 # depends on service


def test_upload_vote_ipfs_description():
Expand Down
38 changes: 35 additions & 3 deletions utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ def get_deployer_account() -> Union[LocalAccount, Account]:
return accounts.load(os.environ["DEPLOYER"]) if (is_live or "DEPLOYER" in os.environ) else accounts[4]


def get_web3_storage_token() -> str:
def get_web3_storage_token(silent=False) -> str:
is_live = get_is_live()
if is_live and "WEB3_STORAGE_TOKEN" not in os.environ:
if is_live and not silent and "WEB3_STORAGE_TOKEN" not in os.environ:
raise EnvironmentError(
"Please set WEB3_STORAGE_TOKEN env variable to the web3.storage API token to be able to "
"upload the vote description to IPFS by calling upload_vote_ipfs_description. Alternatively, "
Expand All @@ -73,6 +73,38 @@ def get_web3_storage_token() -> str:
return os.environ["WEB3_STORAGE_TOKEN"] if (is_live or "WEB3_STORAGE_TOKEN" in os.environ) else ""


def get_pinata_cloud_token(silent=False) -> str:
is_live = get_is_live()
if is_live and not silent and "PINATA_CLOUD_TOKEN" not in os.environ:
raise EnvironmentError(
"Please set PINATA_CLOUD_TOKEN env variable to the pinata.cloud API token to be able to "
"upload the vote description to IPFS by calling upload_vote_ipfs_description. Alternatively, "
"you can only calculate cid without uploading to IPFS by calling calculate_vote_ipfs_description"
)

return os.environ["PINATA_CLOUD_TOKEN"] if (is_live or "PINATA_CLOUD_TOKEN" in os.environ) else ""


def get_infura_io_keys(silent=False) -> Tuple[str, str]:
is_live = get_is_live()
if is_live and not silent and (
"WEB3_INFURA_IPFS_PROJECT_ID" not in os.environ or "WEB3_INFURA_IPFS_PROJECT_SECRET" not in os.environ
):
raise EnvironmentError(
"Please set WEB3_INFURA_IPFS_PROJECT_ID and WEB3_INFURA_IPFS_PROJECT_SECRET env variable "
"to the web3.storage api token"
)
project_id = (
os.environ["WEB3_INFURA_IPFS_PROJECT_ID"] if (is_live or "WEB3_INFURA_IPFS_PROJECT_ID" in os.environ) else ""
)
project_secret = (
os.environ["WEB3_INFURA_IPFS_PROJECT_SECRET"]
if (is_live or "WEB3_INFURA_IPFS_PROJECT_SECRET" in os.environ)
else ""
)
return project_id, project_secret


def prompt_bool() -> Optional[bool]:
choice = input().lower()
if choice in {"yes", "y"}:
Expand Down Expand Up @@ -227,7 +259,7 @@ def dai_token(self) -> interface.ERC20:
@property
def usdt_token(self) -> interface.ERC20:
return interface.ERC20(USDT_TOKEN)

@property
def usdc_token(self) -> interface.ERC20:
return interface.ERC20(USDC_TOKEN)
Expand Down
43 changes: 41 additions & 2 deletions utils/ipfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import requests
from typing import Tuple, TypedDict
from os import linesep
import json

from ipfs_cid import cid_sha256_hash

from utils.config import get_web3_storage_token
from utils.config import get_pinata_cloud_token, get_infura_io_keys, get_web3_storage_token
from utils.checksummed_address import checksum_verify

# https://github.com/multiformats/multibase/blob/master/multibase.csv
Expand Down Expand Up @@ -48,6 +49,41 @@ class IPFSUploadResult(TypedDict):
messages: list[Tuple[str, str]]


# alternative for upload_str_to_web3_storage
def _upload_str_to_infura_io(text: str) -> str:
text_bytes = text.encode("utf-8")
text_file = io.BytesIO(text_bytes)
files = {"file": text_file}
(projectId, projectSecret) = get_infura_io_keys()

endpoint = "https://ipfs.infura.io:5001"

response = requests.post(endpoint + "/api/v0/add?cid-version=1", files=files, auth=(projectId, projectSecret))
response.raise_for_status()
response_json = response.json()

return response_json.get("Hash")


# alternative for upload_str_to_web3_storage
def _upload_str_to_pinata_cloud(text: str) -> str:
text_bytes = text.encode("utf-8")
text_file = io.BytesIO(text_bytes)
files = {"file": text_file}
pinata_cloud_token = get_pinata_cloud_token()

endpoint = "https://api.pinata.cloud"

pinata_options = {"cidVersion": 1, "wrapWithDirectory": False}
payload = {"pinataOptions": json.dumps(pinata_options, separators=(",", ":"))}

response = requests.post(endpoint + "/pinning/pinFileToIPFS", data=payload, files=files, auth=pinata_cloud_token)
response.raise_for_status()
response_json = response.json()

return response_json.get("IpfsHash")


# upload text to web3.storage ipfs
def _upload_str_to_web3_storage(text: str) -> str:
text_bytes = text.encode("utf-8")
Expand All @@ -65,6 +101,10 @@ def _upload_str_to_web3_storage(text: str) -> str:


def _upload_str_to_ipfs(text: str) -> str:
if get_pinata_cloud_token(silent=True):
return _upload_str_to_web3_storage(text)
BATMAH69 marked this conversation as resolved.
Show resolved Hide resolved
if get_infura_io_keys(silent=True):
return _upload_str_to_infura_io(text)
return _upload_str_to_web3_storage(text)


Expand Down Expand Up @@ -93,7 +133,6 @@ async def _fetch_cid_status_from_ipfs_async(cid: str) -> int:

request_urls = [
get_url_by_cid(cid), # faster for uploaded files
f"https://api.web3.storage/status/{cid}", # much faster for not uploaded files
]

async with aiohttp.ClientSession() as session:
Expand Down