Contract Integration
Building An External Contract
For this Example Guide we will walk through building a simple external contract to act on Telos Decide™ ballot results after ballot closure. Developers can use this example as a starting point for designing their own contracts.
In our contract, we will launch a new election ballot and then contractually act on the results received by Telos Decide's broadcast()
action. Upon hearing the broadcast, our contract will read the final results, determine the winning candidate, and then send an inline action back to Telos Decide to update a committee with the election winner.
Example Guide
For this example, we will assume we have control of the testaccounta
account on Telos Testnet and own a balance of 1200 TLOS
. These funds will be used to pay the setup fees for a new treasury, new committee, and new ballot, with some left over.
1. Deposit Funds
To pay for the necessary registration fees our example requires we will deposit 1200 TLOS
into Telos Decide by simply sending a transfer()
with telos.decide
as the recipient.
cleos push action eosio.token transfer '["testaccounta", "telos.decide", "1200.0000 TLOS", "deposit"]' -p testaccounta
Telos Decide will catch this transfer and automatically create an account with your funds. From there, certain actions on the platform will require a fee that will be drawn from this balance (currently limited to creating a treasury, committee, or ballot).
Note there is no TLOS fee for depositing or withdrawing on Telos Decide, and both can be done freely at any time.
2. Treasury Creation
To create the example treasury we simply call the newtreasury()
action on the telos.decide
contract. We will supply the following arguments:
-
manager:
testaccounta
We will make ourself the manager, since we want to have control over the treasury operations for now.
-
max_supply:
500.0 EXMPL
We will limit the supply to 500.0 EXMPL, but we can adjust this later.
-
access:
public
We will choose public, since we want anyone to be able to register themselves and join as a voter.
cleos push action telos.decide newtreasury '["testaccounta", "500.0 EXMPL", "public"]' -p testaccounta
This will create a new public treasury with a max supply of 500.0 EXMPL tokens. Note that the treasury symbol is 1,EXMPL
since we chose to use 1 decimal place of precision and the EXMPL ticker.
Cost: 1000 TLOS
3. Committee Registration
To create the example committee we will call the regcommittee()
action on the telos.decide
contract. We will supply the following arguments:
-
committee_name:
examplecmte
This will be the committee identifier used to locate the committee in the contract table.
-
committee_title: "Example Committee"
This is the human readable title of the committee.
-
treasury_symbol:
1,EXMPL
The treasury symbol we created with the
newtreasury()
action. Don't forget the precision. -
initial_seats:
["seat1", "seat2", "seat3"]
The seat names initially available after successful committee creation. More can be added later.
-
registree:
testaccounta
The account registering the committee. This account will be billed for the committee creation.
cleos push action telos.decide regcommittee '["examplecmte", "Example Committee", "1,EXMPL", ["seat1", "seat2", "seat3"], "testaccounta"]' -p testaccounta
Cost: 100 TLOS
4. Ballot Creation
To create the example ballot we will call the newballot()
action on the telos.decide
contract. We will supply the following arguments:
-
ballot_name:
examplebal
The name identifier for our ballot. This must be unique.
-
category:
election
The category the ballot falls under. Since the winning candidate on our ballot is going to be the first member of our committee, the election category is most appropriate.
-
publisher:
testaccounta
The account who will set up and launch the ballot, and has the ability to cancel or delete if desired.
-
treasury_symbol:
1,EXMPL
The treasury that the ballot will be ran under. We will use the treasury we made earlier, and this means any
EXMPL
holder, including ourselves, will be able to cast a vote on this ballot. -
voting_method:
1token1vote
We will choose 1token1vote, since we want votes for multiple candidates to have the voter's weight cast evenly between the selections. This is a design choice, and any other voting method could have been selected.
-
initial_options:
["testaccountx", "testaccounty", "testaccountz"]
We will place test accounts x, y, and z to be placed as options on the ballot. We could add more before opening up the ballot for voting, but won't for simplicity's sake.
cleos push action telos.decide newballot '["examplebal", "election", "testaccounta", "1,EXMPL", "1token1vote", ["testaccountx", "testaccounty", "testaccountz"]]' -p testaccounta
Once the ballot has been registered, don't forget to actually begin voting be calling openvoting()
. This can be done at any time, so we will wait until we finish setting up our example contract.
Cost: 30 TLOS
5. Example Contract Build and Deployment
To build the example contract we will use the build.sh
provided in this repository's contracts folder, but you can build with the eosio-cpp
tool in the eosio.cdt
if that is preferable.
mkdir build && mkdir build/example
chmod +x build.sh
./build.sh example
To deploy the example contract to our testaccounta
account, we will use the provided deploy.sh
script, but you can deploy manually with the cleos set contract
command as well.
chmod +x deploy.sh
./deploy.sh example testnet
Once the contract is deployed we need to tell it to watch our new ballot and listen for the broadcast()
action. To do this we simply call the watchballot()
action. We will be pushing this transaction to the testaccounta
account, since that's where we deployed our example contract. We'll give it the following arguments:
-
ballot_name:
examplebal
Tells our example contract to watch the
examplebal
ballot on Telos Decide. -
treasury_symbol:
1,EXMPL
The treasury symbol used on the ballot.
-
committee_name:
examplecmte
The committee to update with the ballot winner.
-
seat_name:
seat1
The name of the seat to assign the winner to.
cleos push action testaccounta watchballot '["examplebal", "1,EXMPL", "examplecmte", "seat1"]' -p testaccounta
Next, after telling our contract to watch our ballot, we need to give it permission to execute an inline action to the assignseat()
action so we can immediately elect our winning candidate. To do this we want to add the virtual eosio.code
permission to our contract's active authority with the cleos set account permission
command, like so:
cleos set account permission testaccounta active --add-code -p testaccounta
6. Open Ballot Voting
Now that we have our example contract set up and watching our ballot, we can finally open up our ballot for voting. This is done by simply sending an openvoting()
action to Telos Decide.
Be aware that once voting has begun no further changes to the ballot may be performed. This is to preserve the integrity of the ballot and ensure voters can't be deceived by altering ballot information mid-vote. Ballots may still be cancelled, however.
We will supply the following arguments to our openvoting()
action:
-
ballot_name:
examplebal
This is the name of the ballot to open for voting. Only the ballot publisher will be able to open ballots for voting.
-
end_time:
2019-11-14T13:00:00
This is the time that voting will close in UTC time. Format is
YYYY-MM-DDTHH:MM:SS
cleos push action telos.decide openvoting '["examplebal", "2019-11-14T13:00:00"]' -p testaccounta
Now that voting has been opened on our ballot, all that's left to do is wait for votes to roll in. Results can be seen in real time, as the ballot tracks the live vote count for each ballot option.
7. Closing Out The Ballot
Once the end time on the ballot has been reached, it will immediately stop accepting votes. The ballot can be properly closed out and the results broadcasted by calling the closevoting()
action. We will supply the following arguments:
-
ballot_name:
examplebal
The name of the ballot to close.
-
broadcast:
true
Since we want to broadcast the results back to our example contract, we will give true here.
Once complete, the ballot status will be changed to closed
to show the ballot has been properly ended.
Since we chose to broadcast our results, an inline action has automatically been launched by Telos Decide to its own broadcast()
action that then notifies the ballot publisher account.
The example contract we deployed earlier to the testaccounta
account catches this broadcast()
notification from Telos Decide and interprets the final ballot results to determine the winner. Once determined, our contract automatically executes an inline action to Trail's assignseat()
action, which emplaces the winner into seat1
on the example committee.