Skip to content

Latest commit

 

History

History
18 lines (17 loc) · 13.1 KB

Lesson 4.md

File metadata and controls

18 lines (17 loc) · 13.1 KB

Chapter 2, lesson 4

Transaction costs

Why do transaction costs money in on a blockchain? Well, because it's an open network with common goods, right. And this means that the cost of operating this network kind of falls on all the participants, if you want to run the node, you have to participate in the entirety of the network to work on synchronizing state and processing transactions. And, if the transaction costs were imbalanced or zero than any participant could incur, storage costs or computation costs on the everyone else, and therefore, going to produce a denial of service risk. And that's why in turn in extra care is taken off Earth to account for all possible costs in the in the system. So everything has specific costs, every little detail cannot go for free, because otherwise it opens, can a security call that can be exploited by malicious actors and put down the network for everyone like everyone else. So this means that production costs are quite important. And they are also quite complicated. Because in time, you could store data, you can compute things, you can route messages around all of these things in pure non trivial costs, and therefore it should have some limits and payment for them. So with this, let's take a look like how to flex the contract for you to design a contract as a developer, and you need to understand what what operations cost, how much and who should pay for them. So let's break this down. The every transaction has, like remember that every transaction is a change in the state of a contract. So you have an incoming message, I've got messages, you know, list of actions out of the TVM that produces new state of the contract issues, outgoing messages. This one has actually has some incurious of fees, right. So and these are broken into, like five categories.

Storage fees

First is storage fees and we talked briefly in the previous lesson about how, before anything happens on a contract, we have to account for, for a storage that the contract keeps in the blockchain is in the storage fees computed as a price per byte per second. So your contract may have some number of bytes held for some number of seconds and in between, you know, previous transaction and the country's action, and multiplying one by another, you get the amount of money that will be subtracted from the contract balance. So every time you your contract receives a transaction, it will get some amount of fees subtracted from it. This also explains why if you don't use your wallet for a long time, then your first transaction curious and usually higher fee. This is because it was idle for some time and then it was you know sitting on the blockchain and you suddenly have much more storage fees incurred over this period of time. And then if you do the similar transaction, right in the next minute, it's transaction cost will be much lower because the time delta was much shorter from the previous transaction. So this is storage fees.

Charge fees

Next category fees is a charge for importing external messages into blockchain. These messages are not authenticated, they're just byte strings, they cannot be paid for anywhere else. So this is the job of the first contract worker that processes them to pay for this byte. And you may ask, how do you make sure that you're not being spammed by some random messages? Well, this is done quite Interestingly, every node has sort of a virtual credit line in terms of gas cost. The fee for incoming external messages is a separate a fee called informed like incoming forwarding fee. And this is fixed have has nothing to do with the gas cost. So it's just for like number of bytes that you're sending into the system, you incur some fixed cost, and this is put on the shoulder of the contract that you know, takes care of this message.

Computation fees

The third category is the one of the most interesting one for you is the computation fees, computation fees are expressed in units of gas. So gas as an gasoline in like a fuel, it's a virtual unit that specifies the cost of each instruction and in the network configuration with separately set the price of one gas unit and this price may fluctuate and change with the voting of the validators. So, in case you know, Ton coin price goes way high, and in real terms, this gas price is not is just too expensive and is not realistic, then this parameter could be tuned down, and vice versa. So, if the ton coin suddenly becomes you know, 10 times cheaper than to reflect the real cost of you know, transaction processing, the gas price will be tuned up to match the reality. But at the level of the TVM, all the instructions are expressed in terms of gas units, and these are fixed and hard coded in the implementation of the TVM. And every instruction has some number of, I guess, units that are incurs. So during the TVM execution, all these gas units are added up together and multiplied by the gas price. And these things are subtracted from, from the contracts balance. And obviously, if the contract balance goes down to zero, then TVM stops executing and transaction fails.

Action fees

The fourth kind of fee is called action fees. And these are actually it's a category because for each action, there is a separate cost, there are actions, there are about storing the new state of a contract, their actions that route the outgoing messages and those have, you know, different fees based on the price parameter and the amount of data that that's being transferred or store.

Forwarding fees

Finally, there's a fifth category called outgoing forwarding fees, that is kind of symmetrical to the incoming external messages, but for sending kind of external outgoing messages, and this feature is not fully implemented, and therefore those fees are zero. And think of this as like a logging feature when you're not sending any message to the contract, you've just kind of logging out the data that can be consumed by by the notes. So, this fifth category is not really existing in the implementation of alright right now. So, to recap, we have storage fees, we have the fixed fees for importing external messages, we have computation fees in terms of gas units, and we have action fees for updating the storage of the contract and sending outgoing messages.

What are the important considerations when designing contracts regarding the fees?

