-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fee distribution module #233
Comments
@ethanfrey Can you clarify how this functionality is expected to be working? This is how I imagine the flow:
I do not understand why do we need the It would make sense for me to change to one of the following:
Below is the protobuf declaration that I have created based on the initial description: // Revenue represents an account with funds collected from the fees. This is a
// temporary account used for storing fees that are later distributed between
// the owners.
message Revenue {
// Amount represents the total value stored within this revenue instance.
x.Coin amount = 1;
// Admin key belongs to the governance entities. It can be used to transfer
// stored amount to an another account.
// While not enforced it is best to use a multisig contract here.
bytes admin = 2 [(gogoproto.customtype) = "weave.Address"];
// Recipients holds any number of addresses that the collected revenue is
// distributed to. This does not contain the weights required for the
// transfer operation, but declares addresses that must be involved in each
// funds distribution.
repeated byte recipients = 3 [(gogoproto.customtype) = "weave.Address"];
// MinWeight declares minimal weight value set for a single recipient allowed
// during the funds distribution.
// This value must not be less than zero nor greater than MaxWeight.
int32 MinWeight = 4;
// MaxWeight declares maximum weight value set for a single recipient allowed
// during the funds distribution.
// This value must not be less than MinWeight.
int32 MaxWeight = 5;
}
// DistributeMsg is a request to distribute all funds collected within a single
// revenue instance. Revenue is distributed between recipients. Request must be
// signed using admin key.
message DistributeMsg {
// Revenue ID reference an ID of a revenue instance that the collected fees
// should be distributed between recipients.
bytes revenue_id = 1 [(gogoproto.customname) = "RevenueID"];
// Recipient contains list of recipient addresses together with their
// weights. All recipient addresses declared in corresponding revenue entity
// must be covered, even if the weight is zero.
repeated Recipient recipients = 2;
}
message Recipient {
// An address that the funds should be transferred to.
// This should not be the validator addresses, as the keys used to sign
// blocks should never be in a wallet. This can be the wallets of the admins
// of the validators.
bytes address = 1 [(gogoproto.customtype) = "weave.Address"];
// Weight defines what part of the total revenue goes to this recipient.
// Each recipient receives part of the total revenue amount proportional to
// the weight. For example, if there are two recipients with weights 1 and 2
// accordingly, distribution will be 1/3 to the first address and 2/3 to the
// second one.
// Weight must respect MinWight and MaxWeight declared for the revenue.
int32 weight = 2;
} |
Hi, lots of questions there... I will answer in a few different comments, on the different points. The desired flow is very much like you describe, but these parts must be done in genesis, as the fee collector must be set in genesis.
The design should allow use of "Revenue" for other cases as well, any time multiple people want to split an income stream, but main use for us is setup in genesis to serve as distribution for fee collector. |
Yes, this is just a sanity check. Imagine I am a validator and pledge to run for a month. I want some guarantee I will be paid for work done. This is the real requirement, that governance cannot redirect funds for past work, and if they redirect them for the future, I can stop working, as it is clear I will not be paid. That said, I think your solution here is even better than that whole set of sanity checks I had:
Thus, we actually commit to payout ahead of time (like a salary), and can adjust future payouts based on good/poor work. One key point here is that I cannot execute |
Yes, this looks fine, except for
Since |
I like your idea of We can have a simple DistributeMsg, with IssueMsg and UpdateMsg having a longer list of Recipients. Or you can combine Distribute and Update, so we can only do them at the same time. I think this is simpler than my original ideas and will also add more sanity checks and controls. Thank you for the deep thinking and feedback |
If there will be a single place where all the revenue address is stored (ie. If this package/extension (revenue distribution) is intended to be used with middleware and only a single revenue stream exists, then the revenue bucket feels a bit odd. We create a namespace for a singleton.
This might be cool, but to achieve that each revenue instance must have a unique address. That means that What if a revenue was declaring an address where the funds are stored? For the middleware use case, a gconf could hold the address. For other use cases any other instance of revenue could be used. An example genesis could look like this: {
"feedist": [
{
"address": "addr123",
"admin": "key123",
"recipients": [
{"address": "r1", "weight": 1},
{"address": "r2", "weight": 2},
]
}
],
"gconf": {
"cash:fee_collector": "addr123",
}
} What do you think? |
I understand your concerns. However, let me give you a very concrete example: If we use a revenue to distribute fees among validators, why can't the validator register a Revenue as the recipient as it's share, and split it among all people staking to it. If we do payouts eg. hourly, this would be simple yet good enough to build a sufficiently complex, yet trustable economy. Of course this method really encourages your proposal of pre-registering who receives the funds. Reflecting on my proposal above to merge Distribute and Update, I think they should be separate. With Distribute only taking an address of a Revenue and Update taking new recipient list. We could consider allowing anyone (or any recipient) to call Distribute (with a high gas cost maybe), while Update is restricted to the admin, and automatically runs Distribute before updating the list. What do you think? |
Further on, I don't see Revenue/feedist as a middleware, but a handler that takes some events. It doesn't apply to all transactions (FeeMiddleware does), but only has 3 messages (including issue), which must be called explicitly. As a genesis configuration, this seems roughly correct, with feedist as a list, like multisig. My only change would be to remove address and use the same technique as with multisig. The reason for this is that while we can provide a name to the feedist to use as the primary key if desired, in the permission system, addresses would look like
|
That will do it. I will also provide the too for listing the first N revenue ids.
That sounds good. I will implement what I have in mind right now. I think this is close to your expectation for this functionality. |
With #232 we will not only have microscopic voluntary fees, but hopefully real turnover. Currently the fees collected in the middleware go to a hardcoded address. This address could be set to a real value (not
/dev/null
) in the genesis file, but the question becomes, "what address"?Clearly the answer is my personal public key address :)
But seriously, we could conceive of some multisig that then is responsible for handling payouts according to a schedule, but that seems quite faulty. Better would be the address of a "smart contract" defined in a "distribution" module. This would collect all the fees and then have custom logic as to the distribution of the pool.
The current mvp version is a "distribution contract" that:
recipients
)MinProportion
andMaxProportion
that can limit acceptable range of distribution message if presentUpdateThreshold
(rename this), that is the maximum amount that can be held in the contract when we modify the recipient list.DistributeProportionallyMsg
) that accepts a list of addresses with an integer value for each. It must be approved by the "governance key". This will release all tokens held by this address proportionally to the values given the Msg.MinProportion
andMaxProportion
), all values must be in that rangeUpdateRecipients
) that will change the recipient list to whatever the "governance key" decides.If you have any more questions on this, please add them in a comment.
You can also add sample protobuf defs for the contract, and the two messages to make sure we are on the same page before starting.
The text was updated successfully, but these errors were encountered: