Spaces:
Sleeping
Sleeping
Upload 18 files
Browse files- app.py +50 -0
- polygon/README.txt +28 -0
- polygon/contracts/1_Storage.sol +29 -0
- polygon/contracts/2_Owner.sol +55 -0
- polygon/contracts/3_Ballot.sol +176 -0
- polygon/tests/Ballot_test.sol +28 -0
app.py
CHANGED
|
@@ -131,6 +131,11 @@ class VideoUploadNotification(BaseModel):
|
|
| 131 |
video_name: str
|
| 132 |
sha1sum: str
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
# --- AUTENTICACIÓN (SIMULADA) ---
|
| 135 |
def validate_google_token(token: str) -> Optional[Dict[str, Any]]:
|
| 136 |
"""
|
|
@@ -614,6 +619,51 @@ def send_video_upload_sms(notification: VideoUploadNotification):
|
|
| 614 |
logger.error(f"[VIDEO SMS] Error inesperat enviant SMS al validor: {e}")
|
| 615 |
raise HTTPException(status_code=500, detail="Error inesperat enviant SMS al validor")
|
| 616 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 617 |
@app.post("/api/compliance/record-decision")
|
| 618 |
def record_decision(decision: ValidatorDecision):
|
| 619 |
"""Registra decisión de validador"""
|
|
|
|
| 131 |
video_name: str
|
| 132 |
sha1sum: str
|
| 133 |
|
| 134 |
+
|
| 135 |
+
class LoginSMSRequest(BaseModel):
|
| 136 |
+
phone: str
|
| 137 |
+
code: str
|
| 138 |
+
|
| 139 |
# --- AUTENTICACIÓN (SIMULADA) ---
|
| 140 |
def validate_google_token(token: str) -> Optional[Dict[str, Any]]:
|
| 141 |
"""
|
|
|
|
| 619 |
logger.error(f"[VIDEO SMS] Error inesperat enviant SMS al validor: {e}")
|
| 620 |
raise HTTPException(status_code=500, detail="Error inesperat enviant SMS al validor")
|
| 621 |
|
| 622 |
+
|
| 623 |
+
@app.post("/api/notifications/login-sms")
|
| 624 |
+
def send_login_sms(req: LoginSMSRequest):
|
| 625 |
+
"""Envia un SMS de verificació de mòbil per al login des de demo.
|
| 626 |
+
|
| 627 |
+
Utilitza el webhook de Zapier (ZAPIER_WEBHOOK_CATCH) definit en aquest space.
|
| 628 |
+
El missatge no inclou el codi en pantalla; el codi s'envia només per SMS.
|
| 629 |
+
"""
|
| 630 |
+
|
| 631 |
+
zapier_url = os.getenv("ZAPIER_WEBHOOK_CATCH")
|
| 632 |
+
if not zapier_url:
|
| 633 |
+
logger.error("[LOGIN SMS] ZAPIER_WEBHOOK_CATCH no definit")
|
| 634 |
+
raise HTTPException(status_code=500, detail="ZAPIER webhook not configured")
|
| 635 |
+
|
| 636 |
+
normalized_phone = req.phone.strip().replace(" ", "")
|
| 637 |
+
if not normalized_phone.startswith("+"):
|
| 638 |
+
# Assumir prefix per defecte si no ve amb codi de país
|
| 639 |
+
normalized_phone = "+34" + normalized_phone
|
| 640 |
+
|
| 641 |
+
message = "El teu codi secret de validació és: ****" # No exposem el codi en text o logs
|
| 642 |
+
|
| 643 |
+
try:
|
| 644 |
+
import requests
|
| 645 |
+
|
| 646 |
+
payload = {
|
| 647 |
+
"phone": normalized_phone,
|
| 648 |
+
"message": message,
|
| 649 |
+
"code": req.code,
|
| 650 |
+
}
|
| 651 |
+
resp = requests.post(zapier_url, json=payload, timeout=10)
|
| 652 |
+
if not resp.ok:
|
| 653 |
+
logger.error(
|
| 654 |
+
f"[LOGIN SMS] Error HTTP enviant SMS de login: {resp.status_code} {resp.text}"
|
| 655 |
+
)
|
| 656 |
+
raise HTTPException(status_code=500, detail="Error enviant SMS de login")
|
| 657 |
+
|
| 658 |
+
logger.info(f"[LOGIN SMS] SMS de verificació enviat a {normalized_phone}")
|
| 659 |
+
return {"success": True}
|
| 660 |
+
|
| 661 |
+
except HTTPException:
|
| 662 |
+
raise
|
| 663 |
+
except Exception as e:
|
| 664 |
+
logger.error(f"[LOGIN SMS] Error inesperat enviant SMS de login: {e}")
|
| 665 |
+
raise HTTPException(status_code=500, detail="Error inesperat enviant SMS de login")
|
| 666 |
+
|
| 667 |
@app.post("/api/compliance/record-decision")
|
| 668 |
def record_decision(decision: ValidatorDecision):
|
| 669 |
"""Registra decisión de validador"""
|
polygon/README.txt
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
REMIX DEFAULT WORKSPACE
|
| 2 |
+
|
| 3 |
+
Remix default workspace is present when:
|
| 4 |
+
i. Remix loads for the very first time
|
| 5 |
+
ii. A new workspace is created with 'Default' template
|
| 6 |
+
iii. There are no files existing in the File Explorer
|
| 7 |
+
|
| 8 |
+
This workspace contains 3 directories:
|
| 9 |
+
|
| 10 |
+
1. 'contracts': Holds three contracts with increasing levels of complexity.
|
| 11 |
+
2. 'scripts': Contains four typescript files to deploy a contract. It is explained below.
|
| 12 |
+
3. 'tests': Contains one Solidity test file for 'Ballot' contract & one JS test file for 'Storage' contract.
|
| 13 |
+
|
| 14 |
+
SCRIPTS
|
| 15 |
+
|
| 16 |
+
The 'scripts' folder has four typescript files which help to deploy the 'Storage' contract using 'web3.js' and 'ethers.js' libraries.
|
| 17 |
+
|
| 18 |
+
For the deployment of any other contract, just update the contract name from 'Storage' to the desired contract and provide constructor arguments accordingly
|
| 19 |
+
in the file `deploy_with_ethers.ts` or `deploy_with_web3.ts`
|
| 20 |
+
|
| 21 |
+
In the 'tests' folder there is a script containing Mocha-Chai unit tests for 'Storage' contract.
|
| 22 |
+
|
| 23 |
+
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
|
| 24 |
+
Output from script will appear in remix terminal.
|
| 25 |
+
|
| 26 |
+
Please note, require/import is supported in a limited manner for Remix supported modules.
|
| 27 |
+
For now, modules supported by Remix are ethers, web3, swarmgw, chai, multihashes, remix and hardhat only for hardhat.ethers object/plugin.
|
| 28 |
+
For unsupported modules, an error like this will be thrown: '<module_name> module require is not supported by Remix IDE' will be shown.
|
polygon/contracts/1_Storage.sol
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: GPL-3.0
|
| 2 |
+
|
| 3 |
+
pragma solidity >=0.8.2 <0.9.0;
|
| 4 |
+
|
| 5 |
+
/**
|
| 6 |
+
* @title Storage
|
| 7 |
+
* @dev Store & retrieve value in a variable
|
| 8 |
+
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
|
| 9 |
+
*/
|
| 10 |
+
contract Storage {
|
| 11 |
+
|
| 12 |
+
uint256 number;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* @dev Store value in variable
|
| 16 |
+
* @param num value to store
|
| 17 |
+
*/
|
| 18 |
+
function store(uint256 num) public {
|
| 19 |
+
number = num;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* @dev Return value
|
| 24 |
+
* @return value of 'number'
|
| 25 |
+
*/
|
| 26 |
+
function retrieve() public view returns (uint256){
|
| 27 |
+
return number;
|
| 28 |
+
}
|
| 29 |
+
}
|
polygon/contracts/2_Owner.sol
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: GPL-3.0
|
| 2 |
+
|
| 3 |
+
pragma solidity >=0.7.0 <0.9.0;
|
| 4 |
+
|
| 5 |
+
import "hardhat/console.sol";
|
| 6 |
+
|
| 7 |
+
/**
|
| 8 |
+
* @title Owner
|
| 9 |
+
* @dev Set & change owner
|
| 10 |
+
*/
|
| 11 |
+
contract Owner {
|
| 12 |
+
|
| 13 |
+
address private owner;
|
| 14 |
+
|
| 15 |
+
// event for EVM logging
|
| 16 |
+
event OwnerSet(address indexed oldOwner, address indexed newOwner);
|
| 17 |
+
|
| 18 |
+
// modifier to check if caller is owner
|
| 19 |
+
modifier isOwner() {
|
| 20 |
+
// If the first argument of 'require' evaluates to 'false', execution terminates and all
|
| 21 |
+
// changes to the state and to Ether balances are reverted.
|
| 22 |
+
// This used to consume all gas in old EVM versions, but not anymore.
|
| 23 |
+
// It is often a good idea to use 'require' to check if functions are called correctly.
|
| 24 |
+
// As a second argument, you can also provide an explanation about what went wrong.
|
| 25 |
+
require(msg.sender == owner, "Caller is not owner");
|
| 26 |
+
_;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* @dev Set contract deployer as owner
|
| 31 |
+
*/
|
| 32 |
+
constructor() {
|
| 33 |
+
console.log("Owner contract deployed by:", msg.sender);
|
| 34 |
+
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
|
| 35 |
+
emit OwnerSet(address(0), owner);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* @dev Change owner
|
| 40 |
+
* @param newOwner address of new owner
|
| 41 |
+
*/
|
| 42 |
+
function changeOwner(address newOwner) public isOwner {
|
| 43 |
+
require(newOwner != address(0), "New owner should not be the zero address");
|
| 44 |
+
emit OwnerSet(owner, newOwner);
|
| 45 |
+
owner = newOwner;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* @dev Return owner address
|
| 50 |
+
* @return address of owner
|
| 51 |
+
*/
|
| 52 |
+
function getOwner() external view returns (address) {
|
| 53 |
+
return owner;
|
| 54 |
+
}
|
| 55 |
+
}
|
polygon/contracts/3_Ballot.sol
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: GPL-3.0
|
| 2 |
+
|
| 3 |
+
pragma solidity >=0.7.0 <0.9.0;
|
| 4 |
+
|
| 5 |
+
/**
|
| 6 |
+
* @title Ballot
|
| 7 |
+
* @dev Implements voting process along with vote delegation
|
| 8 |
+
*/
|
| 9 |
+
contract Ballot {
|
| 10 |
+
// This declares a new complex type which will
|
| 11 |
+
// be used for variables later.
|
| 12 |
+
// It will represent a single voter.
|
| 13 |
+
struct Voter {
|
| 14 |
+
uint weight; // weight is accumulated by delegation
|
| 15 |
+
bool voted; // if true, that person already voted
|
| 16 |
+
address delegate; // person delegated to
|
| 17 |
+
uint vote; // index of the voted proposal
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
// This is a type for a single proposal.
|
| 21 |
+
struct Proposal {
|
| 22 |
+
// If you can limit the length to a certain number of bytes,
|
| 23 |
+
// always use one of bytes1 to bytes32 because they are much cheaper
|
| 24 |
+
bytes32 name; // short name (up to 32 bytes)
|
| 25 |
+
uint voteCount; // number of accumulated votes
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
address public chairperson;
|
| 29 |
+
|
| 30 |
+
// This declares a state variable that
|
| 31 |
+
// stores a 'Voter' struct for each possible address.
|
| 32 |
+
mapping(address => Voter) public voters;
|
| 33 |
+
|
| 34 |
+
// A dynamically-sized array of 'Proposal' structs.
|
| 35 |
+
Proposal[] public proposals;
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* @dev Create a new ballot to choose one of 'proposalNames'.
|
| 39 |
+
* @param proposalNames names of proposals
|
| 40 |
+
*/
|
| 41 |
+
constructor(bytes32[] memory proposalNames) {
|
| 42 |
+
chairperson = msg.sender;
|
| 43 |
+
voters[chairperson].weight = 1;
|
| 44 |
+
|
| 45 |
+
// For each of the provided proposal names,
|
| 46 |
+
// create a new proposal object and add it
|
| 47 |
+
// to the end of the array.
|
| 48 |
+
for (uint i = 0; i < proposalNames.length; i++) {
|
| 49 |
+
// 'Proposal({...})' creates a temporary
|
| 50 |
+
// Proposal object and 'proposals.push(...)'
|
| 51 |
+
// appends it to the end of 'proposals'.
|
| 52 |
+
proposals.push(Proposal({
|
| 53 |
+
name: proposalNames[i],
|
| 54 |
+
voteCount: 0
|
| 55 |
+
}));
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
/**
|
| 60 |
+
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
|
| 61 |
+
* @param voter address of voter
|
| 62 |
+
*/
|
| 63 |
+
function giveRightToVote(address voter) external {
|
| 64 |
+
// If the first argument of `require` evaluates
|
| 65 |
+
// to 'false', execution terminates and all
|
| 66 |
+
// changes to the state and to Ether balances
|
| 67 |
+
// are reverted.
|
| 68 |
+
// This used to consume all gas in old EVM versions, but
|
| 69 |
+
// not anymore.
|
| 70 |
+
// It is often a good idea to use 'require' to check if
|
| 71 |
+
// functions are called correctly.
|
| 72 |
+
// As a second argument, you can also provide an
|
| 73 |
+
// explanation about what went wrong.
|
| 74 |
+
require(
|
| 75 |
+
msg.sender == chairperson,
|
| 76 |
+
"Only chairperson can give right to vote."
|
| 77 |
+
);
|
| 78 |
+
require(
|
| 79 |
+
!voters[voter].voted,
|
| 80 |
+
"The voter already voted."
|
| 81 |
+
);
|
| 82 |
+
require(voters[voter].weight == 0, "Voter already has the right to vote.");
|
| 83 |
+
voters[voter].weight = 1;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
/**
|
| 87 |
+
* @dev Delegate your vote to the voter 'to'.
|
| 88 |
+
* @param to address to which vote is delegated
|
| 89 |
+
*/
|
| 90 |
+
function delegate(address to) external {
|
| 91 |
+
// assigns reference
|
| 92 |
+
Voter storage sender = voters[msg.sender];
|
| 93 |
+
require(sender.weight != 0, "You have no right to vote");
|
| 94 |
+
require(!sender.voted, "You already voted.");
|
| 95 |
+
|
| 96 |
+
require(to != msg.sender, "Self-delegation is disallowed.");
|
| 97 |
+
|
| 98 |
+
// Forward the delegation as long as
|
| 99 |
+
// 'to' also delegated.
|
| 100 |
+
// In general, such loops are very dangerous,
|
| 101 |
+
// because if they run too long, they might
|
| 102 |
+
// need more gas than is available in a block.
|
| 103 |
+
// In this case, the delegation will not be executed,
|
| 104 |
+
// but in other situations, such loops might
|
| 105 |
+
// cause a contract to get "stuck" completely.
|
| 106 |
+
while (voters[to].delegate != address(0)) {
|
| 107 |
+
to = voters[to].delegate;
|
| 108 |
+
|
| 109 |
+
// We found a loop in the delegation, not allowed.
|
| 110 |
+
require(to != msg.sender, "Found loop in delegation.");
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
Voter storage delegate_ = voters[to];
|
| 114 |
+
|
| 115 |
+
// Voters cannot delegate to accounts that cannot vote.
|
| 116 |
+
require(delegate_.weight >= 1);
|
| 117 |
+
|
| 118 |
+
// Since 'sender' is a reference, this
|
| 119 |
+
// modifies 'voters[msg.sender]'.
|
| 120 |
+
sender.voted = true;
|
| 121 |
+
sender.delegate = to;
|
| 122 |
+
|
| 123 |
+
if (delegate_.voted) {
|
| 124 |
+
// If the delegate already voted,
|
| 125 |
+
// directly add to the number of votes
|
| 126 |
+
proposals[delegate_.vote].voteCount += sender.weight;
|
| 127 |
+
} else {
|
| 128 |
+
// If the delegate did not vote yet,
|
| 129 |
+
// add to her weight.
|
| 130 |
+
delegate_.weight += sender.weight;
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
/**
|
| 135 |
+
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
|
| 136 |
+
* @param proposal index of proposal in the proposals array
|
| 137 |
+
*/
|
| 138 |
+
function vote(uint proposal) external {
|
| 139 |
+
Voter storage sender = voters[msg.sender];
|
| 140 |
+
require(sender.weight != 0, "Has no right to vote");
|
| 141 |
+
require(!sender.voted, "Already voted.");
|
| 142 |
+
sender.voted = true;
|
| 143 |
+
sender.vote = proposal;
|
| 144 |
+
|
| 145 |
+
// If 'proposal' is out of the range of the array,
|
| 146 |
+
// this will throw automatically and revert all
|
| 147 |
+
// changes.
|
| 148 |
+
proposals[proposal].voteCount += sender.weight;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
/**
|
| 152 |
+
* @dev Computes the winning proposal taking all previous votes into account.
|
| 153 |
+
* @return winningProposal_ index of winning proposal in the proposals array
|
| 154 |
+
*/
|
| 155 |
+
function winningProposal() public view
|
| 156 |
+
returns (uint winningProposal_)
|
| 157 |
+
{
|
| 158 |
+
uint winningVoteCount = 0;
|
| 159 |
+
for (uint p = 0; p < proposals.length; p++) {
|
| 160 |
+
if (proposals[p].voteCount > winningVoteCount) {
|
| 161 |
+
winningVoteCount = proposals[p].voteCount;
|
| 162 |
+
winningProposal_ = p;
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
/**
|
| 168 |
+
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
|
| 169 |
+
* @return winnerName_ the name of the winner
|
| 170 |
+
*/
|
| 171 |
+
function winnerName() external view
|
| 172 |
+
returns (bytes32 winnerName_)
|
| 173 |
+
{
|
| 174 |
+
winnerName_ = proposals[winningProposal()].name;
|
| 175 |
+
}
|
| 176 |
+
}
|
polygon/tests/Ballot_test.sol
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// SPDX-License-Identifier: GPL-3.0
|
| 2 |
+
|
| 3 |
+
pragma solidity >=0.7.0 <0.9.0;
|
| 4 |
+
import "remix_tests.sol"; // this import is automatically injected by Remix.
|
| 5 |
+
import "hardhat/console.sol";
|
| 6 |
+
import "../contracts/3_Ballot.sol";
|
| 7 |
+
|
| 8 |
+
contract BallotTest {
|
| 9 |
+
|
| 10 |
+
bytes32[] proposalNames;
|
| 11 |
+
|
| 12 |
+
Ballot ballotToTest;
|
| 13 |
+
function beforeAll () public {
|
| 14 |
+
proposalNames.push(bytes32("candidate1"));
|
| 15 |
+
ballotToTest = new Ballot(proposalNames);
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
function checkWinningProposal () public {
|
| 19 |
+
console.log("Running checkWinningProposal");
|
| 20 |
+
ballotToTest.vote(0);
|
| 21 |
+
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
|
| 22 |
+
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
function checkWinninProposalWithReturnValue () public view returns (bool) {
|
| 26 |
+
return ballotToTest.winningProposal() == 0;
|
| 27 |
+
}
|
| 28 |
+
}
|