- Decentralization Manager
- Operations and reference
- Reward automation
Reward automation
Governance lifecycle · Security architecture
What it does
Section titled “What it does”Under CIP-104, an application provider earns traffic-based rewards as RewardCouponV2 coupons. When a Decentralized Party is the provider, each coupon names the party as provider and has no beneficiary. A coupon expires after about 36 hours. A Decentralized Party has no wallet automation. Its coupons therefore expire when nobody uses them in time.
Reward automation solves this in two parts:
- The members vote once to create a
CouponReassignmentDelegationcontract. The contract holds a fixed beneficiary split and a list of assigners. - Each Decentralization Manager node runs a background loop. On each sweep, an assigner node assigns every unassigned coupon to the split. The assignment needs no vote.
After the assignment, each beneficiary holds its own coupons. Each beneficiary must mint its coupons before they expire. Decentralization Manager does not mint for the beneficiaries.
Before you start
Section titled “Before you start”- The Decentralized Party has an active
GovernanceRulescontract. - Every participant has the
governance-rewards-automation-v1DAR, uploaded and vetted. The repository publishes it asreleases/v1/governance-rewards-automation-v1-0.1.0.dar. Package and contract operations describes the upload. - Every participant has the Splice packages that the DAR depends on, including
splice-amuletandsplice-api-reward-assignment-v1. - The participant of each assigner hosts the Decentralized Party. Otherwise the assigner cannot read the coupons.
- Each assigner node has party credentials for the Decentralized Party. Authentication and operator roles describes the credentials.
- Each beneficiary has a wallet or an agent that mints its coupons.
Set up the delegation
Section titled “Set up the delegation”Create the delegation with a governance proposal. Complete your first governance action describes the propose, confirm and execute steps.
Step 1
Open the proposal form
Section titled “Open the proposal form”In Parties, select the Decentralized Party. Then select New Proposal.
Step 2
Select the action
Section titled “Select the action”In Proposal Type, under Rewards, select Setup Coupon Reassignment Delegation.
Step 3
Enter the delegation
Section titled “Enter the delegation”- DSO Party: the form fills in the DSO party of the network. Check the value.
- Replaces Delegation: leave it empty for the first delegation.
- Assigners: add each member party that can run the assignment with Add Assigner. Any one assigner is enough.
- Beneficiary split: add each beneficiary with Add Beneficiary. Enter a whole-number Weight for each beneficiary.
Step 4
Submit and execute
Section titled “Submit and execute”Select Submit Proposal. The members confirm the proposal in Approvals. A member then executes it.
The API equivalent is POST /governance/propose. GET /network-info returns the DSO party of the network as dso_party_id.
curl -X POST "https://<decentralization-manager-host>/governance/propose" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "party_id": "<decentralized-party-id>", "rules_contract_id": "<governance-rules-contract-id>", "proposal": { "type": "setup_coupon_reassignment_delegation", "dso": "<dso-party-id>", "assigners": ["<member-party-id-1>", "<member-party-id-2>"], "new_beneficiaries": [ {"beneficiary": "<beneficiary-party-id-1>", "percentage": "0.8"}, {"beneficiary": "<beneficiary-party-id-2>", "percentage": "0.2"} ], "prior_delegation": null } }'| Field | Meaning |
|---|---|
dso |
The DSO party whose coupons the delegation can assign. |
assigners |
The member parties that can run the assignment. Any one of them is enough. |
new_beneficiaries |
The fixed split. Each entry names a party and its percentage as a decimal string. |
prior_delegation |
The contract ID of the delegation to replace, or null for the first delegation. |
Split rules
Section titled “Split rules”Decentralization Manager checks these rules when you propose. The Daml action checks them again at execution.
- The split has at least 1 and at most 20 beneficiaries.
- Each percentage is greater than 0 and at most 1.
- No beneficiary appears twice.
- The percentages sum to exactly 1.0. The ledger compares exact decimals. Three shares of
0.3333333333therefore fail. Balance the last share by hand, for example0.3333333334. - The Decentralized Party keeps no share by default. To keep a share, add the Decentralized Party as a beneficiary.
- The assigner list has at least one party, and no party appears twice.
The web form takes whole-number weights. It converts the weights to exact percentages and gives the rounding remainder to the largest weight. A confirmer can then reproduce the split from the weights.
Change or revoke the delegation
Section titled “Change or revoke the delegation”- Change the split or the assigners: propose Setup Coupon Reassignment Delegation again, and select the active delegation in Replaces Delegation. The execution archives the old delegation and creates the new one in one transaction.
- Stop the automation: propose Revoke Coupon Reassignment Delegation, and select the delegation in Delegation Contract ID. The API type is
revoke_coupon_reassignment_delegationwith the fielddelegation.
Only a governance vote can replace or revoke a delegation. A proposal with an empty Replaces Delegation fails with HTTP 409 while a delegation is active. The same proposal fails with HTTP 503 if the node cannot read the active delegations.
How the automation works
Section titled “How the automation works”Every Decentralization Manager node runs the reward loop. No variable turns it on or off. An active delegation on the ledger turns it on for a Decentralized Party.
- The loop wakes every 60 seconds, or more often if a reward interval is shorter. Each wake counts one heartbeat.
- Every
DECPM_REWARD_AUTOMATION_INTERVAL_SECS, the loop sweeps each Decentralized Party that the node has party credentials for. - The loop reads the active delegation of the party. It skips a party with no delegation.
- The loop checks that the delegation names the member party of this node as an assigner. If not, the node reads the backlog for the expiry gauge only. It also logs a warning.
- The loop reads the unassigned coupons of the party. It skips the coupons that expire within
DECPM_REWARD_MIN_EXPIRY_MARGIN_SECS. It sorts the rest by expiry, soonest first. - The loop assigns all of these coupons with
Delegation_Assigntransactions. Each transaction carries at mostDECPM_REWARD_MAX_CREATESdivided by the beneficiary count, rounded down, with a minimum of one coupon.
Between sweeps, the node reads the backlog every DECPM_REWARD_EXPIRY_READ_INTERVAL_SECS. This read refreshes the expiry gauge and assigns nothing.
The loop handles failures in two ways:
- Contention or a network timeout: the node ends the sweep. The next sweep reads the ledger again. Several assigners often compete for the same coupons. This case is therefore normal.
- Any other ledger rejection: the node sends the coupons of that transaction again, one coupon at a time. It skips each coupon that the ledger still rejects and logs it at the
ERRORlevel. The next sweep tries the skipped coupons again.
If the participant has no rewards package, the automation stays off for that participant. It logs this state at the TRACE level only.
Identity checks
Section titled “Identity checks”Any one assigner can reassign all unassigned coupons of the party at any time. The split in the delegation is the only possible destination. These checks keep the assignment on the correct contracts:
| Check | Where | Result of a failed check |
|---|---|---|
The delegation names this Decentralized Party in its decparty field. If several delegations are active, the newest one applies. |
Decentralization Manager, when it reads the delegation | The node ignores the other delegations and logs a warning when several are active. |
The member party of this node is in assigners. The comparison uses the full party ID, so a party with the same name or namespace does not match. |
Decentralization Manager | The node reports the backlog and assigns nothing. |
The delegation template is Governance.Rewards.CouponReassignmentDelegation:CouponReassignmentDelegation in the configured governance-rewards-automation-v1 package. The node reads the template from the ledger before each assignment. |
Decentralization Manager | The node refuses the delegation and ends the sweep. The log shows refusing delegation from. |
Each coupon comes from Splice.Amulet:RewardCouponV2. It names the Decentralized Party as provider and the delegation DSO as dso, and it has no beneficiary. |
Decentralization Manager, when it reads the coupons | The node does not assign the coupon. |
The assigner in the choice is in assigners. |
Delegation_Assign in Daml |
The ledger rejects the transaction. |
Each coupon is a RewardCouponV2 with the DSO of the delegation. |
Delegation_Assign in Daml |
The ledger rejects the transaction. |
| The new beneficiaries are the split in the contract. The caller cannot supply them. | Delegation_Assign in Daml |
Not applicable. The choice has no beneficiary argument. |
Configuration
Section titled “Configuration”These variables control the timing and the size of the assignments. The Configuration reference lists every DECPM_* variable.
| Variable | Default | Purpose |
|---|---|---|
DECPM_REWARD_AUTOMATION_INTERVAL_SECS |
300 |
Seconds between sweeps. This value sets the cadence only. A sweep assigns every assignable coupon, so the interval changes latency, not throughput. A value of 0 runs as 1 with a warning. |
DECPM_REWARD_EXPIRY_READ_INTERVAL_SECS |
3600 |
Seconds between backlog reads that only refresh decman_reward_oldest_unassigned_expires_in_seconds. The gauge refreshes at the shorter of the two intervals. A value of 0 runs as 1 with a warning. |
DECPM_REWARD_MAX_CREATES |
100 |
Maximum output contracts that one Delegation_Assign creates. Lower it if assignments start to fail. |
DECPM_REWARD_MIN_EXPIRY_MARGIN_SECS |
120 |
Seconds that a coupon must have left before expiry to be assigned. The margin keeps a coupon from expiring during the submission. |
The metrics in the next section use the metrics port DECPM_METRICS_PORT, 9464 by default. The metrics listener has no authentication and binds to DECPM_HOST.
Verify that it works
Section titled “Verify that it works”-
Check the delegation. The first entry is the delegation that the automation uses.
Terminal window curl -s -H "Authorization: Bearer <token>" \"https://<decentralization-manager-host>/coupon-reassignment-delegation?party_id=<decentralized-party-id>"The response lists
cid,dso,assignersandbeneficiary_countfor each active delegation. It normally holds one entry. -
Check the reward metrics on each assigner node.
Terminal window curl -s "http://<node-address>:9464/metrics" | grep '^decman_reward_' -
Confirm that
decman_reward_heartbeat_totalincreases and thatdecman_reward_sweep_totalincreases once per sweep interval. -
Confirm that
decman_reward_coupons_assigned_totalincreases on at least one assigner node when the party earns coupons. The log showsreassigned coupon batchwith the count. -
Check that
decman_reward_oldest_unassigned_expires_in_secondsstays well above zero. The series disappears when the party has no unassigned coupons.
Reward metrics
Section titled “Reward metrics”Each metric except the heartbeat has the label decparty.
| Metric | Meaning |
|---|---|
decman_reward_heartbeat_total |
Wakes of the reward loop. |
decman_reward_sweep_total |
Sweeps started for the party. |
decman_reward_coupons_assigned_total |
Coupons that this node assigned. |
decman_reward_coupons_skipped_total |
Coupons that the ledger rejected one at a time. The next sweep tries them again. |
decman_reward_sweep_zero_assigned_total |
Sweeps that found assignable coupons and assigned none. |
decman_reward_sweep_failed_total |
Sweeps that ended in an error. |
decman_reward_expiry_read_total |
Backlog reads that only refreshed the expiry gauge. |
decman_reward_credentials_unavailable_total |
Passes in which the token request failed for a party whose delegation names this node. |
decman_reward_auth_unregistered_total |
Passes that found no authentication entry for a party whose delegation names this node. |
decman_reward_oldest_unassigned_expires_in_seconds |
Seconds until the oldest unassigned coupon expires, from the last read. |
Useful alerts include a heartbeat that does not increase for 15 minutes, and an expiry gauge below a margin that you choose. Monitoring and logging describes the metrics setup.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause and action |
|---|---|
No decman_reward_sweep_total series for the party |
The node has no party credentials for the party. Configure the credentials. |
Sweeps run, and the log shows the delegation does not name this node as an assigner |
The member party of this node is not an assigner. Replace the delegation with a corrected assigner list. |
| Sweeps run without errors, and no coupon is ever assigned | No delegation is active, the DSO party in the delegation is wrong, or the participant has no rewards package. Check the delegation and the vetted packages. |
decman_reward_sweep_zero_assigned_total increases |
The node found coupons and assigned none. Look for assign chunk failed or coupon rejected on its own in the log. |
decman_reward_coupons_skipped_total increases |
The ledger rejected some coupons. The ERROR log lines name each coupon and the error ID. |
decman_reward_credentials_unavailable_total or decman_reward_auth_unregistered_total increases |
The node cannot get a token for the party. Fix the party credentials. |
The log shows refusing delegation from |
The active delegation does not come from the configured package. Replace it through governance. |
| The proposal fails with HTTP 409 | A delegation is already active. Select it in Replaces Delegation. |
Limits
Section titled “Limits”- The automation sees only the coupons that list the Decentralized Party as an observer. The DSO decides this when it creates a coupon. An invisible coupon looks the same as a coupon that the party never earned.
- A split has at most 20 beneficiaries.
- Canton cannot enforce one delegation for each party. If several delegations are active, the automation uses the newest one.
- The automation does not mint. Each beneficiary must mint its coupons before they expire.
Technical sources
Section titled “Technical sources”- Rewards automation package
CouponReassignmentDelegationtemplateSetupCouponReassignmentDelegationaction- Reward automation loop
- Proposal validation
- Environment variables
Next step
Section titled “Next step”Watch the automation: Monitoring and logging.
Ask the docs
Answers come only from this documentation and can be wrong. Check the linked pages.
Ask a question about BitSafe, CBTC or the developer guides. For example: