diff --git a/.github/images/cardano-govtool-header.png b/.github/images/cardano-govtool-header.png new file mode 100644 index 000000000..f7a7ded9c Binary files /dev/null and b/.github/images/cardano-govtool-header.png differ diff --git a/.github/workflows/build-and-deploy-test-stack.yml b/.github/workflows/build-and-deploy-test-stack.yml index e735e2c0c..a4d3c07a2 100644 --- a/.github/workflows/build-and-deploy-test-stack.yml +++ b/.github/workflows/build-and-deploy-test-stack.yml @@ -75,3 +75,4 @@ jobs: USERSNAP_SPACE_API_KEY: ${{ secrets.USERSNAP_SPACE_API_KEY }} APP_ENV: test PDF_API_URL: ${{ secrets.PDF_API_URL }} + KUBER_API_KEY: ${{secrets.KUBER_API_KEY}} diff --git a/.gitignore b/.gitignore index 05f1097fb..ccd0c617b 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,7 @@ node_storage/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb - +.venv ### Windows ### # Windows thumbnail cache files Thumbs.db diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec1ed241..9981d01ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,26 @@ changes. - +## [sancho-v1.0.18](https://github.com/IntersectMBO/govtool/releases/tag/sancho-v1.0.18) 2024-09-12 + +### Added + +- Add script for Matomo Analytics [Issue 1817](https://github.com/IntersectMBO/govtool/issues/1817) + +### Fixed + +- Correctly show all kinds of votes in the modal showing vote numbers [Issue 1941](https://github.com/IntersectMBO/govtool/issues/1941) +- Fixed terms and conditions link [Issue 1968](https://github.com/IntersectMBO/govtool/issues/1968) +- Hide Delegate button in DRep list and details if user has already delegated to this DRep [Issue 1982](https://github.com/IntersectMBO/govtool/issues/1982) +- Fix condition for disabling voting on different GA types [Issue 2008](https://github.com/IntersectMBO/govtool/issues/2008) +- Fix incorrect copy (ex. github) to (e.g. github) [Issue 1748](https://github.com/IntersectMBO/govtool/issues/1748) + +### Changed + +- Bump @intersect.mbo/pdf-ui to v0.3.9 +- Changed misleading text for direct voter registration [Issue 1976](https://github.com/IntersectMBO/govtool/issues/1976) +- Change base repo README header image to have correct branding and reflect mainnet launch. + ## [sancho-v1.0.17](https://github.com/IntersectMBO/govtool/releases/tag/sancho-v1.0.17) 2024-09-05 ### Added diff --git a/README.md b/README.md index 8a8aea50b..db07e081c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
- +
@@ -24,7 +24,7 @@ The Cardano GovTool enables ada holders to experience the governance features de
#### Mainnet
-- _Coming soon_
+- [gov.tools](https://gov.tools/)
#### SanchoNet
diff --git a/gov-action-loader/backend/.env.example b/gov-action-loader/backend/.env.example
index 8997b5adf..d0868c32f 100644
--- a/gov-action-loader/backend/.env.example
+++ b/gov-action-loader/backend/.env.example
@@ -1,2 +1,4 @@
-KUBER_API_URL=https://sanchonet.kuber.cardanoapi.io
+KUBER_API_URL_SANCHO=https://sanchonet.kuber.cardanoapi.io
+KUBER_API_URL_PREVIEW=https://preview.kuber.cardanoapi.io
+KUBER_API_URL_PREPROD=https://preprod.kuber.cardanoapi.io
KUBER_API_KEY=xxxxxxxxxxxxx
diff --git a/gov-action-loader/backend/app/data/gov-script.plutus b/gov-action-loader/backend/app/data/gov-script.plutus
new file mode 100644
index 000000000..b6af50acb
--- /dev/null
+++ b/gov-action-loader/backend/app/data/gov-script.plutus
@@ -0,0 +1,5 @@
+{
+ "type": "PlutusScriptV3",
+ "description": "",
+ "cborHex": "5908545908510101003232323232323232323232323232323232323232323232323232323232323232323232323232323232259323255333573466e1d20000011180098111bab357426ae88d55cf00104554ccd5cd19b87480100044600422c6aae74004dd51aba1357446ae88d55cf1baa3255333573466e1d200a35573a002226ae84d5d11aab9e00111637546ae84d5d11aba235573c6ea800642b26006003149a2c8a4c301f801c0052000c00e0070018016006901e4070c00e003000c00d20d00fc000c0003003800a4005801c00e003002c00d20c09a0c80e1801c006001801a4101b5881380018000600700148013003801c006005801a410100078001801c006001801a4101001f8001800060070014801b0038018096007001800600690404002600060001801c0052008c00e006025801c006001801a41209d8001800060070014802b003801c006005801a410112f501c3003800c00300348202b7881300030000c00e00290066007003800c00b003482032ad7b806038403060070014803b00380180960003003800a4021801c00e003002c00d20f40380e1801c006001801a41403f800100a0c00e0029009600f0030078040c00e002900a600f003800c00b003301a483403e01a600700180060066034904801e00060001801c0052016c01e00600f801c006001801980c2402900e30000c00e002901060070030128060c00e00290116007003800c00b003483c0ba03860070018006006906432e00040283003800a40498003003800a404d802c00e00f003800c00b003301a480cb0003003800c003003301a4802b00030001801c01e0070018016006603490605c0160006007001800600660349048276000600030000c00e0029014600b003801c00c04b003800c00300348203a2489b00030001801c00e006025801c006001801a4101b11dc2df80018000c0003003800a4055802c00e007003012c00e003000c00d2080b8b872c000c0006007003801809600700180060069040607e4155016000600030000c00e00290166007003012c00e003000c00d2080c001c000c0003003800a405d801c00e003002c00d20c80180e1801c006001801a412007800100a0c00e00290186007003013c0006007001480cb005801801e006003801800e00600500403003800a4069802c00c00f003001c00c007003803c00e003002c00c05300333023480692028c0004014c00c00b003003c00c00f003003c00e00f003800c00b00301480590052008003003800a406d801c00e003002c00d2000c00d2006c00060070018006006900a600060001801c0052038c00e007001801600690006006901260003003800c003003483281300020141801c005203ac00e006027801c006001801a403d800180006007001480f3003801804e00700180060069040404af3c4e302600060001801c005203ec00e006013801c006001801a4101416f0fd20b80018000600700148103003801c006005801a403501c3003800c0030034812b00030000c00e0029021600f003800c00a01ac00e003000c00ccc08d20d00f4800b00030000c0000000000803c00c016008401e006009801c006001801807e0060298000c000401e006007801c0060018018074020c000400e00f003800c00b003010c000802180020070018006006019801805e0003000400600580180760060138000800c00b00330134805200c400e00300080330004006005801a4001801a410112f58000801c00600901260008019806a40118002007001800600690404a75ee01e00060008018046000801801e000300c4832004c025201430094800a0030028052003002c00d2002c000300648010c0092002300748028c0312000300b48018c0292012300948008c0212066801a40018000c0192008300a2233335573e00250002801994004d55ce800cd55cf0008d5d08014c00cd5d10011263009222532900389800a4d2219002912c80344c01526910c80148964cc04cdd68010034564cc03801400626601800e0071801226601800e01518010096400a3000910c008600444002600244004a664600200244246466004460044460040064600444600200646a660080080066a00600224446600644b20051800484ccc02600244666ae68cdc3801000c00200500a91199ab9a33710004003000801488ccd5cd19b89002001800400a44666ae68cdc4801000c00a00122333573466e20008006005000912a999ab9a3371200400222002220052255333573466e2400800444008440040026eb400a42660080026eb000a4264666015001229002914801c8954ccd5cd19b8700400211333573466e1c00c006001002118011229002914801c88cc044cdc100200099b82002003245200522900391199ab9a3371066e08010004cdc1001001c002004403245200522900391199ab9a3371266e08010004cdc1001001c00a00048a400a45200722333573466e20cdc100200099b820020038014000912c99807001000c40062004912c99807001000c400a2002001199919ab9a357466ae880048cc028dd69aba1003375a6ae84008d5d1000934000dd60010a40064666ae68d5d1800c0020052225933006003357420031330050023574400318010600a444aa666ae68cdc3a400000222c22aa666ae68cdc4000a4000226600666e05200000233702900000088994004cdc2001800ccdc20010008cc010008004c01088954ccd5cd19b87480000044400844cc00c004cdc300100091119803112c800c60012219002911919806912c800c4c02401a442b26600a004019130040018c008002590028c804c8888888800d1900991111111002a244b267201722222222008001000c600518000001112a999ab9a3370e004002230001155333573466e240080044600823002229002914801c88ccd5cd19b893370400800266e0800800e00100208c8c0040048c0088cc008008005"
+}
\ No newline at end of file
diff --git a/gov-action-loader/backend/app/funds.py b/gov-action-loader/backend/app/funds.py
index 6899ddc3c..015f0c511 100644
--- a/gov-action-loader/backend/app/funds.py
+++ b/gov-action-loader/backend/app/funds.py
@@ -2,13 +2,15 @@
from fastapi import HTTPException
+from app.network import get_api_url
from app.settings import settings
from app.transaction import (default_proposal_deposit_ada, main_wallet,
submit_tx)
-async def get_ada_balance(address, client):
- utxo_url = settings.kuber_api_url + "/api/v3/utxo"
+async def get_ada_balance(address, client, network):
+
+ utxo_url = get_api_url(network) + "/api/v3/utxo"
kuber_response = await client.get(
utxo_url,
params={"address": address},
@@ -25,8 +27,8 @@ async def get_ada_balance(address, client):
)
-async def get_protocol_params(client):
- pParamsQuery = settings.kuber_api_url + "/api/v3/protocol-params"
+async def get_protocol_params(client, network):
+ pParamsQuery = get_api_url(network) + "/api/v3/protocol-params"
kuber_response = await client.get(
pParamsQuery,
headers={"api-key": settings.kuber_api_key},
@@ -41,7 +43,7 @@ async def get_protocol_params(client):
async def check_balance_and_fund_wallets(
- wallets, supported_proposals_in_single_tx, per_proposal_deposit, client
+ wallets, supported_proposals_in_single_tx, per_proposal_deposit, client, network
):
wallets_with_balance = await asyncio.gather(
*[get_ada_balance(wallet["address"], client) for wallet in wallets]
@@ -82,7 +84,7 @@ async def check_balance_and_fund_wallets(
for wallet in low_balance_wallets
],
}
- tx = await submit_tx(fund_from_main_tx, client)
+ tx = await submit_tx(fund_from_main_tx, client, network)
raise HTTPException(
status_code=412,
detail="This action required multiple transaction and wallet setup TX:"
diff --git a/gov-action-loader/backend/app/main.py b/gov-action-loader/backend/app/main.py
index 268ee1b3d..34bd1ffbc 100644
--- a/gov-action-loader/backend/app/main.py
+++ b/gov-action-loader/backend/app/main.py
@@ -1,21 +1,24 @@
import asyncio
import json
import math
-from typing import Any, Dict
+from typing import Any, Dict, Literal
import httpx
-from fastapi import Depends, FastAPI, HTTPException
+from fastapi import Depends, FastAPI, HTTPException, Header
from app.cors import add_cors
from app.funds import (check_balance_and_fund_wallets, get_ada_balance,
get_protocol_params)
from app.http_utils import get_client
from app.models import MultipleProposal
+from app.network import get_api_url
from app.settings import settings
from app.transaction import (get_base_proposal_for_multiple,
get_default_transaction,
get_proposal_data_from_type,
- main_wallet, submit_proposal_tx)
+ main_wallet, submit_proposal_tx,
+ get_gov_script
+ )
app = FastAPI()
add_cors(app)
@@ -25,13 +28,14 @@
async def submit_multiple_proposals(
multi_proposal: MultipleProposal,
client: httpx.AsyncClient = Depends(get_client),
+ network: str = Header(...)
):
required_proposals = multi_proposal.no_of_proposals
base_proposal = get_base_proposal_for_multiple()
supported_proposals_in_single_tx = 50
maximum_supported_proposals = 10000
- pparams = await get_protocol_params(client)
+ pparams = await get_protocol_params(client, network)
if required_proposals <= supported_proposals_in_single_tx:
tx = await submit_proposal_tx(
@@ -40,6 +44,7 @@ async def submit_multiple_proposals(
| get_proposal_data_from_type(multi_proposal.proposal_type, pparams),
required_proposals,
client,
+ network
)
return [{"proposal_count": required_proposals, "tx_hash": tx}]
elif required_proposals <= maximum_supported_proposals:
@@ -73,6 +78,7 @@ async def submit_multiple_proposals(
supported_proposals_in_single_tx,
per_proposal_deposit,
client,
+ network
)
proposals_numbers_in_last_tx = (
@@ -93,6 +99,7 @@ async def submit_multiple_proposals(
if wallet != required_wallets[-1]
else proposals_numbers_in_last_tx,
client,
+ network
)
for wallet in required_wallets
]
@@ -111,21 +118,26 @@ async def submit_multiple_proposals(
@app.get("/api/balance")
-async def getWalletBalance(client: httpx.AsyncClient = Depends(get_client)):
- return await get_ada_balance(main_wallet["address"], client)
+async def getWalletBalance(client: httpx.AsyncClient = Depends(get_client),
+ network: str = Header(...)):
+ return await get_ada_balance(main_wallet["address"], client, network)
@app.post("/api/load/single")
async def submit_single_proposal(
proposal: Dict[str, Any],
client: httpx.AsyncClient = Depends(get_client),
+ network: str = Header(...)
):
default_transaction = get_default_transaction()
default_proposal_data = default_transaction["proposals"][0]
combined_proposal = default_proposal_data | proposal
default_transaction["proposals"][0] = combined_proposal
- tx_url = settings.kuber_api_url + "/api/v1/tx?submit=true"
+ if "withdraw" in combined_proposal or "parameterupdate" in combined_proposal:
+ if combined_proposal["script"]["cborHex"] == "":
+ combined_proposal["script"] = get_gov_script()
+ tx_url = get_api_url(network) + "/api/v1/tx?submit=true"
kuber_response = await client.post(
tx_url,
json=default_transaction,
@@ -141,22 +153,3 @@ async def submit_single_proposal(
raise HTTPException(
status_code=kuber_response.status_code, detail=kuber_response.text
)
-
-
-@app.get("/api/blockfrost/transaction/{tx_hash}")
-async def get_transaction_from_blockfrost(
- tx_hash: str,
- client: httpx.AsyncClient = Depends(get_client),
-):
- tx_url = settings.blockfrost_api_url + "/txs/" + tx_hash
- tx_response = await client.get(
- tx_url,
- headers={"project_id": settings.blockfrost_project_id},
- )
- if tx_response.status_code == 200:
- return tx_response.json()
- else:
- print(tx_response.text)
- raise HTTPException(
- status_code=tx_response.status_code, detail=tx_response.json()
- )
diff --git a/gov-action-loader/backend/app/models.py b/gov-action-loader/backend/app/models.py
index 8a124a885..01bfa4c09 100644
--- a/gov-action-loader/backend/app/models.py
+++ b/gov-action-loader/backend/app/models.py
@@ -4,3 +4,4 @@
class MultipleProposal(BaseModel):
proposal_type: str
no_of_proposals: int
+
\ No newline at end of file
diff --git a/gov-action-loader/backend/app/network.py b/gov-action-loader/backend/app/network.py
new file mode 100644
index 000000000..5c9fbaa84
--- /dev/null
+++ b/gov-action-loader/backend/app/network.py
@@ -0,0 +1,15 @@
+from fastapi import HTTPException
+from app.settings import settings
+
+NETWORKS = {"preview", "preprod", "sanchonet"}
+
+def get_api_url (network):
+ network_l_case = network.lower()
+ if network_l_case == "sancho" or network_l_case == "sanchonet":
+ return settings.kuber_api_url_sancho
+ elif network_l_case == "preview":
+ return settings.kuber_api_url_preview
+ elif network_l_case == "preprod":
+ return settings.kuber_api_url_preprod
+ else:
+ raise HTTPException(status_code=400, detail=f"Invalid network: {network}. Expected: {NETWORKS}")
\ No newline at end of file
diff --git a/gov-action-loader/backend/app/settings.py b/gov-action-loader/backend/app/settings.py
index 02095e720..fa1eff63c 100644
--- a/gov-action-loader/backend/app/settings.py
+++ b/gov-action-loader/backend/app/settings.py
@@ -2,7 +2,9 @@
class Settings(BaseSettings):
- kuber_api_url: str
+ kuber_api_url_sancho: str
+ kuber_api_url_preview: str
+ kuber_api_url_preprod: str
kuber_api_key: str = ''
settings = Settings()
diff --git a/gov-action-loader/backend/app/transaction.py b/gov-action-loader/backend/app/transaction.py
index 8b980f5aa..0d9027960 100644
--- a/gov-action-loader/backend/app/transaction.py
+++ b/gov-action-loader/backend/app/transaction.py
@@ -10,6 +10,7 @@
from fastapi import HTTPException
+from app.network import get_api_url
from app.settings import settings
main_wallet = {
@@ -90,12 +91,16 @@ def generate_quorom():
return {"numerator": numerator, "denominator": denomintor}
-def generate_hardfork():
- majorProtocolNum = random.randint(1, 9)
- minorProtocolNum = random.randint(1, 9)
+def generate_hardfork(current_pParams):
+ protocol_version = [current_pParams["protocolVersion"]["major"],
+ current_pParams["protocolVersion"]["minor"]]
+
+ # Randomly select an index (0 for major, 1 for minor) and increment it
+ protocol_version[random.randint(0, 1)] += 1
+
return {
"hardfork": {
- "protocolVersion": {"major": majorProtocolNum, "minor": minorProtocolNum}
+ "protocolVersion": {"major": protocol_version[0], "minor": protocol_version[1]}
}
}
@@ -115,6 +120,13 @@ def change_pp_value(random_parameter):
return random_parameter
+def get_gov_script():
+ with open("app/data/gov-script.plutus", "r") as script_file:
+ script_str = script_file.read()
+ script_json = json.loads(script_str)
+ return script_json
+
+
def get_proposal_data_from_type(proposal_type, current_pParams):
match proposal_type:
case "constitution":
@@ -126,7 +138,10 @@ def get_proposal_data_from_type(proposal_type, current_pParams):
}
case "withdrawal":
number_of_addresses = random.randint(1, 5)
- return {"withdraw": generate_withdraw(number_of_addresses)}
+ return {
+ "script": get_gov_script(),
+ "withdraw": generate_withdraw(number_of_addresses),
+ }
case "no-confidence":
return {"noconfidence": True}
case "update-committee":
@@ -138,7 +153,7 @@ def get_proposal_data_from_type(proposal_type, current_pParams):
}
}
case "hardfork":
- return generate_hardfork()
+ return generate_hardfork(current_pParams)
case "update-parameters":
# read current protocol parameters from json
# get one of the keys of the pp
@@ -146,7 +161,10 @@ def get_proposal_data_from_type(proposal_type, current_pParams):
keys = pParams.keys()
rand_key = random.choice(filter_updatable_paramKeys(list(keys)))
# recurse into the innermost element of that key and change it by +-1, all protocol parameter value is either float or int
- return {"parameterupdate": {rand_key: change_pp_value(pParams[rand_key])}}
+ return {
+ "script": get_gov_script(),
+ "parameterupdate": {rand_key: change_pp_value(pParams[rand_key])},
+ }
# added_proposal =
case "info":
return {}
@@ -160,17 +178,42 @@ def get_proposal_data_from_type(proposal_type, current_pParams):
def filter_updatable_paramKeys(keys):
- updatable_keys = {"maxBlockSize", "maxBBSize", "maxTxSize", "maxBHSize", "keyDeposit", "poolDeposit", "eMax",
- "nOpt", "a0", "rho", "tau", "minPoolCost", "coinsPerUTxOByte", "costModels", "prices",
- "maxTxExUnits", "maxBlockExUnits", "maxValSize", "collateralPercentage", "maxCollateralInputs",
- "poolVotingThresholds", "dRepVotingThresholds", "committeeMinSize", "committeeMaxTermLength",
- "govActionLifetime", "govActionDeposit", "dRepDeposit", "dRepActivity"}
+ updatable_keys = {
+ "maxBlockSize",
+ "maxBBSize",
+ "maxTxSize",
+ "maxBHSize",
+ "keyDeposit",
+ "poolDeposit",
+ "eMax",
+ "nOpt",
+ "a0",
+ "rho",
+ "tau",
+ "minPoolCost",
+ "coinsPerUTxOByte",
+ "costModels",
+ "prices",
+ "maxTxExUnits",
+ "maxBlockExUnits",
+ "maxValSize",
+ "collateralPercentage",
+ "maxCollateralInputs",
+ "poolVotingThresholds",
+ "dRepVotingThresholds",
+ "committeeMinSize",
+ "committeeMaxTermLength",
+ "govActionLifetime",
+ "govActionDeposit",
+ "dRepDeposit",
+ "dRepActivity",
+ }
return [x for x in keys if x in updatable_keys]
-async def submit_tx(tx, client, submit=True):
+async def submit_tx(tx, client, network, submit=True):
submit_query = "?submit=true" if submit else ""
- tx_url = settings.kuber_api_url + "/api/v1/tx" + submit_query
+ tx_url = get_api_url(network) + "/api/v1/tx" + submit_query
response = await client.post(
tx_url,
json=tx,
@@ -187,7 +230,7 @@ async def submit_tx(tx, client, submit=True):
raise HTTPException(status_code=response.status_code, detail=response.text)
-async def submit_proposal_tx(wallet, proposal, proposal_numbers, client):
+async def submit_proposal_tx(wallet, proposal, proposal_numbers, client, network):
proposals = [
{
**proposal,
@@ -202,4 +245,4 @@ async def submit_proposal_tx(wallet, proposal, proposal_numbers, client):
"selections": [wallet["address"], wallet["skey"]],
"proposals": proposals,
}
- return await submit_tx(tx, client)
+ return await submit_tx(tx, client, network)
diff --git a/gov-action-loader/frontend/src/App.vue b/gov-action-loader/frontend/src/App.vue
index 4a1d5a291..889b6c540 100644
--- a/gov-action-loader/frontend/src/App.vue
+++ b/gov-action-loader/frontend/src/App.vue
@@ -7,7 +7,8 @@