These docs are a comprehensive guide to the ZTRL protocol, based on the Compound protocol. The protocol codebase will be available to partners and licensees upon request. For more information, please visit Fintrux Network. Your questions help us improve, so please don't hesitate to ask if you can't find what you are looking for here.
Coming Soon (Eth for ETH2 staking collateral on L1 and fDAI for lending/borrowing on L2.)
The ZTRL protocol contracts use a system of exponential math, Exponential.sol, in order to represent fractional quantities with sufficient precision.
Most numbers are represented as a mantissa, an unsigned integer scaled by 1 * 10 ^ 18, in order to perform basic math at a high level of precision.
Prices and exchange rates are scaled by the decimals unique to each asset; zTokens are ERC-20 tokens with 8 decimals, while their underlying tokens vary, and have a public member named decimals.
The zToken Exchange Rate is scaled by the difference in decimals between the zToken and the underlying asset.
oneZTokenInUnderlying = exchangeRateCurrent / (1 * 10 ^ (18 + underlyingDecimals - zTokenDecimals))
There is no underlying contract for ETH, so to do this with zETH, set underlyingDecimals to 18.
To find the number of underlying tokens that can be withdrawed for zTokens, multiply the number of zTokens by the above value oneZTokenInUnderlying.
underlyingTokens = zTokenAmount * oneZTokenInUnderlying
Interest rates for each market update on any block in which the ratio of borrowed assets to supplied assets in the market has changed. The amount interest rates are changed depends on the interest rate model smart contract implemented for the market, and the amount of change in the ratio of borrowed assets to supplied assets in the market.
Interest accrues to all suppliers and borrowers in a market when any Ethereum address interacts with the market’s zToken contract, calling one of these functions: mint, withdraw, borrow, or repay. Successful execution of one of these functions triggers the accrueInterest method, which causes interest to be added to the underlying balance of every supplier and borrower in the market. Interest accrues for the current block, as well as each prior block in which the accrueInterest method was not triggered (no user interacted with the zToken contract). Interest compounds only during blocks in which the zToken contract has one of the aforementioned methods invoked.
The Annual Percentage Yield (APY) for supplying or borrowing in each market can be calculated using the value of supplyRatePerBlock (for supply APY) or borrowRatePerBlock (for borrow APY) in this formula:
Rate = zToken.supplyRatePerBlock(); // Integer
Rate = 37893566
fDAI Mantissa = 1 * 10 ^ 18 (fDAI has 18 decimal places)
Blocks Per Day = 4 * 60 * 24 (based on 4 blocks occurring every minute)
Days Per Year = 365
APY = ((((Rate / fDAI Mantissa * Blocks Per Day + 1) ^ Days Per Year)) - 1) * 100
The gas usage of the protocol functions may fluctuate by market and user. External calls, such as to underlying ERC-20 tokens, may use an arbitrary amount of gas. Any calculations that involve checking account liquidity, have gas costs that increase with the number of entered markets. Thus, while it can be difficult to provide any guarantees about costs, we provide the table below for guidance:
Even thopugh the amount of gas may be the same, the gas cost on Optimism L2 should be much cheaper because it is faster. L1 Ethereum can currently process around 30 transactions per second (TPS) whereby L2 may bump it to a couple of thousand TPS.
Each asset supported by the ZTRL protocol is integrated through a zToken contract, which is an EIP-20 compliant representation of balances supplied to the protocol. By minting zTokens, users (1) earn interest through the zToken's exchange rate, which increases in value relative to the underlying asset, and (2) gain the ability to use zTokens as collateral.
zTokens are the primary means of interacting with the ZTRL protocol; when a user mints, withdraws, borrows, repays a borrow, liquidates a borrow, or transfers zTokens, she will do so using the zToken contract.
There are currently two types of zTokens: ZErc20 and ZEther. Though both types expose the EIP-20 interface, ZErc20 wraps an underlying ERC-20 asset, while ZEther simply wraps Ether itself. As such, the core functions which involve transferring an asset into the protocol have slightly different interfaces depending on the type, each of which is shown below.
Each market has its own Supply interest rate (APR). Interest isn't distributed; instead, simply by holding zTokens, you'll earn interest.
zTokens accumulates interest through their exchange rate — over time, each zToken becomes convertible into an increasing amount of it's underlying asset, even while the number of zTokens in your wallet stays the same.
When a market is launched, the zToken exchange rate (how much ETH one zETH is worth) begins at 0.020000 — and increases at a rate equal to the compounding market interest rate. For example, after one year, the exchange rate might equal 0.021591.
Each user has the same zToken exchange rate; there’s nothing unique to your wallet that you have to worry about.
Let’s say you supply 1,000 fDAI to the ZTRL protocol, when the exchange rate is 0.020070; you would receive 49,825.61 zDAI (1,000/0.020070).
A few months later, you decide it’s time to withdraw your fDAI from the protocol; the exchange rate is now 0.021591:
Your 49,825.61 zDAI is now equal to 1,075.78 fDAI (49,825.61 * 0.021591)
You could withdraw 1,075.78 fDAI, which would withdraw all 49,825.61 zDAI
Or, you could withdraw a portion, such as your original 1,000 fDAI, which would withdraw 46,315.59 zDAI (keeping 3,510.01 zDAI in your wallet)
Each zToken will be visible on Etherscan, and you should be able to view them in the list of tokens associated with your address. zToken balances have been integrated into MetaMask; other wallets may add zToken support.
Yes, but exercise caution! By transferring zTokens, you’re transferring your balance of the underlying asset inside the ZTRL protocol. If you send a zToken to your friend, your balance (viewable in the ZTRL Interface) will decline, and your friend will see their balance increase.
A zToken transfer will fail if the account has entered that zToken market and the transfer would have put the account into a state of negative liquidity.
The mint function transfers an asset into the protocol, which begins accumulating interest based on the current Supply Rate for the asset. The user receives a quantity of zTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.
The withdraw function converts a specified quantity of zTokens into the underlying asset, and returns them to the user. The amount of underlying tokens received is equal to the quantity of zTokens withdrawed, multiplied by the current Exchange Rate. The amount withdrawn must be less than the user's Account Liquidity and the market's available liquidity.
The withdraw underlying function converts zTokens into a specified quantity of the underlying asset, and returns them to the user. The amount of zTokens withdrawn is equal to the quantity of underlying tokens received, divided by the current Exchange Rate. The amount withdrawn must be less than the user's Account Liquidity and the market's available liquidity.
The borrow function transfers an asset from the protocol to the user, and creates a borrow balance which begins accumulating interest based on the Borrow Rate for the asset. The amount borrowed must be less than the user's Account Liquidity and the market's available liquidity.
The repay function transfers an asset into the protocol, reducing the user's borrow balance.
The repay function transfers an asset into the protocol, reducing the target user's borrow balance.
Transfer is an ERC-20 method that allows accounts to send tokens to other Ethereum addresses. A zToken transfer will fail if the account has entered that zToken market and the transfer would have put the account into a state of negative liquidity.
A user who has negative account liquidity is subject to liquidation by other users of the protocol to return his/her account liquidity back to positive (i.e. above the collateral requirement). When a liquidation occurs, a liquidator may repay some or all of an outstanding borrow on behalf of a borrower and in return receive a discounted amount of collateral held by the borrower; this discount is defined as the liquidation incentive.
A liquidator may close up to a certain fixed percentage (i.e. close factor) of any individual outstanding borrow of the underwater account. Unlike in v1, liquidators must interact with each zToken contract in which they wish to repay a borrow and seize another asset as collateral. When collateral is seized, the liquidator is transferred zTokens, which they may withdraw the same as if they had supplied the asset themselves. Users must approve each zToken contract before calling liquidate (i.e. on the borrowed asset which they are repaying), as they are transferring funds into the contract.
Each zToken is convertible into an ever increasing quantity of the underlying asset, as interest accrues in the market. The exchange rate between a zToken and the underlying asset is equal to:
Cash is the amount of underlying balance owned by this zToken contract. One may query the total amount of cash currently available to this market.
Total Borrows is the amount of underlying currently loaned out by the market, and the amount upon which interest is accumulated to suppliers of the market.
A user who borrows assets from the protocol is subject to accumulated interest based on the current borrow rate. Interest is accumulated every block and integrations may use this function to obtain the current value of a user's borrow balance with interest.
At any point in time one may query the contract to get the current borrow rate per block.
Total Supply is the number of tokens currently in circulation in this zToken market. It is part of the EIP-20 interface of the zToken contract.
The user's underlying balance, representing their assets in the protocol, is equal to the user's zToken balance multiplied by the Exchange Rate.
At any point in time one may query the contract to get the current supply rate per block. The supply rate is derived from the borrow rate, reserve factor and the amount of total borrows.
Reserves are an accounting entry in each zToken contract that represents a portion of historical interest set aside as cash which can be withdrawn or transferred through the protocol's governance. A small portion of borrower interest accrues into the protocol, determined by the reserve factor.
The reserve factor defines the portion of borrower interest that is converted into reserves.
The ZTRLtroller is the risk management layer of the ZTRL protocol; it determines how much collateral a user is required to maintain, and whether (and by how much) a user can be liquidated. Each time a user interacts with a zToken, the ZTRLtroller is asked to approve or deny the transaction.
The ZTRLtroller maps user balances to prices (via the Price Oracle) to
risk weights (called LTVs) to make its determinations. Users explicitly list which assets they would like included in their risk scoring, by calling Enter Markets and Exit Market.
The ZTRLtroller is implemented as an upgradeable proxy. The Unitroller proxies all logic to the ZTRLtroller implementation, but storage values are set on the Unitroller. To call ZTRLtroller functions, use the ZTRLtroller ABI on the Unitroller address.
Enter into a list of markets - it is not an error to enter the same market more than once. In order to supply collateral or borrow in a market, it must be entered first.
Exit a market - it is not an error to exit a market which is not currently entered. Exited markets will not count towards account liquidity calculations.
Get the list of markets an account is currently entered into. In order to supply collateral or borrow in a market, it must be entered first. Entered markets count towards account liquidity calculations.
A zToken's LTV can range from 0-90%, and represents the proportionate increase in liquidity (borrow limit) that an account receives by minting the zToken.
Generally, large or liquid assets have high LTVs, while small or illiquid assets have low LTVs. If an asset has a 0% LTV, it can't be used as collateral (or seized in liquidation), though it can still be borrowed. FTX hodlers may get a slightly higher LTV.
LTVs can be increased (or decreased) through ZTRL Governance, as market conditions change.
Account Liquidity represents the USD value borrowable by a user, before it reaches liquidation. Users with a shortfall (negative liquidity) are subject to liquidation, and can’t withdraw or borrow assets until Account Liquidity is positive again.
For each market the user has entered into, their supplied balance is multiplied by the market’s LTV, and summed; borrow balances are then subtracted, to equal Account Liquidity. Borrowing an asset reduces Account Liquidity for each USD borrowed; withdrawing an asset reduces Account Liquidity by the asset’s LTV times each USD withdrawn.
Because the ZTRL protocol exclusively uses unsigned integers, Account Liquidity returns either a surplus or shortfall.
The percent, ranging from 0% to 100%, of a liquidatable account's borrow that can be repaid in a single liquidate transaction. If a user has multiple borrowed assets, the closeFactor applies to any single borrowed asset, not the aggregated value of a user’s outstanding borrowing.
The additional collateral given to liquidators as an incentive to perform liquidation of underwater accounts. For example, if the liquidation incentive is 1.1, liquidators receive an extra 10% of the borrowers collateral for every unit they close.
The "ZTRL speed" unique to each market is an unsigned integer that specifies the amount of ZTRL that is distributed, per block, to suppliers and borrowers in each market. This number can be changed for individual markets by calling the _setZTRLSpeed method through a successful ZTRLGovernance proposal.
The ZTRLtroller contract’s ZTRLRate is an unsigned integer that indicates the rate at which the protocol distributes ZTRL to markets’ suppliers or borrowers, every Ethereum block. The value is the amount of ZTRL (in wei), per block, allocated for the markets. Note that not every market has ZTRL distributed to its participants (see Market Metadata).
The ZTRLRate indicates how much ZTRL goes to the suppliers or borrowers, so doubling this number shows how much ZTRL goes to all suppliers and borrowers combined. The code examples implement reading the amount of ZTRL distributed, per Ethereum block, to all markets.
Every ZTRL user accrues ZTRL for each block they are supplying to or borrowing from the protocol. Users may call the ZTRLtroller's claimZTRL method at any time to transfer ZTRL accrued to their address.
The ZTRLtroller contract has an array called getAllMarkets that contains the addresses of each zToken contract. Each address in the getAllMarkets array can be used to fetch a metadata struct in the ZTRLtroller’s markets constant. See the ZTRLtroller Storage contract for the Market struct definition.
The ZTRL protocol is governed and upgraded by ZTRL token-holders, using three distinct components; the ZTRL token, governance module (Governor Alpha), and Timelock. Together, these contracts allow the community to propose, vote, and implement changes through the administrative functions of a zToken or the ZTRLtroller. Proposals can include changes like adjusting an interest rate model, to adding support for a new asset.
Any address with more than 100,000 ZTRL delegated to it may propose governance actions, which are executable code. When a proposal is created, the community can submit their votes during a 3 day voting period. If a majority, and at least 400,000 votes are cast for the proposal, it is queued in the Timelock, and can be implemented after 2 days.
ZTRL is an ERC-20 token that allows the owner to delegate voting rights to any address, including their own address. Changes to the owner’s token balance automatically adjust the voting rights of the delegate.
Delegate votes from the sender to the delegatee. Users can delegate to 1 address at a time, and the number of votes added to the delegatee’s vote count is equivalent to the balance of ZTRL in the user’s account. Votes are delegated from the current block and onward, until the sender delegates again, or transfers their ZTRL.
Delegate votes from the signatory to the delegatee. This method has the same purpose as Delegate but it instead enables offline signatures to participate in ZTRL governance vote delegation. For more details on how to create an offline signature, review EIP-712.
Gets the balance of votes for an account as of the current block.
Gets the prior number of votes for an account at a specific block number. The block number passed must be a finalized block or the function will revert.
Governor Alpha is the governance module of the protocol; it allows addresses with more than 1,000,000 ZTRL to propose changes to the protocol. Addresses that held voting weight, at the start of the proposal, invoked through the getpriorvotes function, can submit their votes during a 3 day voting period. If a majority, and at least 4,000,000 votes are cast for the proposal, it is queued in the Timelock, and can be implemented after 2 days.
The required minimum number of votes in support of a proposal for it to succeed.
The minimum number of votes required for an account to create a proposal.
The maximum number of actions that can be included in a proposal. Actions are functions calls that will be made when a proposal succeeds and executes.
The number of Ethereum blocks to wait before voting on a proposal may begin. This value is added to the current block number when a proposal is created. This can be changed through governance.
The duration of voting on a proposal, in Ethereum blocks.
Create a Proposal to change the protocol. E.g., A proposal can set a zToken's interest rate model or risk parameters on the ZTRLtroller.
Proposals will be voted on by delegated voters. If there is sufficient support before the voting period ends, the proposal shall be automatically enacted. Enacted proposals are queued and executed in the ZTRL Timelock contract.
The sender must hold more ZTRL than the current proposal threshold (proposalThreshold()) as of the immediately previous block. If the threshold is 1,000,000 ZTRL, the sender must have been delegated more than 1% of all ZTRL in order to create a proposal. The proposal can have up to 10 actions (based on proposalMaxOperations()).
The proposer cannot create another proposal if they currently have a pending or active proposal. It is not possible to queue two identical actions in the same block (due to a restriction in the Timelock), therefore actions in a single proposal must be unique, and unique proposals that share an identical action must be queued in different blocks.
After a proposal has succeeded, any address can call the queue method to move the proposal into the Timelock queue. A proposal can only be queued if it has succeeded.
After the Timelock delay period, any account may invoke the execute method to apply the changes from the proposal to the target contracts. This will invoke each of the actions described in the proposal.
This function is payable so the Timelock contract can invoke payable functions that were selected in the proposal. E.g., A proposal can add reserves to a market like cETH, set a zToken's interest rate model, or set risk parameters on the ZTRLtroller.
Cancel a proposal that has not yet been executed. The Guardian is the only one who may execute this unless the proposer does not maintain the delegates required to create a proposal. If the proposer does not have more delegates than the proposal threshold, anyone can cancel the proposal.
Gets the actions of a selected proposal. Pass a proposal ID and get the targets, values, signatures and calldatas of that proposal.
Gets a proposal ballot receipt of the indicated voter.
Gets the proposal state for the specified proposal. The return value, ProposalState is an enumerated type defined in the Governor Alpha contract.
Cast a vote on a proposal. The account's voting weight is determined by the number of votes the account had delegated to it at the time the proposal state became active.
Cast a vote on a proposal. The account's voting weight is determined by the number of votes the account had delegated at the time that proposal state became active. This method has the same purpose as Cast Vote but it instead enables offline signatures to participate in ZTRL Governance voting. For more details on how to create an offline signature, review EIP-712.
Each zToken contract and the ZTRLtroller contract allow the Timelock address to modify them. The Timelock contract can modify system parameters, logic, and contracts in a 'time-delayed, opt-out' upgrade pattern.
The Timelock has a hard-coded minimum delay of 2 days, which is the least amount of notice possible for a governance action. Each proposed action will be published at a minimum of 2 days in the future from the time of announcement. Major upgrades, such as changing the risk system, may have a 14 day delay.
The Timelock is controlled by the governance module; pending and completed governance actions can be monitored on the Timelock Dashboard.
The ZTRLtroller contract designates a Pause Guardian address capable of disabling protocol functionality. Used only in the event of an unforeseen vulnerability, the Pause Guardian has one and only one ability: to disable a select set of functions: Mint, Borrow, Transfer, and Liquidate. The Pause Guardian cannot unpause an action, nor can it ever prevent users from calling Withdraw, or Repay Borrow to close positions and exit the protocol.
ZTRL token-holders designate the Pause Guardian address, which is currently held by ZTRL Labs, Inc.
The Open Price Feed allows Reporters to sign price data using a known public key, which Posters (any Ethereum address) can submit on-chain.
The protocol codebase will hosted on GitHub, and maintained by the community.
Coinbase Pro is the first Reporter using the Open Price Feed format, signing prices every few minutes for assets traded on the exchange.
Additional exchanges are developing their own Reporters for their own trading pairs, and new views can be deployed to use their prices.
BTC, ETH, DAI, REP, ZRX, BAT, KNC, LINK, COMP, UNI
BTC, ETH, DAI, ZRX, BAT, KNC, LINK, COMP
The above endpoints are also mirrored at prices.staked.finance.
* Signing and publishing prices, but not yet a designated reporter in UniswapAnchoredView.
The ZTRL protocol uses a View contract ("Price Feed") which verifies that reported prices fall within an acceptable bound of the time-weighted average price of the token/ETH pair on Uniswap v2, a sanity check referred to as the Anchor price.
The contracts can be found on-chain as follows:
The Open Price Feed consists of two main contracts.
This architecture allows multiple views to use the same underlying price data, but to verify the prices in their own way.
Posts an array of prices to OpenOraclePriceData and saves them to UniswapAnchoredView if the prices are within a bound of the anchor price and are signed by a reporter. Prices are expected to be in USD with 6 decimals of precision.
Get the most recent price for a token in USD with 6 decimals of precision.
Get the most recent price for a token in USD with 18 decimals of precision.
Each token the Open Price Feed supports needs corresponding configuration metadata. The configuration for each token is set in the constructor and is immutable.
The fields of the config are:
Posts a message signed by the reporter that invalidates the reporter's prices and causes the anchor price to be used directly instead. To be used in cases of emergency, if the reporter thinks their key may be compromised. Any Ethereum address can send the invalidation message.
Get the reporter's address. Currently Coinbase Pro's address.
Get the anchor period, the minimum amount of time in seconds over which to take the time-weighted average price from Uniswap.
Get the highest and lowest ratio of the reported price to the anchor price that will still trigger the price to be updated. Given in 18 decimals of precision (eg: 90% => 90e16).
The security of the ZTRL protocol is our highest priority; our ISO-27001 certified development team, alongside third-party auditors and consultants, has invested considerable effort to create a protocol that we believe is safe and dependable. All contract code and balances are publicly verifiable, and security researchers are eligible for a bug bounty for reporting undiscovered vulnerabilities.
We believe that size, visibility, and time are the true test for the security of a smart contract; please exercise caution, and make your own determination of security and suitability.
Security is core to our values, and we value the input of hackers acting in good faith to help us maintain the highest standard for the security and safety of the Ethereum ecosystem. The ZTRL protocol, while it has gone through professional audits and formal verification, depends on new technology that may contain undiscovered vulnerabilities.
ZTRL encourages the community to audit our contracts and security; we also encourage the responsible disclosure of any issues. This program is intended to recognize the value of working with the community of independent security researchers, and sets out our definition of good faith in the context of finding and reporting vulnerabilities, as well as what you can expect from us in return.
ZTRL offers substantial rewards for discoveries that can prevent the loss of assets, the freezing of assets, or harm to a user, commensurate with the severity and exploitability of the vulnerability. ZTRL will pay a reward of $500 to $150,000 for eligible discoveries according to the terms and conditions provided below.
The primary scope of the bug bounty program is for vulnerabilities affecting the on-chain ZTRL protocol, deployed to the Ethereum Mainnet, for contract addresses listed in this developer documentation.
This list may change as new contracts are deployed, or as existing contracts are removed from usage. Vulnerabilities in contracts built on top of the Protocol by third-party developers (such as smart contract wallets) are not in-scope, nor are vulnerabilities that require ownership of an admin key.
The secondary scope of the bug bounty program is for vulnerabilities affecting the ZTRL Interface hosted at app.staked.finance that could conceivably result in exploitation of user accounts.
Finally, test contracts (Rinkeby and other testnets) and staging servers are out of scope, unless the discovered vulnerability also affects the ZTRL protocol or Interface, or could otherwise be exploited in a way that risks user funds.
Submit all bug bounty disclosures to firstname.lastname@example.org. The disclosure must include clear and concise steps to reproduce the discovered vulnerability in either written or video format. ZTRL will follow up promptly with acknowledgement of the disclosure.
To be eligible for bug bounty reward consideration, you must:
To encourage vulnerability research and to avoid any confusion between good-faith hacking and malicious attack, we require that you:
When working with us according to this program, you can expect us to: