--- tags: Spec-Agreements --- # Streamr Agreements Implementation Guide ![](https://i.imgur.com/jAhKhpQ.jpg) - Red / Pink: User decision process - Blue: Token State - Orange: Smart Contract Method Actuation - Green: Contract State - Purple: Policies / rule set - Purple Diamond: Event-Driven Map ## State !!!! DO WE WANT TO REPRESENT THE DATA AS A STRUCT OF TYPE BROKER AND THEN ADD ALL OF THESE THINGS IN? !!!! ### Global State | Symbol | Variable Name | Variable Type | Description | | --------|------- | ----- | ----- | | $R$| unallocated_funds| uint64| The set of all funds in the agreement which have not yet been allocated| | $\mathcal{B}$| committed_brokers | list[address] | A list holding the addresses of all the currently committed brokers| | $a_i$| allocation | dictionary[address, uint64] | A dictionary mapping the broker ID to their allocation earned and not yet disbursed.| | $s_i$| broker_stake | dictionary[address, uint64] | A dictionary mapping the broker ID to their stake put into the system.| | $\lambda_i$| time_attached | dictionary[address, uint64] | A dictionary mapping the broker ID to their time in the system.| ### Stateful Metrics These variables can all be computed from variables within either the local or global state. | Symbol | Variable Name | Variable Type | Description | Formula | | --------|------- | ----- | ----- | ----- | | $A$| allocated_funds| uint64| The set of all funds in the agreement which have been allocated| $\sum_{i\in \mathcal{B}} a_i$ | | $n$ | n_committed_brokers | uint64 | The total number of committed brokers in the system | $\vert \mathcal{B}\vert$ | | $S$ | total_broker_stake | uint64 | The summation of all the stake within the system from active brokers | $\sum_{i\in \mathcal{B}} s_i$ | | $\Delta A$ | allocation_per_epoch | uint64 | The allocation to brokers over an epoch | $\sum_{i\in \mathcal{B}} \Delta a_i$ | ## Parameters | Symbol | Variable Name | Variable Type | Description | | ---------- | ------------------------- | ---------------- | ---------------- | | $s_{\min}$ | minimum_stake| uint64 | The minimum stake which a broker must provide to join. | | $\Delta t$ | epoch_length | uint64 | The length of time for the epoch | | $\tau$ | minimum_epochs | uint64 | The minimum number of epochs a broker must have been in the system before leaving under certain policies. | | $n_{\min}$ | min_brokers | uint64 | The minimum number of brokers for the system | | $n_{\max}$ | max_brokers | uint64 | The maximum number of brokers for the system | | $R_{0}$ | initial_funding | uint64 | The starting amount of funding in the system. | ## State Transitions ### Broker Mechanisms #### Join First, when a broker makes the behavioral decision to join the agreement, the availability to join is checked: $CAJ(n, n_{max}, s_i, i)\rightarrow y$ If y equals 0, the action will be canceled otherwise if it is equal to 1, the set of brokers will be extended and this stake will increase $S$. $$ \mathcal{B}^+ = \mathcal{B} \cup \{i\}\\ S^+ = S+s_i\\ $$ These actions mutate the state in that the tracking of stakes now has a pointer for the address of the new broker and as well this new broker's address is added. ``` def add_broker( global_state, broker_address, new_stake): global_state["S"] += new_stake global_state["s"][broker_address] = new_stake global_state["B"].append(broker_address) global_state["lambda"][broker_address] = 0 ``` #### Leave When the behavioral decision to leave has been made, a check is done to determine if the broker will be penalized. $CPL(R, \lambda_i, a_i)\rightarrow y$ If y = 1, then there is no penalized leaving and that mechanism handles the action from that point, otherwise it is handled within a penalized leaving. #### Leave (Non-Penalized) The broker may leave and will recover their stake and as well will realize their allocations, taking them from the system. $$\mathcal{B}^+ = \mathcal{B}-\{i\}\\ S^+ = S-s_i \\ A^+= A-a_i$$ This also implies that the stateful metric of $F$ will be updated as so: $$F^+= F-a_i$$ The broker will receive $s_i + a_i$. Before removing the broker, the system state must be updated to handle both the stake and the allocations being taken out. ``` def remove_broker_no_penalty( global_state, broker_address): stake = global_state["s"][broker_address] allocation = global_state["a"][broker_address] global_state["S"] -= stake global_state["s"].remove(broker_address) global_state["a"].remove(broker_address) global_state["B"].remove(broker_address) global_state["lambda"].remove(broker_address) ``` #### Leave (Penalized) In a case where these conditions are not met, we will have the similar closing out of local broker state, but now they will be forfeiting their stake which will go to the pool of unallocated funds. $$\mathcal{B}^+ = \mathcal{B}-\{i\}\\ S^+ = S-s_i \\ A^+= A-a_i\\ R^+ = R+s_i$$ This also implies that the stateful metric of $F$ will be updated as so: $$F^+= F-a_i+s_i$$ The broker will receive $a_i$. ``` def remove_broker_no_penalty( global_state, broker_address): stake = global_state["s"][broker_address] allocation = global_state["a"][broker_address] global_state["S"] -= stake global_state["R"] += stake global_state["s"].remove(broker_address) global_state["a"].remove(broker_address) global_state["B"].remove(broker_address) global_state["lambda"].remove(broker_address) ``` #### Claim This action is started by the behavior claim action. There are no constraints on frequency of claims that a broker may make. It is assumed that a claim is for the full amount exactly. When broker i initiates a claim, the following state variables will be updated: $A^+= A-a_i$ As well, they will receive receive a_i and the local state for the broker will be set to 0 on their claims. $a_i= 0$ ``` def claim( global_state, broker_address): allocation = global_state["a"][broker_address] global_state["A"] -= allocation global_state["a"][broker_address] = 0 ``` ### Payer Mechanisms A payer, who may or may not be the owner may contribute funds to the agreement in order to ensure its continued existence. #### Payments A payer takes the action pay by providing a quantity of DATA tokens $\Delta F$ which increased the unallocated funds (and thus also the total funds). $$ F^+ = F+\Delta F\\ R^+ = R + \Delta F $$ ``` def add_funds( global_state, new_funds): global_state["R"] += new_funds ``` ### System Mechanisms #### Time Update & Allocation (Synthetic State Change) The synthetic state change is a change the contract state that is not realized until later. In this case the automatic allocation of funds from the pool $R$ to the pool $A$ is implicit. Although rewards each participant is entitled to are determinstic, they are not actually resolved until they make a claim, see broker actions. Per epoch $t$ of length $\Delta t$, let $\mathcal{B}_t$ be the set of brokers $i\in \mathcal{B}$ for the entirety of the epoch, then: $$a_i^+ = a_i +Allocate(\mathcal{B}, R, s_{i}, p_{i}, n)\\ \Delta A=\sum Allocate(\mathcal{B}, R, s_{i}, p_{i}, n)\\ A^+ = A+\Delta A\\ R^+=R-\Delta A$$ Note that $F^+ = F$ so no tokens are actually flowing, this book-keeping reflects the amount of funds broker $i$ will recieve in the event that they make a claim or other payout event occurs. As well, for each broker, there will be an update to its time in the system. $\lambda_i^+ = \lambda_i + 1$ ``` def allocate(global_state): new_allocations = Allocate(global_state) for i in new_allocations.keys(): global_state["a"][i] += new_allocations[i] global_state["lambda"][i] += 1 total_allocation = sum(new_allocations.values()) global_state["R"] -= total_allocation global_state["A"] += total_allocation ``` ## Handlers ### Check Global Penalized Leaving This action controls the ability of the entire set of brokers to be able to leave with or without penalty on their stake. While individual brokers may be allowed to leave if they satisfy certain requirements, when this returns true all brokers will be able to leave without penalty. $CGPL(R, n)\rightarrow y$ where $y \in {0,1}$ ### Check Local Penalized Leaving This action controls the ability of the individual broker to leave without a penalty on stake. One possible example policy would be when a broker has been in the system for a minimum period of time. $CLPL(\lambda_i, a_i)\rightarrow y$ where $y \in {0,1}$ ### Check Penalized Leaving This function serves as a wrapper around the two penalized leaving functions prior to determine if a broker can leave without penalty. $CPL(R, \lambda_i, a_i)\rightarrow CGPL(R) \text{ OR } CLPL(\lambda_i, a_i) \rightarrow y$ where $y \in {0,1}$ ### Check Ability to Join This action controls the logic of whether or not an individual broker is capable of joining the agreement. $CAJ(n, n_{max}, s_i, i)\rightarrow y$ where $y \in {0,1}$ The most likely constraints placed on the broker join would be that there are less than a maximum number of brokers in the system, and the broker has a minimum stake required to join. ## Hooks ## Possible Extensions ### Global State Addition | Symbol | Variable Name | Variable Type | Description | | --------|------- | ----- | ----- | | $p_i$| performance_metric | dictionary[address, float] | A dictionary mapping the broker ID to their current performance metric/weighting. Represented in numbers between 0-1 or null if system is not using.|