> For the complete documentation index, see [llms.txt](https://docs.paladin.vote/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.paladin.vote/governance/holy-pal-hpal/smart-contract.md).

# Smart Contract

## ERC20 basic methods:

This contract inherits the basic ERC20 methods:

Read-only:

* `name()`
* `symbol()`
* `decimals()`
* `balanceOf()`
* `totalSupply()`
* `allowance()`

Write:

* `approve()`
* `increaseAllowance()`
* `decreaseAllowance()`
* `transfer()`
* `transferFrom()`

And emit the basic ERC20 Events.

## Read-Only methods:

### Structs:

**UserLock**

Lock of an user

<table><thead><tr><th width="150.28068096137451">type</th><th width="150">name</th><th width="426.03974357814207">desc</th></tr></thead><tbody><tr><td>uint128</td><td>amount</td><td>Amount of locked balance</td></tr><tr><td>uint48</td><td>startTimestamp</td><td>Start of the Lock</td></tr><tr><td>uint48</td><td>duration</td><td>Duration of the Lock</td></tr><tr><td>uint32</td><td>fromBlock</td><td>BlockNumber for the Lock</td></tr></tbody></table>

**TotalLock**

Struct tracking the total amount locked

<table><thead><tr><th width="150.28068096137451">type</th><th width="150">name</th><th width="426.03974357814207">desc</th></tr></thead><tbody><tr><td>uint224</td><td>total</td><td>Total locked Supply</td></tr><tr><td>uint32</td><td>fromBlock</td><td>BlockNumber for the last update</td></tr></tbody></table>

**Checkpoint**

Checkpoints for users votes

<table><thead><tr><th width="150.28068096137451">type</th><th width="150">name</th><th width="426.03974357814207">desc</th></tr></thead><tbody><tr><td>uint32</td><td>fromBlock</td><td>BlockNumber for the last update</td></tr><tr><td>uint224</td><td>votes</td><td>Amount of vote of the user</td></tr></tbody></table>

**DelegateCheckpoint**

Checkpoints for users Delegates

<table><thead><tr><th width="150.28068096137451">type</th><th width="150">name</th><th width="426.03974357814207">desc</th></tr></thead><tbody><tr><td>uint32</td><td>fromBlock</td><td>BlockNumber for the last update</td></tr><tr><td>address</td><td>delegate</td><td>Address of the delegate</td></tr></tbody></table>

### Constants:

**WEEK**

`uint256 public constant WEEK = 604800;`

Seconds in a Week

**MONTH**

`uint256 public constant MONTH = 2629800;`

Seconds in a Month

**ONE\_YEAR**

`uint256 public constant ONE_YEAR = 31557600;`

Seconds in a Year

**UNIT**

`uint256 public constant UNIT = 1e18;`

1e18 scale

**MAX\_BPS**

`uint256 public constant MAX_BPS = 10000;`

Max BPS value (100%)

**COOLDOWN\_PERIOD**

`uint256 public constant COOLDOWN_PERIOD = 864000; // 10 days`

Period to wait before unstaking tokens

**UNSTAKE\_PERIOD**

`uint256 public constant UNSTAKE_PERIOD = 172800; // 2 days`

Duration of the unstaking period. After that period, unstaking cooldown is expired

**UNLOCK\_DELAY**

`uint256 public constant UNLOCK_DELAY = 1209600; // 2 weeks`

Period to unlock/re-lock tokens without possibility of punishement

**MIN\_LOCK\_DURATION**

`uint256 public constant MIN_LOCK_DURATION = 7889400; // 3 months`

Minimum duration of a Lock

**MAX\_LOCK\_DURATION**

`uint256 public constant MAX_LOCK_DURATION = 63115200; // 2 years`

Maximum duration of a Lock

### Immutables:

**pal**

`IERC20 pal`

Address of the PAL token

**rewardsVault**

`address rewardsVault`

Address of the vault holding the PAL rewards

**startDropPerSecond**

`uint256 startDropPerSecond`

Amount of rewards distributed per second at the start

**dropDecreaseDuration**

`uint256 dropDecreaseDuration`

Duration (in seconds) of the DropPerSecond decrease period

**startDropTimestamp**

`uint256 startDropTimestamp`

Timestamp: start of the DropPerSecond decrease period

**baseLockBonusRatio**

`uint256 baseLockBonusRatio`

Base reward multiplier for lock

**minLockBonusRatio**

`uint256 minLockBonusRatio`

Minimum reward multiplier for minimum lock duration

**maxLockBonusRatio**

`uint256 maxLockBonusRatio`

Maximum reward multiplier for maximum duration

### Storage:

**userLocks**

`mapping(address => UserLock[]) userLocks`

Array of all user Locks, ordered from oldest to newest (only the las Lock is the one active)

**currentTotalLocked**

`uint256 currentTotalLocked`

Current Total locked Supply

**totalLocks**

`TotalLock[] totalLocks`

List of TotalLocks, ordered from oldest to newest

**cooldowns**

`mapping(address => uint256) cooldowns`

User Cooldowns

**delegates**

`mapping(address => address) delegates`

mapping tracking the Delegator for each Delegatee

**checkpoints**

`mapping(address => Checkpoint[]) checkpoints`

List of Vote checkpoints for each user

**delegateCheckpoints**

`mapping(address => DelegateCheckpoint[]) delegateCheckpoints`

List of Delegate checkpoints for each user

**kickRatioPerWeek**

`uint256 kickRatioPerWeek`

Ratio (in BPS) of locked balance applied of penalty for each week over lock end

**bonusLockVoteRatio**

`uint256 bonusLockVoteRatio`

Ratio of bonus votes applied on user locked balance

**emergency**

`bool emergency`

Allow emergency withdraws

**rewardIndex**

`uint256 rewardIndex`

Global reward index

**lastRewardUpdate**

`uint256 lastRewardUpdate`

Timestamp of last update for global reward index

**endDropPerSecond**

`uint256 endDropPerSecond`

Amount of rewards distributed per second at the end of the decrease duration

**currentDropPerSecond**

`uint256 currentDropPerSecond`

Current amount of rewards distributed per second

**lastDropUpdate**

`uint256 lastDropUpdate`

Timestamp of last update for currentDropPerSecond

**userRewardIndex**

`mapping(address => uint256) userRewardIndex`

Last reward index for each user

**claimableRewards**

`mapping(address => uint256) claimableRewards`

Current amount of rewards claimable for the user

**rewardsLastUpdate**

`mapping(address => uint256) rewardsLastUpdate`

Timestamp of last update for user rewards

**userCurrentBonusRatio**

`mapping(address => uint256) userCurrentBonusRatio`

Last updated Bonus Ratio for rewards

**userBonusRatioDecrease**

`mapping(address => uint256) userBonusRatioDecrease`

Value by which user Bonus Ratio decrease each second

### View methods:

**getNewReceiverCooldown**

`function getNewReceiverCooldown(address sender, address receiver, uint256 amount) external view returns(uint256)`

Estimates the new Cooldown for the receiver, based on sender & amount of transfer

**getUserLockCount**

`function getUserLockCount(address user) external view returns(uint256)`

Get the total number of Locks for an user

**getUserLock**

`function getUserLock(address user) external view returns(UserLock memory)`

Get the current user Lock

**getUserPastLock**

`function getUserPastLock(address user, uint256 blockNumber) external view returns(UserLock memory)`

Get the user Lock at a given block (returns empty Lock if not existing / block number too old)

**getTotalLockLength**

`function getTotalLockLength() external view returns(uint256)`

Get the total count of TotalLock

**getCurrentTotalLock**

`function getCurrentTotalLock() external view returns(TotalLock memory)`

Get the latest TotalLock

**getPastTotalLock**

`function getPastTotalLock(uint256 blockNumber) external view returns(TotalLock memory)`

Get the TotalLock at a given block

**availableBalanceOf**

`function availableBalanceOf(address user) external view returns(uint256)`

Get the user available balance (available = staked - locked)

**allBalancesOf**

`function allBalancesOf(address user) external view returns( uint256 staked, uint256 locked, uint256 available )`

Get all balances for a given user:

* staked : staked balance
* locked : locked balance
* available : available balance (staked - locked)

**estimateClaimableRewards**

`function estimateClaimableRewards(address user) external view returns(uint256)`

Get the estimated current amount of rewards claimable by the user

**numCheckpoints**

`function numCheckpoints(address account) external view virtual returns (uint256)`

Current number of vote checkpoints for the user

**getCurrentVotes**

`function getCurrentVotes(address user) external view returns (uint256)`

Get the user current voting power (with bonus voting power from the Lock)

**getPastVotes**

`function getPastVotes(address user, uint256 blockNumber) external view returns(uint256)`

Get the user voting power for a given block (with bonus voting power from the Lock)

**getPastDelegate**

`function getPastDelegate(address account, uint256 blockNumber) public view returns (address)`

Get the user delegate at a given block

## Write methods:&#x20;

**stake**

`function stake(uint256 amount) external returns(uint256)`

Deposits PAL & mints hPAL tokens

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>Amount to stake (in wei)</td></tr></tbody></table>

***Returns :*** uint256 : amount of hPAL minted

**cooldown**

`function cooldown() external`

Updates the Cooldown for the caller

**unstake**

`function unstake(uint256 amount, address receiver) external returns(uint256)`

Burns hPAL & withdraws PAL

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>Amount to withdraw (in wei)</td></tr><tr><td>receiver</td><td>address</td><td>address to receive the withdrawn PAL</td></tr></tbody></table>

***Returns :*** uint256 : amount withdrawn

**lock**

`function lock(uint256 amount, uint256 duration) external`

Locks hPAL for a given duration

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>Amount of the hPAL balance to lock (in wei)</td></tr><tr><td>duration</td><td>uint256</td><td>duration of the Lock (in seconds)</td></tr></tbody></table>

**increaseLockDuration**

`function increaseLockDuration(uint256 duration) external`

Increase the user current Lock duration (& restarts the Lock)

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>duration</td><td>uint256</td><td>new duration for the Lock (in seconds)</td></tr></tbody></table>

**increaseLock**

`function increaseLock(uint256 amount) external`

Increase the amount of hPAL locked for the user

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>new amount of hPAL to be locked (in total) (in wei)</td></tr></tbody></table>

**unlock**

`function unlock() external`

Registers a new user wanting to sell its delegation

**kick**

`function kick(address user) external`

Removes an user Lock if too long after expiry, and applies a penalty

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>user</td><td>address</td><td>address of the user to kick out of a Lock</td></tr></tbody></table>

**stakeAndLock**

`function stakeAndLock(uint256 amount, uint256 duration) external returns(uint256)`

Staked PAL to get hPAL, and locks it for the given duration

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>amount of PAL to stake and lock (in wei)</td></tr><tr><td>duration</td><td>uint256</td><td>duration of the Lock (in seconds)</td></tr></tbody></table>

***Returns :*** uint256 : amount of hPAL minted

**stakeAndIncreaseLock**

`function stakeAndIncreaseLock(uint256 amount, uint256 duration) external returns(uint256)`

Stake more PAL into hPAL & add them to the current user Lock

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>amount of PAL to stake and lock (in wei)</td></tr><tr><td>duration</td><td>uint256</td><td>duration of the Lock (in seconds)</td></tr></tbody></table>

***Returns :*** uint256 : amount of hPAL minted

**delegate**

`function delegate(address delegatee) external`

Delegates the caller voting power to another address

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>delegatee</td><td>address</td><td>address to delegate to</td></tr></tbody></table>

**claim**

`function claim(uint256 amount) external`

Claim the given amount of rewards for the caller

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>amount to claim (in wei)</td></tr></tbody></table>

**updateRewardState**

`function updateRewardState() external`

Updates the global Reward State for the contract

**updateUserRewardState**

`function updateUserRewardState(address user) external`

Updates the given user Reward State

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>user</td><td>address</td><td>address of the user to update</td></tr></tbody></table>

**emergencyWithdraw**

`function emergencyWithdraw(uint256 amount, address receiver) external returns(uint256)`

Allow to withdraw with override of the lock & cooldown in case of emergency

***Parameters :***&#x20;

<table data-header-hidden><thead><tr><th width="150">name</th><th width="150">type</th><th width="363.2">desc</th></tr></thead><tbody><tr><td><strong>name</strong></td><td><strong>type</strong></td><td><strong>desc</strong></td></tr><tr><td>amount</td><td>uint256</td><td>amount to withdraw (in wei)</td></tr><tr><td>receiver</td><td>address</td><td>address to receive the withdrawn funds</td></tr></tbody></table>

***Returns :*** uint256 : amount withdrawn

## Events

**Stake**

`event Stake(address indexed user, uint256 amount);`

Emitted when an user stake PAL in the contract

**Unstake**

`event Unstake(address indexed user, uint256 amount);`

Emitted when an user burns hPAL to withdraw PAL

**Cooldown**

`event Cooldown(address indexed user);`

Emitted when an user triggers the cooldown period

**Lock**

`event Lock(address indexed user, uint256 amount, uint256 indexed startTimestamp, uint256 indexed duration, uint256 totalLocked);`

Emitted when an user creates or update its Lock

**Unlock**

`event Unlock(address indexed user, uint256 amount, uint256 totalLocked);`

Emitted when an user exits the Lock

**Kick**

`event Kick(address indexed user, address indexed kicker, uint256 amount, uint256 penalty, uint256 totalLocked);`

Emitted when an user is kicked out of the Lock

**ClaimRewards**

`event ClaimRewards(address indexed user, uint256 amount);`

Emitted when an user claim the rewards

**DelegateChanged**

`event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);`

Emitted when the delegate of an address changes

**DelegateVotesChanged**

`event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);`

Emitted when the votes of a delegate is updated

**EmergencyUnstake**

`event EmergencyUnstake(address indexed user, uint256 amount);`

Emitted when un user withdraw through the emergency method


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.paladin.vote/governance/holy-pal-hpal/smart-contract.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
