diff --git a/README.md b/README.md index 34040c3..8891eda 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,11 @@ On hashes: There are two hashes. One for the actual terms of service file (never referred in the smart contracts) and one for the message (template-based) that users need to sign with their wallet. +## Deployments + +- *Polygon*: `0xbe1418df0bAd87577de1A41385F19c6e77312780` +- *Ethereum*: `0xd63c1bE9D8B56CCcD6fd2Dd9F9c030c6a9916f5F` (latest hash `b24acbfc1295902f1b26e0815e32e6edbdb67c0dacb9b378a556035f7f9b6c52`) + ## Getting started Install framework with Poetry: @@ -86,7 +91,7 @@ Using Foundry. Compile: ```shell -foundry up +foundryup forge build ``` @@ -109,7 +114,7 @@ forge create \ Save the address. Because Polygonscan is a hard mistress and tends to crash, verify manually: ```shell -export CONTRACT_ADDRESS=0xbe1418df0bAd87577de1A41385F19c6e77312780 +export CONTRACT_ADDRESS=0xbe1418df0bAd87577de1A41385F19c6e77312780 scripts/verify-deployment.sh ``` @@ -200,7 +205,7 @@ scripts/set-terms-of-service.sh - Run `python scripts/update.py` to change on-chain state and get a new hash - Update `lib/assets/tos/tos-map.js` -Example: +Example (Polygon): ```shell # TOS version will be automatically picked from the smart contract @@ -210,9 +215,20 @@ export CONTRACT_ADDRESS=0xbe1418df0bAd87577de1A41385F19c6e77312780 export JSON_RPC_POLYGON= export DEPLOY_PRIVATE_KEY= python scripts/update.py - ``` +Example (Ethereum): + +```shell +# TOS version will be automatically picked from the smart contract +poetry shell +export TOS_DATE=2024-03-20 +export CONTRACT_ADDRESS=0xd63c1bE9D8B56CCcD6fd2Dd9F9c030c6a9916f5F +export JSON_RPC_POLYGON= +export DEPLOY_PRIVATE_KEY= +python scripts/update-ethereum.py + + ## Deployment A deployment can be found on Polygon [0xbe1418df0bAd87577de1A41385F19c6e77312780](https://polygonscan.com/address/0xbe1418df0bAd87577de1A41385F19c6e77312780). diff --git a/scripts/deploy-ethereum.sh b/scripts/deploy-ethereum.sh new file mode 100755 index 0000000..08dc009 --- /dev/null +++ b/scripts/deploy-ethereum.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e +set -x +set -u + +forge create \ + --rpc-url $JSON_RPC_ETHEREUM \ + --private-key $DEPLOY_PRIVATE_KEY \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + --verify \ + src/TermsOfService.sol:TermsOfService diff --git a/scripts/update-ethereum.py b/scripts/update-ethereum.py new file mode 100755 index 0000000..d593fa3 --- /dev/null +++ b/scripts/update-ethereum.py @@ -0,0 +1,84 @@ +"""Update terms of service""" + +import os +import json +import sys +from pathlib import Path +from terms_of_service.acceptance_message import TRADING_STRATEGY_ACCEPTANCE_MESSAGE, get_signing_hash +from web3 import Web3, HTTPProvider +from web3.middleware import geth_poa_middleware, construct_sign_and_send_raw_middleware +from eth_account import Account + + +def get_abi_by_filename(fname: str) -> dict: + """Reads a embedded ABI file and returns it. + + Example:: + + abi = get_abi_by_filename("ERC20Mock.json") + + You are most likely interested in the keys `abi` and `bytecode` of the JSON file. + + Loaded ABI files are cache in in-process memory to speed up future loading. + + Any results are cached. + + :param web3: Web3 instance + :param fname: `JSON filename from supported contract lists `_. + :return: Full contract interface, including `bytecode`. + """ + + here = Path(__file__).resolve().parent + abi_path = here / ".." / "abi" / Path(fname) + with open(abi_path, "rt", encoding="utf-8") as f: + abi = json.load(f) + return abi["abi"] + + +assert os.environ.get("DEPLOY_PRIVATE_KEY"), "Set DEPLOY_PRIVATE_KEY env" +assert os.environ.get("JSON_RPC_ETHEREUM"), "Set JSON_RPC_ETHEREUM env" +assert os.environ.get("CONTRACT_ADDRESS"), "Set $CONTRACT_ADDRESS env" +assert os.environ.get("TOS_DATE"), "Set $TOS_DATE env" + +web3 = Web3(HTTPProvider(os.environ["JSON_RPC_ETHEREUM"])) +account = Account.from_key(os.environ["DEPLOY_PRIVATE_KEY"]) +web3.middleware_onion.add(construct_sign_and_send_raw_middleware(account)) +web3.middleware_onion.inject(geth_poa_middleware, layer=0) + +abi = get_abi_by_filename("TermsOfService.json") +Contract = web3.eth.contract(abi=abi) +contract = Contract(os.environ["CONTRACT_ADDRESS"]) + +current_version = contract.functions.latestTermsOfServiceVersion().call() +version = current_version + 1 + +date = os.environ["TOS_DATE"] +new_line_escaped_msg = TRADING_STRATEGY_ACCEPTANCE_MESSAGE.format( + version=version, + date=date, +) + +acceptance_message_hash = get_signing_hash(new_line_escaped_msg) +acceptance_message = f"{new_line_escaped_msg}" +terms_of_service_version = str(version) +gas = web3.eth.get_balance(account.address) / 10**18 + +new_line = "\n" +escaped_new_line = "\\n" + +print(f"Deployer: {account.address}") +print(f"Contract: {contract.address}") +print(f"Acceptance message: {acceptance_message.replace(new_line, escaped_new_line)}") +print(f"Acceptance hash: {acceptance_message_hash.hex()}") +print(f"Version: {version}") +print(f"Date: {date}") +print(f"Gas balance: {gas}") + +confirm = input("Confirm send tx [y/n] ") +if confirm != "y": + sys.exit(1) + +tx_hash = contract.functions.updateTermsOfService(version, acceptance_message_hash, acceptance_message).transact({"from": account.address}) +print("Confirming ", tx_hash.hex()) +web3.eth.wait_for_transaction_receipt(tx_hash) +