From 7dfcfc57b787177c1818610ad45d0272c95deabf Mon Sep 17 00:00:00 2001 From: Chris Womack Date: Tue, 19 Mar 2024 17:20:39 -0400 Subject: [PATCH 1/4] ARC-0038 --- arc-0038/README.md | 1133 ++++++++++++++++++++++++++++++++++++++++ arc-0038/arc_0038.aleo | 490 +++++++++++++++++ arc-0038/arc_0038.leo | 427 +++++++++++++++ 3 files changed, 2050 insertions(+) create mode 100644 arc-0038/README.md create mode 100644 arc-0038/arc_0038.aleo create mode 100644 arc-0038/arc_0038.leo diff --git a/arc-0038/README.md b/arc-0038/README.md new file mode 100644 index 0000000..acd3cb1 --- /dev/null +++ b/arc-0038/README.md @@ -0,0 +1,1133 @@ +--- +arc: 0038 +title: Create Delegated Staking Standard Program with Commission +authors: chris@demoxlabs.xyz mike@demoxlabs.xyz evan@demoxlabs.xyz +discussion: https://github.com/AleoHQ/ARCs/discussions/66 +topic: Application +status: Draft +created: 3/19/2024 +--- + +## Abstract +Validators play an important role in running the network. Validators receive rewards for taking on this responsibility, and this reward is split among the delegators bonded to them. To account for the cost associated with running a validator node, validators should be allowed to take a commission on the rewards earned from bonding to them. This proposal would also allow smaller delegators to participate in staking, as the minimum stake may be prohibitive for smaller investors. + +## Specification +### Required Constants: +``` +const ADMIN: address = aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; +``` +Validators will need to set an admin address from which they will be able to control certain aspects of the program, such as the commission rate and address of the validator node +``` +const CORE_PROTOCOL: address = aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny; +``` +This is the precompiled address of the program itself. This is used for reading the mapping values for the program in `credits.aleo` (account, bonded, etc). +``` +const SHARES_TO_MICROCREDITS: u64 = 1_000u64; +``` +Shares in the protocol are equivalent to nanocredits (at time of initial deposit) in order to more precisely calculate the commission due to the validator. This constant helps us move between shares and microcredits for the initial deposit. + + +``` +const PRECISION_UNSIGNED: u128 = 1000u128; +``` +A constant used for added precision when performing integer calculations + + +``` +const MAX_COMMISSION_RATE: u128 = 500u128; +``` +This is the maximum allowed commission rate for the program. It is relative to the `PRECISION_UNSIGNED` above. e.g. 100u128 = 10% + + +``` +const UNBONDING_PERIOD: u32 = 360u32; +``` +The unbonding period in blocks, as defined in `credits.aleo` Used for batching withdrawal requests + + +``` +const MINIMUM_BOND_POOL: u64 = 10_000_000_000u64; +``` +This minimum stake as defined in `credits.aleo`. Used to ensure an attempt to unbond the full balance will successfully remove the value in the `bonded` mapping. + + +### Required Mappings: +``` +mapping is_initialized: u8 => bool; +``` +Key `0u8` stores a boolean showing whether the program has been initialized by the admin. + + +``` +mapping commission_percent: u8 => u128; +``` +Key `0u8` stores the percentage of rewards taken as commission. Relative to `PRECISION_UNSIGNED` e.g. 100u128 = 10% + + +``` +mapping validator: u8 => address; +``` +Key `0u8` stores the current address used for bonding to the validator. +Key `1u8` stores the next address to use for bonding, in the case the validator address needs to be updated. Automatically reset when the pool funds are bonded. +``` +mapping total_balance: u8 => u64; +``` +Key `0u8` stores the total balance of microcredits that have been deposited to the program, excluding commissions. + + +``` +mapping total_shares: u8 => u64; +``` +Key `0u8` stores the total number of shares owned by delegators. Shares represent the portion of the `total_balance` that a delegator owns. For example, upon withdrawal, we calculate the amount of microcredits to disburse to the delegator based on the portion of the shares pool they own. (`delegator_shares / total_shares`) + + +``` +mapping delegator_shares: address => u64; +``` +Maps from a delegator to the number of shares they own. + + +``` +mapping pending_withdrawal: u8 => u64; +``` +Key `0u8` stores the total amount of microcredits that delegators are waiting to withdraw. + + +``` +mapping current_batch_height: u8 => u32; +``` +Key `0u8` stores the height at which the current batch of withdrawals will be available for claim. Used to prevent indefinite unbonding due to withdrawals. If the value is not present or equal to `0u32`, there is no batch currently unbonding, and a new batch may be started. + + +``` +mapping withdrawals: address => withdrawal_state; +``` +Maps from a delegator to their `withdrawal_state`, which contains the amount of microcredits they have pending withdrawal and the height at which they will be available to claim. + + +### Required Structs: +``` +struct withdrawal_state: + microcredits as u64; + claim_block as u32; + +``` +In normal operation of the protocol, most if not all of the credits owned by the protocol will be bonded to a validator, which means withdrawals must unbond, claim, and then transfer credits to the withdrawer. +To keep track of the amount of microcredits a withdrawer can claim, as well as to keep track of when the withdrawer can claim, this struct holds both properties. For use of this struct, see the `withdrawals` mapping, `create_withdraw_claim` finalize block, and the `withdraw_public` finalize block. + +### Required Records: +None + +### Required Functions: + +#### Initialize +The `initialize` function takes two arguments: `commission_rate`, the initial commission rate as a `u128` and `validator_address`, the `address` of the validator the program will bond to. +The transition is straightforward - we assert that it is the admin calling this function and that the commission rate is within bounds. +The finalize confirms that the program has not already been initialized and then sets `is_initialized` to true and sets the initial values for each of the program’s mappings. +```aleo +function initialize: + input r0 as u128.public; + input r1 as address.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + lt r0 1000u128 into r2; + assert.eq r2 true; + lte r0 500u128 into r3; + assert.eq r3 true; + async initialize r0 r1 into r4; + output r4 as staking_lite.aleo/initialize.future; + + +finalize initialize: + input r0 as u128.public; + input r1 as address.public; + get is_initialized[0u8] into r2; + assert.eq r2 false; + set r0 into commission_percent[0u8]; + set r1 into validator[0u8]; + set 0u64 into total_shares[0u8]; + set 0u64 into total_balance[0u8]; + set 0u64 into pending_withdrawal[0u8]; + set 0u32 into current_batch_height[0u8]; +``` +```leo + transition initialize(commission_rate: u128, validator_address: address) { + assert_eq(self.caller, ADMIN); + assert(commission_rate < PRECISION_UNSIGNED); + assert(commission_rate <= MAX_COMMISSION_RATE); + + + return then finalize(commission_rate, validator_address); + } + + + finalize initialize(commission_rate: u128, validator_address: address) { + assert_eq(is_initialized.get(0u8), false); + + + commission_percent.set(0u8, commission_rate); + validator.set(0u8, validator_address); + total_shares.set(0u8, 0u64); + total_balance.set(0u8, 0u64); + pending_withdrawal.set(0u8, 0u64); + current_batch_height.set(0u8, 0u32); + } +``` +#### Initial Deposit +`initial_deposit` takes three arguments: `input_record` (`credits.aleo/credits` record) and `microcredits` (`u64`) which are used to transfer credits into the program, and `validator_address` (`address`) used to call `bond_public` with the credits transferred in. Note: once `transfer_public_signer` is added to `credits.aleo`, we won’t need to accept private records and can instead only take microcredits as the singular argument for this function. Currently, `transfer_public` uses the caller and not the signer to transfer credits, which means the protocol address would be transferring credits from and to itself in this function. +The transition simply asserts that the admin is calling this function and handles the calls to `credits.aleo` for transferring and bonding. +The finalize block first confirms that the program has been initialized, and there are no funds present in the program. It then initializes the balance of microcredits and shares (in nanocredits) and assigns the new shares to the admin in `delegator_shares`. +```aleo +function initial_deposit: + input r0 as credits.aleo/credits.record; + input r1 as u64.public; + input r2 as address.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + call credits.aleo/transfer_public_to_public r0 aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny r1 into r3 r4; + call credits.aleo/bond_public r2 r1 into r5; + async initial_deposit r4 r5 r1 into r6; + output r3 as credits.aleo/credits.record; + output r6 as staking_lite.aleo/initial_deposit.future; + + +finalize initial_deposit: + input r0 as credits.aleo/transfer_public_to_public.future; + input r1 as credits.aleo/bond_public.future; + input r2 as u64.public; + await r0; + await r1; + get is_initialized[0u8] into r3; + assert.eq r3 true; + get.or_use total_balance[0u8] 0u64 into r4; + get.or_use total_shares[0u8] 0u64 into r5; + assert.eq r4 0u64; + assert.eq r5 0u64; + set r2 into total_balance[0u8]; + mul r2 1_000u64 into r6; + set r6 into total_shares[0u8]; + set r2 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; +``` +```leo + transition initial_deposit( + input_record: credits.aleo/credits, + microcredits: u64, + validator_address: address + ) -> credits.aleo/credits { + assert_eq(self.caller, ADMIN); + // Must be a credits record because credits.aleo uses self.caller for transfers + let updated_record: credits.aleo/credits = credits.aleo/transfer_public_to_public(input_record, CORE_PROTOCOL, microcredits); + credits.aleo/bond_public(validator_address, microcredits); + + + return (updated_record) then finalize(microcredits); + } + + + finalize initial_deposit(microcredits: u64) { + assert(is_initialized.get(0u8)); + + + let balance: u64 = total_balance.get_or_use(0u8, 0u64); + let shares: u64 = total_shares.get_or_use(0u8, 0u64); + assert_eq(balance, 0u64); + assert_eq(shares, 0u64); + + + total_balance.set(0u8, microcredits); + total_shares.set(0u8, microcredits * SHARES_TO_MICROCREDITS); + delegator_shares.set(ADMIN, microcredits); + } +``` +#### Get Commission +`get_commission` is an inline function (i.e. a helper function that, when compiled to aleo instructions, is inserted directly everywhere it is called) that takes two arguments: `rewards` the total amount of rewards earned from bonding in microcredits, and `commission_rate` the current commission rate of the program both as `u128`s +`get_commission` is used to calculate the portion of rewards that is owed to the validator as commission. We use `u128`s for safety against overflow when multiplying and normalize back to `u64` by dividing by `PRECISION_UNSIGNED`. +```leo + inline get_commission( + rewards: u128, + commission_rate: u128, + ) -> u64 { + let commission: u128 = rewards * commission_rate / PRECISION_UNSIGNED; + let commission_64: u64 = commission as u64; + return commission_64; + } +``` +#### Calculate New Shares +`calculate_new_shares` is an inline function that takes three arguments: `balance` the total balance of microcredits in the program (deposits + rewards), `deposit` the amount of microcredits being deposited, and `shares` the total amount of shares outstanding. +`calculate_new_shares` is used to determine the amount of shares to mint for the depositor. This is determined by first calculating the ratio of the current amount of shares and the current balance in microcredits. The goal is to keep this ratio constant, so we determine the number of shares to mint based on the relative change in microcredits. +This code represents the following formula: +`new_shares = ( total_shares / total_balance) * (total_balance + deposit) - total_shares` +```leo + inline calculate_new_shares(balance: u128, deposit: u128, shares: u128) -> u64 { + let pool_ratio: u128 = ((shares * PRECISION_UNSIGNED) / balance); + let new_total_shares: u128 = (balance + deposit) * pool_ratio; + let diff: u128 = (new_total_shares / PRECISION_UNSIGNED) - shares; + let shares_to_mint: u64 = diff as u64; + return shares_to_mint; + } +``` +#### Set Commission Percent +`set_commission_percent` takes one argument: `new_commission_rate` as a `u128` which will be set as the new value for `commission_percent[0u8]` +The transition simply confirms that the program admin is calling this function and that the new commission rate is within bounds. +The concerns of the finalize block are to: +- First claim any remaining commission at the current commission percent, by distributing shares to the program admin +- Set the new commission rate +```aleo +function set_commission_percent: + input r0 as u128.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + lt r0 1000u128 into r1; + assert.eq r1 true; + lte r0 500u128 into r2; + assert.eq r2 true; + async set_commission_percent r0 into r3; + output r3 as staking_lite.aleo/set_commission_percent.future; + + +finalize set_commission_percent: + input r0 as u128.public; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r1; + get total_balance[0u8] into r2; + get total_shares[0u8] into r3; + gt r1 r2 into r4; + sub r1 r2 into r5; + ternary r4 r5 0u64 into r6; + get commission_percent[0u8] into r7; + cast r6 into r8 as u128; + mul r8 r7 into r9; + div r9 1000u128 into r10; + cast r10 into r11 as u64; + sub r6 r11 into r12; + add r2 r12 into r13; + cast r13 into r14 as u128; + cast r11 into r15 as u128; + cast r3 into r16 as u128; + mul r16 1000u128 into r17; + div r17 r14 into r18; + add r14 r15 into r19; + mul r19 r18 into r20; + div r20 1000u128 into r21; + sub r21 r16 into r22; + cast r22 into r23 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; + add r24 r23 into r25; + set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r3 r23 into r26; + set r26 into total_shares[0u8]; + add r13 r11 into r27; + set r27 into total_balance[0u8]; + set r7 into commission_percent[0u8]; +``` +```leo + transition set_commission_percent(new_commission_rate: u128) { + assert_eq(self.caller, ADMIN); + assert(new_commission_rate < PRECISION_UNSIGNED); + assert(new_commission_rate <= MAX_COMMISSION_RATE); + + + return then finalize(new_commission_rate); + } + + + finalize set_commission_percent(new_commission_rate: u128) { + // Make sure all commission is claimed before changing the rate + let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + + + commission_percent.set(0u8, commission_rate); + } +``` +#### Set Next Validator +`set_next_validator` takes one argument: `validator_address`, the new `address` that the program will bond to after any currently bonded funds are unbonded. +The transition simply confirms that only the admin may call this function, and the finalize block handles setting the value into `validator[1u8]` +```aleo +function set_next_validator: + input r0 as address.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + async set_next_validator r0 into r1; + output r1 as staking_lite.aleo/set_next_validator.future; + + +finalize set_next_validator: + input r0 as address.public; + set r0 into validator[1u8]; +``` +```leo + // Update the validator address, to be applied automatically on the next bond_all call + transition set_next_validator(validator_address: address) { + assert_eq(self.caller, ADMIN); + + + return then finalize(validator_address); + } + + + finalize set_next_validator(validator_address: address) { + validator.set(1u8, validator_address); + } +``` +#### Unbond All +`unbond_all` takes one argument: `pool_balance` which is the total amount of microcredits to unbond, as a `u64` +The transition simply calls `unbond_public` with the supplied value, and is permissionless. +The finalize block handles the following: +- Confirming that the admin has set a value for the next validator, as `unbond_all` should only occur as part of a validator address change +- Distributing any outstanding commission to the validator +- Asserting that the amount unbonded will result in a complete unbonding (by checking that any difference between `pool_balance` and the actual amount bonded is less than the minimum stake amount) +```aleo +function unbond_all: + input r0 as u64.public; + call credits.aleo/unbond_public r0 into r1; + async unbond_all r1 r0 into r2; + output r2 as staking_lite.aleo/unbond_all.future; + + +finalize unbond_all: + input r0 as credits.aleo/unbond_public.future; + input r1 as u64.public; + await r0; + contains validator[1u8] into r2; + assert.eq r2 true; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + get total_balance[0u8] into r4; + get total_shares[0u8] into r5; + gt r3 r4 into r6; + sub r3 r4 into r7; + ternary r6 r7 0u64 into r8; + get commission_percent[0u8] into r9; + cast r8 into r10 as u128; + mul r10 r9 into r11; + div r11 1000u128 into r12; + cast r12 into r13 as u64; + sub r8 r13 into r14; + add r4 r14 into r15; + cast r15 into r16 as u128; + cast r13 into r17 as u128; + cast r5 into r18 as u128; + mul r18 1000u128 into r19; + div r19 r16 into r20; + add r16 r17 into r21; + mul r21 r20 into r22; + div r22 1000u128 into r23; + sub r23 r18 into r24; + cast r24 into r25 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; + add r26 r25 into r27; + set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r5 r25 into r28; + set r28 into total_shares[0u8]; + add r15 r13 into r29; + set r29 into total_balance[0u8]; + sub r3 r1 into r30; + lt r30 10_000_000_000u64 into r31; + assert.eq r31 true; +``` +```leo + transition unbond_all(pool_balance: u64) { + credits.aleo/unbond_public(pool_balance); + + + return then finalize(pool_balance); + } + + + finalize unbond_all(pool_balance: u64) { + let next_validator: bool = validator.contains(1u8); + assert(next_validator); + + + // Make sure all commission is claimed before unbonding + let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + + + // Assert that the pool will be fully unbonded + let residual_balance: u64 = bonded - pool_balance; + assert(residual_balance < MINIMUM_BOND_POOL); + } +``` +#### Claim Unbond +`claim_unbond` takes no arguments. The transition simply calls `claim_unbond_public`, to claim any unbonded credits - whether from a withdrawal or as a result of `unbond_all`. +The finalize block removes the value of `current_batch_height` to allow a new withdrawal batch to begin. +```aleo +function claim_unbond: + call credits.aleo/claim_unbond_public into r0; + async claim_unbond r0 into r1; + output r1 as staking_lite.aleo/claim_unbond.future; + + +finalize claim_unbond: + input r0 as credits.aleo/claim_unbond_public.future; + await r0; + remove current_batch_height[0u8]; +``` +```leo + transition claim_unbond() { + credits.aleo/claim_unbond_public(); + + return then finalize(); + } + + + finalize claim_unbond() { + current_batch_height.remove(0u8); + } +``` +#### Bond All +`bond_all` takes two arguments: `validator_address` as an address, and `amount` as a u64. +The transition part is straightforward – the credits.aleo program is called to bond credits held by the protocol to the validator, either the next validator if one is set or to the current validator. +In a nutshell, the concerns of the finalize portion of bond_all are to: +- Ensure there’s not credits unbonding, which means we would be unable to bond to a validator +- Bond all available microcredits to the validator (next or current). Available microcredits depends on pending withdrawals. +- If a next validator is set, bond to the next validator. Set the next validator as the current validator, and remove the next validator. + +```aleo + + +function bond_all: + input r0 as address.public; + input r1 as u64.public; + call credits.aleo/bond_public r0 r1 into r2; + async bond_all r2 r0 r1 into r3; + output r3 as staking_lite.aleo/bond_all.future; + + +finalize bond_all: + input r0 as credits.aleo/bond_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + assert.eq r3 0u64; + get.or_use credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r4; + get pending_withdrawal[0u8] into r5; + sub r4 r5 into r6; + assert.eq r2 r6; + contains validator[1u8] into r7; + get validator[1u8] into r8; + get validator[0u8] into r9; + ternary r7 r8 r9 into r10; + assert.eq r1 r10; + set r10 into validator[0u8]; + remove validator[1u8]; +``` +```leo + + + transition bond_all(validator_address: address, amount: u64) { + // Call will fail if there is any balance still bonded to another validator + credits.aleo/bond_public(validator_address, amount); + + + return then finalize(validator_address, amount); + } + + + finalize bond_all(validator_address: address, amount: u64) { + let unbonding_balance: u64 = 0u64; // credits.aleo/unbonding.get(CORE_PROTOCOL); + assert_eq(unbonding_balance, 0u64); + + + let account_balance: u64 = 0u64; // credits.aleo/account.get(CORE_PROTOCOL); + let pending_withdrawals: u64 = pending_withdrawal.get(0u8); + let available_balance: u64 = account_balance - pending_withdrawals; + assert_eq(amount, available_balance); + + + // Set validator + let has_next_validator: bool = validator.contains(1u8); + let current_validator: address = has_next_validator ? validator.get(1u8) : validator.get(0u8); + assert_eq(validator_address, current_validator); + + + validator.set(0u8, current_validator); + validator.remove(1u8); + } +``` +#### Claim Commission +`claim_commission` takes no arguments. `claim_commission` is intended for the admin of the protocol to harvest rewards from staking at any point. +In a nutshell, the concerns of the finalize portion of `claim_commission` are to: +- Distribute commission shares for the protocol admin +- Update the protocol state +```aleo +function claim_commission: + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + async claim_commission into r0; + output r0 as staking_lite.aleo/claim_commission.future; + + +finalize claim_commission: + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r0; + get total_balance[0u8] into r1; + get total_shares[0u8] into r2; + gt r0 r1 into r3; + sub r0 r1 into r4; + ternary r3 r4 0u64 into r5; + get commission_percent[0u8] into r6; + cast r5 into r7 as u128; + mul r7 r6 into r8; + div r8 1000u128 into r9; + cast r9 into r10 as u64; + sub r5 r10 into r11; + add r1 r11 into r12; + cast r12 into r13 as u128; + cast r10 into r14 as u128; + cast r2 into r15 as u128; + mul r15 1000u128 into r16; + div r16 r13 into r17; + add r13 r14 into r18; + mul r18 r17 into r19; + div r19 1000u128 into r20; + sub r20 r15 into r21; + cast r21 into r22 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r23; + add r23 r22 into r24; + set r24 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r2 r22 into r25; + set r25 into total_shares[0u8]; + add r12 r10 into r26; + set r26 into total_balance[0u8]; +``` +```leo + transition claim_commission() { + assert_eq(self.caller, ADMIN); + return then finalize(); + } + + + finalize claim_commission() { + // Distribute shares for new commission + let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + } +``` +#### Deposit Public +`deposit_public` takes two arguments: `input_record` as a `credits.aleo/credits` record, and `microcredits` as a u64. Note: once `transfer_public_signer` is added to `credits.aleo`, we won’t need to accept private records and can instead only take microcredits as the singular argument for this function. Currently, `transfer_public` uses the caller and not the signer to transfer credits, which means the protocol address would be transferring credits from and to itself in this function. +The transition part is straightforward – the `credits.aleo` program is called to transfer credits from the depositor to the protocol address. +In a nutshell, the concerns of the finalize portion of `deposit_public` are to: +- Distribute commission shares for the protocol admin +- Distribute shares for the depositor, in direct proportion to the amount of the protocol credits pool they just contributed to +- Update the protocol state +Deposit public does not automatically bond the credits. This is for several reasons. By not directly bonding credits, we do not enforce a minimum deposit. We also save the depositor on fees, since the constraints of the bond call are not a part of the overall transition. `bond_all` must be called in order to bond the microcredits held by the protocol to the validator. +```aleo +function deposit_public: + input r0 as credits.aleo/credits.record; + input r1 as u64.public; + call credits.aleo/transfer_public_to_public r0 aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny r1 into r2 r3; + async deposit_public r3 self.caller r1 into r4; + output r2 as credits.aleo/credits.record; + output r4 as staking_lite.aleo/deposit_public.future; + + +finalize deposit_public: + input r0 as credits.aleo/transfer_public_to_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + get total_balance[0u8] into r4; + get total_shares[0u8] into r5; + gt r3 r4 into r6; + sub r3 r4 into r7; + ternary r6 r7 0u64 into r8; + get commission_percent[0u8] into r9; + cast r8 into r10 as u128; + mul r10 r9 into r11; + div r11 1000u128 into r12; + cast r12 into r13 as u64; + sub r8 r13 into r14; + add r4 r14 into r15; + cast r15 into r16 as u128; + cast r13 into r17 as u128; + cast r5 into r18 as u128; + mul r18 1000u128 into r19; + div r19 r16 into r20; + add r16 r17 into r21; + mul r21 r20 into r22; + div r22 1000u128 into r23; + sub r23 r18 into r24; + cast r24 into r25 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; + add r26 r25 into r27; + set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r5 r25 into r28; + add r15 r13 into r29; + cast r29 into r30 as u128; + cast r2 into r31 as u128; + cast r28 into r32 as u128; + mul r32 1000u128 into r33; + div r33 r30 into r34; + add r30 r31 into r35; + mul r35 r34 into r36; + div r36 1000u128 into r37; + sub r37 r32 into r38; + cast r38 into r39 as u64; + gte r39 1u64 into r40; + assert.eq r40 true; + get.or_use delegator_shares[r1] 0u64 into r41; + add r41 r39 into r42; + set r42 into delegator_shares[r1]; + add r28 r39 into r43; + set r43 into total_shares[0u8]; + add r29 r2 into r44; + set r44 into total_balance[0u8]; +``` +```leo +transition deposit_public( + input_record: credits.aleo/credits, + microcredits: u64 + ) -> credits.aleo/credits { + // Must be a credits record because credits.aleo uses self.caller for transfers + let updated_record: credits.aleo/credits = credits.aleo/transfer_public_to_public(input_record, CORE_PROTOCOL, microcredits); + + + return (updated_record) then finalize(self.caller, microcredits); + } + + + finalize deposit_public( + caller: address, + microcredits: u64 + ) { + // Distribute shares for new commission + let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + + current_shares += new_commission_shares; + current_balance += new_commission; + + + // Calculate mint for deposit + let new_shares: u64 = calculate_new_shares(current_balance as u128, microcredits as u128, current_shares as u128); + + + // Ensure mint amount is valid + assert(new_shares >= 1u64); + + + // Update delegator_shares mapping + let shares: u64 = delegator_shares.get_or_use(caller, 0u64); + delegator_shares.set(caller, shares + new_shares); + + + // Update total shares + total_shares.set(0u8, current_shares + new_shares); + + + // Update total_balance + total_balance.set(0u8, current_balance + microcredits); + } +``` +#### Withdraw Public +`withdraw_public` takes two arguments: `withdrawal_shares` and `total_withdrawal`, both as u64s. Withdrawal shares are the amount of shares to burn in exchange for `total_withdrawal` microcredits. +`withdraw_public` is meant to be used in the normal operation of the protocol – most credits (excepting deposits and pending withdrawals) should be bonded to the validator. +The transition part is straightforward – the `credits.aleo` program is called to unbond the `total_withdrawal` microcredits from the protocol address. +In a nutshell, the concerns of the finalize portion of `withdraw_public` are to: +- Determine whether this withdrawal will fit into the current withdraw batch, if one is taking place +- Distribute commission shares for the protocol admin +- Ensure that the `total_withdrawal` microcredits are less than or equal to the proportion of microcredits held by the withdrawal_shares +- Update the protocol state +- Set a withdraw claim for the withdrawer so that they may withdraw their shares at a given `claim_height` +```aleo +function withdraw_public: + input r0 as u64.public; + input r1 as u64.public; + call credits.aleo/unbond_public r1 into r2; + async withdraw_public r2 r0 r1 self.caller into r3; + output r3 as staking_lite.aleo/withdraw_public.future; + + +finalize withdraw_public: + input r0 as credits.aleo/unbond_public.future; + input r1 as u64.public; + input r2 as u64.public; + input r3 as address.public; + await r0; + contains withdrawals[r3] into r4; + assert.eq r4 false; + get.or_use current_batch_height[0u8] 0u32 into r5; + add block.height 360u32 into r6; + is.eq r5 0u32 into r7; + gte r5 r6 into r8; + or r7 r8 into r9; + assert.eq r9 true; + get delegator_shares[r3] into r10; + gte r10 r1 into r11; + assert.eq r11 true; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r12; + get total_balance[0u8] into r13; + get total_shares[0u8] into r14; + gt r12 r13 into r15; + sub r12 r13 into r16; + ternary r15 r16 0u64 into r17; + get commission_percent[0u8] into r18; + cast r17 into r19 as u128; + mul r19 r18 into r20; + div r20 1000u128 into r21; + cast r21 into r22 as u64; + sub r17 r22 into r23; + add r13 r23 into r24; + cast r24 into r25 as u128; + cast r22 into r26 as u128; + cast r14 into r27 as u128; + mul r27 1000u128 into r28; + div r28 r25 into r29; + add r25 r26 into r30; + mul r30 r29 into r31; + div r31 1000u128 into r32; + sub r32 r27 into r33; + cast r33 into r34 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r35; + add r35 r34 into r36; + set r36 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r14 r34 into r37; + add r24 r22 into r38; + cast r1 into r39 as u128; + mul r39 1000u128 into r40; + cast r37 into r41 as u128; + div r40 r41 into r42; + cast r38 into r43 as u128; + mul r43 r42 into r44; + div r44 1000u128 into r45; + cast r2 into r46 as u128; + gte r45 r46 into r47; + assert.eq r47 true; + div block.height 1_000u32 into r48; + mul r48 1_000u32 into r49; + add r49 1_000u32 into r50; + ternary r7 r50 r5 into r51; + set r51 into current_batch_height[0u8]; + cast r2 r51 into r52 as withdrawal_state; + set r52 into withdrawals[r3]; + get pending_withdrawal[0u8] into r53; + add r53 r2 into r54; + set r54 into pending_withdrawal[0u8]; + sub r38 r2 into r55; + set r55 into total_balance[0u8]; + sub r37 r1 into r56; + set r56 into total_shares[0u8]; + sub r10 r1 into r57; + set r57 into delegator_shares[r3]; +``` +```leo +transition withdraw_public(withdrawal_shares: u64, total_withdrawal: u64) { + credits.aleo/unbond_public(total_withdrawal); + + + return then finalize(withdrawal_shares, total_withdrawal, self.caller); + } + + + finalize withdraw_public(withdrawal_shares: u64, total_withdrawal: u64, owner: address) { + // Assert that they don't have any pending withdrawals + let currently_withdrawing: bool = withdrawals.contains(owner); + assert_eq(currently_withdrawing, false); + + + // Determine if the withdrawal can fit into the current batch + let current_batch: u32 = current_batch_height.get_or_use(0u8, 0u32); + let min_claim_height: u32 = block.height + UNBONDING_PERIOD; + let new_batch: bool = current_batch == 0u32; + let unbonding_allowed: bool = new_batch || current_batch >= min_claim_height; + assert(unbonding_allowed); + + + // Assert that they have enough to withdraw + let delegator_balance: u64 = delegator_shares.get(owner); + assert(delegator_balance >= withdrawal_shares); + + + // Distribute shares for new commission + let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + + current_shares += new_commission_shares; + current_balance += new_commission; + + + // Calculate withdrawal amount + let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; + let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + + + // If the calculated withdrawal amount is greater than total_withdrawal, the excess will stay in the pool + assert(withdrawal_calculation >= total_withdrawal as u128); + + + // Update withdrawals mappings + let batch_height: u32 = new_batch ? get_new_batch_height(block.height) : current_batch; + current_batch_height.set(0u8, batch_height); + let withdrawal: withdrawal_state = withdrawal_state { + microcredits: total_withdrawal, + claim_block: batch_height + }; + withdrawals.set(owner, withdrawal); + + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + + + // Update total balance + total_balance.set(0u8, current_balance - total_withdrawal); + + + // Update total shares + total_shares.set(0u8, current_shares - withdrawal_shares); + + + // Update delegator_shares mapping + delegator_shares.set(owner, delegator_balance - withdrawal_shares); + } +``` +#### Get New Batch Height +`get_new_batch_height` is an inline function (i.e. a helper function that, when compiled to aleo instructions, is inserted directly everywhere it is called) takes one argument: `height` as a u32, representing the current block height. +`get_new_batch_height` rounds up the current `block.height` to the nearest 1000th block height. Given an input of 0, we expect an output of 1000. Given input of 999, we expect an output of 1000. +```leo + inline get_new_batch_height(height: u32) -> u32 { + let rounded_down: u32 = (height) / 1_000u32 * 1_000u32; + let rounded_up: u32 = rounded_down + 1_000u32; + return rounded_up; + } +``` +#### Create Withdraw Claim +`create_withdraw_claim` takes one argument: `withdrawal_shares`, as a u64. Withdrawal shares are the amount of shares to burn in exchange for their proportional amount of the protocol’s microcredits. +`create_withdraw_claim` is intended to be used in special circumstances for the protocol. The credits of the protocol should all be unbonded, which means that credits are not earning rewards, and withdrawers do not need to call `unbond_public` from the `credits.aleo` program. +In a nutshell, the concerns of the finalize portion of `create_withdraw_claim` are to: +- Assert that the protocol is fully unbonded from any validator +- Ensure that the withdrawer can withdraw – i.e. they are not currently withdrawing and they have at least as many shares as they are attempting to burn +- Create a `withdrawal_state` so that the withdrawer may claim their credits +- Update the protocol state +```aleo +function create_withdraw_claim: + input r0 as u64.public; + async create_withdraw_claim r0 self.caller into r1; + output r1 as staking_lite.aleo/create_withdraw_claim.future; + + +finalize create_withdraw_claim: + input r0 as u64.public; + input r1 as address.public; + contains withdrawals[r1] into r2; + assert.eq r2 false; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + assert.eq r3 0u64; + get delegator_shares[r1] into r4; + gte r4 r0 into r5; + assert.eq r5 true; + get total_balance[0u8] into r6; + get total_shares[0u8] into r7; + cast r0 into r8 as u128; + mul r8 1000u128 into r9; + cast r7 into r10 as u128; + div r9 r10 into r11; + cast r6 into r12 as u128; + mul r12 r11 into r13; + div r13 1000u128 into r14; + cast r14 into r15 as u64; + cast r15 block.height into r16 as withdrawal_state; + set r16 into withdrawals[r1]; + get pending_withdrawal[0u8] into r17; + add r17 r15 into r18; + set r18 into pending_withdrawal[0u8]; + sub r6 r15 into r19; + set r19 into total_balance[0u8]; + sub r7 r0 into r20; + set r20 into total_shares[0u8]; + sub r4 r0 into r21; + set r21 into delegator_shares[r1]; +```leo + transition create_withdraw_claim(withdrawal_shares: u64) { + return then finalize(withdrawal_shares, self.caller); + } + + + finalize create_withdraw_claim(withdrawal_shares: u64, owner: address) { + // Assert that they don't have any pending withdrawals + let currently_withdrawing: bool = withdrawals.contains(owner); + assert_eq(currently_withdrawing, false); + + + let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); + assert_eq(bonded, 0u64); + + + // Assert that they have enough to withdraw + let delegator_balance: u64 = delegator_shares.get(owner); + assert(delegator_balance >= withdrawal_shares); + + + // Calculate withdrawal amount + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; + let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + let total_withdrawal: u64 = withdrawal_calculation as u64; + + + // Update withdrawals mappings + let withdrawal: withdrawal_state = withdrawal_state { + microcredits: total_withdrawal, + claim_block: block.height + }; + withdrawals.set(owner, withdrawal); + + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + + + // Update total balance + total_balance.set(0u8, current_balance - total_withdrawal); + + + // Update total shares + total_shares.set(0u8, current_shares - withdrawal_shares); + + + // Update delegator_shares mapping + delegator_shares.set(owner, delegator_balance - withdrawal_shares); + } +``` +#### Claim Withdrawal Public +`claim_withdrawal_public` takes two arguments: `recipient` as an address, and `amount` as a u64. Given that a withdrawer has a withdrawal claim, they can pass in a `recipient` to receive `amount`. Note, to keep the protocol simple, the `amount` must be the full amount of their withdrawal claim. +`claim_withdrawal_public` is intended to be used at any point that the withdrawer has a withdraw claim with a `claim_height` that is greater than or equal to the current block height. +In a nutshell, the concerns of the finalize portion of `claim_withdrawal_public` are to: +- Ensure that the withdrawer can withdraw and that the withdrawer is withdrawing everything in the claim +- Remove the `withdrawal_state` so that the withdrawer may claim more credits in a separate withdrawal process +- Update the protocol state +```aleo +function claim_withdrawal_public: + input r0 as address.public; + input r1 as u64.public; + call credits.aleo/transfer_public r0 r1 into r2; + async claim_withdrawal_public r2 r0 r1 into r3; + output r3 as staking_lite.aleo/claim_withdrawal_public.future; + + +finalize claim_withdrawal_public: + input r0 as credits.aleo/transfer_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get withdrawals[r1] into r3; + gte block.height r3.claim_block into r4; + assert.eq r4 true; + assert.eq r3.microcredits r2; + remove withdrawals[r1]; + get pending_withdrawal[0u8] into r5; + sub r5 r2 into r6; + set r6 into pending_withdrawal[0u8]; +``` +```leo + transition claim_withdrawal_public(recipient: address, amount: u64) { + credits.aleo/transfer_public(recipient, amount); + + + return then finalize(recipient, amount); + } + + + finalize claim_withdrawal_public(owner: address, amount: u64) { + let withdrawal: withdrawal_state = withdrawals.get(owner); + assert(block.height >= withdrawal.claim_block); + assert_eq(withdrawal.microcredits, amount); + + + // Remove withdrawal +withdrawals.remove(owner); + + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending - amount); + } +``` + + +## Test Cases +We are implementing test cases to ensure this protocol works as expected, and always allows for depositors to recollect their funds. The major test cases we want to include in our suite are: +- The protocol funds are always able to be withdrawn by depositors +-- In normal operation +-- When everything has unbonded through the protocol +-- When a validator forcibly unbonds the protocol’s stake +- unbond_all always unbonds everything without exception +- validators may not hike up commission rates to affect unclaimed commission +- Depositors get their proportional amount of shares, rounded down the nearest share (at 1000 shares per microcredit for precision) + +## Dependencies +As this is an application ARC, there are no dependencies other than what is currently available in Aleo, except for transfer_public_signer, which is necessary to remove private state from the program. + +## Backwards Compatibility +Not necessary. + +## Security & Compliance +This is an application ARC standard, so this only affects the security of managing assets on-chain. We will have this code audited by an external firm. For compliance purposes, this program will operate publicly, without records. + +## References +- [Previous ARC-38 discussion](https://github.com/AleoHQ/ARCs/discussions/52) +- [Pull request for delegated staking](https://github.com/demox-labs/aleo-staking/pull/1) \ No newline at end of file diff --git a/arc-0038/arc_0038.aleo b/arc-0038/arc_0038.aleo new file mode 100644 index 0000000..2c71173 --- /dev/null +++ b/arc-0038/arc_0038.aleo @@ -0,0 +1,490 @@ +import credits.aleo; +program arc_0038.aleo; + +struct withdrawal_state: + microcredits as u64; + claim_block as u32; + + +mapping is_initialized: + key as u8.public; + value as boolean.public; + + +mapping commission_percent: + key as u8.public; + value as u128.public; + + +mapping validator: + key as u8.public; + value as address.public; + + +mapping total_balance: + key as u8.public; + value as u64.public; + + +mapping total_shares: + key as u8.public; + value as u64.public; + + +mapping delegator_shares: + key as address.public; + value as u64.public; + + +mapping pending_withdrawal: + key as u8.public; + value as u64.public; + + +mapping current_batch_height: + key as u8.public; + value as u32.public; + + +mapping withdrawals: + key as address.public; + value as withdrawal_state.public; + +function initialize: + input r0 as u128.public; + input r1 as address.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + lt r0 1000u128 into r2; + assert.eq r2 true; + lte r0 500u128 into r3; + assert.eq r3 true; + async initialize r0 r1 into r4; + output r4 as arc_0038.aleo/initialize.future; + +finalize initialize: + input r0 as u128.public; + input r1 as address.public; + get is_initialized[0u8] into r2; + assert.eq r2 false; + set r0 into commission_percent[0u8]; + set r1 into validator[0u8]; + set 0u64 into total_shares[0u8]; + set 0u64 into total_balance[0u8]; + set 0u64 into pending_withdrawal[0u8]; + set 0u32 into current_batch_height[0u8]; + + +function initial_deposit: + input r0 as credits.aleo/credits.record; + input r1 as u64.public; + input r2 as address.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + call credits.aleo/transfer_private_to_public r0 aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny r1 into r3 r4; + call credits.aleo/bond_public r2 r1 into r5; + async initial_deposit r4 r5 r1 into r6; + output r3 as credits.aleo/credits.record; + output r6 as arc_0038.aleo/initial_deposit.future; + +finalize initial_deposit: + input r0 as credits.aleo/transfer_private_to_public.future; + input r1 as credits.aleo/bond_public.future; + input r2 as u64.public; + await r0; + await r1; + get is_initialized[0u8] into r3; + assert.eq r3 true; + get.or_use total_balance[0u8] 0u64 into r4; + get.or_use total_shares[0u8] 0u64 into r5; + assert.eq r4 0u64; + assert.eq r5 0u64; + set r2 into total_balance[0u8]; + mul r2 1_000u64 into r6; + set r6 into total_shares[0u8]; + set r2 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + + + + + + +function set_commission_percent: + input r0 as u128.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + lt r0 1000u128 into r1; + assert.eq r1 true; + lte r0 500u128 into r2; + assert.eq r2 true; + async set_commission_percent r0 into r3; + output r3 as arc_0038.aleo/set_commission_percent.future; + +finalize set_commission_percent: + input r0 as u128.public; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r1; + get total_balance[0u8] into r2; + get total_shares[0u8] into r3; + gt r1 r2 into r4; + sub r1 r2 into r5; + ternary r4 r5 0u64 into r6; + get commission_percent[0u8] into r7; + cast r6 into r8 as u128; + mul r8 r7 into r9; + div r9 1000u128 into r10; + cast r10 into r11 as u64; + sub r6 r11 into r12; + add r2 r12 into r13; + cast r13 into r14 as u128; + cast r11 into r15 as u128; + cast r3 into r16 as u128; + mul r16 1000u128 into r17; + div r17 r14 into r18; + add r14 r15 into r19; + mul r19 r18 into r20; + div r20 1000u128 into r21; + sub r21 r16 into r22; + cast r22 into r23 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; + add r24 r23 into r25; + set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r3 r23 into r26; + set r26 into total_shares[0u8]; + add r13 r11 into r27; + set r27 into total_balance[0u8]; + set r7 into commission_percent[0u8]; + + +function set_next_validator: + input r0 as address.public; + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + async set_next_validator r0 into r1; + output r1 as arc_0038.aleo/set_next_validator.future; + +finalize set_next_validator: + input r0 as address.public; + set r0 into validator[1u8]; + + +function unbond_all: + input r0 as u64.public; + call credits.aleo/unbond_public r0 into r1; + async unbond_all r1 r0 into r2; + output r2 as arc_0038.aleo/unbond_all.future; + +finalize unbond_all: + input r0 as credits.aleo/unbond_public.future; + input r1 as u64.public; + await r0; + contains validator[1u8] into r2; + assert.eq r2 true; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + get total_balance[0u8] into r4; + get total_shares[0u8] into r5; + gt r3 r4 into r6; + sub r3 r4 into r7; + ternary r6 r7 0u64 into r8; + get commission_percent[0u8] into r9; + cast r8 into r10 as u128; + mul r10 r9 into r11; + div r11 1000u128 into r12; + cast r12 into r13 as u64; + sub r8 r13 into r14; + add r4 r14 into r15; + cast r15 into r16 as u128; + cast r13 into r17 as u128; + cast r5 into r18 as u128; + mul r18 1000u128 into r19; + div r19 r16 into r20; + add r16 r17 into r21; + mul r21 r20 into r22; + div r22 1000u128 into r23; + sub r23 r18 into r24; + cast r24 into r25 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; + add r26 r25 into r27; + set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r5 r25 into r28; + set r28 into total_shares[0u8]; + add r15 r13 into r29; + set r29 into total_balance[0u8]; + sub r3 r1 into r30; + lt r30 10_000_000_000u64 into r31; + assert.eq r31 true; + + +function claim_unbond: + call credits.aleo/claim_unbond_public into r0; + async claim_unbond r0 into r1; + output r1 as arc_0038.aleo/claim_unbond.future; + +finalize claim_unbond: + input r0 as credits.aleo/claim_unbond_public.future; + await r0; + remove current_batch_height[0u8]; + + +function bond_all: + input r0 as address.public; + input r1 as u64.public; + call credits.aleo/bond_public r0 r1 into r2; + async bond_all r2 r0 r1 into r3; + output r3 as arc_0038.aleo/bond_all.future; + +finalize bond_all: + input r0 as credits.aleo/bond_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + assert.eq r3 0u64; + get.or_use credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r4; + get pending_withdrawal[0u8] into r5; + sub r4 r5 into r6; + assert.eq r2 r6; + contains validator[1u8] into r7; + get validator[1u8] into r8; + get validator[0u8] into r9; + ternary r7 r8 r9 into r10; + assert.eq r1 r10; + set r10 into validator[0u8]; + remove validator[1u8]; + + +function claim_commission: + assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + async claim_commission into r0; + output r0 as arc_0038.aleo/claim_commission.future; + +finalize claim_commission: + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r0; + get total_balance[0u8] into r1; + get total_shares[0u8] into r2; + gt r0 r1 into r3; + sub r0 r1 into r4; + ternary r3 r4 0u64 into r5; + get commission_percent[0u8] into r6; + cast r5 into r7 as u128; + mul r7 r6 into r8; + div r8 1000u128 into r9; + cast r9 into r10 as u64; + sub r5 r10 into r11; + add r1 r11 into r12; + cast r12 into r13 as u128; + cast r10 into r14 as u128; + cast r2 into r15 as u128; + mul r15 1000u128 into r16; + div r16 r13 into r17; + add r13 r14 into r18; + mul r18 r17 into r19; + div r19 1000u128 into r20; + sub r20 r15 into r21; + cast r21 into r22 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r23; + add r23 r22 into r24; + set r24 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r2 r22 into r25; + set r25 into total_shares[0u8]; + add r12 r10 into r26; + set r26 into total_balance[0u8]; + + +function deposit_public: + input r0 as credits.aleo/credits.record; + input r1 as u64.public; + call credits.aleo/transfer_private_to_public r0 aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny r1 into r2 r3; + async deposit_public r3 self.caller r1 into r4; + output r2 as credits.aleo/credits.record; + output r4 as arc_0038.aleo/deposit_public.future; + +finalize deposit_public: + input r0 as credits.aleo/transfer_private_to_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + get total_balance[0u8] into r4; + get total_shares[0u8] into r5; + gt r3 r4 into r6; + sub r3 r4 into r7; + ternary r6 r7 0u64 into r8; + get commission_percent[0u8] into r9; + cast r8 into r10 as u128; + mul r10 r9 into r11; + div r11 1000u128 into r12; + cast r12 into r13 as u64; + sub r8 r13 into r14; + add r4 r14 into r15; + cast r15 into r16 as u128; + cast r13 into r17 as u128; + cast r5 into r18 as u128; + mul r18 1000u128 into r19; + div r19 r16 into r20; + add r16 r17 into r21; + mul r21 r20 into r22; + div r22 1000u128 into r23; + sub r23 r18 into r24; + cast r24 into r25 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; + add r26 r25 into r27; + set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r5 r25 into r28; + add r15 r13 into r29; + cast r29 into r30 as u128; + cast r2 into r31 as u128; + cast r28 into r32 as u128; + mul r32 1000u128 into r33; + div r33 r30 into r34; + add r30 r31 into r35; + mul r35 r34 into r36; + div r36 1000u128 into r37; + sub r37 r32 into r38; + cast r38 into r39 as u64; + gte r39 1u64 into r40; + assert.eq r40 true; + get.or_use delegator_shares[r1] 0u64 into r41; + add r41 r39 into r42; + set r42 into delegator_shares[r1]; + add r28 r39 into r43; + set r43 into total_shares[0u8]; + add r29 r2 into r44; + set r44 into total_balance[0u8]; + + + + +function withdraw_public: + input r0 as u64.public; + input r1 as u64.public; + call credits.aleo/unbond_public r1 into r2; + async withdraw_public r2 r0 r1 self.caller into r3; + output r3 as arc_0038.aleo/withdraw_public.future; + +finalize withdraw_public: + input r0 as credits.aleo/unbond_public.future; + input r1 as u64.public; + input r2 as u64.public; + input r3 as address.public; + await r0; + contains withdrawals[r3] into r4; + assert.eq r4 false; + get.or_use current_batch_height[0u8] 0u32 into r5; + add block.height 360u32 into r6; + is.eq r5 0u32 into r7; + gte r5 r6 into r8; + or r7 r8 into r9; + assert.eq r9 true; + get delegator_shares[r3] into r10; + gte r10 r1 into r11; + assert.eq r11 true; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r12; + get total_balance[0u8] into r13; + get total_shares[0u8] into r14; + gt r12 r13 into r15; + sub r12 r13 into r16; + ternary r15 r16 0u64 into r17; + get commission_percent[0u8] into r18; + cast r17 into r19 as u128; + mul r19 r18 into r20; + div r20 1000u128 into r21; + cast r21 into r22 as u64; + sub r17 r22 into r23; + add r13 r23 into r24; + cast r24 into r25 as u128; + cast r22 into r26 as u128; + cast r14 into r27 as u128; + mul r27 1000u128 into r28; + div r28 r25 into r29; + add r25 r26 into r30; + mul r30 r29 into r31; + div r31 1000u128 into r32; + sub r32 r27 into r33; + cast r33 into r34 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r35; + add r35 r34 into r36; + set r36 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r14 r34 into r37; + add r24 r22 into r38; + cast r1 into r39 as u128; + mul r39 1000u128 into r40; + cast r37 into r41 as u128; + div r40 r41 into r42; + cast r38 into r43 as u128; + mul r43 r42 into r44; + div r44 1000u128 into r45; + cast r2 into r46 as u128; + gte r45 r46 into r47; + assert.eq r47 true; + div block.height 1_000u32 into r48; + mul r48 1_000u32 into r49; + add r49 1_000u32 into r50; + ternary r7 r50 r5 into r51; + set r51 into current_batch_height[0u8]; + cast r2 r51 into r52 as withdrawal_state; + set r52 into withdrawals[r3]; + get pending_withdrawal[0u8] into r53; + add r53 r2 into r54; + set r54 into pending_withdrawal[0u8]; + sub r38 r2 into r55; + set r55 into total_balance[0u8]; + sub r37 r1 into r56; + set r56 into total_shares[0u8]; + sub r10 r1 into r57; + set r57 into delegator_shares[r3]; + + +function create_withdraw_claim: + input r0 as u64.public; + async create_withdraw_claim r0 self.caller into r1; + output r1 as arc_0038.aleo/create_withdraw_claim.future; + +finalize create_withdraw_claim: + input r0 as u64.public; + input r1 as address.public; + contains withdrawals[r1] into r2; + assert.eq r2 false; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; + assert.eq r3 0u64; + get delegator_shares[r1] into r4; + gte r4 r0 into r5; + assert.eq r5 true; + get total_balance[0u8] into r6; + get total_shares[0u8] into r7; + cast r0 into r8 as u128; + mul r8 1000u128 into r9; + cast r7 into r10 as u128; + div r9 r10 into r11; + cast r6 into r12 as u128; + mul r12 r11 into r13; + div r13 1000u128 into r14; + cast r14 into r15 as u64; + cast r15 block.height into r16 as withdrawal_state; + set r16 into withdrawals[r1]; + get pending_withdrawal[0u8] into r17; + add r17 r15 into r18; + set r18 into pending_withdrawal[0u8]; + sub r6 r15 into r19; + set r19 into total_balance[0u8]; + sub r7 r0 into r20; + set r20 into total_shares[0u8]; + sub r4 r0 into r21; + set r21 into delegator_shares[r1]; + + +function claim_withdrawal_public: + input r0 as address.public; + input r1 as u64.public; + call credits.aleo/transfer_public r0 r1 into r2; + async claim_withdrawal_public r2 r0 r1 into r3; + output r3 as arc_0038.aleo/claim_withdrawal_public.future; + +finalize claim_withdrawal_public: + input r0 as credits.aleo/transfer_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get withdrawals[r1] into r3; + gte block.height r3.claim_block into r4; + assert.eq r4 true; + assert.eq r3.microcredits r2; + remove withdrawals[r1]; + get pending_withdrawal[0u8] into r5; + sub r5 r2 into r6; + set r6 into pending_withdrawal[0u8]; diff --git a/arc-0038/arc_0038.leo b/arc-0038/arc_0038.leo new file mode 100644 index 0000000..3f3f79e --- /dev/null +++ b/arc-0038/arc_0038.leo @@ -0,0 +1,427 @@ +import credits.aleo; + +program arc_0038.aleo { + // Owner of the program + const ADMIN: address = aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; + // Address of this program + const CORE_PROTOCOL: address = aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny; + const SHARES_TO_MICROCREDITS: u64 = 1_000u64; + const PRECISION_UNSIGNED: u128 = 1000u128; + const MAX_COMMISSION_RATE: u128 = 500u128; + const UNBONDING_PERIOD: u32 = 360u32; + const MINIMUM_BOND_POOL: u64 = 10_000_000_000u64; + + // 0u8 -> Whether the program has been initialized + mapping is_initialized: u8 => bool; + + /** Commission rate: 0u8 -> u128 + * percentage of rewards taken as commission + * relative to precision of 1000 + * e.g. 100u128 = 10% + */ + mapping commission_percent: u8 => u128; + + // 0u8 -> address of validator + // 1u8 -> the address of the next validator, automatically updated after calling "bond_all" + mapping validator: u8 => address; + + // 0u8 -> total balance of microcredits pooled + mapping total_balance: u8 => u64; + + // 0u8 -> total pool of delegator shares + mapping total_shares: u8 => u64; + + // address -> number of shares held by the delegator with this address + mapping delegator_shares: address => u64; + + // 0u8 -> balance pending withdrawal in microcredits + mapping pending_withdrawal: u8 => u64; + + /** Unbonding allowed: 0u8 -> + * The height at which the current withdrawal batch will be done unbonding + * if not present or == 0u32, a new batch can begin unbonding + */ + mapping current_batch_height: u8 => u32; + + struct withdrawal_state { + microcredits: u64; + claim_block: u32; + } + + // address -> pending withdrawal for the delegator with this address + mapping withdrawals: address => withdrawal_state; + + transition initialize(public commission_rate: u128, public validator_address: address) { + assert_eq(self.caller, ADMIN); + assert(commission_rate < PRECISION_UNSIGNED); + assert(commission_rate <= MAX_COMMISSION_RATE); + + return then finalize(commission_rate, validator_address); + } + + finalize initialize(commission_rate: u128, validator_address: address) { + assert_eq(is_initialized.get(0u8), false); + + commission_percent.set(0u8, commission_rate); + validator.set(0u8, validator_address); + total_shares.set(0u8, 0u64); + total_balance.set(0u8, 0u64); + pending_withdrawal.set(0u8, 0u64); + current_batch_height.set(0u8, 0u32); + } + + transition initial_deposit( + public input_record: credits.aleo/credits, + public microcredits: u64, + public validator_address: address + ) -> credits.aleo/credits { + assert_eq(self.caller, ADMIN); + // Must be a credits record because credits.aleo uses self.caller for transfers + let updated_record: credits.aleo/credits = credits.aleo/transfer_private_to_public(input_record, CORE_PROTOCOL, microcredits); + credits.aleo/bond_public(validator_address, microcredits); + + return (updated_record) then finalize(microcredits); + } + + finalize initial_deposit(microcredits: u64) { + assert(is_initialized.get(0u8)); + + let balance: u64 = total_balance.get_or_use(0u8, 0u64); + let shares: u64 = total_shares.get_or_use(0u8, 0u64); + assert_eq(balance, 0u64); + assert_eq(shares, 0u64); + + total_balance.set(0u8, microcredits); + total_shares.set(0u8, microcredits * SHARES_TO_MICROCREDITS); + delegator_shares.set(ADMIN, microcredits); + } + + inline get_commission( + rewards: u128, + commission_rate: u128, + ) -> u64 { + let commission: u128 = rewards * commission_rate / PRECISION_UNSIGNED; + let commission_64: u64 = commission as u64; + return commission_64; + } + + inline calculate_new_shares(balance: u128, deposit: u128, shares: u128) -> u64 { + let pool_ratio: u128 = ((shares * PRECISION_UNSIGNED) / balance); + let new_total_shares: u128 = (balance + deposit) * pool_ratio; + let diff: u128 = (new_total_shares / PRECISION_UNSIGNED) - shares; + let shares_to_mint: u64 = diff as u64; + return shares_to_mint; + } + + transition set_commission_percent(public new_commission_rate: u128) { + assert_eq(self.caller, ADMIN); + assert(new_commission_rate < PRECISION_UNSIGNED); + assert(new_commission_rate <= MAX_COMMISSION_RATE); + + return then finalize(new_commission_rate); + } + + finalize set_commission_percent(new_commission_rate: u128) { + // Make sure all commission is claimed before changing the rate + let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + + commission_percent.set(0u8, commission_rate); + } + + // Update the validator address, to be applied automatically on the next bond_all call + transition set_next_validator(public validator_address: address) { + assert_eq(self.caller, ADMIN); + + return then finalize(validator_address); + } + + finalize set_next_validator(validator_address: address) { + validator.set(1u8, validator_address); + } + + transition unbond_all(public pool_balance: u64) { + credits.aleo/unbond_public(pool_balance); + + return then finalize(pool_balance); + } + + finalize unbond_all(pool_balance: u64) { + let next_validator: bool = validator.contains(1u8); + assert(next_validator); + + // Make sure all commission is claimed before unbonding + let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + + // Assert that the pool will be fully unbonded + let residual_balance: u64 = bonded - pool_balance; + assert(residual_balance < MINIMUM_BOND_POOL); + } + + transition claim_unbond() { + credits.aleo/claim_unbond_public(); + + return then finalize(); + } + + finalize claim_unbond() { + current_batch_height.remove(0u8); + } + + transition bond_all(public validator_address: address, public amount: u64) { + // Call will fail if there is any balance still bonded to another validator + credits.aleo/bond_public(validator_address, amount); + + return then finalize(validator_address, amount); + } + + finalize bond_all(validator_address: address, amount: u64) { + let unbonding_balance: u64 = pending_withdrawal.get(1u8); // credits.aleo/unbonding.get(CORE_PROTOCOL); + assert_eq(unbonding_balance, 0u64); + + let account_balance: u64 = pending_withdrawal.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); + let pending_withdrawals: u64 = pending_withdrawal.get(0u8); + let available_balance: u64 = account_balance - pending_withdrawals; + assert_eq(amount, available_balance); + + // Set validator + let has_next_validator: bool = validator.contains(1u8); + let current_validator: address = has_next_validator ? validator.get(1u8) : validator.get(0u8); + assert_eq(validator_address, current_validator); + + validator.set(0u8, current_validator); + validator.remove(1u8); + } + + transition claim_commission() { + assert_eq(self.caller, ADMIN); + return then finalize(); + } + + finalize claim_commission() { + // Distribute shares for new commission + let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + } + + transition deposit_public( + public input_record: credits.aleo/credits, + public microcredits: u64 + ) -> credits.aleo/credits { + // Must be a credits record because credits.aleo uses self.caller for transfers + let updated_record: credits.aleo/credits = credits.aleo/transfer_private_to_public(input_record, CORE_PROTOCOL, microcredits); + + return (updated_record) then finalize(self.caller, microcredits); + } + + finalize deposit_public( + caller: address, + microcredits: u64 + ) { + // Distribute shares for new commission + let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + current_shares += new_commission_shares; + current_balance += new_commission; + + // Calculate mint for deposit + let new_shares: u64 = calculate_new_shares(current_balance as u128, microcredits as u128, current_shares as u128); + + // Ensure mint amount is valid + assert(new_shares >= 1u64); + + // Update delegator_shares mapping + let shares: u64 = delegator_shares.get_or_use(caller, 0u64); + delegator_shares.set(caller, shares + new_shares); + + // Update total shares + total_shares.set(0u8, current_shares + new_shares); + + // Update total_balance + total_balance.set(0u8, current_balance + microcredits); + } + + transition withdraw_public(public withdrawal_shares: u64, public total_withdrawal: u64) { + credits.aleo/unbond_public(total_withdrawal); + + return then finalize(withdrawal_shares, total_withdrawal, self.caller); + } + + finalize withdraw_public(withdrawal_shares: u64, total_withdrawal: u64, owner: address) { + // Assert that they don't have any pending withdrawals + let currently_withdrawing: bool = withdrawals.contains(owner); + assert_eq(currently_withdrawing, false); + + // Determine if the withdrawal can fit into the current batch + let current_batch: u32 = current_batch_height.get_or_use(0u8, 0u32); + let min_claim_height: u32 = block.height + UNBONDING_PERIOD; + let new_batch: bool = current_batch == 0u32; + let unbonding_allowed: bool = new_batch || current_batch >= min_claim_height; + assert(unbonding_allowed); + + // Assert that they have enough to withdraw + let delegator_balance: u64 = delegator_shares.get(owner); + assert(delegator_balance >= withdrawal_shares); + + // Distribute shares for new commission + let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + current_shares += new_commission_shares; + current_balance += new_commission; + + // Calculate withdrawal amount + let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; + let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + + // If the calculated withdrawal amount is greater than total_withdrawal, the excess will stay in the pool + assert(withdrawal_calculation >= total_withdrawal as u128); + + // Update withdrawals mappings + let batch_height: u32 = new_batch ? get_new_batch_height(block.height) : current_batch; + current_batch_height.set(0u8, batch_height); + let withdrawal: withdrawal_state = withdrawal_state { + microcredits: total_withdrawal, + claim_block: batch_height + }; + withdrawals.set(owner, withdrawal); + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + + // Update total balance + total_balance.set(0u8, current_balance - total_withdrawal); + + // Update total shares + total_shares.set(0u8, current_shares - withdrawal_shares); + + // Update delegator_shares mapping + delegator_shares.set(owner, delegator_balance - withdrawal_shares); + } + + inline get_new_batch_height(height: u32) -> u32 { + let rounded_down: u32 = (height) / 1_000u32 * 1_000u32; + let rounded_up: u32 = rounded_down + 1_000u32; + return rounded_up; + } + + transition create_withdraw_claim(public withdrawal_shares: u64) { + return then finalize(withdrawal_shares, self.caller); + } + + finalize create_withdraw_claim(withdrawal_shares: u64, owner: address) { + // Assert that they don't have any pending withdrawals + let currently_withdrawing: bool = withdrawals.contains(owner); + assert_eq(currently_withdrawing, false); + + let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + assert_eq(bonded, 0u64); + + // Assert that they have enough to withdraw + let delegator_balance: u64 = delegator_shares.get(owner); + assert(delegator_balance >= withdrawal_shares); + + // Calculate withdrawal amount + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; + let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + let total_withdrawal: u64 = withdrawal_calculation as u64; + + // Update withdrawals mappings + let withdrawal: withdrawal_state = withdrawal_state { + microcredits: total_withdrawal, + claim_block: block.height + }; + withdrawals.set(owner, withdrawal); + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + + // Update total balance + total_balance.set(0u8, current_balance - total_withdrawal); + + // Update total shares + total_shares.set(0u8, current_shares - withdrawal_shares); + + // Update delegator_shares mapping + delegator_shares.set(owner, delegator_balance - withdrawal_shares); + } + + transition claim_withdrawal_public(public recipient: address, public amount: u64) { + credits.aleo/transfer_public(recipient, amount); + + return then finalize(recipient, amount); + } + + finalize claim_withdrawal_public(owner: address, amount: u64) { + let withdrawal: withdrawal_state = withdrawals.get(owner); + assert(block.height >= withdrawal.claim_block); + assert_eq(withdrawal.microcredits, amount); + + // Remove withdrawal + withdrawals.remove(owner); + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending - amount); + } +} \ No newline at end of file From 29345d33e87c8a8f0d8ff4c57b64360d5895e274 Mon Sep 17 00:00:00 2001 From: Chris Womack Date: Thu, 21 Mar 2024 14:19:17 -0400 Subject: [PATCH 2/4] Update resource files --- arc-0038/arc_0038.aleo | 475 +++++++++++++++++++++-------------------- arc-0038/arc_0038.leo | 53 ++++- 2 files changed, 285 insertions(+), 243 deletions(-) diff --git a/arc-0038/arc_0038.aleo b/arc-0038/arc_0038.aleo index 2c71173..f057a50 100644 --- a/arc-0038/arc_0038.aleo +++ b/arc-0038/arc_0038.aleo @@ -119,37 +119,38 @@ function set_commission_percent: finalize set_commission_percent: input r0 as u128.public; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r1; - get total_balance[0u8] into r2; - get total_shares[0u8] into r3; - gt r1 r2 into r4; - sub r1 r2 into r5; - ternary r4 r5 0u64 into r6; - get commission_percent[0u8] into r7; - cast r6 into r8 as u128; - mul r8 r7 into r9; - div r9 1000u128 into r10; - cast r10 into r11 as u64; - sub r6 r11 into r12; - add r2 r12 into r13; - cast r13 into r14 as u128; - cast r11 into r15 as u128; - cast r3 into r16 as u128; - mul r16 1000u128 into r17; - div r17 r14 into r18; - add r14 r15 into r19; - mul r19 r18 into r20; - div r20 1000u128 into r21; - sub r21 r16 into r22; - cast r22 into r23 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; - add r24 r23 into r25; - set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r3 r23 into r26; - set r26 into total_shares[0u8]; - add r13 r11 into r27; - set r27 into total_balance[0u8]; - set r7 into commission_percent[0u8]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r1 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r1 into r2; + get total_balance[0u8] into r3; + get total_shares[0u8] into r4; + gt r2.microcredits r3 into r5; + sub r2.microcredits r3 into r6; + ternary r5 r6 0u64 into r7; + get commission_percent[0u8] into r8; + cast r7 into r9 as u128; + mul r9 r8 into r10; + div r10 1000u128 into r11; + cast r11 into r12 as u64; + sub r7 r12 into r13; + add r3 r13 into r14; + cast r14 into r15 as u128; + cast r12 into r16 as u128; + cast r4 into r17 as u128; + mul r17 1000u128 into r18; + div r18 r15 into r19; + add r15 r16 into r20; + mul r20 r19 into r21; + div r21 1000u128 into r22; + sub r22 r17 into r23; + cast r23 into r24 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r25; + add r25 r24 into r26; + set r26 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r4 r24 into r27; + set r27 into total_shares[0u8]; + add r14 r12 into r28; + set r28 into total_balance[0u8]; + set r0 into commission_percent[0u8]; function set_next_validator: @@ -175,39 +176,40 @@ finalize unbond_all: await r0; contains validator[1u8] into r2; assert.eq r2 true; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - get total_balance[0u8] into r4; - get total_shares[0u8] into r5; - gt r3 r4 into r6; - sub r3 r4 into r7; - ternary r6 r7 0u64 into r8; - get commission_percent[0u8] into r9; - cast r8 into r10 as u128; - mul r10 r9 into r11; - div r11 1000u128 into r12; - cast r12 into r13 as u64; - sub r8 r13 into r14; - add r4 r14 into r15; - cast r15 into r16 as u128; - cast r13 into r17 as u128; - cast r5 into r18 as u128; - mul r18 1000u128 into r19; - div r19 r16 into r20; - add r16 r17 into r21; - mul r21 r20 into r22; - div r22 1000u128 into r23; - sub r23 r18 into r24; - cast r24 into r25 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; - add r26 r25 into r27; - set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r5 r25 into r28; - set r28 into total_shares[0u8]; - add r15 r13 into r29; - set r29 into total_balance[0u8]; - sub r3 r1 into r30; - lt r30 10_000_000_000u64 into r31; - assert.eq r31 true; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + get total_balance[0u8] into r5; + get total_shares[0u8] into r6; + gt r4.microcredits r5 into r7; + sub r4.microcredits r5 into r8; + ternary r7 r8 0u64 into r9; + get commission_percent[0u8] into r10; + cast r9 into r11 as u128; + mul r11 r10 into r12; + div r12 1000u128 into r13; + cast r13 into r14 as u64; + sub r9 r14 into r15; + add r5 r15 into r16; + cast r16 into r17 as u128; + cast r14 into r18 as u128; + cast r6 into r19 as u128; + mul r19 1000u128 into r20; + div r20 r17 into r21; + add r17 r18 into r22; + mul r22 r21 into r23; + div r23 1000u128 into r24; + sub r24 r19 into r25; + cast r25 into r26 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; + add r27 r26 into r28; + set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r6 r26 into r29; + set r29 into total_shares[0u8]; + add r16 r14 into r30; + set r30 into total_balance[0u8]; + sub r4.microcredits r1 into r31; + lt r31 10_000_000_000u64 into r32; + assert.eq r32 true; function claim_unbond: @@ -233,18 +235,19 @@ finalize bond_all: input r1 as address.public; input r2 as u64.public; await r0; - get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - assert.eq r3 0u64; - get.or_use credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r4; - get pending_withdrawal[0u8] into r5; - sub r4 r5 into r6; - assert.eq r2 r6; - contains validator[1u8] into r7; - get validator[1u8] into r8; - get validator[0u8] into r9; - ternary r7 r8 r9 into r10; - assert.eq r1 r10; - set r10 into validator[0u8]; + cast 0u64 0u32 into r3 as credits.aleo/unbond_state; + get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + assert.eq r4.microcredits 0u64; + get credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] into r5; + get pending_withdrawal[0u8] into r6; + sub r5 r6 into r7; + assert.eq r2 r7; + contains validator[1u8] into r8; + get validator[1u8] into r9; + get validator[0u8] into r10; + ternary r8 r9 r10 into r11; + assert.eq r1 r11; + set r11 into validator[0u8]; remove validator[1u8]; @@ -254,36 +257,37 @@ function claim_commission: output r0 as arc_0038.aleo/claim_commission.future; finalize claim_commission: - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r0; - get total_balance[0u8] into r1; - get total_shares[0u8] into r2; - gt r0 r1 into r3; - sub r0 r1 into r4; - ternary r3 r4 0u64 into r5; - get commission_percent[0u8] into r6; - cast r5 into r7 as u128; - mul r7 r6 into r8; - div r8 1000u128 into r9; - cast r9 into r10 as u64; - sub r5 r10 into r11; - add r1 r11 into r12; - cast r12 into r13 as u128; - cast r10 into r14 as u128; - cast r2 into r15 as u128; - mul r15 1000u128 into r16; - div r16 r13 into r17; - add r13 r14 into r18; - mul r18 r17 into r19; - div r19 1000u128 into r20; - sub r20 r15 into r21; - cast r21 into r22 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r23; - add r23 r22 into r24; - set r24 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r2 r22 into r25; - set r25 into total_shares[0u8]; - add r12 r10 into r26; - set r26 into total_balance[0u8]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r0 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r0 into r1; + get total_balance[0u8] into r2; + get total_shares[0u8] into r3; + gt r1.microcredits r2 into r4; + sub r1.microcredits r2 into r5; + ternary r4 r5 0u64 into r6; + get commission_percent[0u8] into r7; + cast r6 into r8 as u128; + mul r8 r7 into r9; + div r9 1000u128 into r10; + cast r10 into r11 as u64; + sub r6 r11 into r12; + add r2 r12 into r13; + cast r13 into r14 as u128; + cast r11 into r15 as u128; + cast r3 into r16 as u128; + mul r16 1000u128 into r17; + div r17 r14 into r18; + add r14 r15 into r19; + mul r19 r18 into r20; + div r20 1000u128 into r21; + sub r21 r16 into r22; + cast r22 into r23 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; + add r24 r23 into r25; + set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r3 r23 into r26; + set r26 into total_shares[0u8]; + add r13 r11 into r27; + set r27 into total_balance[0u8]; function deposit_public: @@ -299,53 +303,54 @@ finalize deposit_public: input r1 as address.public; input r2 as u64.public; await r0; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - get total_balance[0u8] into r4; - get total_shares[0u8] into r5; - gt r3 r4 into r6; - sub r3 r4 into r7; - ternary r6 r7 0u64 into r8; - get commission_percent[0u8] into r9; - cast r8 into r10 as u128; - mul r10 r9 into r11; - div r11 1000u128 into r12; - cast r12 into r13 as u64; - sub r8 r13 into r14; - add r4 r14 into r15; - cast r15 into r16 as u128; - cast r13 into r17 as u128; - cast r5 into r18 as u128; - mul r18 1000u128 into r19; - div r19 r16 into r20; - add r16 r17 into r21; - mul r21 r20 into r22; - div r22 1000u128 into r23; - sub r23 r18 into r24; - cast r24 into r25 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; - add r26 r25 into r27; - set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r5 r25 into r28; - add r15 r13 into r29; - cast r29 into r30 as u128; - cast r2 into r31 as u128; - cast r28 into r32 as u128; - mul r32 1000u128 into r33; - div r33 r30 into r34; - add r30 r31 into r35; - mul r35 r34 into r36; - div r36 1000u128 into r37; - sub r37 r32 into r38; - cast r38 into r39 as u64; - gte r39 1u64 into r40; - assert.eq r40 true; - get.or_use delegator_shares[r1] 0u64 into r41; - add r41 r39 into r42; - set r42 into delegator_shares[r1]; - add r28 r39 into r43; - set r43 into total_shares[0u8]; - add r29 r2 into r44; - set r44 into total_balance[0u8]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + get total_balance[0u8] into r5; + get total_shares[0u8] into r6; + gt r4.microcredits r5 into r7; + sub r4.microcredits r5 into r8; + ternary r7 r8 0u64 into r9; + get commission_percent[0u8] into r10; + cast r9 into r11 as u128; + mul r11 r10 into r12; + div r12 1000u128 into r13; + cast r13 into r14 as u64; + sub r9 r14 into r15; + add r5 r15 into r16; + cast r16 into r17 as u128; + cast r14 into r18 as u128; + cast r6 into r19 as u128; + mul r19 1000u128 into r20; + div r20 r17 into r21; + add r17 r18 into r22; + mul r22 r21 into r23; + div r23 1000u128 into r24; + sub r24 r19 into r25; + cast r25 into r26 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; + add r27 r26 into r28; + set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r6 r26 into r29; + add r16 r14 into r30; + cast r30 into r31 as u128; + cast r2 into r32 as u128; + cast r29 into r33 as u128; + mul r33 1000u128 into r34; + div r34 r31 into r35; + add r31 r32 into r36; + mul r36 r35 into r37; + div r37 1000u128 into r38; + sub r38 r33 into r39; + cast r39 into r40 as u64; + gte r40 1u64 into r41; + assert.eq r41 true; + get.or_use delegator_shares[r1] 0u64 into r42; + add r42 r40 into r43; + set r43 into delegator_shares[r1]; + add r29 r40 into r44; + set r44 into total_shares[0u8]; + add r30 r2 into r45; + set r45 into total_balance[0u8]; @@ -374,60 +379,61 @@ finalize withdraw_public: get delegator_shares[r3] into r10; gte r10 r1 into r11; assert.eq r11 true; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r12; - get total_balance[0u8] into r13; - get total_shares[0u8] into r14; - gt r12 r13 into r15; - sub r12 r13 into r16; - ternary r15 r16 0u64 into r17; - get commission_percent[0u8] into r18; - cast r17 into r19 as u128; - mul r19 r18 into r20; - div r20 1000u128 into r21; - cast r21 into r22 as u64; - sub r17 r22 into r23; - add r13 r23 into r24; - cast r24 into r25 as u128; - cast r22 into r26 as u128; - cast r14 into r27 as u128; - mul r27 1000u128 into r28; - div r28 r25 into r29; - add r25 r26 into r30; - mul r30 r29 into r31; - div r31 1000u128 into r32; - sub r32 r27 into r33; - cast r33 into r34 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r35; - add r35 r34 into r36; - set r36 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r14 r34 into r37; - add r24 r22 into r38; - cast r1 into r39 as u128; - mul r39 1000u128 into r40; - cast r37 into r41 as u128; - div r40 r41 into r42; - cast r38 into r43 as u128; - mul r43 r42 into r44; - div r44 1000u128 into r45; - cast r2 into r46 as u128; - gte r45 r46 into r47; - assert.eq r47 true; - div block.height 1_000u32 into r48; - mul r48 1_000u32 into r49; - add r49 1_000u32 into r50; - ternary r7 r50 r5 into r51; - set r51 into current_batch_height[0u8]; - cast r2 r51 into r52 as withdrawal_state; - set r52 into withdrawals[r3]; - get pending_withdrawal[0u8] into r53; - add r53 r2 into r54; - set r54 into pending_withdrawal[0u8]; - sub r38 r2 into r55; - set r55 into total_balance[0u8]; - sub r37 r1 into r56; - set r56 into total_shares[0u8]; - sub r10 r1 into r57; - set r57 into delegator_shares[r3]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r12 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r12 into r13; + get total_balance[0u8] into r14; + get total_shares[0u8] into r15; + gt r13.microcredits r14 into r16; + sub r13.microcredits r14 into r17; + ternary r16 r17 0u64 into r18; + get commission_percent[0u8] into r19; + cast r18 into r20 as u128; + mul r20 r19 into r21; + div r21 1000u128 into r22; + cast r22 into r23 as u64; + sub r18 r23 into r24; + add r14 r24 into r25; + cast r25 into r26 as u128; + cast r23 into r27 as u128; + cast r15 into r28 as u128; + mul r28 1000u128 into r29; + div r29 r26 into r30; + add r26 r27 into r31; + mul r31 r30 into r32; + div r32 1000u128 into r33; + sub r33 r28 into r34; + cast r34 into r35 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r36; + add r36 r35 into r37; + set r37 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r15 r35 into r38; + add r25 r23 into r39; + cast r1 into r40 as u128; + mul r40 1000u128 into r41; + cast r38 into r42 as u128; + div r41 r42 into r43; + cast r39 into r44 as u128; + mul r44 r43 into r45; + div r45 1000u128 into r46; + cast r2 into r47 as u128; + gte r46 r47 into r48; + assert.eq r48 true; + div block.height 1_000u32 into r49; + mul r49 1_000u32 into r50; + add r50 1_000u32 into r51; + ternary r7 r51 r5 into r52; + set r52 into current_batch_height[0u8]; + cast r2 r52 into r53 as withdrawal_state; + set r53 into withdrawals[r3]; + get pending_withdrawal[0u8] into r54; + add r54 r2 into r55; + set r55 into pending_withdrawal[0u8]; + sub r39 r2 into r56; + set r56 into total_balance[0u8]; + sub r38 r1 into r57; + set r57 into total_shares[0u8]; + sub r10 r1 into r58; + set r58 into delegator_shares[r3]; function create_withdraw_claim: @@ -440,32 +446,33 @@ finalize create_withdraw_claim: input r1 as address.public; contains withdrawals[r1] into r2; assert.eq r2 false; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - assert.eq r3 0u64; - get delegator_shares[r1] into r4; - gte r4 r0 into r5; - assert.eq r5 true; - get total_balance[0u8] into r6; - get total_shares[0u8] into r7; - cast r0 into r8 as u128; - mul r8 1000u128 into r9; - cast r7 into r10 as u128; - div r9 r10 into r11; - cast r6 into r12 as u128; - mul r12 r11 into r13; - div r13 1000u128 into r14; - cast r14 into r15 as u64; - cast r15 block.height into r16 as withdrawal_state; - set r16 into withdrawals[r1]; - get pending_withdrawal[0u8] into r17; - add r17 r15 into r18; - set r18 into pending_withdrawal[0u8]; - sub r6 r15 into r19; - set r19 into total_balance[0u8]; - sub r7 r0 into r20; - set r20 into total_shares[0u8]; - sub r4 r0 into r21; - set r21 into delegator_shares[r1]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + assert.eq r4.microcredits 0u64; + get delegator_shares[r1] into r5; + gte r5 r0 into r6; + assert.eq r6 true; + get total_balance[0u8] into r7; + get total_shares[0u8] into r8; + cast r0 into r9 as u128; + mul r9 1000u128 into r10; + cast r8 into r11 as u128; + div r10 r11 into r12; + cast r7 into r13 as u128; + mul r13 r12 into r14; + div r14 1000u128 into r15; + cast r15 into r16 as u64; + cast r16 block.height into r17 as withdrawal_state; + set r17 into withdrawals[r1]; + get pending_withdrawal[0u8] into r18; + add r18 r16 into r19; + set r19 into pending_withdrawal[0u8]; + sub r7 r16 into r20; + set r20 into total_balance[0u8]; + sub r8 r0 into r21; + set r21 into total_shares[0u8]; + sub r5 r0 into r22; + set r22 into delegator_shares[r1]; function claim_withdrawal_public: @@ -487,4 +494,4 @@ finalize claim_withdrawal_public: remove withdrawals[r1]; get pending_withdrawal[0u8] into r5; sub r5 r2 into r6; - set r6 into pending_withdrawal[0u8]; + set r6 into pending_withdrawal[0u8]; \ No newline at end of file diff --git a/arc-0038/arc_0038.leo b/arc-0038/arc_0038.leo index 3f3f79e..0b56daa 100644 --- a/arc-0038/arc_0038.leo +++ b/arc-0038/arc_0038.leo @@ -123,7 +123,12 @@ program arc_0038.aleo { finalize set_commission_percent(new_commission_rate: u128) { // Make sure all commission is claimed before changing the rate - let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -138,7 +143,7 @@ program arc_0038.aleo { total_shares.set(0u8, current_shares + new_commission_shares); total_balance.set(0u8, current_balance + new_commission); - commission_percent.set(0u8, commission_rate); + commission_percent.set(0u8, new_commission_rate); } // Update the validator address, to be applied automatically on the next bond_all call @@ -163,7 +168,12 @@ program arc_0038.aleo { assert(next_validator); // Make sure all commission is claimed before unbonding - let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -201,10 +211,15 @@ program arc_0038.aleo { } finalize bond_all(validator_address: address, amount: u64) { - let unbonding_balance: u64 = pending_withdrawal.get(1u8); // credits.aleo/unbonding.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let unbonding_balance: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; assert_eq(unbonding_balance, 0u64); - let account_balance: u64 = pending_withdrawal.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); + let account_balance: u64 = total_balance.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); let pending_withdrawals: u64 = pending_withdrawal.get(0u8); let available_balance: u64 = account_balance - pending_withdrawals; assert_eq(amount, available_balance); @@ -225,7 +240,12 @@ program arc_0038.aleo { finalize claim_commission() { // Distribute shares for new commission - let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -256,7 +276,12 @@ program arc_0038.aleo { microcredits: u64 ) { // Distribute shares for new commission - let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -311,7 +336,12 @@ program arc_0038.aleo { assert(delegator_balance >= withdrawal_shares); // Distribute shares for new commission - let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -371,7 +401,12 @@ program arc_0038.aleo { let currently_withdrawing: bool = withdrawals.contains(owner); assert_eq(currently_withdrawing, false); - let bonded: u64 = pending_withdrawal.get(1u8); // credits.aleo/bonded.get(CORE_PROTOCOL); + // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; assert_eq(bonded, 0u64); // Assert that they have enough to withdraw From 634ada73d74a4b6ece4da7625296f215a3081cc2 Mon Sep 17 00:00:00 2001 From: Chris Womack Date: Thu, 21 Mar 2024 14:34:08 -0400 Subject: [PATCH 3/4] Update README --- arc-0038/README.md | 963 +++++++++++++++++++++++---------------------- 1 file changed, 482 insertions(+), 481 deletions(-) diff --git a/arc-0038/README.md b/arc-0038/README.md index acd3cb1..bd3cbe1 100644 --- a/arc-0038/README.md +++ b/arc-0038/README.md @@ -284,37 +284,38 @@ function set_commission_percent: finalize set_commission_percent: input r0 as u128.public; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r1; - get total_balance[0u8] into r2; - get total_shares[0u8] into r3; - gt r1 r2 into r4; - sub r1 r2 into r5; - ternary r4 r5 0u64 into r6; - get commission_percent[0u8] into r7; - cast r6 into r8 as u128; - mul r8 r7 into r9; - div r9 1000u128 into r10; - cast r10 into r11 as u64; - sub r6 r11 into r12; - add r2 r12 into r13; - cast r13 into r14 as u128; - cast r11 into r15 as u128; - cast r3 into r16 as u128; - mul r16 1000u128 into r17; - div r17 r14 into r18; - add r14 r15 into r19; - mul r19 r18 into r20; - div r20 1000u128 into r21; - sub r21 r16 into r22; - cast r22 into r23 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; - add r24 r23 into r25; - set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r3 r23 into r26; - set r26 into total_shares[0u8]; - add r13 r11 into r27; - set r27 into total_balance[0u8]; - set r7 into commission_percent[0u8]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r1 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r1 into r2; + get total_balance[0u8] into r3; + get total_shares[0u8] into r4; + gt r2.microcredits r3 into r5; + sub r2.microcredits r3 into r6; + ternary r5 r6 0u64 into r7; + get commission_percent[0u8] into r8; + cast r7 into r9 as u128; + mul r9 r8 into r10; + div r10 1000u128 into r11; + cast r11 into r12 as u64; + sub r7 r12 into r13; + add r3 r13 into r14; + cast r14 into r15 as u128; + cast r12 into r16 as u128; + cast r4 into r17 as u128; + mul r17 1000u128 into r18; + div r18 r15 into r19; + add r15 r16 into r20; + mul r20 r19 into r21; + div r21 1000u128 into r22; + sub r22 r17 into r23; + cast r23 into r24 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r25; + add r25 r24 into r26; + set r26 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r4 r24 into r27; + set r27 into total_shares[0u8]; + add r14 r12 into r28; + set r28 into total_balance[0u8]; + set r0 into commission_percent[0u8]; ``` ```leo transition set_commission_percent(new_commission_rate: u128) { @@ -328,26 +329,28 @@ finalize set_commission_percent: finalize set_commission_percent(new_commission_rate: u128) { - // Make sure all commission is claimed before changing the rate - let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); - let current_balance: u64 = total_balance.get(0u8); - let current_shares: u64 = total_shares.get(0u8); - let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; - let commission_rate: u128 = commission_percent.get(0u8); - let new_commission: u64 = get_commission(rewards as u128, commission_rate); - current_balance += rewards - new_commission; - - - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); - let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); - delegator_shares.set(ADMIN, current_commission + new_commission_shares); - - - total_shares.set(0u8, current_shares + new_commission_shares); - total_balance.set(0u8, current_balance + new_commission); - - - commission_percent.set(0u8, commission_rate); + // Make sure all commission is claimed before changing the rate + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + + commission_percent.set(0u8, new_commission_rate); } ``` #### Set Next Validator @@ -400,39 +403,40 @@ finalize unbond_all: await r0; contains validator[1u8] into r2; assert.eq r2 true; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - get total_balance[0u8] into r4; - get total_shares[0u8] into r5; - gt r3 r4 into r6; - sub r3 r4 into r7; - ternary r6 r7 0u64 into r8; - get commission_percent[0u8] into r9; - cast r8 into r10 as u128; - mul r10 r9 into r11; - div r11 1000u128 into r12; - cast r12 into r13 as u64; - sub r8 r13 into r14; - add r4 r14 into r15; - cast r15 into r16 as u128; - cast r13 into r17 as u128; - cast r5 into r18 as u128; - mul r18 1000u128 into r19; - div r19 r16 into r20; - add r16 r17 into r21; - mul r21 r20 into r22; - div r22 1000u128 into r23; - sub r23 r18 into r24; - cast r24 into r25 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; - add r26 r25 into r27; - set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r5 r25 into r28; - set r28 into total_shares[0u8]; - add r15 r13 into r29; - set r29 into total_balance[0u8]; - sub r3 r1 into r30; - lt r30 10_000_000_000u64 into r31; - assert.eq r31 true; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + get total_balance[0u8] into r5; + get total_shares[0u8] into r6; + gt r4.microcredits r5 into r7; + sub r4.microcredits r5 into r8; + ternary r7 r8 0u64 into r9; + get commission_percent[0u8] into r10; + cast r9 into r11 as u128; + mul r11 r10 into r12; + div r12 1000u128 into r13; + cast r13 into r14 as u64; + sub r9 r14 into r15; + add r5 r15 into r16; + cast r16 into r17 as u128; + cast r14 into r18 as u128; + cast r6 into r19 as u128; + mul r19 1000u128 into r20; + div r20 r17 into r21; + add r17 r18 into r22; + mul r22 r21 into r23; + div r23 1000u128 into r24; + sub r24 r19 into r25; + cast r25 into r26 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; + add r27 r26 into r28; + set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r6 r26 into r29; + set r29 into total_shares[0u8]; + add r16 r14 into r30; + set r30 into total_balance[0u8]; + sub r4.microcredits r1 into r31; + lt r31 10_000_000_000u64 into r32; + assert.eq r32 true; ``` ```leo transition unbond_all(pool_balance: u64) { @@ -444,32 +448,33 @@ finalize unbond_all: finalize unbond_all(pool_balance: u64) { - let next_validator: bool = validator.contains(1u8); - assert(next_validator); - - - // Make sure all commission is claimed before unbonding - let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); - let current_balance: u64 = total_balance.get(0u8); - let current_shares: u64 = total_shares.get(0u8); - let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; - let commission_rate: u128 = commission_percent.get(0u8); - let new_commission: u64 = get_commission(rewards as u128, commission_rate); - current_balance += rewards - new_commission; - - - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); - let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); - delegator_shares.set(ADMIN, current_commission + new_commission_shares); - - - total_shares.set(0u8, current_shares + new_commission_shares); - total_balance.set(0u8, current_balance + new_commission); - - - // Assert that the pool will be fully unbonded - let residual_balance: u64 = bonded - pool_balance; - assert(residual_balance < MINIMUM_BOND_POOL); + let next_validator: bool = validator.contains(1u8); + assert(next_validator); + + // Make sure all commission is claimed before unbonding + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); + + // Assert that the pool will be fully unbonded + let residual_balance: u64 = bonded - pool_balance; + assert(residual_balance < MINIMUM_BOND_POOL); } ``` #### Claim Unbond @@ -523,18 +528,19 @@ finalize bond_all: input r1 as address.public; input r2 as u64.public; await r0; - get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - assert.eq r3 0u64; - get.or_use credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r4; - get pending_withdrawal[0u8] into r5; - sub r4 r5 into r6; - assert.eq r2 r6; - contains validator[1u8] into r7; - get validator[1u8] into r8; - get validator[0u8] into r9; - ternary r7 r8 r9 into r10; - assert.eq r1 r10; - set r10 into validator[0u8]; + cast 0u64 0u32 into r3 as credits.aleo/unbond_state; + get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + assert.eq r4.microcredits 0u64; + get credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] into r5; + get pending_withdrawal[0u8] into r6; + sub r5 r6 into r7; + assert.eq r2 r7; + contains validator[1u8] into r8; + get validator[1u8] into r9; + get validator[0u8] into r10; + ternary r8 r9 r10 into r11; + assert.eq r1 r11; + set r11 into validator[0u8]; remove validator[1u8]; ``` ```leo @@ -550,24 +556,26 @@ finalize bond_all: finalize bond_all(validator_address: address, amount: u64) { - let unbonding_balance: u64 = 0u64; // credits.aleo/unbonding.get(CORE_PROTOCOL); - assert_eq(unbonding_balance, 0u64); - - - let account_balance: u64 = 0u64; // credits.aleo/account.get(CORE_PROTOCOL); - let pending_withdrawals: u64 = pending_withdrawal.get(0u8); - let available_balance: u64 = account_balance - pending_withdrawals; - assert_eq(amount, available_balance); - - - // Set validator - let has_next_validator: bool = validator.contains(1u8); - let current_validator: address = has_next_validator ? validator.get(1u8) : validator.get(0u8); - assert_eq(validator_address, current_validator); - - - validator.set(0u8, current_validator); - validator.remove(1u8); + // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let unbonding_balance: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + assert_eq(unbonding_balance, 0u64); + + let account_balance: u64 = total_balance.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); + let pending_withdrawals: u64 = pending_withdrawal.get(0u8); + let available_balance: u64 = account_balance - pending_withdrawals; + assert_eq(amount, available_balance); + + // Set validator + let has_next_validator: bool = validator.contains(1u8); + let current_validator: address = has_next_validator ? validator.get(1u8) : validator.get(0u8); + assert_eq(validator_address, current_validator); + + validator.set(0u8, current_validator); + validator.remove(1u8); } ``` #### Claim Commission @@ -583,36 +591,37 @@ function claim_commission: finalize claim_commission: - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r0; - get total_balance[0u8] into r1; - get total_shares[0u8] into r2; - gt r0 r1 into r3; - sub r0 r1 into r4; - ternary r3 r4 0u64 into r5; - get commission_percent[0u8] into r6; - cast r5 into r7 as u128; - mul r7 r6 into r8; - div r8 1000u128 into r9; - cast r9 into r10 as u64; - sub r5 r10 into r11; - add r1 r11 into r12; - cast r12 into r13 as u128; - cast r10 into r14 as u128; - cast r2 into r15 as u128; - mul r15 1000u128 into r16; - div r16 r13 into r17; - add r13 r14 into r18; - mul r18 r17 into r19; - div r19 1000u128 into r20; - sub r20 r15 into r21; - cast r21 into r22 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r23; - add r23 r22 into r24; - set r24 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r2 r22 into r25; - set r25 into total_shares[0u8]; - add r12 r10 into r26; - set r26 into total_balance[0u8]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r0 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r0 into r1; + get total_balance[0u8] into r2; + get total_shares[0u8] into r3; + gt r1.microcredits r2 into r4; + sub r1.microcredits r2 into r5; + ternary r4 r5 0u64 into r6; + get commission_percent[0u8] into r7; + cast r6 into r8 as u128; + mul r8 r7 into r9; + div r9 1000u128 into r10; + cast r10 into r11 as u64; + sub r6 r11 into r12; + add r2 r12 into r13; + cast r13 into r14 as u128; + cast r11 into r15 as u128; + cast r3 into r16 as u128; + mul r16 1000u128 into r17; + div r17 r14 into r18; + add r14 r15 into r19; + mul r19 r18 into r20; + div r20 1000u128 into r21; + sub r21 r16 into r22; + cast r22 into r23 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; + add r24 r23 into r25; + set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r3 r23 into r26; + set r26 into total_shares[0u8]; + add r13 r11 into r27; + set r27 into total_balance[0u8]; ``` ```leo transition claim_commission() { @@ -622,23 +631,26 @@ finalize claim_commission: finalize claim_commission() { - // Distribute shares for new commission - let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); - let current_balance: u64 = total_balance.get(0u8); - let current_shares: u64 = total_shares.get(0u8); - let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; - let commission_rate: u128 = commission_percent.get(0u8); - let new_commission: u64 = get_commission(rewards as u128, commission_rate); - current_balance += rewards - new_commission; - - - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); - let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); - delegator_shares.set(ADMIN, current_commission + new_commission_shares); - - - total_shares.set(0u8, current_shares + new_commission_shares); - total_balance.set(0u8, current_balance + new_commission); + // Distribute shares for new commission + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + total_shares.set(0u8, current_shares + new_commission_shares); + total_balance.set(0u8, current_balance + new_commission); } ``` #### Deposit Public @@ -660,57 +672,58 @@ function deposit_public: finalize deposit_public: - input r0 as credits.aleo/transfer_public_to_public.future; + input r0 as credits.aleo/transfer_private_to_public.future; input r1 as address.public; input r2 as u64.public; await r0; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - get total_balance[0u8] into r4; - get total_shares[0u8] into r5; - gt r3 r4 into r6; - sub r3 r4 into r7; - ternary r6 r7 0u64 into r8; - get commission_percent[0u8] into r9; - cast r8 into r10 as u128; - mul r10 r9 into r11; - div r11 1000u128 into r12; - cast r12 into r13 as u64; - sub r8 r13 into r14; - add r4 r14 into r15; - cast r15 into r16 as u128; - cast r13 into r17 as u128; - cast r5 into r18 as u128; - mul r18 1000u128 into r19; - div r19 r16 into r20; - add r16 r17 into r21; - mul r21 r20 into r22; - div r22 1000u128 into r23; - sub r23 r18 into r24; - cast r24 into r25 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r26; - add r26 r25 into r27; - set r27 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r5 r25 into r28; - add r15 r13 into r29; - cast r29 into r30 as u128; - cast r2 into r31 as u128; - cast r28 into r32 as u128; - mul r32 1000u128 into r33; - div r33 r30 into r34; - add r30 r31 into r35; - mul r35 r34 into r36; - div r36 1000u128 into r37; - sub r37 r32 into r38; - cast r38 into r39 as u64; - gte r39 1u64 into r40; - assert.eq r40 true; - get.or_use delegator_shares[r1] 0u64 into r41; - add r41 r39 into r42; - set r42 into delegator_shares[r1]; - add r28 r39 into r43; - set r43 into total_shares[0u8]; - add r29 r2 into r44; - set r44 into total_balance[0u8]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + get total_balance[0u8] into r5; + get total_shares[0u8] into r6; + gt r4.microcredits r5 into r7; + sub r4.microcredits r5 into r8; + ternary r7 r8 0u64 into r9; + get commission_percent[0u8] into r10; + cast r9 into r11 as u128; + mul r11 r10 into r12; + div r12 1000u128 into r13; + cast r13 into r14 as u64; + sub r9 r14 into r15; + add r5 r15 into r16; + cast r16 into r17 as u128; + cast r14 into r18 as u128; + cast r6 into r19 as u128; + mul r19 1000u128 into r20; + div r20 r17 into r21; + add r17 r18 into r22; + mul r22 r21 into r23; + div r23 1000u128 into r24; + sub r24 r19 into r25; + cast r25 into r26 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; + add r27 r26 into r28; + set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r6 r26 into r29; + add r16 r14 into r30; + cast r30 into r31 as u128; + cast r2 into r32 as u128; + cast r29 into r33 as u128; + mul r33 1000u128 into r34; + div r34 r31 into r35; + add r31 r32 into r36; + mul r36 r35 into r37; + div r37 1000u128 into r38; + sub r38 r33 into r39; + cast r39 into r40 as u64; + gte r40 1u64 into r41; + assert.eq r41 true; + get.or_use delegator_shares[r1] 0u64 into r42; + add r42 r40 into r43; + set r43 into delegator_shares[r1]; + add r29 r40 into r44; + set r44 into total_shares[0u8]; + add r30 r2 into r45; + set r45 into total_balance[0u8]; ``` ```leo transition deposit_public( @@ -726,47 +739,45 @@ transition deposit_public( finalize deposit_public( - caller: address, - microcredits: u64 + caller: address, + microcredits: u64 ) { - // Distribute shares for new commission - let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); - let current_balance: u64 = total_balance.get(0u8); - let current_shares: u64 = total_shares.get(0u8); - let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; - let commission_rate: u128 = commission_percent.get(0u8); - let new_commission: u64 = get_commission(rewards as u128, commission_rate); - current_balance += rewards - new_commission; - - - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); - let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); - delegator_shares.set(ADMIN, current_commission + new_commission_shares); - - - current_shares += new_commission_shares; - current_balance += new_commission; - - - // Calculate mint for deposit - let new_shares: u64 = calculate_new_shares(current_balance as u128, microcredits as u128, current_shares as u128); - - - // Ensure mint amount is valid - assert(new_shares >= 1u64); - - - // Update delegator_shares mapping - let shares: u64 = delegator_shares.get_or_use(caller, 0u64); - delegator_shares.set(caller, shares + new_shares); - - - // Update total shares - total_shares.set(0u8, current_shares + new_shares); - - - // Update total_balance - total_balance.set(0u8, current_balance + microcredits); + // Distribute shares for new commission + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + current_shares += new_commission_shares; + current_balance += new_commission; + + // Calculate mint for deposit + let new_shares: u64 = calculate_new_shares(current_balance as u128, microcredits as u128, current_shares as u128); + + // Ensure mint amount is valid + assert(new_shares >= 1u64); + + // Update delegator_shares mapping + let shares: u64 = delegator_shares.get_or_use(caller, 0u64); + delegator_shares.set(caller, shares + new_shares); + + // Update total shares + total_shares.set(0u8, current_shares + new_shares); + + // Update total_balance + total_balance.set(0u8, current_balance + microcredits); } ``` #### Withdraw Public @@ -805,60 +816,61 @@ finalize withdraw_public: get delegator_shares[r3] into r10; gte r10 r1 into r11; assert.eq r11 true; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r12; - get total_balance[0u8] into r13; - get total_shares[0u8] into r14; - gt r12 r13 into r15; - sub r12 r13 into r16; - ternary r15 r16 0u64 into r17; - get commission_percent[0u8] into r18; - cast r17 into r19 as u128; - mul r19 r18 into r20; - div r20 1000u128 into r21; - cast r21 into r22 as u64; - sub r17 r22 into r23; - add r13 r23 into r24; - cast r24 into r25 as u128; - cast r22 into r26 as u128; - cast r14 into r27 as u128; - mul r27 1000u128 into r28; - div r28 r25 into r29; - add r25 r26 into r30; - mul r30 r29 into r31; - div r31 1000u128 into r32; - sub r32 r27 into r33; - cast r33 into r34 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r35; - add r35 r34 into r36; - set r36 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r14 r34 into r37; - add r24 r22 into r38; - cast r1 into r39 as u128; - mul r39 1000u128 into r40; - cast r37 into r41 as u128; - div r40 r41 into r42; - cast r38 into r43 as u128; - mul r43 r42 into r44; - div r44 1000u128 into r45; - cast r2 into r46 as u128; - gte r45 r46 into r47; - assert.eq r47 true; - div block.height 1_000u32 into r48; - mul r48 1_000u32 into r49; - add r49 1_000u32 into r50; - ternary r7 r50 r5 into r51; - set r51 into current_batch_height[0u8]; - cast r2 r51 into r52 as withdrawal_state; - set r52 into withdrawals[r3]; - get pending_withdrawal[0u8] into r53; - add r53 r2 into r54; - set r54 into pending_withdrawal[0u8]; - sub r38 r2 into r55; - set r55 into total_balance[0u8]; - sub r37 r1 into r56; - set r56 into total_shares[0u8]; - sub r10 r1 into r57; - set r57 into delegator_shares[r3]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r12 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r12 into r13; + get total_balance[0u8] into r14; + get total_shares[0u8] into r15; + gt r13.microcredits r14 into r16; + sub r13.microcredits r14 into r17; + ternary r16 r17 0u64 into r18; + get commission_percent[0u8] into r19; + cast r18 into r20 as u128; + mul r20 r19 into r21; + div r21 1000u128 into r22; + cast r22 into r23 as u64; + sub r18 r23 into r24; + add r14 r24 into r25; + cast r25 into r26 as u128; + cast r23 into r27 as u128; + cast r15 into r28 as u128; + mul r28 1000u128 into r29; + div r29 r26 into r30; + add r26 r27 into r31; + mul r31 r30 into r32; + div r32 1000u128 into r33; + sub r33 r28 into r34; + cast r34 into r35 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r36; + add r36 r35 into r37; + set r37 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r15 r35 into r38; + add r25 r23 into r39; + cast r1 into r40 as u128; + mul r40 1000u128 into r41; + cast r38 into r42 as u128; + div r41 r42 into r43; + cast r39 into r44 as u128; + mul r44 r43 into r45; + div r45 1000u128 into r46; + cast r2 into r47 as u128; + gte r46 r47 into r48; + assert.eq r48 true; + div block.height 1_000u32 into r49; + mul r49 1_000u32 into r50; + add r50 1_000u32 into r51; + ternary r7 r51 r5 into r52; + set r52 into current_batch_height[0u8]; + cast r2 r52 into r53 as withdrawal_state; + set r53 into withdrawals[r3]; + get pending_withdrawal[0u8] into r54; + add r54 r2 into r55; + set r55 into pending_withdrawal[0u8]; + sub r39 r2 into r56; + set r56 into total_balance[0u8]; + sub r38 r1 into r57; + set r57 into total_shares[0u8]; + sub r10 r1 into r58; + set r58 into delegator_shares[r3]; ``` ```leo transition withdraw_public(withdrawal_shares: u64, total_withdrawal: u64) { @@ -870,77 +882,70 @@ transition withdraw_public(withdrawal_shares: u64, total_withdrawal: u64) { finalize withdraw_public(withdrawal_shares: u64, total_withdrawal: u64, owner: address) { - // Assert that they don't have any pending withdrawals - let currently_withdrawing: bool = withdrawals.contains(owner); - assert_eq(currently_withdrawing, false); - - - // Determine if the withdrawal can fit into the current batch - let current_batch: u32 = current_batch_height.get_or_use(0u8, 0u32); - let min_claim_height: u32 = block.height + UNBONDING_PERIOD; - let new_batch: bool = current_batch == 0u32; - let unbonding_allowed: bool = new_batch || current_batch >= min_claim_height; - assert(unbonding_allowed); - - - // Assert that they have enough to withdraw - let delegator_balance: u64 = delegator_shares.get(owner); - assert(delegator_balance >= withdrawal_shares); - - - // Distribute shares for new commission - let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); - let current_balance: u64 = total_balance.get(0u8); - let current_shares: u64 = total_shares.get(0u8); - let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; - let commission_rate: u128 = commission_percent.get(0u8); - let new_commission: u64 = get_commission(rewards as u128, commission_rate); - current_balance += rewards - new_commission; - - - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); - let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); - delegator_shares.set(ADMIN, current_commission + new_commission_shares); - - - current_shares += new_commission_shares; - current_balance += new_commission; - - - // Calculate withdrawal amount - let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; - let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; - - - // If the calculated withdrawal amount is greater than total_withdrawal, the excess will stay in the pool - assert(withdrawal_calculation >= total_withdrawal as u128); - - - // Update withdrawals mappings - let batch_height: u32 = new_batch ? get_new_batch_height(block.height) : current_batch; - current_batch_height.set(0u8, batch_height); - let withdrawal: withdrawal_state = withdrawal_state { - microcredits: total_withdrawal, - claim_block: batch_height - }; - withdrawals.set(owner, withdrawal); - - - // Update pending withdrawal - let currently_pending: u64 = pending_withdrawal.get(0u8); - pending_withdrawal.set(0u8, currently_pending + total_withdrawal); - - - // Update total balance - total_balance.set(0u8, current_balance - total_withdrawal); - - - // Update total shares - total_shares.set(0u8, current_shares - withdrawal_shares); - - - // Update delegator_shares mapping - delegator_shares.set(owner, delegator_balance - withdrawal_shares); + // Assert that they don't have any pending withdrawals + let currently_withdrawing: bool = withdrawals.contains(owner); + assert_eq(currently_withdrawing, false); + + // Determine if the withdrawal can fit into the current batch + let current_batch: u32 = current_batch_height.get_or_use(0u8, 0u32); + let min_claim_height: u32 = block.height + UNBONDING_PERIOD; + let new_batch: bool = current_batch == 0u32; + let unbonding_allowed: bool = new_batch || current_batch >= min_claim_height; + assert(unbonding_allowed); + + // Assert that they have enough to withdraw + let delegator_balance: u64 = delegator_shares.get(owner); + assert(delegator_balance >= withdrawal_shares); + + // Distribute shares for new commission + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let commission_rate: u128 = commission_percent.get(0u8); + let new_commission: u64 = get_commission(rewards as u128, commission_rate); + current_balance += rewards - new_commission; + + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); + delegator_shares.set(ADMIN, current_commission + new_commission_shares); + + current_shares += new_commission_shares; + current_balance += new_commission; + + // Calculate withdrawal amount + let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; + let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + + // If the calculated withdrawal amount is greater than total_withdrawal, the excess will stay in the pool + assert(withdrawal_calculation >= total_withdrawal as u128); + + // Update withdrawals mappings + let batch_height: u32 = new_batch ? get_new_batch_height(block.height) : current_batch; + current_batch_height.set(0u8, batch_height); + let withdrawal: withdrawal_state = withdrawal_state { + microcredits: total_withdrawal, + claim_block: batch_height + }; + withdrawals.set(owner, withdrawal); + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + + // Update total balance + total_balance.set(0u8, current_balance - total_withdrawal); + + // Update total shares + total_shares.set(0u8, current_shares - withdrawal_shares); + + // Update delegator_shares mapping + delegator_shares.set(owner, delegator_balance - withdrawal_shares); } ``` #### Get New Batch Height @@ -973,32 +978,33 @@ finalize create_withdraw_claim: input r1 as address.public; contains withdrawals[r1] into r2; assert.eq r2 false; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] 0u64 into r3; - assert.eq r3 0u64; - get delegator_shares[r1] into r4; - gte r4 r0 into r5; - assert.eq r5 true; - get total_balance[0u8] into r6; - get total_shares[0u8] into r7; - cast r0 into r8 as u128; - mul r8 1000u128 into r9; - cast r7 into r10 as u128; - div r9 r10 into r11; - cast r6 into r12 as u128; - mul r12 r11 into r13; - div r13 1000u128 into r14; - cast r14 into r15 as u64; - cast r15 block.height into r16 as withdrawal_state; - set r16 into withdrawals[r1]; - get pending_withdrawal[0u8] into r17; - add r17 r15 into r18; - set r18 into pending_withdrawal[0u8]; - sub r6 r15 into r19; - set r19 into total_balance[0u8]; - sub r7 r0 into r20; - set r20 into total_shares[0u8]; - sub r4 r0 into r21; - set r21 into delegator_shares[r1]; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; + get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + assert.eq r4.microcredits 0u64; + get delegator_shares[r1] into r5; + gte r5 r0 into r6; + assert.eq r6 true; + get total_balance[0u8] into r7; + get total_shares[0u8] into r8; + cast r0 into r9 as u128; + mul r9 1000u128 into r10; + cast r8 into r11 as u128; + div r10 r11 into r12; + cast r7 into r13 as u128; + mul r13 r12 into r14; + div r14 1000u128 into r15; + cast r15 into r16 as u64; + cast r16 block.height into r17 as withdrawal_state; + set r17 into withdrawals[r1]; + get pending_withdrawal[0u8] into r18; + add r18 r16 into r19; + set r19 into pending_withdrawal[0u8]; + sub r7 r16 into r20; + set r20 into total_balance[0u8]; + sub r8 r0 into r21; + set r21 into total_shares[0u8]; + sub r5 r0 into r22; + set r22 into delegator_shares[r1]; ```leo transition create_withdraw_claim(withdrawal_shares: u64) { return then finalize(withdrawal_shares, self.caller); @@ -1006,51 +1012,48 @@ finalize create_withdraw_claim: finalize create_withdraw_claim(withdrawal_shares: u64, owner: address) { - // Assert that they don't have any pending withdrawals - let currently_withdrawing: bool = withdrawals.contains(owner); - assert_eq(currently_withdrawing, false); - - - let bonded: u64 = 0u64; // credits.aleo/bonded.get(CORE_PROTOCOL); - assert_eq(bonded, 0u64); - - - // Assert that they have enough to withdraw - let delegator_balance: u64 = delegator_shares.get(owner); - assert(delegator_balance >= withdrawal_shares); - - - // Calculate withdrawal amount - let current_balance: u64 = total_balance.get(0u8); - let current_shares: u64 = total_shares.get(0u8); - let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; - let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; - let total_withdrawal: u64 = withdrawal_calculation as u64; - - - // Update withdrawals mappings - let withdrawal: withdrawal_state = withdrawal_state { - microcredits: total_withdrawal, - claim_block: block.height - }; - withdrawals.set(owner, withdrawal); - - - // Update pending withdrawal - let currently_pending: u64 = pending_withdrawal.get(0u8); - pending_withdrawal.set(0u8, currently_pending + total_withdrawal); - - - // Update total balance - total_balance.set(0u8, current_balance - total_withdrawal); - - - // Update total shares - total_shares.set(0u8, current_shares - withdrawal_shares); - - - // Update delegator_shares mapping - delegator_shares.set(owner, delegator_balance - withdrawal_shares); + // Assert that they don't have any pending withdrawals + let currently_withdrawing: bool = withdrawals.contains(owner); + assert_eq(currently_withdrawing, false); + + // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; + let default: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + assert_eq(bonded, 0u64); + + // Assert that they have enough to withdraw + let delegator_balance: u64 = delegator_shares.get(owner); + assert(delegator_balance >= withdrawal_shares); + + // Calculate withdrawal amount + let current_balance: u64 = total_balance.get(0u8); + let current_shares: u64 = total_shares.get(0u8); + let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; + let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + let total_withdrawal: u64 = withdrawal_calculation as u64; + + // Update withdrawals mappings + let withdrawal: withdrawal_state = withdrawal_state { + microcredits: total_withdrawal, + claim_block: block.height + }; + withdrawals.set(owner, withdrawal); + + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + + // Update total balance + total_balance.set(0u8, current_balance - total_withdrawal); + + // Update total shares + total_shares.set(0u8, current_shares - withdrawal_shares); + + // Update delegator_shares mapping + delegator_shares.set(owner, delegator_balance - withdrawal_shares); } ``` #### Claim Withdrawal Public @@ -1093,18 +1096,16 @@ finalize claim_withdrawal_public: finalize claim_withdrawal_public(owner: address, amount: u64) { - let withdrawal: withdrawal_state = withdrawals.get(owner); - assert(block.height >= withdrawal.claim_block); - assert_eq(withdrawal.microcredits, amount); - - - // Remove withdrawal -withdrawals.remove(owner); + let withdrawal: withdrawal_state = withdrawals.get(owner); + assert(block.height >= withdrawal.claim_block); + assert_eq(withdrawal.microcredits, amount); + // Remove withdrawal + withdrawals.remove(owner); - // Update pending withdrawal - let currently_pending: u64 = pending_withdrawal.get(0u8); - pending_withdrawal.set(0u8, currently_pending - amount); + // Update pending withdrawal + let currently_pending: u64 = pending_withdrawal.get(0u8); + pending_withdrawal.set(0u8, currently_pending - amount); } ``` From 4250ed8e84b4235ffe5e87810b447f6b14207237 Mon Sep 17 00:00:00 2001 From: Chris Womack Date: Thu, 28 Mar 2024 12:38:54 -0400 Subject: [PATCH 4/4] Update resource fiels --- arc-0038/arc_0038.aleo | 572 +++++++++++++++++++++++++---------------- arc-0038/arc_0038.leo | 245 ++++++++++++------ 2 files changed, 517 insertions(+), 300 deletions(-) diff --git a/arc-0038/arc_0038.aleo b/arc-0038/arc_0038.aleo index f057a50..5feb2bd 100644 --- a/arc-0038/arc_0038.aleo +++ b/arc-0038/arc_0038.aleo @@ -5,6 +5,20 @@ struct withdrawal_state: microcredits as u64; claim_block as u32; +// copied from credits.aleo, as structs are not importable +struct bond_state: + // The address of the validator. + validator as address; + // The amount of microcredits that are currently bonded to the specified validator. + microcredits as u64; + +// copied from credits.aleo, as structs are not importable +// The `unbond_state` struct tracks the microcredits that are currently unbonding, along with the unlock height. +struct unbond_state: + // The amount of microcredits that are currently unbonding. + microcredits as u64; + // The block height at which the unbonding will be complete, and can be claimed. + height as u32; mapping is_initialized: key as u8.public; @@ -26,6 +40,11 @@ mapping total_balance: value as u64.public; +mapping pending_deposits: + key as u8.public; + value as u64.public; + + mapping total_shares: key as u8.public; value as u64.public; @@ -64,29 +83,30 @@ function initialize: finalize initialize: input r0 as u128.public; input r1 as address.public; - get is_initialized[0u8] into r2; + get.or_use is_initialized[0u8] false into r2; assert.eq r2 false; + set true into is_initialized[0u8]; set r0 into commission_percent[0u8]; set r1 into validator[0u8]; set 0u64 into total_shares[0u8]; set 0u64 into total_balance[0u8]; + set 0u64 into pending_deposits[0u8]; set 0u64 into pending_withdrawal[0u8]; + set 0u64 into pending_withdrawal[1u8]; set 0u32 into current_batch_height[0u8]; function initial_deposit: - input r0 as credits.aleo/credits.record; - input r1 as u64.public; - input r2 as address.public; + input r0 as u64.public; + input r1 as address.public; assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; - call credits.aleo/transfer_private_to_public r0 aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny r1 into r3 r4; - call credits.aleo/bond_public r2 r1 into r5; - async initial_deposit r4 r5 r1 into r6; - output r3 as credits.aleo/credits.record; - output r6 as arc_0038.aleo/initial_deposit.future; + call credits.aleo/transfer_public_as_signer aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt r0 into r2; + call credits.aleo/bond_public r1 aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt r0 into r3; + async initial_deposit r2 r3 r0 into r4; + output r4 as arc_0038.aleo/initial_deposit.future; finalize initial_deposit: - input r0 as credits.aleo/transfer_private_to_public.future; + input r0 as credits.aleo/transfer_public_as_signer.future; input r1 as credits.aleo/bond_public.future; input r2 as u64.public; await r0; @@ -98,14 +118,39 @@ finalize initial_deposit: assert.eq r4 0u64; assert.eq r5 0u64; set r2 into total_balance[0u8]; - mul r2 1_000u64 into r6; + mul r2 1000u64 into r6; set r6 into total_shares[0u8]; - set r2 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + set r6 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + + + +function get_commission_test: + input r0 as u128.private; + input r1 as u128.private; + mul r0 r1 into r2; + div r2 1000u128 into r3; + cast r3 into r4 as u64; + output r4 as u64.private; +function calculate_new_shares_test: + input r0 as u128.private; + input r1 as u128.private; + input r2 as u128.private; + input r3 as u128.private; + add r0 r1 into r4; + mul r3 1000u128 into r5; + add r4 r2 into r6; + mul r5 r6 into r7; + mul r4 1000u128 into r8; + div r7 r8 into r9; + sub r9 r3 into r10; + cast r10 into r11 as u64; + output r11 as u64.private; + function set_commission_percent: input r0 as u128.public; @@ -119,8 +164,8 @@ function set_commission_percent: finalize set_commission_percent: input r0 as u128.public; - cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r1 as credits.aleo/bond_state; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r1 into r2; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r1 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r1 into r2; get total_balance[0u8] into r3; get total_shares[0u8] into r4; gt r2.microcredits r3 into r5; @@ -133,23 +178,26 @@ finalize set_commission_percent: cast r11 into r12 as u64; sub r7 r12 into r13; add r3 r13 into r14; - cast r14 into r15 as u128; - cast r12 into r16 as u128; - cast r4 into r17 as u128; - mul r17 1000u128 into r18; - div r18 r15 into r19; - add r15 r16 into r20; - mul r20 r19 into r21; - div r21 1000u128 into r22; - sub r22 r17 into r23; - cast r23 into r24 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r25; - add r25 r24 into r26; - set r26 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r4 r24 into r27; - set r27 into total_shares[0u8]; - add r14 r12 into r28; - set r28 into total_balance[0u8]; + get pending_deposits[0u8] into r15; + cast r14 into r16 as u128; + cast r15 into r17 as u128; + cast r12 into r18 as u128; + cast r4 into r19 as u128; + add r16 r17 into r20; + mul r19 1000u128 into r21; + add r20 r18 into r22; + mul r21 r22 into r23; + mul r20 1000u128 into r24; + div r23 r24 into r25; + sub r25 r19 into r26; + cast r26 into r27 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r28; + add r28 r27 into r29; + set r29 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r4 r27 into r30; + set r30 into total_shares[0u8]; + add r14 r12 into r31; + set r31 into total_balance[0u8]; set r0 into commission_percent[0u8]; @@ -176,40 +224,45 @@ finalize unbond_all: await r0; contains validator[1u8] into r2; assert.eq r2 true; - cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; - get total_balance[0u8] into r5; - get total_shares[0u8] into r6; - gt r4.microcredits r5 into r7; - sub r4.microcredits r5 into r8; - ternary r7 r8 0u64 into r9; - get commission_percent[0u8] into r10; - cast r9 into r11 as u128; - mul r11 r10 into r12; - div r12 1000u128 into r13; - cast r13 into r14 as u64; - sub r9 r14 into r15; - add r5 r15 into r16; - cast r16 into r17 as u128; - cast r14 into r18 as u128; - cast r6 into r19 as u128; - mul r19 1000u128 into r20; - div r20 r17 into r21; - add r17 r18 into r22; - mul r22 r21 into r23; - div r23 1000u128 into r24; - sub r24 r19 into r25; - cast r25 into r26 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; - add r27 r26 into r28; - set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r6 r26 into r29; - set r29 into total_shares[0u8]; - add r16 r14 into r30; - set r30 into total_balance[0u8]; - sub r4.microcredits r1 into r31; - lt r31 10_000_000_000u64 into r32; - assert.eq r32 true; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r3 into r4; + assert.eq r4.microcredits 0u64; + cast 0u64 0u32 into r5 as unbond_state; + get.or_use credits.aleo/unbonding[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r5 into r6; + get pending_withdrawal[0u8] into r7; + sub r6.microcredits r7 into r8; + get total_balance[0u8] into r9; + get total_shares[0u8] into r10; + gt r8 r9 into r11; + sub r8 r9 into r12; + ternary r11 r12 0u64 into r13; + get commission_percent[0u8] into r14; + cast r13 into r15 as u128; + mul r15 r14 into r16; + div r16 1000u128 into r17; + cast r17 into r18 as u64; + sub r13 r18 into r19; + add r9 r19 into r20; + get pending_deposits[0u8] into r21; + cast r20 into r22 as u128; + cast r21 into r23 as u128; + cast r18 into r24 as u128; + cast r10 into r25 as u128; + add r22 r23 into r26; + mul r25 1000u128 into r27; + add r26 r24 into r28; + mul r27 r28 into r29; + mul r26 1000u128 into r30; + div r29 r30 into r31; + sub r31 r25 into r32; + cast r32 into r33 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r34; + add r34 r33 into r35; + set r35 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r10 r33 into r36; + set r36 into total_shares[0u8]; + add r20 r18 into r37; + set r37 into total_balance[0u8]; function claim_unbond: @@ -221,12 +274,17 @@ finalize claim_unbond: input r0 as credits.aleo/claim_unbond_public.future; await r0; remove current_batch_height[0u8]; + get pending_withdrawal[0u8] into r1; + get pending_withdrawal[1u8] into r2; + add r2 r1 into r3; + set 0u64 into pending_withdrawal[0u8]; + set r3 into pending_withdrawal[1u8]; function bond_all: input r0 as address.public; input r1 as u64.public; - call credits.aleo/bond_public r0 r1 into r2; + call credits.aleo/bond_public r0 aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt r1 into r2; async bond_all r2 r0 r1 into r3; output r3 as arc_0038.aleo/bond_all.future; @@ -235,30 +293,58 @@ finalize bond_all: input r1 as address.public; input r2 as u64.public; await r0; - cast 0u64 0u32 into r3 as credits.aleo/unbond_state; - get.or_use credits.aleo/unbonding[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; - assert.eq r4.microcredits 0u64; - get credits.aleo/account[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] into r5; - get pending_withdrawal[0u8] into r6; - sub r5 r6 into r7; - assert.eq r2 r7; - contains validator[1u8] into r8; - get validator[1u8] into r9; - get validator[0u8] into r10; - ternary r8 r9 r10 into r11; - assert.eq r1 r11; - set r11 into validator[0u8]; + get.or_use credits.aleo/account[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] 0u64 into r3; + get pending_withdrawal[1u8] into r4; + gte r3 r4 into r5; + assert.eq r5 true; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r6 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r6 into r7; + get total_balance[0u8] into r8; + get pending_deposits[0u8] into r9; + add r9 r8 into r10; + sub r10 r7.microcredits into r11; + set r11 into pending_deposits[0u8]; + set r7.microcredits into total_balance[0u8]; + get validator[1u8] into r12; + assert.eq r1 r12; + set r12 into validator[0u8]; remove validator[1u8]; +function bond_deposits: + input r0 as address.public; + input r1 as u64.public; + call credits.aleo/bond_public r0 aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt r1 into r2; + async bond_deposits r2 r0 r1 into r3; + output r3 as arc_0038.aleo/bond_deposits.future; + +finalize bond_deposits: + input r0 as credits.aleo/bond_public.future; + input r1 as address.public; + input r2 as u64.public; + await r0; + get.or_use credits.aleo/account[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] 0u64 into r3; + get pending_withdrawal[1u8] into r4; + gte r3 r4 into r5; + assert.eq r5 true; + get total_balance[0u8] into r6; + get pending_deposits[0u8] into r7; + sub r7 r2 into r8; + set r8 into pending_deposits[0u8]; + add r6 r2 into r9; + set r9 into total_balance[0u8]; + contains validator[1u8] into r10; + assert.eq r10 false; + + function claim_commission: assert.eq self.caller aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; async claim_commission into r0; output r0 as arc_0038.aleo/claim_commission.future; finalize claim_commission: - cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r0 as credits.aleo/bond_state; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r0 into r1; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r0 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r0 into r1; get total_balance[0u8] into r2; get total_shares[0u8] into r3; gt r1.microcredits r2 into r4; @@ -271,40 +357,41 @@ finalize claim_commission: cast r10 into r11 as u64; sub r6 r11 into r12; add r2 r12 into r13; - cast r13 into r14 as u128; - cast r11 into r15 as u128; - cast r3 into r16 as u128; - mul r16 1000u128 into r17; - div r17 r14 into r18; - add r14 r15 into r19; - mul r19 r18 into r20; - div r20 1000u128 into r21; - sub r21 r16 into r22; - cast r22 into r23 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r24; - add r24 r23 into r25; - set r25 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r3 r23 into r26; - set r26 into total_shares[0u8]; - add r13 r11 into r27; - set r27 into total_balance[0u8]; + get pending_deposits[0u8] into r14; + cast r13 into r15 as u128; + cast r14 into r16 as u128; + cast r11 into r17 as u128; + cast r3 into r18 as u128; + add r15 r16 into r19; + mul r18 1000u128 into r20; + add r19 r17 into r21; + mul r20 r21 into r22; + mul r19 1000u128 into r23; + div r22 r23 into r24; + sub r24 r18 into r25; + cast r25 into r26 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; + add r27 r26 into r28; + set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r3 r26 into r29; + set r29 into total_shares[0u8]; + add r13 r11 into r30; + set r30 into total_balance[0u8]; function deposit_public: - input r0 as credits.aleo/credits.record; - input r1 as u64.public; - call credits.aleo/transfer_private_to_public r0 aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny r1 into r2 r3; - async deposit_public r3 self.caller r1 into r4; - output r2 as credits.aleo/credits.record; - output r4 as arc_0038.aleo/deposit_public.future; + input r0 as u64.public; + call credits.aleo/transfer_public_as_signer aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt r0 into r1; + async deposit_public r1 self.caller r0 into r2; + output r2 as arc_0038.aleo/deposit_public.future; finalize deposit_public: - input r0 as credits.aleo/transfer_private_to_public.future; + input r0 as credits.aleo/transfer_public_as_signer.future; input r1 as address.public; input r2 as u64.public; await r0; - cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r3 into r4; get total_balance[0u8] into r5; get total_shares[0u8] into r6; gt r4.microcredits r5 into r7; @@ -317,40 +404,47 @@ finalize deposit_public: cast r13 into r14 as u64; sub r9 r14 into r15; add r5 r15 into r16; - cast r16 into r17 as u128; - cast r14 into r18 as u128; - cast r6 into r19 as u128; - mul r19 1000u128 into r20; - div r20 r17 into r21; - add r17 r18 into r22; - mul r22 r21 into r23; - div r23 1000u128 into r24; - sub r24 r19 into r25; - cast r25 into r26 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r27; - add r27 r26 into r28; - set r28 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r6 r26 into r29; - add r16 r14 into r30; - cast r30 into r31 as u128; - cast r2 into r32 as u128; - cast r29 into r33 as u128; - mul r33 1000u128 into r34; - div r34 r31 into r35; - add r31 r32 into r36; - mul r36 r35 into r37; - div r37 1000u128 into r38; - sub r38 r33 into r39; - cast r39 into r40 as u64; - gte r40 1u64 into r41; - assert.eq r41 true; - get.or_use delegator_shares[r1] 0u64 into r42; - add r42 r40 into r43; - set r43 into delegator_shares[r1]; - add r29 r40 into r44; - set r44 into total_shares[0u8]; - add r30 r2 into r45; - set r45 into total_balance[0u8]; + get pending_deposits[0u8] into r17; + cast r16 into r18 as u128; + cast r17 into r19 as u128; + cast r14 into r20 as u128; + cast r6 into r21 as u128; + add r18 r19 into r22; + mul r21 1000u128 into r23; + add r22 r20 into r24; + mul r23 r24 into r25; + mul r22 1000u128 into r26; + div r25 r26 into r27; + sub r27 r21 into r28; + cast r28 into r29 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r30; + add r30 r29 into r31; + set r31 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r6 r29 into r32; + add r16 r14 into r33; + set r33 into total_balance[0u8]; + cast r33 into r34 as u128; + cast r17 into r35 as u128; + cast r2 into r36 as u128; + cast r32 into r37 as u128; + add r34 r35 into r38; + mul r37 1000u128 into r39; + add r38 r36 into r40; + mul r39 r40 into r41; + mul r38 1000u128 into r42; + div r41 r42 into r43; + sub r43 r37 into r44; + cast r44 into r45 as u64; + gte r45 1u64 into r46; + assert.eq r46 true; + get.or_use delegator_shares[r1] 0u64 into r47; + add r47 r45 into r48; + set r48 into delegator_shares[r1]; + add r32 r45 into r49; + set r49 into total_shares[0u8]; + get pending_deposits[0u8] into r50; + add r50 r2 into r51; + set r51 into pending_deposits[0u8]; @@ -379,61 +473,86 @@ finalize withdraw_public: get delegator_shares[r3] into r10; gte r10 r1 into r11; assert.eq r11 true; - cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r12 as credits.aleo/bond_state; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r12 into r13; - get total_balance[0u8] into r14; - get total_shares[0u8] into r15; - gt r13.microcredits r14 into r16; - sub r13.microcredits r14 into r17; - ternary r16 r17 0u64 into r18; - get commission_percent[0u8] into r19; - cast r18 into r20 as u128; - mul r20 r19 into r21; - div r21 1000u128 into r22; - cast r22 into r23 as u64; - sub r18 r23 into r24; - add r14 r24 into r25; - cast r25 into r26 as u128; - cast r23 into r27 as u128; - cast r15 into r28 as u128; - mul r28 1000u128 into r29; - div r29 r26 into r30; - add r26 r27 into r31; - mul r31 r30 into r32; - div r32 1000u128 into r33; - sub r33 r28 into r34; + cast 0u64 0u32 into r12 as unbond_state; + get.or_use credits.aleo/unbonding[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r12 into r13; + get pending_withdrawal[0u8] into r14; + sub r13.microcredits r14 into r15; + get pending_deposits[0u8] into r16; + sub r15 r2 into r17; + add r17 r16 into r18; + gte r18 1000000000u64 into r19; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r20 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r20 into r21; + gte r21.microcredits 1000000000u64 into r22; + not r19 into r23; + or r22 r23 into r24; + assert.eq r24 true; + add r21.microcredits r2 into r25; + get total_balance[0u8] into r26; + get total_shares[0u8] into r27; + gt r25 r26 into r28; + sub r25 r26 into r29; + ternary r28 r29 0u64 into r30; + get commission_percent[0u8] into r31; + cast r30 into r32 as u128; + mul r32 r31 into r33; + div r33 1000u128 into r34; cast r34 into r35 as u64; - get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r36; - add r36 r35 into r37; - set r37 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; - add r15 r35 into r38; - add r25 r23 into r39; - cast r1 into r40 as u128; - mul r40 1000u128 into r41; - cast r38 into r42 as u128; - div r41 r42 into r43; - cast r39 into r44 as u128; - mul r44 r43 into r45; - div r45 1000u128 into r46; - cast r2 into r47 as u128; - gte r46 r47 into r48; - assert.eq r48 true; - div block.height 1_000u32 into r49; - mul r49 1_000u32 into r50; - add r50 1_000u32 into r51; - ternary r7 r51 r5 into r52; - set r52 into current_batch_height[0u8]; - cast r2 r52 into r53 as withdrawal_state; - set r53 into withdrawals[r3]; - get pending_withdrawal[0u8] into r54; - add r54 r2 into r55; - set r55 into pending_withdrawal[0u8]; - sub r39 r2 into r56; - set r56 into total_balance[0u8]; - sub r38 r1 into r57; - set r57 into total_shares[0u8]; - sub r10 r1 into r58; - set r58 into delegator_shares[r3]; + sub r30 r35 into r36; + add r26 r36 into r37; + cast r37 into r38 as u128; + cast r16 into r39 as u128; + cast r35 into r40 as u128; + cast r27 into r41 as u128; + add r38 r39 into r42; + mul r41 1000u128 into r43; + add r42 r40 into r44; + mul r43 r44 into r45; + mul r42 1000u128 into r46; + div r45 r46 into r47; + sub r47 r41 into r48; + cast r48 into r49 as u64; + get.or_use delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru] 0u64 into r50; + add r50 r49 into r51; + set r51 into delegator_shares[aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru]; + add r27 r49 into r52; + add r37 r35 into r53; + cast r53 into r54 as u128; + cast r16 into r55 as u128; + add r54 r55 into r56; + cast r1 into r57 as u128; + cast r56 into r58 as u128; + mul r57 r58 into r59; + mul r59 1000u128 into r60; + cast r52 into r61 as u128; + mul r61 1000u128 into r62; + div r60 r62 into r63; + cast r2 into r64 as u128; + gte r63 r64 into r65; + assert.eq r65 true; + div block.height 1000u32 into r66; + mul r66 1000u32 into r67; + add r67 1000u32 into r68; + ternary r7 r68 r5 into r69; + set r69 into current_batch_height[0u8]; + cast r2 r69 into r70 as withdrawal_state; + set r70 into withdrawals[r3]; + add r14 r2 into r71; + set r71 into pending_withdrawal[0u8]; + sub r53 r2 into r72; + set r72 into total_balance[0u8]; + sub r52 r1 into r73; + set r73 into total_shares[0u8]; + sub r10 r1 into r74; + set r74 into delegator_shares[r3]; + + +function get_new_batch_height_test: + input r0 as u32.private; + div r0 1000u32 into r1; + mul r1 1000u32 into r2; + add r2 1000u32 into r3; + output r3 as u32.private; function create_withdraw_claim: @@ -446,33 +565,40 @@ finalize create_withdraw_claim: input r1 as address.public; contains withdrawals[r1] into r2; assert.eq r2 false; - cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as credits.aleo/bond_state; - get.or_use credits.aleo/bonded[aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny] r3 into r4; + cast aleo1q6qstg8q8shwqf5m6q5fcenuwsdqsvp4hhsgfnx5chzjm3secyzqt9mxm8 0u64 into r3 as bond_state; + get.or_use credits.aleo/bonded[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r3 into r4; assert.eq r4.microcredits 0u64; - get delegator_shares[r1] into r5; - gte r5 r0 into r6; - assert.eq r6 true; - get total_balance[0u8] into r7; - get total_shares[0u8] into r8; - cast r0 into r9 as u128; - mul r9 1000u128 into r10; - cast r8 into r11 as u128; - div r10 r11 into r12; - cast r7 into r13 as u128; - mul r13 r12 into r14; - div r14 1000u128 into r15; - cast r15 into r16 as u64; - cast r16 block.height into r17 as withdrawal_state; - set r17 into withdrawals[r1]; - get pending_withdrawal[0u8] into r18; - add r18 r16 into r19; - set r19 into pending_withdrawal[0u8]; - sub r7 r16 into r20; - set r20 into total_balance[0u8]; - sub r8 r0 into r21; - set r21 into total_shares[0u8]; - sub r5 r0 into r22; - set r22 into delegator_shares[r1]; + cast 0u64 0u32 into r5 as unbond_state; + get.or_use credits.aleo/unbonding[aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt] r5 into r6; + assert.eq r6.microcredits 0u64; + get delegator_shares[r1] into r7; + gte r7 r0 into r8; + assert.eq r8 true; + get total_balance[0u8] into r9; + get pending_deposits[0u8] into r10; + cast r9 into r11 as u128; + cast r10 into r12 as u128; + add r11 r12 into r13; + get total_shares[0u8] into r14; + cast r0 into r15 as u128; + mul r15 r13 into r16; + mul r16 1000u128 into r17; + cast r14 into r18 as u128; + mul r18 1000u128 into r19; + div r17 r19 into r20; + cast r20 into r21 as u64; + add block.height 1u32 into r22; + cast r21 r22 into r23 as withdrawal_state; + set r23 into withdrawals[r1]; + get pending_withdrawal[1u8] into r24; + add r24 r21 into r25; + set r25 into pending_withdrawal[1u8]; + sub r9 r21 into r26; + set r26 into total_balance[0u8]; + sub r14 r0 into r27; + set r27 into total_shares[0u8]; + sub r7 r0 into r28; + set r28 into delegator_shares[r1]; function claim_withdrawal_public: @@ -492,6 +618,6 @@ finalize claim_withdrawal_public: assert.eq r4 true; assert.eq r3.microcredits r2; remove withdrawals[r1]; - get pending_withdrawal[0u8] into r5; + get pending_withdrawal[1u8] into r5; sub r5 r2 into r6; - set r6 into pending_withdrawal[0u8]; \ No newline at end of file + set r6 into pending_withdrawal[1u8]; diff --git a/arc-0038/arc_0038.leo b/arc-0038/arc_0038.leo index 0b56daa..c3f33f0 100644 --- a/arc-0038/arc_0038.leo +++ b/arc-0038/arc_0038.leo @@ -4,12 +4,12 @@ program arc_0038.aleo { // Owner of the program const ADMIN: address = aleo1kf3dgrz9lqyklz8kqfy0hpxxyt78qfuzshuhccl02a5x43x6nqpsaapqru; // Address of this program - const CORE_PROTOCOL: address = aleo17hwvp7fl5da40hd29heasjjm537uqce489hhuc3lwhxfm0njucpq0rvfny; - const SHARES_TO_MICROCREDITS: u64 = 1_000u64; + const CORE_PROTOCOL: address = aleo1j0zju7f0fpgv98gulyywtkxk6jca99l6425uqhnd5kccu4jc2grstjx0mt; + const SHARES_TO_MICROCREDITS: u64 = 1000u64; const PRECISION_UNSIGNED: u128 = 1000u128; const MAX_COMMISSION_RATE: u128 = 500u128; const UNBONDING_PERIOD: u32 = 360u32; - const MINIMUM_BOND_POOL: u64 = 10_000_000_000u64; + const MINIMUM_BOND_AMOUNT: u64 = 1000000000u64; // 0u8 -> Whether the program has been initialized mapping is_initialized: u8 => bool; @@ -28,13 +28,17 @@ program arc_0038.aleo { // 0u8 -> total balance of microcredits pooled mapping total_balance: u8 => u64; + // 0u8 -> balance of deposits that have not been bonded, updated when calling "bond_all" + mapping pending_deposits: u8 => u64; + // 0u8 -> total pool of delegator shares mapping total_shares: u8 => u64; // address -> number of shares held by the delegator with this address mapping delegator_shares: address => u64; - // 0u8 -> balance pending withdrawal in microcredits + // 0u8 -> balance pending withdrawal currently unbonding + // 1u8 -> balance pending withdrawal owned by the program mapping pending_withdrawal: u8 => u64; /** Unbonding allowed: 0u8 -> @@ -44,8 +48,8 @@ program arc_0038.aleo { mapping current_batch_height: u8 => u32; struct withdrawal_state { - microcredits: u64; - claim_block: u32; + microcredits: u64, + claim_block: u32 } // address -> pending withdrawal for the delegator with this address @@ -60,27 +64,30 @@ program arc_0038.aleo { } finalize initialize(commission_rate: u128, validator_address: address) { - assert_eq(is_initialized.get(0u8), false); + let initialized: bool = is_initialized.get_or_use(0u8, false); + assert_eq(initialized, false); + is_initialized.set(0u8, true); commission_percent.set(0u8, commission_rate); validator.set(0u8, validator_address); total_shares.set(0u8, 0u64); total_balance.set(0u8, 0u64); + pending_deposits.set(0u8, 0u64); pending_withdrawal.set(0u8, 0u64); + pending_withdrawal.set(1u8, 0u64); current_batch_height.set(0u8, 0u32); } transition initial_deposit( - public input_record: credits.aleo/credits, public microcredits: u64, public validator_address: address - ) -> credits.aleo/credits { + ) { assert_eq(self.caller, ADMIN); - // Must be a credits record because credits.aleo uses self.caller for transfers - let updated_record: credits.aleo/credits = credits.aleo/transfer_private_to_public(input_record, CORE_PROTOCOL, microcredits); + // credits.aleo/transfer_public_as_signer(CORE_PROTOCOL, microcredits); + credits.aleo/transfer_public(CORE_PROTOCOL, microcredits); credits.aleo/bond_public(validator_address, microcredits); - return (updated_record) then finalize(microcredits); + return then finalize(microcredits); } finalize initial_deposit(microcredits: u64) { @@ -92,8 +99,9 @@ program arc_0038.aleo { assert_eq(shares, 0u64); total_balance.set(0u8, microcredits); - total_shares.set(0u8, microcredits * SHARES_TO_MICROCREDITS); - delegator_shares.set(ADMIN, microcredits); + shares = microcredits * SHARES_TO_MICROCREDITS; + total_shares.set(0u8, shares); + delegator_shares.set(ADMIN, shares); } inline get_commission( @@ -105,14 +113,22 @@ program arc_0038.aleo { return commission_64; } - inline calculate_new_shares(balance: u128, deposit: u128, shares: u128) -> u64 { - let pool_ratio: u128 = ((shares * PRECISION_UNSIGNED) / balance); - let new_total_shares: u128 = (balance + deposit) * pool_ratio; - let diff: u128 = (new_total_shares / PRECISION_UNSIGNED) - shares; + transition get_commission_test(rewards: u128, commission_rate: u128) -> u64 { + return get_commission(rewards, commission_rate); + } + + inline calculate_new_shares(bonded_balance: u128, pending_deposit_pool: u128, deposit: u128, shares: u128) -> u64 { + let full_balance: u128 = bonded_balance + pending_deposit_pool; + let new_total_shares: u128 = (shares * PRECISION_UNSIGNED) * (full_balance + deposit) / (full_balance * PRECISION_UNSIGNED); + let diff: u128 = new_total_shares - shares; let shares_to_mint: u64 = diff as u64; return shares_to_mint; } + transition calculate_new_shares_test(bonded_balance: u128, pending_deposit_pool: u128, deposit: u128, shares: u128) -> u64 { + return calculate_new_shares(bonded_balance, pending_deposit_pool, deposit, shares); + } + transition set_commission_percent(public new_commission_rate: u128) { assert_eq(self.caller, ADMIN); assert(new_commission_rate < PRECISION_UNSIGNED); @@ -124,11 +140,11 @@ program arc_0038.aleo { finalize set_commission_percent(new_commission_rate: u128) { // Make sure all commission is claimed before changing the rate // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -136,7 +152,8 @@ program arc_0038.aleo { let new_commission: u64 = get_commission(rewards as u128, commission_rate); current_balance += rewards - new_commission; - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let pending_deposit_pool: u64 = pending_deposits.get(0u8); + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, pending_deposit_pool as u128, new_commission as u128, current_shares as u128); let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); delegator_shares.set(ADMIN, current_commission + new_commission_shares); @@ -167,30 +184,37 @@ program arc_0038.aleo { let next_validator: bool = validator.contains(1u8); assert(next_validator); - // Make sure all commission is claimed before unbonding // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; + // Assert that the pool was fully unbonded + assert_eq(bonded, 0u64); + + // Make sure all commission is claimed before unbonding + let base_unbonding: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let unbonding: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base_unbonding).microcredits; + let unbonding_withdrawals: u64 = pending_withdrawal.get(0u8); + let previously_bonded: u64 = unbonding - unbonding_withdrawals; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); - let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; + let rewards: u64 = previously_bonded > current_balance ? previously_bonded - current_balance : 0u64; let commission_rate: u128 = commission_percent.get(0u8); let new_commission: u64 = get_commission(rewards as u128, commission_rate); current_balance += rewards - new_commission; - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let pending_deposit_pool: u64 = pending_deposits.get(0u8); + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, pending_deposit_pool as u128, new_commission as u128, current_shares as u128); let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); delegator_shares.set(ADMIN, current_commission + new_commission_shares); total_shares.set(0u8, current_shares + new_commission_shares); total_balance.set(0u8, current_balance + new_commission); - - // Assert that the pool will be fully unbonded - let residual_balance: u64 = bonded - pool_balance; - assert(residual_balance < MINIMUM_BOND_POOL); } transition claim_unbond() { @@ -201,6 +225,12 @@ program arc_0038.aleo { finalize claim_unbond() { current_batch_height.remove(0u8); + let unbonding_withdrawals: u64 = pending_withdrawal.get(0u8); + let already_claimed: u64 = pending_withdrawal.get(1u8); + already_claimed += unbonding_withdrawals; + + pending_withdrawal.set(0u8, 0u64); + pending_withdrawal.set(1u8, already_claimed); } transition bond_all(public validator_address: address, public amount: u64) { @@ -211,28 +241,54 @@ program arc_0038.aleo { } finalize bond_all(validator_address: address, amount: u64) { - // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + let account_balance: u64 = total_balance.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); + let pending_withdrawals: u64 = pending_withdrawal.get(1u8); + assert(account_balance >= pending_withdrawals); + + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let unbonding_balance: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; - assert_eq(unbonding_balance, 0u64); + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; + let current_balance: u64 = total_balance.get(0u8); + let pending_deposit_balance: u64 = pending_deposits.get(0u8); - let account_balance: u64 = total_balance.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); - let pending_withdrawals: u64 = pending_withdrawal.get(0u8); - let available_balance: u64 = account_balance - pending_withdrawals; - assert_eq(amount, available_balance); + pending_deposit_balance = pending_deposit_balance + current_balance - bonded; + pending_deposits.set(0u8, pending_deposit_balance); + total_balance.set(0u8, bonded); // Set validator - let has_next_validator: bool = validator.contains(1u8); - let current_validator: address = has_next_validator ? validator.get(1u8) : validator.get(0u8); - assert_eq(validator_address, current_validator); + let next_validator: address = validator.get(1u8); + assert_eq(validator_address, next_validator); - validator.set(0u8, current_validator); + validator.set(0u8, next_validator); validator.remove(1u8); } + transition bond_deposits(public validator_address: address, public amount: u64) { + // Call will fail if there is any balance still bonded to another validator + credits.aleo/bond_public(validator_address, amount); + + return then finalize(validator_address, amount); + } + + finalize bond_deposits(validator_address: address, amount: u64) { + let account_balance: u64 = total_balance.get(1u8); // credits.aleo/account.get(CORE_PROTOCOL); + let pending_withdrawals: u64 = pending_withdrawal.get(1u8); + assert(account_balance >= pending_withdrawals); + + let current_balance: u64 = total_balance.get(0u8); + let pending_deposit_balance: u64 = pending_deposits.get(0u8); + + pending_deposit_balance = pending_deposit_balance - amount; + pending_deposits.set(0u8, pending_deposit_balance); + total_balance.set(0u8, current_balance + amount); + + let has_next_validator: bool = validator.contains(1u8); + assert_eq(has_next_validator, false); + } + transition claim_commission() { assert_eq(self.caller, ADMIN); return then finalize(); @@ -241,11 +297,11 @@ program arc_0038.aleo { finalize claim_commission() { // Distribute shares for new commission // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -253,7 +309,8 @@ program arc_0038.aleo { let new_commission: u64 = get_commission(rewards as u128, commission_rate); current_balance += rewards - new_commission; - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let pending_deposit_pool: u64 = pending_deposits.get(0u8); + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, pending_deposit_pool as u128, new_commission as u128, current_shares as u128); let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); delegator_shares.set(ADMIN, current_commission + new_commission_shares); @@ -262,13 +319,11 @@ program arc_0038.aleo { } transition deposit_public( - public input_record: credits.aleo/credits, public microcredits: u64 - ) -> credits.aleo/credits { - // Must be a credits record because credits.aleo uses self.caller for transfers - let updated_record: credits.aleo/credits = credits.aleo/transfer_private_to_public(input_record, CORE_PROTOCOL, microcredits); - - return (updated_record) then finalize(self.caller, microcredits); + ) { + // credits.aleo/transfer_public_as_signer(CORE_PROTOCOL, microcredits); + credits.aleo/transfer_public(CORE_PROTOCOL, microcredits); + return then finalize(self.caller, microcredits); } finalize deposit_public( @@ -277,11 +332,11 @@ program arc_0038.aleo { ) { // Distribute shares for new commission // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -289,15 +344,18 @@ program arc_0038.aleo { let new_commission: u64 = get_commission(rewards as u128, commission_rate); current_balance += rewards - new_commission; - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let pending_deposit_pool: u64 = pending_deposits.get(0u8); + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, pending_deposit_pool as u128, new_commission as u128, current_shares as u128); let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); delegator_shares.set(ADMIN, current_commission + new_commission_shares); current_shares += new_commission_shares; current_balance += new_commission; + // Update total balance + total_balance.set(0u8, current_balance); // Calculate mint for deposit - let new_shares: u64 = calculate_new_shares(current_balance as u128, microcredits as u128, current_shares as u128); + let new_shares: u64 = calculate_new_shares(current_balance as u128, pending_deposit_pool as u128, microcredits as u128, current_shares as u128); // Ensure mint amount is valid assert(new_shares >= 1u64); @@ -309,8 +367,9 @@ program arc_0038.aleo { // Update total shares total_shares.set(0u8, current_shares + new_shares); - // Update total_balance - total_balance.set(0u8, current_balance + microcredits); + // Update pending_deposits + let pending: u64 = pending_deposits.get(0u8); + pending_deposits.set(0u8, pending + microcredits); } transition withdraw_public(public withdrawal_shares: u64, public total_withdrawal: u64) { @@ -335,13 +394,30 @@ program arc_0038.aleo { let delegator_balance: u64 = delegator_shares.get(owner); assert(delegator_balance >= withdrawal_shares); - // Distribute shares for new commission + // Prevent a full unbond if there are pending deposits to maintain the minimum bond amount + // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; + let base_unbonding: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let unbonding: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base_unbonding).microcredits; + let unbonding_withdrawals: u64 = pending_withdrawal.get(0u8); + let newly_unbonded: u64 = unbonding - unbonding_withdrawals; + let pending_deposit_pool: u64 = pending_deposits.get(0u8); + let sufficient_deposits: bool = newly_unbonded - total_withdrawal + pending_deposit_pool >= MINIMUM_BOND_AMOUNT; + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; // credits.aleo/bonded.get(CORE_PROTOCOL).microcredits; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; + // Allow the withdrawal if the pool is still bonded, or if there are not enough deposits to maintain the minimum bond amount + assert(bonded >= MINIMUM_BOND_AMOUNT || !sufficient_deposits); + + // Distribute shares for new commission + // Add back the withdrawal amount to appropriately calculate rewards before the withdrawal + bonded += total_withdrawal; let current_balance: u64 = total_balance.get(0u8); let current_shares: u64 = total_shares.get(0u8); let rewards: u64 = bonded > current_balance ? bonded - current_balance : 0u64; @@ -349,16 +425,18 @@ program arc_0038.aleo { let new_commission: u64 = get_commission(rewards as u128, commission_rate); current_balance += rewards - new_commission; - let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, new_commission as u128, current_shares as u128); + let new_commission_shares: u64 = calculate_new_shares(current_balance as u128, pending_deposit_pool as u128, new_commission as u128, current_shares as u128); let current_commission: u64 = delegator_shares.get_or_use(ADMIN, 0u64); delegator_shares.set(ADMIN, current_commission + new_commission_shares); current_shares += new_commission_shares; current_balance += new_commission; + // Calculate full pool size + let full_pool: u128 = current_balance as u128 + pending_deposit_pool as u128; + // Calculate withdrawal amount - let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; - let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + let withdrawal_calculation: u128 = (withdrawal_shares as u128 * full_pool as u128 * PRECISION_UNSIGNED) / (current_shares as u128 * PRECISION_UNSIGNED); // If the calculated withdrawal amount is greater than total_withdrawal, the excess will stay in the pool assert(withdrawal_calculation >= total_withdrawal as u128); @@ -373,8 +451,7 @@ program arc_0038.aleo { withdrawals.set(owner, withdrawal); // Update pending withdrawal - let currently_pending: u64 = pending_withdrawal.get(0u8); - pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + pending_withdrawal.set(0u8, unbonding_withdrawals + total_withdrawal); // Update total balance total_balance.set(0u8, current_balance - total_withdrawal); @@ -387,11 +464,15 @@ program arc_0038.aleo { } inline get_new_batch_height(height: u32) -> u32 { - let rounded_down: u32 = (height) / 1_000u32 * 1_000u32; - let rounded_up: u32 = rounded_down + 1_000u32; + let rounded_down: u32 = (height) / 1000u32 * 1000u32; + let rounded_up: u32 = rounded_down + 1000u32; return rounded_up; } + transition get_new_batch_height_test(height: u32) -> u32 { + return get_new_batch_height(height); + } + transition create_withdraw_claim(public withdrawal_shares: u64) { return then finalize(withdrawal_shares, self.caller); } @@ -401,35 +482,45 @@ program arc_0038.aleo { let currently_withdrawing: bool = withdrawals.contains(owner); assert_eq(currently_withdrawing, false); - // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; - let default: withdrawal_state = withdrawal_state { + // Simulate call to credits.aleo/bonded.get_or_use(CORE_PROTOCOL).microcredits; + let base: withdrawal_state = withdrawal_state { microcredits: 0u64, claim_block: 0u32 }; - let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, default).microcredits; + let bonded: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base).microcredits; assert_eq(bonded, 0u64); + // Simulate call to credits.aleo/unbonding.get_or_use(CORE_PROTOCOL).microcredits; + let base_unbonding: withdrawal_state = withdrawal_state { + microcredits: 0u64, + claim_block: 0u32 + }; + let unbonding: u64 = withdrawals.get_or_use(CORE_PROTOCOL, base_unbonding).microcredits; + assert_eq(unbonding, 0u64); + // Assert that they have enough to withdraw let delegator_balance: u64 = delegator_shares.get(owner); assert(delegator_balance >= withdrawal_shares); // Calculate withdrawal amount let current_balance: u64 = total_balance.get(0u8); + let pending_deposit_pool: u64 = pending_deposits.get(0u8); + let full_pool: u128 = current_balance as u128 + pending_deposit_pool as u128; let current_shares: u64 = total_shares.get(0u8); - let withdraw_ratio: u128 = (withdrawal_shares as u128 * PRECISION_UNSIGNED) / current_shares as u128; - let withdrawal_calculation: u128 = (current_balance as u128 * withdraw_ratio) / PRECISION_UNSIGNED; + let withdrawal_calculation: u128 = (withdrawal_shares as u128 * full_pool * PRECISION_UNSIGNED) / (current_shares as u128 * PRECISION_UNSIGNED); let total_withdrawal: u64 = withdrawal_calculation as u64; // Update withdrawals mappings + let claim_height: u32 = block.height + 1u32; let withdrawal: withdrawal_state = withdrawal_state { microcredits: total_withdrawal, - claim_block: block.height + claim_block: claim_height }; withdrawals.set(owner, withdrawal); // Update pending withdrawal - let currently_pending: u64 = pending_withdrawal.get(0u8); - pending_withdrawal.set(0u8, currently_pending + total_withdrawal); + let currently_pending: u64 = pending_withdrawal.get(1u8); + pending_withdrawal.set(1u8, currently_pending + total_withdrawal); // Update total balance total_balance.set(0u8, current_balance - total_withdrawal); @@ -456,7 +547,7 @@ program arc_0038.aleo { withdrawals.remove(owner); // Update pending withdrawal - let currently_pending: u64 = pending_withdrawal.get(0u8); - pending_withdrawal.set(0u8, currently_pending - amount); + let currently_pending: u64 = pending_withdrawal.get(1u8); + pending_withdrawal.set(1u8, currently_pending - amount); } } \ No newline at end of file