Part 3: Getting the payouts
Now that the customer-facing entrypoint of the contract is ready, you can set up the administrator-related entrypoint.
In this case, Pedro needs a way to reset the stock of tacos and send the tez from the contract to his account.
You could do this in two entrypoints, but for simplicity this tutorial shows how to do both of these things in one entrypoint named payout.
Adding administrator information
Also for the sake of simplicity, the contract provides no way to change Pedro's account address after the contract is deployed. In production applications, the address of the administrator should be in the contract storage and an entrypoint should allow the current administrator to change the administrator address. As it is, this contract cannot change the administrator address after it is deployed, so use caution.
In the
payoutentrypoint, add this code to verify that the administrator is calling the entrypoint:// Ensure that only the admin can call this entrypointif (Tezos.get_sender() != storage.admin_address) {failwith("Only the admin can call this entrypoint");}The function
Tezos.get_senderreturns the address of the account that called the smart contract.Add this code to generate the operation that sends tez to the administrator account:
// Create contract object that represents the target accountconst receiver_contract = $match(Tezos.get_contract_opt(storage.admin_address), {"Some": (contract) => contract,"None": () => failwith("Couldn't find account"),});// Create operation to send tezconst payout_operation = Tezos.Operation.transaction(unit, Tezos.get_balance(), receiver_contract);Sending tez to a user account means treating the user account as though it is a smart contract account. This way, sending tez to a user account works in the same way as sending tez to a smart contract.
The
Tezos.Operation.transactionfunction creates a Tezos transaction. There are many kinds of internal transactions in Tezos, but most smart contracts deal with these transactions:- Transferring tez to another account
- Calling an entrypoint on a smart contract
Calling an entrypoint on a smart contract (either the current contract or another contract) is beyond the scope of this tutorial. For information, see Calling a contract.
The
Tezos.Operation.transactionfunction takes these parameters:- The parameter to pass, in this case
unit, which means no value - The amount of tez to include with the transaction, in this case all of the tez the contract has, denoted by the
Tezos.get_balancefunction - The address of the target contract
Add this code to calculate the new value of the storage, using the existing admin address and the default taco data:
// Restore stock of tacosconst new_storage: storage = {admin_address: storage.admin_address,taco_data: default_taco_data,};Replace the
payoutentrypoint'sreturnstatement with this code:return [[payout_operation], new_storage];Creating the transaction is not enough to run it; you must return it in the list of operations at the end of the entrypoint.
The complete entrypoint looks like this:
That's all you need to do to reset the storage and send the contract's tez to the administrator.
If you want to extend this logic, try separating the payout entrypoint into separate entrypoints for paying out the tez and resetting the stock of tacos.
Testing the new entrypoint
Of course, after you implement the payout entrypoint, you should add tests for it.
At the end of the test function, add this code to get the current balance of Pedro's account before calling the entrypoint:
// Test the payout entrypoint as the administratorconst admin_balance_before = Test.Address.get_balance(admin_address);Add this code to set the account that smart contract calls come from in the test scenario:
Test.State.set_source(admin_address);Now when you call the
Test.Contract.transferfunction, the transaction comes from Pedro's account.Add this code to call the
payoutentrypoint and verify that the storage was updated, as in previous tests:const payout_result =Test.Contract.transfer(Test.Typed_address.get_entrypoint("payout", contract.taddr),unit,0 as tez);$match(payout_result, {"Success": (_s) => (() => {const storage = Test.Typed_address.get_storage(contract.taddr);// Check that the stock has been resetAssert.assert(eq_in_map(Map.find(1 as nat, TacoShop.default_taco_data),storage.taco_data,1 as nat));Assert.assert(eq_in_map(Map.find(2 as nat, TacoShop.default_taco_data),storage.taco_data,2 as nat));Test.IO.log("Successfully reset taco storage");})(),"Fail": (_err) => failwith("Failed to reset taco storage"),});Add this code to verify that Pedro's account received the tez from the contract:
// Check that the admin account got a payoutconst admin_balance_after = Test.Address.get_balance(admin_address);Assert.assert(Test.Compare.lt(admin_balance_before, admin_balance_after));The exact amounts differ because calling the
payoutentrypoint costs a small fee, but this code verifies that Pedro's account has more tez in it after calling thepayoutentrypoint.Add this code to generate a test account and verify that it can't call the
payoutentrypoint because it is not the administrator:// Verify that the entrypoint fails if called by someone elseconst other_user_account = Test.Account.address(1 as nat);Test.State.set_source(other_user_account);const failed_payout_result =Test.Contract.transfer(Test.Typed_address.get_entrypoint("payout", contract.taddr),unit,0 as tez);$match(failed_payout_result, {"Success": (_s) => failwith("A non-admin user was able to call the payout entrypoint"),"Fail": (_err) => Test.IO.log("Successfully prevented a non-admin user from calling the payout entrypoint"),});Run the test with
ligo run test taco_shop.jsligoand verify that the test runs successfully.
The complete contract and tests looks like this:
Now you can allow different users to do different things in the contract.
Conclusion
Now you have a contract that Pedro can use to sell tacos and manage the profits and the taco stock. From here you can expand the contract in many ways, such as:
- Adding more types of tacos
- Changing how the price of tacos is calculated
- Expanding the administrator functionality
- Accepting more than the price of the taco as a tip
- Adding more tests
You can also try deploying the contract to a test network and trying it in a real Tezos environment. For a tutorial that covers deploying a contract, see Deploy a smart contract on docs.tezos.com.