First one, there is a difference a strategist who pays for the messages at the sender or the receiver. So normally, you would for the safety reason, you will design a system that sender covers the cost of processing qnd this is reflected in the so called like gas limit. So by default when the transaction comes in, the gas limit is set to the amount of money attached to the internal message. So if you send one coin and that's how much would be spent or guessing that means that for your contract safe to process the message without any extra checks, because if this message is maliciously crafted, and trigger some expensive computation that will only consume its own money that has been tested. However, you could change this gas limit, or you can even call a function called accept message that expense this gas limit to the entire balance of your contract. And this means that your contracts takes responsibility for processing this message and will pay for any fees associated with this. And you want to do this when either it's processing the external message that cannot be no hold any money at all. For instance, you receive the signed message into your own wallet, and you verify the signature and then after that, you can accept the message and pay the fees from your own bells. Or in case it's a some sort of like public service where that this message is coming from a good party, maybe it's registered and we're, like authenticate in some way. And then you say, okay, you don't have to pay the fees yourself, I will pay the fees for processing this message. So it could be designed this way. But generally, the safest option is to make the sender have predictable amount to attach to this message and cover all the fees that they incur. second consideration is regarding the storage. So if you have a multi contract system, it's important to keep in mind the storage costs. So if you take care of the computation costs, and all the kind of transit costs, like outgoing messages, like storing things, these could be designed to be predictable. But over time, you would incur the storage costs and this is something that is kind of orthogonal to, to the rest. And, for instance, if you have a token, that is implemented as a contract, and you don't really like move it around, then it will slowly eat out its own balance for its own storage. And typically, the tokens have enough coins for like a year. But then you have to renewed storage by throwing a little bit of coins into it. And usually will do that when you make it transactions. And so if you design some sort of system, you want to make sure that every time there is an incoming transaction, you also take take, take some amount of coins from incoming message to fund the, the storage of the audit contract. Or you should think about systems that would, from time to time, like monitor the state of the contracts, and refill their coins, just so they don't get frozen. And this is something that is kind of hard to just calculate at once, because you don't really control how frequently people interact with your contract. So you really should, like set up a separate infrastructure to monitor th rent of the contracts that you care about. One example is a fully decentralized contract that is kind of not owned by anyone, then you could think of this as like, it's everyone's job to see if they care about this contract to throw in some coins to make it leap. So if they're, you know, a lot of traders using some decks router, and this next router is really like doesn't belong to anyone and all this traders would be like just checking the health of the system and sometimes just donating some coins to make it live for long, or maybe you could make a one time donation that covers the cost 400 years. In the future, this is also a possibility.Finally, the consideration regarding the transaction costs is about bounded execution. So, in turn, you could write arbitrary applications that could you know, have variable length data and loops. And this makes reasoning about transaction costs quite difficult. And also, if you have some kind of like, arbitrary long, any user generated data that you store that can be populated by arbitrary users, like let's say you have a list of list of participants with their, like virtual accounts in your contract. This is kind of hard to scale and one of the reasons why it's hard to scale is because of this unpredictable gas cost that operation with this data structure would incur. So, your best strategy is to have all the storage in your contracts to be explicitly bounded. So if you want to have lists, they should have, you know, some limits, or they should be statically. initialized, like you know that, you know, this contract is initialized with like 5 members like multisig, or like 10 members, but exactly the limit. And this helps you to calculate the amount of fees that every every operation this contract may incur. And make sure that incoming transaction has that amount of coins attached to it. And therefore, you never end up in a situation where either your contract runs out of money, or the centers of transactions attach insufficient amount of money.

Credit trick

Finally, regarding the external messages. External messages cannot have the funds attached. So there is a trick called credit that makes it look like you have a temporary credit of 10,000 gas units for executing incoming message. And this is just enough to verify, like, unpack a packet message and verify its signature. And this is needed to process external messages that come into the wallet contracts. And maybe some others, but primarily, the word contracts, because they, they just receive the bytes from the user. And they have to pay the cost of processing this transaction. And the nodes that verify those transactions, they implement this kind of virtual credit, they say ‘Okay’ you have this 10,000 gas units as a credit, you can try to check if this message is valid, if the message is just some garbage that anyone could throw into the blockchain, then it probably would not be correctly signed. So it will be thrown out after running out of this, you know, 10,000 guests limit. But if it's not, then the contract can call the function except message and therefore expand the guest limit to its entire balance. And after that point, this the gas that was already consumed, plus all the guests will be no further consumed will be subtracted from the contract balance. And this credit kind of allows you to match the two styles of messages, the external ones and internal ones together. So from the perspective of the contract writer, you're still kind of having the illusion that the message may have some coins attached. And in case of external messages, zero, but you're allowed to run for like a little bit of time, and then you accept the message, pay for all the fees and that, like do all the actions that you're supposed to do in the contract.