# Introduction

Union is a member-owned credit protocol built on Ethereum where members can underwrite lines of credit to other member addresses.

<figure><img src="/files/EHt0Rk0K4a0xlZfiSm5v" alt=""><figcaption></figcaption></figure>

Union operates as a DAO and enables any address to accumulate a credit line on-chain in a permission-less, crypto-native way. The protocol itself is not an underwriter of risk, but rather a mechanism to lower the cost of coordinating trust into available credit.

The Union Protocol is a credit network that enables any address to accumulate a credit line on-chain in a permission-less, crypto-native way. The protocol itself is not an underwriter of risk, rather a mechanism to lower the cost of coordinating trust into available credit.

\
\
By aggregating lines of credit, Union Members can source capital at a lower cost than any single member could on their own. This enables a virtuous circle of more available credit, lower borrowing costs, and increased lending activity.

For a quick intro, check out the [**Introducing Union**](https://medium.com/union-finance/introducing-union-70f58f27ba10) blog post.

## What does Union do that wasn’t previously possible?

1. Creates efficiency. 1 Dai can be vouching for multiple accounts and contracts until it's actively borrowed.
2. Brings real world trust on-chain while retaining pseudonymity.
3. A whole market of new businesses in between 0%-150% LTV products.
4. Smart Contracts/DAOs get with credit lines.
5. Invest by extending credit.
6. There’s additional UX mechanisms and experiences you can create to incentivize vouching for different behaviors ie vouch for charity (microfinance), invest in a dao by vouching (instead of a convertible note), etc

## Union Dapp Guides

To make exploring and interacting with the protocol easy, the Union community has created a frontend.

To get started, [**check out the guides here**](/user-guides/becoming-a-member), or jump right into the ap&#x70;**.**

Join the credit network: <http://app.union.finance/>

## Communication Channels

:bird:[**Twitter**](https://twitter.com/unionprotocol)

:octopus:[**GitHub**](https://github.com/unioncredit)

:tada:[**Discord**](https://discord.com/invite/VXWnjq26zy)


# Important Links

Useful links about the Union ecosystem.

**Dev Information**

* GitHub: <https://github.com/unioncredit>
* Developer docs: <https://docs.union.finance>

**dApps**

* Interface dApp: <https://app.union.finance>
* Analytics charts: <https://data.union.finance/optimism>
* Older V1 interface: <https://v1.union.finance>

**Socials and DAO**

* Main website: <https://union.finance>
* Warpcast Channel: <https://warpcast.com/~/channel/union>
* Warpcast : <https://warpcast.com/union>
* Forum (in Discord): <https://discord.com/invite/gearbox>
* Twitter: <https://twitter.com/unionprotocol>
* Blog: <https://union.mirror.xyz/>
* DAO Voting: <https://www.tally.xyz/gov/union>


# How Union Works


# Plain English (Detailed)

**Key Protocol Activities**

* [#user-staking-dai](#user-staking-dai "mention")
* [#stakers-earning-union-from-comptroller](#stakers-earning-union-from-comptroller "mention")
* [#members-borrow-dai](#members-borrow-dai "mention")
* [#members-vouch-for-other-members](#members-vouch-for-other-members "mention")
* [#applying-becoming-a-member](#applying-becoming-a-member "mention")
* [#utoken-liquidity-providers](#utoken-liquidity-providers "mention")
* [#governance](#governance "mention")

## User Staking DAI

* User stakes DAI (or other token) in the {UserManager contract}
* Dai Staked is further deposited into the {assetmanager} which can hold tokens as vanilla DAI or drop it into a 3rd party lending pool(such as Compound) in a way that the principal and interest are both accounted for. Principal is owned by staker & Interest is owned by the protocol ‘dao’.
* Staked funds exist in 3 states:
  * **“Free Stake”** = Withdrawable & Earning: These are staked funds that aren’t backing an active borrow
  * **“Utilized Stake”** = Earning & Non-withdrawable: These are staked funds that are backing an active borrow
  * **“Defaulted Stake”** = Not-earning & Not-withdrawable: These are staked funds that are backing a delinquent borrow.

## Stakers earning Union from Comptroller

* The Treasury contract drips UNION to the comptroller at a specified amount per block
* The comptroller lets UNION Token be claimed by stakers based on a combination of their individual activity and global staking behavior.
* The algorithm that determines how much UNION each user can claim each block is a half decay curve with total dai staked as the input.
  * *That equation currently is a table approximation of a half decay function re: the more dai staked the less union is claimable, until the amount staked = midpoint at which the rate drops off precipitously. The midpoint is meant to be the available credit, so we aren’t over-incentivizing staking when there is no available credit, but we’ll likely set this high in the beginning to speed up decentralization.*
* The output of that curve is then used to get a “UNIONperDAIperBlock” rate which is multiplied by your current stake.
* If you’re not a member that rate is multiplied by 0.75 if you’re not a member yet. If you are a member it is multiplied by you utilization rate (utilized stake / total non frozen stake) effectively giving a 2x bonus for each DAI backing an active non defaulted borrow.
* Stake utilization is meant to give control of the protocol to those who vouch for people who borrow and dont default. And the 0.75 is to allow non-members a non financial means but still sybil resistant way to acquire the UNION. The curve is upgradeable without requiring a fork or a withdrawal of the dai staked. All of this is handled by the {Comptroller.sol Contract}

## Members Borrow Dai

* A Member can borrow dai up to \[available credit limit] from a separate lending pool(uToken).
* Available credit limit = Credit Limit - Outstanding Balance
* \[Credit limit] is based on the credit limit algorithm.
* The Credit Limit Algorithm determines both how much can be borrowed and how much stake should be frozen when a borrow takes place. Currently this is SumOfTrust: Credit Limit = Sum(Available Vouch) and the locked amount locks the borrowed amount starting from the highest vouch.
* Origination Fee: To account for the front loaded risk Borrowed amount on withdrawal = Amount withdrawn multiplied by 1 + the {origination fee}.
* Balance increases at {apr\_rate} vouched principle is not withdraw-able up to the amount of vouchees balance
* A loan is considered in default if a minimum payment (the interest accrued) hasn’t been paid in X # of blocks This is currently set to \[30 days]
* Borrow amount safety limit factors:

  * Min/Max loan size
  * Global debt ceiling for each asset
  * Current balance of the lending pool, including bootstrap deposit and interest earned from staking funds

  ### Borrower repays loan
* After a member withdraws a credit line they have a balance owed
* They can repay as much or as little at any time, including a pay off all setting.
* The different payment UX between duration and/or loan type(e.g. revolving-line, installment) can be handled through differing UI’s or aided by helper contracts.
* If a borrower is late, they can’t withdraw more and their vouchers stop earning UnionTokens on the stake that’s underwriting that borrow.
* If they make a payment to get back in good standing their voucher starts earning again. Any address can repay the debt of any address.
* The interest of all loans is split between the reserve and utoken holders based on the reserve factor. If it’s set to 1.0 all interest is directed to the reserve.

## Members vouch for other members

* Any member can set an amount they trust any other address.
* If trust>stake: vouch = stake.
* If trust\<stake: vouch = trust
* Update Trust: can increase or decrease by calling updateTrust. Can’t lower below what is currently being utilized.
* A member can't vouch for themselves.
* A vouch can be removed by either the vouchee or the voucher.
* A vouch can only be removed if unused
* A voucher can voluntarily writeoff() an active loan at any time by giving up locked stake. This is irreversible.
* As of v2 if the borrower is overdue for a max period(60 days) then anyone can call the writeoff() function.

## Applying/Becoming a member

* To be added to the member list you must register your address.
* A member can add Trust to any address. So non-member could have a membership & credit limit waiting for them.
* Calling that function requires a burn of UNION. This is to prevent spam attacks and account for the marginal increase in system wide costs of adding another member.

## uToken Liquidity Providers

* Anyone can deposit DAI to the uToken contract and mint uDAI
* uDAI is a claim on the DAI deposited into the contract by uDAI minters.
* This DAI is borrowed by members with Credit Limits
* When DAI is repaid any interest is sent to uDAI holders or the Reserve based on the reserve factor. uDAI can be burned to claim DAI at the internal exchange rate

## AssetManager

All tokens are held in the assetmanager contract

![](https://lh4.googleusercontent.com/gXZ8E5ngSrPeCK1OrhVnxH4dHVg7JmnfrxPgmjgQ3rbJgN9aX_uFrLMlNoP1BCFKRTj_1k7Lt6OlnqtJ1K-XPZGriWGT3HYve7NhfPhYsi1eamTvDqgiDrPz22Yhbpegsyzzok5ega3oCnzBTA)

The asset manager is capable of depositing assets into other markets(Adaptors) at a specified ratio.

Currently there are adaptors for Compound.v2 and AAVE.v2. If any interest is earned that interest goes to grow the available credit but is not distributed to UNION holders, stakers, or uDAI minters.

## Governance

The primary purpose of the [UNION Token](/governance/union-token) is to govern the protocol.

All parameters, as well as upgradeable proxy logic are changeable via majority vote of the UNION Token holders.

Can add new markets with any erc20 asset and parameterizations.

Initial UNION supply is 1 Billion and governance can mint 2% a year after 4yrs.


# Use Cases

Credit is simply giving someone a dollar and expecting them to return it - be it a lifeline, an open line of credit, or an investment bridge. Union functions as a primitive so that anyone can lend, borrow, or build credit on-chain.

**Union is more a foundation to build on than a retail product.**

There are many types of credit relationships that could be defined in Union:

* 1 → 1: This is a lot of overhead for the use case of a single person lending a friend $20. (Passive Nature: When you borrow from a friend)
* Many → 1: Groups working together: 3 people vouching $100 for a 4th, gives the 4th access to $300.
* 1 → Many: a single Underwriter vouching for a param, a DAO providing it’s members a credit line
* Many -> Many: All of the above contributing

It will be up to devs to build product experiences.


# v1->v2 changelog

This is version 2 of the core protocol

<figure><img src="/files/JGZuV7dt85Igt6GRTAWC" alt=""><figcaption></figcaption></figure>

Announcement Post: <https://union.mirror.xyz/uPnUm4TLNyWoRfNAVCJwxT0TeNPBgNCg04vuDv5sIGk>

If you are looking for the v1 ui to access the arbitrum or ethereum v1 deployments: <https://v1.union.finance>

If you are looking for the v1 docs: <https://v1-docs.union.finance>

## Major Upgrades between v1 and v2:

1. In Version 2, the maximum number of addresses one can underwrite has increased from 25 -> 1000 and be underwritten by from 25 -> 400 from a single account.
2. **Bad debt no longer accrues indefinitely**: If you vouch for a defaulter, the writeoff function becomes publicly callable after a set amount of time in default. This is essential for growing liquidity— on Optimism this will be set to 60 days, meaning if someone borrows and makes no payments, anyone will be able to call the function after 90 days.
3. **Union Lens Contract** : Union is a call-intensive protocol. This view layer contract consolidates view helpers making it a lot easier to build frontends and integrations.
4. **More efficient and predictable gas costs** : v2 uses struct packing and other techniques to dramatically reduce storage read and writes compared to v1 . Vouching now has a constant gas cost, in V1 the gas cost was dependent on number of vouchers(*O(n)*) but in v2 it is constant (*O(1)*).
5. **Changed from block-based timing to seconds**, makes 'due by' dates 1000x more human and integration friendly.


# FAQ

Answers to questions that are frequently asked.

This section can help you find answers to the most common questions. The other answers you can  find by reading the page names of the docs on the left.

* Where is the UNION token contract and vestings? - [here](/governance/union-token).
* How do you determine who and how much credit to give? - [you do](/user-guides/vouching-and-lending).
* How do i get the money back if they dont pay me back? - you don't, this is the risk of credit.&#x20;
* &#x20;
* (ask your questions on warpast)


# UNION DAO

##

## Contribute

To learn more about opportunities to get involved, contribute, and build, jump into the Union Discord. The DAO welcomes people of all backgrounds and skill levels.

{% embed url="<https://discord.gg/qu9JYpxKzy>" %}

## UNION token

All the details describing the UNION token can be found in the blog post below.

{% content-ref url="/pages/ZCkHg4LuabqBW8brdqIH" %}
[UNION Token](/governance/union-token)
{% endcontent-ref %}

{% embed url="<https://medium.com/union-finance/intro-to-union-token-and-dao-d6040e0e7a8b>" %}

Early contributors, members, and other community members are eligible to claim UNION. Check your eligibility below.

{% embed url="<https://airdrop.union.finance>" %}

## Vote

To vote, delegate, and view active proposals, head to the Union dapp or Tally governance ui.

{% embed url="<https://app.union.finance/governance>" %}

{% embed url="<https://www.tally.xyz/gov/union>" %}

If voting and delegation is new or unfamiliar, use the guide below to learn more.

{% content-ref url="/pages/WY3JMMP1VrdyDn1PGCKw" %}
[Voting & Delegation](/user-guides/voting-and-delegation)
{% endcontent-ref %}


# Community Delegates

Become a delegate or delegate your UNION as a holder!

How to delegate your UNION It takes literally 1 minute!&#x20;

Choose a delegate you like from the list below.&#x20;

## How to delegate your UNION tokens

{% hint style="info" %}
Notice: This is just a delegate function, you are not transferring and your UNION are not locked. You can undelegate or move them freely. But if you do either one of these things, your delegate will lose the corresponding number of votes, of course.
{% endhint %}

{% content-ref url="/pages/WY3JMMP1VrdyDn1PGCKw" %}
[Voting & Delegation](/user-guides/voting-and-delegation)
{% endcontent-ref %}

<br>


# Gaurdians & Multisigs

### **Pause Gaurdians**

As a safety mechanism some of the contracts are able to be paused if a hack, exploit, or bug is detected early enough. It can only pause the protocol, and the pauseGaurdian is DAO changeable.&#x20;

### **Relevant Addresses:**

**Ethereum**

*

**Optimism**

* 3 of 6 opMultiSig: 0x652AbFA76d8Adf89560f110322FC63156C5aE5c8 2.
* opPauseGaurdian: 0xF7dc916eC6ee854b3a32f5D8DcF2ED0582e05Dc3 3.
* opOwner: 0x946A2C918F3D928B918C01D813644f27Bcd29D96


# UNION Token

Supply information

## Supply Information

UNION token contract and distribution information.

UNION token is an ERC20 utility token, starting as a governance token for the protocol - and then possibly taking any other new function the DAO could envision for it.&#x20;

The initial supply of UNION is 1,000,000,000 (1 billion). with the dao having the ability to mint up to 2%/year (starting 2025/12/22).

Token contract ERC-20: [0x5dfe42eea70a3e6f93ee54ed9c321af07a85535c](https://etherscan.io/token/0x5dfe42eea70a3e6f93ee54ed9c321af07a85535c)

Decimals: 18&#x20;

Symbol: $UNION&#x20;

Coingecko: <https://www.coingecko.com/en/coins/union-finance>

## Initial Supply Information

1 Billion UNION tokens have been minted at genesis.

* 20% to Community DAO — 200,000,000 (+ 2%/yr mint in perpetuity)
* 46.78% to Initial and Future Team — 466,666,666: 1yr cliff + 4yr vest
* 33.22% to Early Supporter s — 332,166,667: 1yr lockup + 1yr vest

### Community-governed Treasury  <a href="#e418" id="e418"></a>

200,000,000 Union was initially allocated to the following community owned contracts.

* **150M is in a Treasury Vester** **contract** that will vest into the Treasury contract with the following hardcoded params:&#x20;
  * *vestingBegin: 2022/12/22,*&#x20;
  * *vestingEnd: 2025/12/22,*&#x20;
  * *vestingAmount: 150M*
* **38.9M is in the Treasury Contract** and immediately accessible by governance.
* **9M pre-funded the EthComptroller Contract** — Also has a drip() from the treasury starting from block #13816842 at a rate of 1 UNION per block for a total amount of 20M UNION. so, the end block will be blk# 15816842
* **2M to a merkledrop contract** — Ended.
* **100K pre-funded the ArbComptroller**.

## Bridged Tokens

UNION has been bridged to Arbitrum and Optimism.

:blue\_circle:Contract Address · arbUNION: [0x6DBDe0E7e563E34A53B1130D6B779ec8eD34B4B9](https://arbiscan.io/address/0x6DBDe0E7e563E34A53B1130D6B779ec8eD34B4B9)

:red\_circle:Contract Address · opUNION: [0xB025ee78b54B5348BD638Fe4a6D77Ec2F813f4f9](https://optimistic.etherscan.io/address/0xB025ee78b54B5348BD638Fe4a6D77Ec2F813f4f9)

## <br>


# Governance Process

An overview of Union DAO’s governance processes and how to get involved.

This document is a suggested process for developing and advancing Union Governance Proposals. It is a living document intended to be owned, modified and enforced by the Union community.

## **Venues**

UIP drafts are created and edited in the open [**Union DAO Github repo**](https://github.com/unioncredit/UIPs/issues). Proposal code, tests, and simulations also live here.

Governance proposals are surfaced and discussed in the [**governance channel**](https://discord.com/channels/714982388970684468/923268201486377010) in the Union Discord Server.

Delegate tokens, view active Union Improvement Proposals (UIPs), and vote in the [**Union dapp**](https://app.union.finance/governance).

View proposals, delegate votes, and vote, in [**Tally**](https://www.withtally.com/governance/eip155:1:0xe1b3F07a9032F0d3deDf3E96c395A4Da74130f6e), a Union integration.

This list is not exhaustive and will be updated by the community.

## **Process**

### **Defining a Proposal**

**A proposal** is \*\*\*\* a series of smart contract operations, or parameter updates, to be executed on-chain by accounts the DAO controls.

Proposals can outline transfers of tokens as well as arbitrary smart contract calls. For example, a proposal could outline funding a multisig wallet. Another could outline upgrading a Union core contract. Executable proposals have a quorum requirement of 4% and require a minimum of 50% in-favor votes to pass.

### **Phase I: Temperature Check & Proposal**

Phase I is primarily handled off-chain.

### **Temperature Check**

To determine if there is enough demand for change, a Temperature Check is used. A Temperature Check is a soft signaling method used to gauge community sentiment. It often garners key feedback that may inform the proceeding of a proposal.

To initiate a Temperature Check, ask a general, non-biased question to the community in the[ **Union Discord Governance Channel**](https://discord.com/channels/714982388970684468/923268201486377010) about a potential change. For example, “Should Union increase integrations with third parties?”

### **Drafting a Proposal**

**Step 1**

Create an issue in the open [**Union Github repository**](https://github.com/unioncredit/UIPs/issues). The issue is the plain english version of a UIP. Go [**here**](https://github.com/unioncredit/UIPs/blob/main/.github/ISSUE_TEMPLATE/uip-template.md) \*\*\*\* to view the recommended template. The repo is set to automatically present the template when a new issue is created. Be sure to follow the correct naming convention.

UIPs are sequentially numbered and follow a naming convention: UIP \[####] - \[UIP title].

Number a new UIP by adding 1 to the most recent one created. For example, if you see UIP 0024 as the highest numbered UIP, name yours UIP 0025.

The purpose of the Draft Proposal is to generate discussion. Engage with the community to build support, solicit delegates to provide feedback on the proposal, and be willing and ready to respond to questions. Share your viewpoint, but be respectful of other people's perspectives as well.

**Step 2**

Reach out to the Union Discord Admins to discuss your proposal in a Community Call or on a Twitter Space.

All necessary code for your proposal will need to be written while it is in draft stage, including simulations and tests validating the stated effect of the proposal. It is recommended that the content and scope of the proposal are stable before doing this. Be sure to include links to PRs in the UIP.

### **Phase II: Active Proposal**

Phase II is primarily handled on-chain.

### **Active Proposal — Governance Portal**

Once there is a general consensus that the proposal meets community standards as a valid proposal and necessary code is finalized and proved executable, advance your proposal to a vote by submitting it to the governor contract.

To enact an Executable Proposal, ensure at least 10M UNION is delegated to your address or find a delegate who has enough delegated UNION to meet the proposal threshold to propose on your behalf.

Once the propose() function has been called, the proposal is queued and voting will begin in 24 hours. The voting period is 3 days. Ongoing discussion can take place in the Union Discord [**Governance Channel**](https://discord.com/channels/714982388970684468/923268201486377010) or in the [**Union repo**](https://github.com/unioncredit/UIPs/issues), directly on the draft content. If the proposal succeeds, a 24 hour timelock will be enacted before the proposed code is executed.

All other networks are controlled via bridge from L1 governance.

***


# Technical Implementations

### Proposal Structure

Create a folder for each new proposal under the `proposals_in_review/` and have the following files:

* `addresses.js` - has all the proposal-related addresses grouped by chains (for testnet, mainnet, etc.).
* `proposal.js` - contains the actual logic and content of the proposal.
* `submitProposal.js` - the script to submit the proposal.
* `test/` - the test folder contains all the testing and simulation scripts.

### Steps to create, test, and submit a proposal

* Write the proposal logic and content in `proposal.js` , and add related addresses in `addresses.js` file
* Create tests and simulations in the `test/` folder
* Run tests with `yarn test`
* Run the simulations with `yarn sim:in_review`
* Submit the proposal to the testnet: `yarn submit --network kovan proposals_in_review/NEW_PROPOSAL/submitProposal.js`
* Manual test and review the proposal on testnet
* Submit the proposal to mainnet: `yarn submit --network mainnet proposals_in_review/NEW_PROPOSAL/submitProposal.js`


# Glossary of Terms

### **UNION**

An ERC-20 token that represents voting rights. Each UNION is one vote. The more UNION a user has in their wallet, the more weight their vote or delegation holds.

### **Delegation**

UNION holders cannot vote or create proposals until they delegate their UNION to an address. Delegation can be given to one address at a time, including the holder’s own address.

### **Executable Proposal**

An executable proposal is a type of proposal that is executed by the governance contract through timelock. It can replace the governance contract, transfer tokens from the community treasury, or perform an almost infinite range of other on-chain actions. In order to create a proposal, an address must have at least 10M UNION tokens delegated to their address.

### **Quorum**

In order for a vote to pass, a certain percentage of UNION tokens must vote in the affirmative. The current quorum requirement is 4%. The quorum requirement ensures that the only measures that have adequate voter participation pass.

### **Voting on Executable Proposals**

Users can vote for or against single proposals once they have voting rights delegated to their address. Votes can be cast while a proposal is in the “Active” state in[ **the Union dapp**](https://app.union.finance/governance) or in [**Tally**](https://www.withtally.com/governance/eip155:1:0xe1b3F07a9032F0d3deDf3E96c395A4Da74130f6e). If quorum is reached and the majority of votes are in favor, the proposal may be queued in the Timelock.

### **Review/Delay Period**

The period in which the proposal has been deployed on chain but is not yet open for voting.

### **Voting Period**

Once an executable proposal has been queued, it will go live after 24 hours. Union community members will then have a 3-day period (the Voting Period) to cast their votes.

### **Timelock**

All governance actions are delayed for a minimum of 1-day by the timelock contract before they can be executed.


# Union Contracts Overview

![](https://lh4.googleusercontent.com/PWOH67yPs-tqU1SwHSdPvqfUJ1JCbAxTCGdBQRMONQ4shcJjSv1ZM9p2u_MKR7ed_-Gbn4UHOQsGO6dBaBFkINtNyG6PcmzjZb2HNnCc0oAH_4LnNqn3aIgu7fUPmZYFHAHUwdM4gWxFidC-_A)

### Core Components

1. uToken: Manages the Union members credit lines, and their vouchees and borrowers info.
2. CreditLimitModel: Calculate the user's credit line based on the trust he receives from the vouchees.
3. MarketRegistry: Registering and managing all the lending markets.
4. LendingMarket: Union members can borrow and repay thru this component.
5. ‌AssetManager: Manage the token balances staked by the users and deposited by admins, and invest tokens to the integrated underlying lending protocols.
6. UserManager: Manages all the stakers deposited, locked, and frozen stakings
7. InterestRateModel: Calculate the borrowers' interest rate.
8. MoneyMarketAdaptor: Working with AssetManager to support external money markets, like Compound etc.


# Contract ABIs & Addresses

Union is an upgradeable system. Each deployed contract is actually a "Proxy" that points to an "Implementation" contract. All addresses can be found in the folder `deployments/${network}`.

## Networks

{% tabs %}
{% tab title="Optimism" %}

<table><thead><tr><th width="185">Contract</th><th width="446.66666666666663">Address</th><th>Upgradeable</th></tr></thead><tbody><tr><td>UserManager</td><td><a href="https://optimistic.etherscan.io/address/0x8E195D65b9932185Fcc76dB5144534e0f3597628">0x8E195D65b9932185Fcc76dB5144534e0f3597628</a></td><td>✅</td></tr><tr><td>MarketRegistry</td><td><a href="https://optimistic.etherscan.io/address/0x6d44E3b49a6e85Cc316Ef19B423e84A63F7c6D0C">0x6d44E3b49a6e85Cc316Ef19B423e84A63F7c6D0C</a></td><td>✅</td></tr><tr><td>Comptroller</td><td><a href="https://optimistic.etherscan.io/address/0x06a31efa04453C5F9C0A711Cdb96075308C9d6E3">0x06a31efa04453C5F9C0A711Cdb96075308C9d6E3</a></td><td>✅</td></tr><tr><td>uDAI</td><td><a href="https://optimistic.etherscan.io/address/0xE478b5e7A423d7CDb224692d0a816CA146A744b2">0xE478b5e7A423d7CDb224692d0a816CA146A744b2</a></td><td>✅</td></tr><tr><td>AssetManager</td><td><a href="https://optimistic.etherscan.io/address/0xE4ADdfdf5641EB4e15F60a81F63CEd4884B49823">0xE4ADdfdf5641EB4e15F60a81F63CEd4884B49823</a></td><td>✅</td></tr><tr><td>PureTokenAdapter</td><td><a href="https://optimistic.etherscan.io/address/0x9B8d609eccE72d6f71c026539370F7b5E55A1343">0x9B8d609eccE72d6f71c026539370F7b5E55A1343</a></td><td>✅</td></tr><tr><td>aaveV3Adapter</td><td><a href="https://optimistic.etherscan.io/address/0x9C69fC4edcce8D3410Ab59E9F9d9e65e5F41350B">0x9C69fC4edcce8D3410Ab59E9F9d9e65e5F41350B</a></td><td>✅</td></tr><tr><td>UnionToken</td><td><a href="https://optimistic.etherscan.io/address/0xB025ee78b54B5348BD638Fe4a6D77Ec2F813f4f9">0xB025ee78b54B5348BD638Fe4a6D77Ec2F813f4f9</a></td><td>❌</td></tr><tr><td>OpOwner</td><td><a href="https://optimistic.etherscan.io/address/0x946A2C918F3D928B918C01D813644f27Bcd29D96">0x946A2C918F3D928B918C01D813644f27Bcd29D96</a></td><td>❌</td></tr><tr><td>DAI</td><td><a href="https://optimistic.etherscan.io/address/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1">0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1</a></td><td>❌</td></tr></tbody></table>
{% endtab %}

{% tab title="Base Sepolia" %}

<table><thead><tr><th width="185">Contract</th><th width="442.66666666666663">Address</th><th>Upgradeable</th></tr></thead><tbody><tr><td>UserManager</td><td>0x4C52c9E49aa6a5029c0F94753c533DFEBcf8AabA</td><td>✅</td></tr><tr><td>MarketRegistry</td><td>0x4AA4868189e2A650B8423d3A36F6c044005cc8C4</td><td>✅</td></tr><tr><td>Comptroller</td><td>0x84CEE16a05C48268724f11512cb405097f96eDB2</td><td>✅</td></tr><tr><td>uUSDC</td><td>0x01Cc03de0742dF77b934C3aFA848AE2BB73576Ed</td><td>✅</td></tr><tr><td>AssetManager</td><td>0x311B84A6ca1196efd1CEc7E4fa09D8C2C171492A</td><td>✅</td></tr><tr><td>PureTokenAdapter</td><td>0x95BB25c0A11347C8DE402904dce3Be628a4521C0</td><td>✅</td></tr><tr><td>UnionToken</td><td>0xc124047253c87EF90aF9f4EFC12C281b479c4769</td><td>❌</td></tr><tr><td>OpOwner</td><td>0x0D25131E098DfB65746ecC3C527865A7bBA71886</td><td>❌</td></tr><tr><td>USDC</td><td>0x036CbD53842c5426634e7929541eC2318f3dCF7e</td><td>❌</td></tr></tbody></table>
{% endtab %}
{% endtabs %}


# Core Components

## Core Components

![](https://lh4.googleusercontent.com/PWOH67yPs-tqU1SwHSdPvqfUJ1JCbAxTCGdBQRMONQ4shcJjSv1ZM9p2u_MKR7ed_-Gbn4UHOQsGO6dBaBFkINtNyG6PcmzjZb2HNnCc0oAH_4LnNqn3aIgu7fUPmZYFHAHUwdM4gWxFidC-_A)

{% embed url="<https://github.com/unioncredit/union-v2-contracts>" %}

## Union Contracts V2

Built using [foundry](https://book.getfoundry.sh/) and [hardhat](https://hardhat.org/)

### Deployments

* Goerli
  * [contract addersses](https://github.com/unioncredit/union-v2-contracts/blob/master/deployments/goerli/deployment.json)
  * [deployment config](https://github.com/unioncredit/union-v2-contracts/blob/master/deployments/goerli/config.json)

### Install

To install dependencies:

```
git clone git@github.com:unioncredit/union-v2-contracts.git && cd union-v2-contracts
yarn install
```

### Compile

To compile with hardhat:

```
yarn hh:compile
```

### Foundry

Union V1.5 Contracts also includes a suit of tests (fuzzing tests) writte in solidity with foundry

To install Foundry (assuming a Linux or macOS System):

```
curl -L https://foundry.paradigm.xyz | bash
```

This will download foundryup. To start Foundry, run:

```
foundryup
```

To install dependencies:

```
forge install
```

To run tests:

```
forge test
```

The following modifiers are also available:

* Level 2 (-vv): Logs emitted during tests are also displayed.
* Level 3 (-vvv): Stack traces for failing tests are also displayed.
* Level 4 (-vvvv): Stack traces for all tests are displayed, and setup traces for failing tests are displayed.
* Level 5 (-vvvvv): Stack traces and setup traces are always displayed.

```
forge -vv
```

To profile gas usage:

```
forge test --gas-report
forge snapshot
```

### Fork Tests

Integration tests can be run using hardhat.

```
yarn hh:test
```

They can also be run in fork mode. (Some tests can be excluded from running in fork mode and other tests can only run in fork mode)

```
FORK_NODE_URL=<URL> FORK_BLOCK=<NUMBER> yarn hh:test
```

You can also define which config should be used for the fork mode.

```
CONFIG=arbitrum FORK_NODE_URL=<URL> FORK_BLOCK=<NUMBER> yarn hh:test
```

### Format

```
yarn format
```


# UserManager

*Manages the Union members stake and vouches.*

### Vouch

```solidity
struct Vouch {
  address staker;
  uint96 trust;
  uint96 locked;
  uint64 lastUpdated;
}
```

### Staker

```solidity
struct Staker {
  bool isMember;
  uint96 stakedAmount;
  uint96 locked;
}
```

### Index

```solidity
struct Index {
  bool isSet;
  uint128 idx;
}
```

### Vouchee

```solidity
struct Vouchee {
  address borrower;
  uint96 voucherIndex;
}
```

### maxStakeAmount

```solidity
uint96 maxStakeAmount
```

@dev Max amount that can be staked of the staking token

### stakingToken

```solidity
address stakingToken
```

@dev The staking token that is staked in the comptroller

### unionToken

```solidity
address unionToken
```

@dev Address of the UNION token contract

### assetManager

```solidity
address assetManager
```

@dev Address of the asset manager contract

### uToken

```solidity
contract IUToken uToken
```

@dev uToken contract

### comptroller

```solidity
contract IComptroller comptroller
```

@dev Comptroller contract

### effectiveCount

```solidity
uint256 effectiveCount
```

*Number of vouches needed to become a member*

### newMemberFee

```solidity
uint256 newMemberFee
```

@dev New member fee

### totalStaked

```solidity
uint256 totalStaked
```

@dev Total amount of staked staked token

### totalFrozen

```solidity
uint256 totalFrozen
```

@dev Total amount of stake frozen

### maxOverdueBlocks

```solidity
uint256 maxOverdueBlocks
```

@dev Max blocks can be overdue for

### maxVouchers

```solidity
uint256 maxVouchers
```

*Max voucher limit*

### stakers

```solidity
mapping(address => struct UserManager.Staker) stakers
```

@dev Union Stakers

### vouchers

```solidity
mapping(address => struct UserManager.Vouch[]) vouchers
```

@dev Staker (borrower) mapped to recieved vouches (staker)

### voucherIndexes

```solidity
mapping(address => mapping(address => struct UserManager.Index)) voucherIndexes
```

*Borrower mapped to Staker mapped to index in vouchers array*

### vouchees

```solidity
mapping(address => struct UserManager.Vouchee[]) vouchees
```

@dev Staker (staker) mapped to vouches given (borrower)

### voucheeIndexes

```solidity
mapping(address => mapping(address => struct UserManager.Index)) voucheeIndexes
```

*Borrower mapped to Staker mapped to index in vochee array*

### memberFrozen

```solidity
mapping(address => uint256) memberFrozen
```

*Stakers frozen amounts*

### AuthFailed

```solidity
error AuthFailed()
```

### ErrorSelfVouching

```solidity
error ErrorSelfVouching()
```

### TrustAmountLtLocked

```solidity
error TrustAmountLtLocked()
```

### NoExistingMember

```solidity
error NoExistingMember()
```

### NotEnoughStakers

```solidity
error NotEnoughStakers()
```

### StakeLimitReached

```solidity
error StakeLimitReached()
```

### AssetManagerDepositFailed

```solidity
error AssetManagerDepositFailed()
```

### AssetManagerWithdrawFailed

```solidity
error AssetManagerWithdrawFailed()
```

### InsufficientBalance

```solidity
error InsufficientBalance()
```

### LockedStakeNonZero

```solidity
error LockedStakeNonZero()
```

### NotOverdue

```solidity
error NotOverdue()
```

### ExceedsLocked

```solidity
error ExceedsLocked()
```

### AmountZero

```solidity
error AmountZero()
```

### LockedRemaining

```solidity
error LockedRemaining()
```

### VoucherNotFound

```solidity
error VoucherNotFound()
```

### VouchWhenOverdue

```solidity
error VouchWhenOverdue()
```

### MaxVouchees

```solidity
error MaxVouchees()
```

### InvalidParams

```solidity
error InvalidParams()
```

### LogAddMember

```solidity
event LogAddMember(address member)
```

@dev Add new member event @param member New member address

### LogUpdateTrust

```solidity
event LogUpdateTrust(address staker, address borrower, uint256 trustAmount)
```

@dev Update vouch for existing member event @param staker Trustee address @param borrower The address gets vouched for @param trustAmount Vouch amount

### LogRegisterMember

```solidity
event LogRegisterMember(address account, address borrower)
```

@dev New member application event @param account New member's voucher address @param borrower New member address

### LogCancelVouch

```solidity
event LogCancelVouch(address account, address borrower)
```

@dev Cancel vouching for other member event @param account New member's voucher address @param borrower The address gets vouched for

### LogStake

```solidity
event LogStake(address account, uint256 amount)
```

@dev Stake event @param account The staker's address @param amount The amount of tokens to stake

### LogUnstake

```solidity
event LogUnstake(address account, uint256 amount)
```

@dev Unstake event @param account The staker's address @param amount The amount of tokens to unstake

### LogDebtWriteOff

```solidity
event LogDebtWriteOff(address staker, address borrower, uint256 amount)
```

@dev DebtWriteOff event @param staker The staker's address @param borrower The borrower's address @param amount The amount of write off

### LogSetUToken

```solidity
event LogSetUToken(address uToken)
```

@dev set utoken address @param uToken new uToken address

### LogSetNewMemberFee

```solidity
event LogSetNewMemberFee(uint256 oldMemberFee, uint256 newMemberFee)
```

@dev set new member fee @param oldMemberFee old member fee @param newMemberFee new member fee

### LogSetMaxStakeAmount

```solidity
event LogSetMaxStakeAmount(uint256 oldMaxStakeAmount, uint256 newMaxStakeAmount)
```

@dev set max stake amount @param oldMaxStakeAmount Old amount @param newMaxStakeAmount New amount

### LogSetMaxOverdueBlocks

```solidity
event LogSetMaxOverdueBlocks(uint256 oldMaxOverdueBlocks, uint256 newMaxOverdueBlocks)
```

@dev set max overdue blocks @param oldMaxOverdueBlocks Old value @param newMaxOverdueBlocks New value

### LogSetEffectiveCount

```solidity
event LogSetEffectiveCount(uint256 oldEffectiveCount, uint256 newEffectiveCount)
```

@dev set effective count @param oldEffectiveCount Old value @param newEffectiveCount New value

### LogSetMaxVouchers

```solidity
event LogSetMaxVouchers(uint256 maxVouchers)
```

*Set max voucher*

#### Parameters

| Name        | Type    | Description           |
| ----------- | ------- | --------------------- |
| maxVouchers | uint256 | new max voucher limit |

### \_\_UserManager\_init

```solidity
function __UserManager_init(address assetManager_, address unionToken_, address stakingToken_, address comptroller_, address admin_, uint256 maxOverdueBlocks_, uint256 effectiveCount_, uint256 maxVouchers_) public
```

### onlyMember

```solidity
modifier onlyMember(address account)
```

### onlyMarket

```solidity
modifier onlyMarket()
```

### onlyComptroller

```solidity
modifier onlyComptroller()
```

### setMaxStakeAmount

```solidity
function setMaxStakeAmount(uint96 maxStakeAmount_) external
```

*Set the max amount that a user can stake Emits {LogSetMaxStakeAmount} event*

#### Parameters

| Name             | Type   | Description          |
| ---------------- | ------ | -------------------- |
| maxStakeAmount\_ | uint96 | The max stake amount |

### setUToken

```solidity
function setUToken(address uToken_) external
```

*set the UToken contract address Emits {LogSetUToken} event*

#### Parameters

| Name     | Type    | Description             |
| -------- | ------- | ----------------------- |
| uToken\_ | address | UToken contract address |

### setNewMemberFee

```solidity
function setNewMemberFee(uint256 amount) external
```

*set New Member fee The amount of UNION an account must burn to become a member Emits {LogSetNewMemberFee} event*

#### Parameters

| Name   | Type    | Description           |
| ------ | ------- | --------------------- |
| amount | uint256 | New member fee amount |

### setMaxOverdueBlocks

```solidity
function setMaxOverdueBlocks(uint256 _maxOverdueBlocks) external
```

*set New max overdue blocks Emits {LogSetMaxOverdueBlocks} event*

#### Parameters

| Name               | Type    | Description                |
| ------------------ | ------- | -------------------------- |
| \_maxOverdueBlocks | uint256 | New maxOverdueBlocks value |

### setEffectiveCount

```solidity
function setEffectiveCount(uint256 _effectiveCount) external
```

*set New effective count this is the number of vouches an account needs in order to register as a member Emits {LogSetEffectiveCount} event*

#### Parameters

| Name             | Type    | Description              |
| ---------------- | ------- | ------------------------ |
| \_effectiveCount | uint256 | New effectiveCount value |

### setMaxVouchers

```solidity
function setMaxVouchers(uint256 _maxVouchers) external
```

### checkIsMember

```solidity
function checkIsMember(address account) public view returns (bool)
```

@dev Check if the account is a valid member @param account Member address @return Address whether is member

### getCreditLimit

```solidity
function getCreditLimit(address borrower) external view returns (uint256 total)
```

@dev Get the member's available credit limit @dev IMPORTANT: This function can take up a tonne of gas as the vouchers\[address] array grows in size. the maxVoucher limit will ensure this function can always run within a single block but it is intended only to be used as a view function called from a UI @param borrower Member address @return total Credit line amount

### getVoucherCount

```solidity
function getVoucherCount(address borrower) external view returns (uint256)
```

@dev Get the count of vouchers Vouchers are addresses that this borrower is recieving a vouch from. @param borrower Address of borrower

### getVoucheeCount

```solidity
function getVoucheeCount(address staker) external view returns (uint256)
```

@dev Get the count of vouchees Voucheers are addresses that this staker is vouching for @param staker Address of staker

### getStakerBalance

```solidity
function getStakerBalance(address account) external view returns (uint256)
```

@dev Get the user's deposited stake amount @param account Member address @return Deposited stake amount

### getFrozenInfo

```solidity
function getFrozenInfo(address staker, uint256 pastBlocks) public view returns (uint256 memberTotalFrozen, uint256 memberFrozenCoinAge)
```

@dev Get frozen coin age @param staker Address of staker @param pastBlocks Number of blocks past to calculate coin age from coin age = min(block.number - lastUpdated, pastBlocks) \* amount

### getTotalLockedStake

```solidity
function getTotalLockedStake(address staker) external view returns (uint256)
```

@dev Get Total locked stake @param staker Staker address

### getLockedStake

```solidity
function getLockedStake(address staker, address borrower) external view returns (uint256)
```

@dev Get staker locked stake for a borrower @param staker Staker address @param borrower Borrower address @return LockedStake

### getVouchingAmount

```solidity
function getVouchingAmount(address _staker, address borrower) external view returns (uint256)
```

@dev Get vouching amount @param \_staker Staker address @param borrower Borrower address

### addMember

```solidity
function addMember(address account) external
```

@dev Manually add union members and bypass all the requirements of `registerMember` Only accepts calls from the admin Emit {LogAddMember} event @param account Member address

### updateTrust

```solidity
function updateTrust(address borrower, uint96 trustAmount) external
```

@dev Update the trust amount for exisitng members. @dev Trust is the amount of the underlying token you would in theory be happy to lend to another member. Vouch is derived from trust and stake. Vouch is the minimum of trust and staked amount. Emits {LogUpdateTrust} event @param borrower Account address @param trustAmount Trust amount

### cancelVouch

```solidity
function cancelVouch(address staker, address borrower) public
```

@dev Remove voucher for memeber Can be called by either the borrower or the staker. It will remove the voucher from the voucher array by replacing it with the last item of the array and reseting the array size to -1 by poping off the last item Only callable by a member when the contract is not paused Emit {LogCancelVouch} event @param staker Staker address @param borrower borrower address

### registerMemberWithPermit

```solidity
function registerMemberWithPermit(address newMember, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external
```

@notice Register a a member using a signed permit @dev See registerMember @param newMember New member address @param value Amount approved by permit @param deadline Timestamp for when the permit expires @param v secp256k1 signature part @param r secp256k1 signature part @param s secp256k1 signature part

### registerMember

```solidity
function registerMember(address newMember) public virtual
```

@notice Register a a member, and burn an application fees @dev In order to register as a member an address must be recieving x amount of vouches greater than 0 from stakers. x is defined by `effectiveCount` Emits {LogRegisterMember} event @param newMember New member address

### stake

```solidity
function stake(uint96 amount) public
```

@notice Stake staking tokens @dev Stake is used to underwrite loans and becomes locked if a member a staker has vouched for borrows against it. Stake also earns rewards from the comptroller Emits a {LogStake} event. @param amount Amount to stake

### unstake

```solidity
function unstake(uint96 amount) external
```

@notice Unstake staking token @dev Tokens can only be unstaked if they are not locked. ie a vouchee is not borrowing against them. Emits {LogUnstake} event @param amount Amount to unstake

### withdrawRewards

```solidity
function withdrawRewards() external
```

@dev collect staker rewards from the comptroller

### debtWriteOff

```solidity
function debtWriteOff(address staker, address borrower, uint96 amount) external
```

@notice Write off a borrowers debt @dev Used the stakers locked stake to write off the loan, transfering the Stake to the AssetManager and adjusting balances in the AssetManager and the UToken to repay the principal @dev Emits {LogDebtWriteOff} event @param borrower address of borrower @param amount amount to writeoff

### updateLocked

```solidity
function updateLocked(address borrower, uint96 amount, bool lock) external
```

@notice Borrowing from the market @dev Locks/Unlocks the borrowers stakers staked amounts in a first in First out order. Meaning the members that vouched for this borrower first will be the first members to get their stake locked or unlocked following a borrow or repayment. @param borrower The address of the borrower @param amount Lock/Unlock amount @param lock If the amount is being locked or unlocked

### \_updateFrozen

```solidity
function _updateFrozen(address staker, uint256 pastBlocks) internal returns (uint256, uint256)
```

*Update the frozen info for a single staker*

#### Parameters

| Name       | Type    | Description     |
| ---------- | ------- | --------------- |
| staker     | address | Staker address  |
| pastBlocks | uint256 | The past blocks |

#### Return Values

| Name | Type    | Description                                                                                                     |
| ---- | ------- | --------------------------------------------------------------------------------------------------------------- |
| \[0] | uint256 | memberTotalFrozen Total frozen amount for this staker memberFrozenCoinAge Total frozen coin age for this staker |
| \[1] | uint256 |                                                                                                                 |

### updateFrozenInfo

```solidity
function updateFrozenInfo(address staker, uint256 pastBlocks) external returns (uint256, uint256)
```

*Update the frozen info by the comptroller*

#### Parameters

| Name       | Type    | Description     |
| ---------- | ------- | --------------- |
| staker     | address | Staker address  |
| pastBlocks | uint256 | The past blocks |

#### Return Values

| Name | Type    | Description                                                                                                     |
| ---- | ------- | --------------------------------------------------------------------------------------------------------------- |
| \[0] | uint256 | memberTotalFrozen Total frozen amount for this staker memberFrozenCoinAge Total frozen coin age for this staker |
| \[1] | uint256 |                                                                                                                 |

### batchUpdateFrozenInfo

```solidity
function batchUpdateFrozenInfo(address[] stakers) external
```

*Update the frozen info for external scripts*

#### Parameters

| Name    | Type       | Description     |
| ------- | ---------- | --------------- |
| stakers | address\[] | Stakers address |

### \_min

```solidity
function _min(uint96 a, uint96 b) private pure returns (uint96)
```


# UserManagerERC20

### stakeWithERC20Permit

```solidity
function stakeWithERC20Permit(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external
```

@dev Stake using ERC20 permit @param amount Amount @param deadline Timestamp for when the permit expires @param v secp256k1 signature part @param r secp256k1 signature part @param s secp256k1 signature part


# UserManagerDAI

### stakeWithPermit

```solidity
function stakeWithPermit(uint256 amount, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external
```

@dev Stake using DAI permit @param amount Amount to stake @param nonce Nonce @param expiry Timestamp for when the permit expires @param v secp256k1 signature part @param r secp256k1 signature part @param s secp256k1 signature part


# uToken

@title UToken Contract @dev Union accountBorrows can borrow and repay thru this component.

### BorrowSnapshot

```solidity
struct BorrowSnapshot {
  uint256 principal;
  uint256 interest;
  uint256 interestIndex;
  uint256 lastRepay;
}
```

### WAD

```solidity
uint256 WAD
```

*Wad do you want*

### BORROW\_RATE\_MAX\_MANTISSA

```solidity
uint256 BORROW_RATE_MAX_MANTISSA
```

*Maximum borrow rate that can ever be applied (.005% / block)*

### RESERVE\_FACTORY\_MAX\_MANTISSA

```solidity
uint256 RESERVE_FACTORY_MAX_MANTISSA
```

@dev Maximum fraction of interest that can be set aside for reserves

### initialExchangeRateMantissa

```solidity
uint256 initialExchangeRateMantissa
```

@dev Initial exchange rate used when minting the first UTokens (used when totalSupply = 0)

### reserveFactorMantissa

```solidity
uint256 reserveFactorMantissa
```

@dev Fraction of interest currently set aside for reserves

### accrualBlockNumber

```solidity
uint256 accrualBlockNumber
```

@dev Block number that interest was last accrued at

### borrowIndex

```solidity
uint256 borrowIndex
```

@dev Accumulator of the total earned interest rate since the opening of the market

### totalBorrows

```solidity
uint256 totalBorrows
```

@dev Total amount of outstanding borrows of the underlying in this market

### totalReserves

```solidity
uint256 totalReserves
```

@dev Total amount of reserves of the underlying held in this marke

### totalRedeemable

```solidity
uint256 totalRedeemable
```

@dev Calculates the exchange rate from the underlying to the uToken

### overdueBlocks

```solidity
uint256 overdueBlocks
```

@dev overdue duration, based on the number of blocks

### originationFee

```solidity
uint256 originationFee
```

@dev fee paid at loan origin

### debtCeiling

```solidity
uint256 debtCeiling
```

@dev The debt limit for the whole system

### maxBorrow

```solidity
uint256 maxBorrow
```

@dev Max amount that can be borrowed by a single member

### minBorrow

```solidity
uint256 minBorrow
```

@dev Min amount that can be borrowed by a single member

### assetManager

```solidity
address assetManager
```

@dev Asset manager contract address

### userManager

```solidity
address userManager
```

@dev User manager contract address

### underlying

```solidity
address underlying
```

*Address of underlying token*

### interestRateModel

```solidity
contract IInterestRateModel interestRateModel
```

*Interest rate model used for calculating interest rate*

### accountBorrows

```solidity
mapping(address => struct UToken.BorrowSnapshot) accountBorrows
```

Mapping of account addresses to outstanding borrow balances

### AccrueInterestFailed

```solidity
error AccrueInterestFailed()
```

### AmountExceedGlobalMax

```solidity
error AmountExceedGlobalMax()
```

### AmountExceedMaxBorrow

```solidity
error AmountExceedMaxBorrow()
```

### AmountLessMinBorrow

```solidity
error AmountLessMinBorrow()
```

### AmountZero

```solidity
error AmountZero()
```

### BorrowRateExceedLimit

```solidity
error BorrowRateExceedLimit()
```

### WithdrawFailed

```solidity
error WithdrawFailed()
```

### CallerNotMember

```solidity
error CallerNotMember()
```

### CallerNotUserManager

```solidity
error CallerNotUserManager()
```

### InitExchangeRateNotZero

```solidity
error InitExchangeRateNotZero()
```

### InsufficientFundsLeft

```solidity
error InsufficientFundsLeft()
```

### MemberIsOverdue

```solidity
error MemberIsOverdue()
```

### ReserveFactoryExceedLimit

```solidity
error ReserveFactoryExceedLimit()
```

### DepositToAssetManagerFailed

```solidity
error DepositToAssetManagerFailed()
```

### LogNewMarketInterestRateModel

```solidity
event LogNewMarketInterestRateModel(address oldInterestRateModel, address newInterestRateModel)
```

@dev Change of the interest rate model @param oldInterestRateModel Old interest rate model address @param newInterestRateModel New interest rate model address

### LogMint

```solidity
event LogMint(address minter, uint256 underlyingAmount, uint256 uTokenAmount)
```

@dev Mint uToken by depositing token @param minter address of minter @param underlyingAmount amount of underlying token @param uTokenAmount amount of uToken

### LogRedeem

```solidity
event LogRedeem(address redeemer, uint256 amountIn, uint256 amountOut, uint256 redeemAmount)
```

@dev Redeem token for uToken

### LogReservesAdded

```solidity
event LogReservesAdded(address reserver, uint256 actualAddAmount, uint256 totalReservesNew)
```

@dev Token added to the reserves @param reserver address of sender that added to reservers @param actualAddAmount amount of tokens added @param totalReservesNew new total reserve amount

### LogReservesReduced

```solidity
event LogReservesReduced(address receiver, uint256 reduceAmount, uint256 totalReservesNew)
```

@dev Token removed from the reserves @param receiver reciever address of tokens @param reduceAmount amount of tokens to withdraw @param totalReservesNew new total reserves amount

### LogBorrow

```solidity
event LogBorrow(address account, address to, uint256 amount, uint256 fee)
```

@dev Event borrow @param account Member address @param amount Borrow amount @param fee Origination fee

### LogRepay

```solidity
event LogRepay(address payer, address account, uint256 amount)
```

@dev Event repay @param account Member address @param amount Repay amount

### onlyMember

```solidity
modifier onlyMember(address account)
```

@dev modifier limit member

### onlyUserManager

```solidity
modifier onlyUserManager()
```

### \_\_UToken\_init

```solidity
function __UToken_init(string name_, string symbol_, address underlying_, uint256 initialExchangeRateMantissa_, uint256 reserveFactorMantissa_, uint256 originationFee_, uint256 debtCeiling_, uint256 maxBorrow_, uint256 minBorrow_, uint256 overdueBlocks_, address admin_) public
```

### setAssetManager

```solidity
function setAssetManager(address assetManager_) external
```

@dev set Asset Manager contract address Accept claims only from the admin

### setUserManager

```solidity
function setUserManager(address userManager_) external
```

@dev set User Manager contract address Accept claims only from the admin

### setOriginationFee

```solidity
function setOriginationFee(uint256 originationFee_) external
```

@dev Change loan origination fee value Accept claims only from the admin @param originationFee\_ Fees deducted for each loan transaction

### setDebtCeiling

```solidity
function setDebtCeiling(uint256 debtCeiling_) external
```

@dev Update the market debt ceiling to a fixed amount, for example, 1 billion DAI etc. Accept claims only from the admin @param debtCeiling\_ The debt limit for the whole system

### setMinBorrow

```solidity
function setMinBorrow(uint256 minBorrow_) external
```

@dev Update the minimum loan size Accept claims only from the admin @param minBorrow\_ Minimum loan amount per user

### setMaxBorrow

```solidity
function setMaxBorrow(uint256 maxBorrow_) external
```

@dev Update the max loan size Accept claims only from the admin @param maxBorrow\_ Max loan amount per user

### setOverdueBlocks

```solidity
function setOverdueBlocks(uint256 overdueBlocks_) external
```

@dev Change loan overdue duration, based on the number of blocks Accept claims only from the admin @param overdueBlocks\_ Maximum late repayment block. The number of arrivals is a default

### setInterestRateModel

```solidity
function setInterestRateModel(address newInterestRateModel_) external
```

@dev Change to a different interest rate model Accept claims only from the admin @param newInterestRateModel\_ New interest rate model address

### setReserveFactor

```solidity
function setReserveFactor(uint256 reserveFactorMantissa_) external
```

@dev set reserve factor mantissa Accept claims only from the admin

### getRemainingDebtCeiling

```solidity
function getRemainingDebtCeiling() public view returns (uint256)
```

@dev Returns the remaining amount that can be borrowed from the market. @return Remaining total amount

### getLastRepay

```solidity
function getLastRepay(address account) public view returns (uint256)
```

@dev Get the last repay block @param account Member address @return lastRepay

### checkIsOverdue

```solidity
function checkIsOverdue(address account) public view returns (bool isOverdue)
```

@dev Check if the member's loan is overdue @param account Member address @return isOverdue

### calculatingFee

```solidity
function calculatingFee(uint256 amount) public view returns (uint256)
```

@dev Get the origination fee @param amount Amount to be calculated @return Handling fee

### getBorrowed

```solidity
function getBorrowed(address account) public view returns (uint256)
```

@dev Get the borrowed principle @param account Member address @return borrowed

### borrowBalanceView

```solidity
function borrowBalanceView(address account) public view returns (uint256)
```

@dev Get a member's current owed balance, including the principle and interest but without updating the user's states. @param account Member address @return Borrowed amount

### borrowBalanceStoredInternal

```solidity
function borrowBalanceStoredInternal(address account) internal view returns (uint256)
```

@dev Get a member's total owed, including the principle and the interest calculated based on the interest index. @param account Member address @return Borrowed amount

### borrowRatePerBlock

```solidity
function borrowRatePerBlock() public view returns (uint256)
```

@dev Get the borrowing interest rate per block @return Borrow rate

### supplyRatePerBlock

```solidity
function supplyRatePerBlock() external view returns (uint256)
```

Returns the current per-block supply interest rate for this UToken

#### Return Values

| Name | Type    | Description                                        |
| ---- | ------- | -------------------------------------------------- |
| \[0] | uint256 | The supply interest rate per block, scaled by 1e18 |

### exchangeRateStored

```solidity
function exchangeRateStored() public view returns (uint256)
```

Calculates the exchange rate from the underlying to the UToken

*This function does not accrue interest before calculating the exchange rate*

#### Return Values

| Name | Type    | Description                             |
| ---- | ------- | --------------------------------------- |
| \[0] | uint256 | Calculated exchange rate scaled by 1e18 |

### calculatingInterest

```solidity
function calculatingInterest(address account) public view returns (uint256)
```

@dev Calculating member's borrowed interest @param account Member address @return Interest amount

### exchangeRateCurrent

```solidity
function exchangeRateCurrent() public returns (uint256)
```

Accrue interest then return the up-to-date exchange rate

#### Return Values

| Name | Type    | Description                             |
| ---- | ------- | --------------------------------------- |
| \[0] | uint256 | Calculated exchange rate scaled by 1e18 |

### balanceOfUnderlying

```solidity
function balanceOfUnderlying(address owner) external view returns (uint256)
```

Get the underlying balance of the `owner`

*This also accrues interest in a transaction*

#### Parameters

| Name  | Type    | Description                         |
| ----- | ------- | ----------------------------------- |
| owner | address | The address of the account to query |

#### Return Values

| Name | Type    | Description                               |
| ---- | ------- | ----------------------------------------- |
| \[0] | uint256 | The amount of underlying owned by `owner` |

### borrow

```solidity
function borrow(address to, uint256 amount) external
```

@dev Borrowing from the market Accept claims only from the member Borrow amount must in the range of creditLimit, minBorrow, maxBorrow, debtCeiling and not overdue @param amount Borrow amount

### repayBorrow

```solidity
function repayBorrow(address borrower, uint256 repayAmount) external
```

Repay outstanding borrow

*Repay borrow see \_repayBorrowFresh*

### \_repayBorrowFresh

```solidity
function _repayBorrowFresh(address payer, address borrower, uint256 amount) internal
```

@dev Repay the loan Accept claims only from the member Updated member lastPaymentEpoch only when the repayment amount is greater than interest @param payer Payer address @param borrower Borrower address @param amount Repay amount

### accrueInterest

```solidity
function accrueInterest() public returns (bool)
```

@dev Accrue interest @return Accrue interest finished

### debtWriteOff

```solidity
function debtWriteOff(address borrower, uint256 amount) external
```

### mint

```solidity
function mint(uint256 mintAmount) external
```

*Mint uTokens by depositing tokens*

#### Parameters

| Name       | Type    | Description               |
| ---------- | ------- | ------------------------- |
| mintAmount | uint256 | Amount of uTokens to mint |

### redeem

```solidity
function redeem(uint256 amountIn, uint256 amountOut) external
```

User redeems uTokens in exchange for the underlying asset

*Assumes interest has already been accrued up to the current block*

#### Parameters

| Name      | Type    | Description                                                                                         |
| --------- | ------- | --------------------------------------------------------------------------------------------------- |
| amountIn  | uint256 | The number of uTokens to redeem into underlying (only one of amountIn or amountOut may be non-zero) |
| amountOut | uint256 | The number of underlying tokens to receive from (only one of amountIn or amountOut may be non-zero) |

### addReserves

```solidity
function addReserves(uint256 addAmount) external
```

*Add tokens to the reseve*

#### Parameters

| Name      | Type    | Description             |
| --------- | ------- | ----------------------- |
| addAmount | uint256 | amount of tokens to add |

### removeReserves

```solidity
function removeReserves(address receiver, uint256 reduceAmount) external
```

*Remove tokens to the reseve*

#### Parameters

| Name         | Type    | Description                |
| ------------ | ------- | -------------------------- |
| receiver     | address | address to recieve tokens  |
| reduceAmount | uint256 | amount of tokens to remove |

### getBlockNumber

```solidity
function getBlockNumber() internal view returns (uint256)
```

@dev Function to simply retrieve block number This exists mainly for inheriting test contracts to stub this result.

### \_depositToAssetManager

```solidity
function _depositToAssetManager(uint256 amount) internal
```

@dev Deposit tokens to the asset manager


# UDai

### repayBorrowWithPermit

```solidity
function repayBorrowWithPermit(address borrower, uint256 amount, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external
```


# UErc20

Inherited from UToken; accept ERC20 token of the standard Permit interface

## Functions:

* ``[`repayBorrowWithERC20Permit( address borrower, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s )`](uerc20.md#repayborrowwitherc20permit-address-borrower-uint256-amount-uint256-deadline-uint8-v-bytes32-r-bytes3)``

### **`repayBorrowWithERC20Permit( address borrower, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s )`**

Repay erc20 token with permit


# AssetManager

@title AssetManager @author Union @dev Manage the token assets deposited by components and admins, and invest tokens to the integrated underlying lending protocols.

### marketRegistry

```solidity
address marketRegistry
```

*Address of market registry*

### withdrawSeq

```solidity
uint256[] withdrawSeq
```

*Withdraw Seuqence Priority sequence of money market indices for processing withdraws*

### balances

```solidity
mapping(address => mapping(address => uint256)) balances
```

*Record admin or userManager balance Maps user to token to balance*

### totalPrincipal

```solidity
mapping(address => uint256) totalPrincipal
```

*Total balance of a token Maps token to balance (deposited)*

### supportedMarkets

```solidity
mapping(address => bool) supportedMarkets
```

*Supported markets Mapping of tokens to boolean (isSupported)*

### moneyMarkets

```solidity
contract IMoneyMarketAdapter[] moneyMarkets
```

*Money Market Adapters*

### supportedTokensList

```solidity
address[] supportedTokensList
```

*Supported tokens*

### LogDeposit

```solidity
event LogDeposit(address token, address account, uint256 amount)
```

@dev Emit when making a deposit @param token Depositing token address @param account Account address @param amount Deposit amount, in wei

### LogWithdraw

```solidity
event LogWithdraw(address token, address account, uint256 amount, uint256 remaining)
```

@dev Emit when withdrawing from AssetManager @param token Depositing token address @param account Account address @param amount Withdraw amount, in wei @param remaining The amount cannot be withdrawn

### LogRebalance

```solidity
event LogRebalance(address tokenAddress, uint256[] percentages)
```

@dev Emit when rebalancing among the integrated money markets @param tokenAddress The address of the token to be rebalanced @param percentages Array of the percentages of the tokens to deposit to the money markets

### UnsupportedToken

```solidity
error UnsupportedToken()
```

### AuthFailed

```solidity
error AuthFailed()
```

### NotParity

```solidity
error NotParity()
```

### AmountZero

```solidity
error AmountZero()
```

### InsufficientBalance

```solidity
error InsufficientBalance()
```

### TokenExists

```solidity
error TokenExists()
```

### \_\_AssetManager\_init

```solidity
function __AssetManager_init(address _marketRegistry) external
```

### checkMarketSupported

```solidity
modifier checkMarketSupported(address token)
```

### onlyAuth

```solidity
modifier onlyAuth(address token)
```

### setMarketRegistry

```solidity
function setMarketRegistry(address _marketRegistry) external
```

### setWithdrawSequence

```solidity
function setWithdrawSequence(uint256[] newSeq) external
```

@dev Set withdraw sequence @param newSeq priority sequence of money market indices to be used while withdrawing

### getPoolBalance

```solidity
function getPoolBalance(address tokenAddress) public view returns (uint256)
```

@dev Get the balance of asset manager, plus the total amount of tokens deposited to all the underlying lending protocols @param tokenAddress ERC20 token address @return Pool balance

### getLoanableAmount

```solidity
function getLoanableAmount(address tokenAddress) public view returns (uint256)
```

@dev Returns the amount of the lending pool balance minus the amount of total staked. @param tokenAddress ERC20 token address @return loanAmount Amount can be borrowed

### totalSupply

```solidity
function totalSupply(address tokenAddress) external returns (uint256)
```

@dev Get the total amount of tokens deposited to all the integrated underlying protocols without side effects. @param tokenAddress ERC20 token address @return tokenSupply Total market balance

### totalSupplyView

```solidity
function totalSupplyView(address tokenAddress) public view returns (uint256)
```

@dev Get the total amount of tokens deposited to all the integrated underlying protocols, but without side effects. Safe to call anytime, but may not get the most updated number for the current block. Call totalSupply() for that purpose. @param tokenAddress ERC20 token address @return tokenSupply Total market balance

### isMarketSupported

```solidity
function isMarketSupported(address tokenAddress) public view returns (bool)
```

@dev Check if there is an underlying protocol available for the given ERC20 token. @param tokenAddress ERC20 token address @return Whether is supported

### moneyMarketsCount

```solidity
function moneyMarketsCount() external view returns (uint256)
```

@dev Get the number of supported underlying protocols. @return MoneyMarkets length

### supportedTokensCount

```solidity
function supportedTokensCount() external view returns (uint256)
```

@dev Get the count of supported tokens @return Number of supported tokens

### getMoneyMarket

```solidity
function getMoneyMarket(address tokenAddress, uint256 marketId) external view returns (uint256 rate, uint256 tokenSupply)
```

@dev Get the supported lending protocol @param tokenAddress ERC20 token address @param marketId MoneyMarkets array index @return rate tokenSupply, rate(compound is supplyRatePerBlock 1e18, aave is supplyRatePerYear 1e27)

### deposit

```solidity
function deposit(address token, uint256 amount) external returns (bool)
```

@dev Deposit tokens to AssetManager, and those tokens will be passed along to adapters to deposit to integrated asset protocols if any is available. @param token ERC20 token address @param amount ERC20 token address @return Deposited amount

### withdraw

```solidity
function withdraw(address token, address account, uint256 amount) external returns (bool)
```

@dev Withdraw from AssetManager @param token ERC20 token address @param account User address @param amount ERC20 token address @return Withdraw amount

### debtWriteOff

```solidity
function debtWriteOff(address token, uint256 amount) external
```

*Write of Debt*

### addToken

```solidity
function addToken(address tokenAddress) external
```

@dev Add a new ERC20 token to support in AssetManager @param tokenAddress ERC20 token address

### removeToken

```solidity
function removeToken(address tokenAddress) external
```

@dev Remove a ERC20 token to support in AssetManager @param tokenAddress ERC20 token address

### addAdapter

```solidity
function addAdapter(address adapterAddress) external
```

@dev Add a new adapter for the underlying lending protocol @param adapterAddress adapter address

### removeAdapter

```solidity
function removeAdapter(address adapterAddress) external
```

@dev Remove a adapter for the underlying lending protocol @param adapterAddress adapter address

### approveAllMarketsMax

```solidity
function approveAllMarketsMax(address tokenAddress) public
```

@dev For a give token set allowance for all integrated money markets @param tokenAddress ERC20 token address

### approveAllTokensMax

```solidity
function approveAllTokensMax(address adapterAddress) public
```

@dev For a give moeny market set allowance for all underlying tokens @param adapterAddress Address of adaptor for money market

### rebalance

```solidity
function rebalance(address tokenAddress, uint256[] percentages) external
```

\_Take all the supply of `tokenAddress` and redistribute it according to `percentages`.

Rejects if the token is not supported.\_

#### Parameters

| Name         | Type       | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| tokenAddress | address    | Address of the token that is going to be rebalanced                                                                                                                                                                                                                                                                                                                                                                                                          |
| percentages  | uint256\[] | A list of percentages, expressed as units in 10000, indicating how to deposit the tokens in each underlying money market. The length of this array is one less than the amount of money markets: the last money market will receive the remaining tokens. For example, if there are 3 money markets, and you want to rebalance so that the first one has 10.5% of the tokens, the second one 55%, and the third one 34.5%, this param will be \[1050, 5500]. |

### \_checkSenderBalance

```solidity
function _checkSenderBalance(address sender, address tokenAddress, uint256 amount) private view returns (bool)
```

### \_isUToken

```solidity
function _isUToken(address sender, address token) private view returns (bool)
```

### \_isUserManager

```solidity
function _isUserManager(address sender, address token) private view returns (bool)
```


# Comptroller

@author Compound -> Union Finance @title Comptroller @dev For the time being, only the reward calculation of a single token is supported, and the contract needs to be revised after determining the reward calculation scheme of multiple tokens

### Info

```solidity
struct Info {
  uint256 updatedBlock;
  uint256 inflationIndex;
  uint256 accrued;
}
```

### UserManagerState

```solidity
struct UserManagerState {
  uint256 totalFrozen;
  uint256 totalStaked;
}
```

### UserManagerAccountState

```solidity
struct UserManagerAccountState {
  uint256 totalStaked;
  uint256 totalFrozen;
  uint256 totalLocked;
  uint256 pastBlocksFrozenCoinAge;
  bool isMember;
}
```

### INIT\_INFLATION\_INDEX

```solidity
uint256 INIT_INFLATION_INDEX
```

*Initial inflation index*

### nonMemberRatio

```solidity
uint256 nonMemberRatio
```

*Non member reward multiplier rate (75%)*

### memberRatio

```solidity
uint256 memberRatio
```

*Member reward multiplier rate (100%)*

### halfDecayPoint

```solidity
uint256 halfDecayPoint
```

*Half decay point to reduce rewards at*

### gInflationIndex

```solidity
uint256 gInflationIndex
```

*store the latest inflation index*

### gLastUpdatedBlock

```solidity
uint256 gLastUpdatedBlock
```

*block number when updating the inflation index*

### unionToken

```solidity
contract IERC20Upgradeable unionToken
```

*$UNION token contract*

### marketRegistry

```solidity
contract IMarketRegistry marketRegistry
```

*The market registry contract*

### users

```solidity
mapping(address => mapping(address => struct Comptroller.Info)) users
```

*Map account to token to Info*

### LogWithdrawRewards

```solidity
event LogWithdrawRewards(address account, uint256 amount)
```

@dev Withdraw rewards event @param account The staker's address @param amount The amount of Union tokens to withdraw

### SenderNotUserManager

```solidity
error SenderNotUserManager()
```

### NotZero

```solidity
error NotZero()
```

### FrozenCoinAge

```solidity
error FrozenCoinAge()
```

### InflationIndexTooSmall

```solidity
error InflationIndexTooSmall()
```

### \_\_Comptroller\_init

```solidity
function __Comptroller_init(address unionToken_, address marketRegistry_, uint256 _halfDecayPoint) public
```

### onlyUserManager

```solidity
modifier onlyUserManager(address token)
```

### setHalfDecayPoint

```solidity
function setHalfDecayPoint(uint256 point) public
```

*Set the half decay point*

### getRewardsMultiplier

```solidity
function getRewardsMultiplier(address account, address token) external view returns (uint256)
```

@dev Get the reward multipier based on the account status @param account Account address @param token ERC20 token address @return Multiplier number (in wei)

### calculateRewardsByBlocks

```solidity
function calculateRewardsByBlocks(address account, address token, uint256 futureBlocks) public view returns (uint256)
```

@dev Calculate unclaimed rewards based on blocks @param account User address @param token Staking token address @param futureBlocks Number of blocks in the future @return Unclaimed rewards

### calculateRewards

```solidity
function calculateRewards(address account, address token) external view returns (uint256)
```

@dev Calculate currently unclaimed rewards @param account Account address @param token Staking token address @return Unclaimed rewards

### inflationPerBlock

```solidity
function inflationPerBlock(uint256 effectiveTotalStake) public view returns (uint256)
```

@dev Calculate inflation per block @param effectiveTotalStake Effective total stake @return Inflation amount, div totalSupply is the inflation rate

### withdrawRewards

```solidity
function withdrawRewards(address account, address token) external returns (uint256)
```

@dev Withdraw rewards @param token Staking token address @return Amount of rewards

### updateTotalStaked

```solidity
function updateTotalStaked(address token, uint256 totalStaked) external returns (bool)
```

@dev When total staked change update inflation index @param totalStaked totalStaked amount @return Whether succeeded

### \_getUserManagerState

```solidity
function _getUserManagerState(contract IUserManager userManager) internal view returns (struct Comptroller.UserManagerState)
```

*Get UserManager global state values*

### \_getUserInfoView

```solidity
function _getUserInfoView(contract IUserManager userManager, address account, address token, uint256 futureBlocks) internal view returns (struct Comptroller.UserManagerAccountState, struct Comptroller.Info, uint256)
```

*Get UserManager user specific state (view function does NOT update UserManage state)*

#### Parameters

| Name         | Type                  | Description          |
| ------------ | --------------------- | -------------------- |
| userManager  | contract IUserManager | UserManager contract |
| account      | address               | Account address      |
| token        | address               | Token address        |
| futureBlocks | uint256               | Blocks in the future |

### \_getUserInfo

```solidity
function _getUserInfo(contract IUserManager userManager, address account, address token, uint256 futureBlocks) internal returns (struct Comptroller.UserManagerAccountState, struct Comptroller.Info, uint256)
```

*Get UserManager user specific state (function does update UserManage state)*

#### Parameters

| Name         | Type                  | Description          |
| ------------ | --------------------- | -------------------- |
| userManager  | contract IUserManager | UserManager contract |
| account      | address               | Account address      |
| token        | address               | Token address        |
| futureBlocks | uint256               | Blocks in the future |

### \_calculateRewardsByBlocks

```solidity
function _calculateRewardsByBlocks(address account, address token, uint256 pastBlocks, struct Comptroller.Info userInfo, struct Comptroller.UserManagerState userManagerState, struct Comptroller.UserManagerAccountState userManagerAccountState) internal view returns (uint256)
```

@dev Calculate currently unclaimed rewards @param account Account address @param token Staking token address @param userManagerState User manager global state @return Unclaimed rewards

### \_getInflationIndexNew

```solidity
function _getInflationIndexNew(uint256 totalStaked_, uint256 blockDelta) internal view returns (uint256)
```

@dev Calculate new inflation index based on # of blocks @param totalStaked\_ Number of total staked tokens in the system @param blockDelta Number of blocks @return New inflation index

### \_calculateRewards

```solidity
function _calculateRewards(address account, address token, uint256 totalStaked, uint256 userStaked, uint256 frozenCoinAge, uint256 pastBlocks, uint256 inflationIndex) internal view returns (uint256)
```

### \_getUserManager

```solidity
function _getUserManager(address token) internal view returns (contract IUserManager)
```

*Get the UserManager contract. First try and load it from state if it has been previously saved and fallback to loading it from the marketRegistry*

#### Return Values

| Name | Type                  | Description          |
| ---- | --------------------- | -------------------- |
| \[0] | contract IUserManager | userManager contract |

### \_inflationPerBlock

```solidity
function _inflationPerBlock(uint256 effectiveTotalStake) internal view returns (uint256)
```

@dev See Comptroller.inflationPerBlock

### \_lookup

```solidity
function _lookup(uint256 index) internal pure returns (uint256)
```

### \_getInflationIndex

```solidity
function _getInflationIndex(uint256 effectiveAmount, uint256 inflationIndex, uint256 blockDelta) internal view returns (uint256)
```

### \_getRewardsMultiplier

```solidity
function _getRewardsMultiplier(uint256 userStaked, uint256 lockedStake, uint256 totalFrozen_, bool isMember_) internal pure returns (uint256)
```


# MarketRegistry

*Register uToken and UserManager contracts to their tokens*

### userManagers

```solidity
mapping(address => address) userManagers
```

Token address mapped to userManager

*Assumption there will only ever be one UserManager per token*

### uTokens

```solidity
mapping(address => address) uTokens
```

Token address mapped to uToken

*Assumption there will only ever be one UToken per token*

### LogAddUToken

```solidity
event LogAddUToken(address tokenAddress, address contractAddress)
```

*New UToken contract registered*

#### Parameters

| Name            | Type    | Description                         |
| --------------- | ------- | ----------------------------------- |
| tokenAddress    | address | The address of the underlying token |
| contractAddress | address | The contract address                |

### LogAddUserManager

```solidity
event LogAddUserManager(address tokenAddress, address contractAddress)
```

*New UserManager contract registered*

#### Parameters

| Name            | Type    | Description                         |
| --------------- | ------- | ----------------------------------- |
| tokenAddress    | address | The address of the underlying token |
| contractAddress | address | The contract address                |

### \_\_MarketRegistry\_init

```solidity
function __MarketRegistry_init() public
```

@dev Initialization function

### hasUToken

```solidity
function hasUToken(address token) external view returns (bool)
```

### hasUserManager

```solidity
function hasUserManager(address token) external view returns (bool)
```

### setUToken

```solidity
function setUToken(address token, address uToken) external
```

*Register a new UToken contract*

#### Parameters

| Name   | Type    | Description                        |
| ------ | ------- | ---------------------------------- |
| token  | address | The underlying token e.g DAI       |
| uToken | address | the address of the uToken contract |

### setUserManager

```solidity
function setUserManager(address token, address userManager) external
```

*Register a new UToken contract*

#### Parameters

| Name        | Type    | Description                             |
| ----------- | ------- | --------------------------------------- |
| token       | address | The underlying token e.g DAI            |
| userManager | address | the address of the UserManager contract |


# InterestRateModel

*The interest rate model used by UTokens*

### BORROW\_RATE\_MAX\_MANTISSA

```solidity
uint256 BORROW_RATE_MAX_MANTISSA
```

*Maximum borrow rate that can ever be applied (0.005% / block)*

### interestRatePerBlock

```solidity
uint256 interestRatePerBlock
```

*IInterest rate per block*

### ReserveFactorExceeded

```solidity
error ReserveFactorExceeded()
```

### BorrowRateExceeded

```solidity
error BorrowRateExceeded()
```

### LogNewInterestParams

```solidity
event LogNewInterestParams(uint256 interestRate)
```

@dev Update interest parameters event @param interestRate New interest rate, 1e18 = 100%

### constructor

```solidity
constructor(uint256 interestRatePerBlock_) public
```

### getBorrowRate

```solidity
function getBorrowRate() public view returns (uint256)
```

*Get borrow rate per block*

### getSupplyRate

```solidity
function getSupplyRate(uint256 reserveFactorMantissa) public view returns (uint256)
```

*Get supply rate for given reserve factor If reserve factor is 100% interest acrues to the reserves If reserves factor is 0 interest acrues to uDAI minters*

#### Parameters

| Name                  | Type    | Description                 |
| --------------------- | ------- | --------------------------- |
| reserveFactorMantissa | uint256 | The reserve factor (scaled) |

### setInterestRate

```solidity
function setInterestRate(uint256 _interestRatePerBlock) external
```

*Set new interest rate per block Interest rate per block must be less than the max rate 0.005% / block*

#### Parameters

| Name                   | Type    | Description   |
| ---------------------- | ------- | ------------- |
| \_interestRatePerBlock | uint256 | Interest rate |


# PureTokenAdapter

### assetManager

```solidity
address assetManager
```

*The address of the AssetManager*

### floorMap

```solidity
mapping(address => uint256) floorMap
```

*Mapping of token address to floor balance*

### ceilingMap

```solidity
mapping(address => uint256) ceilingMap
```

*Mapping of token address to ceiling balance*

### \_\_PureTokenAdapter\_init

```solidity
function __PureTokenAdapter_init(address _assetManager) public
```

### TokenNotSupported

```solidity
error TokenNotSupported()
```

### SenderNotAssetManager

```solidity
error SenderNotAssetManager()
```

### checkTokenSupported

```solidity
modifier checkTokenSupported(address tokenAddress)
```

*Check supplied token address is supported*

### onlyAssetManager

```solidity
modifier onlyAssetManager()
```

*Check sender is the asset manager*

### setAssetManager

```solidity
function setAssetManager(address _assetManager) external
```

*Set the asset manager contract*

#### Parameters

| Name           | Type    | Description      |
| -------------- | ------- | ---------------- |
| \_assetManager | address | The AssetManager |

### setFloor

```solidity
function setFloor(address tokenAddress, uint256 floor) external
```

*Set the floor balance for this token. When assets are deposited into adapters the floors are filled first*

#### Parameters

| Name         | Type    | Description       |
| ------------ | ------- | ----------------- |
| tokenAddress | address | The Token address |
| floor        | uint256 | Floor balance     |

### setCeiling

```solidity
function setCeiling(address tokenAddress, uint256 ceiling) external
```

*Set the ceiling balance for this token. The ceiling is the max balance we want to be managed by this adapter*

#### Parameters

| Name         | Type    | Description       |
| ------------ | ------- | ----------------- |
| tokenAddress | address | The Token address |
| ceiling      | uint256 | Ceiling balance   |

### getRate

```solidity
function getRate(address) external pure returns (uint256)
```

*Get the underlying market rate The PureAdapter doesn't have an underlying market so we return 0*

### getSupply

```solidity
function getSupply(address tokenAddress) external view returns (uint256)
```

*Get total supply of this Contracts*

#### Parameters

| Name         | Type    | Description                   |
| ------------ | ------- | ----------------------------- |
| tokenAddress | address | The token to check supply for |

### getSupplyView

```solidity
function getSupplyView(address tokenAddress) external view returns (uint256)
```

*Get total supply of this Contracts including any balance that has been deposited into the underlying market. As the PureAdapter doesn't have an underlying market this is the same as getSupply*

#### Parameters

| Name         | Type    | Description                   |
| ------------ | ------- | ----------------------------- |
| tokenAddress | address | The token to check supply for |

### supportsToken

```solidity
function supportsToken(address tokenAddress) external view returns (bool)
```

*Check if this token is supported*

#### Parameters

| Name         | Type    | Description        |
| ------------ | ------- | ------------------ |
| tokenAddress | address | The token to check |

### deposit

```solidity
function deposit(address tokenAddress) external
```

*Deposits the given amount of tokens in the underlying money market.*

### withdraw

```solidity
function withdraw(address tokenAddress, address recipient, uint256 tokenAmount) external
```

*Withdraw tokens from this adapter Only callable by the AssetManager*

#### Parameters

| Name         | Type    | Description                  |
| ------------ | ------- | ---------------------------- |
| tokenAddress | address | Token to withdraw            |
| recipient    | address | Recieved by                  |
| tokenAmount  | uint256 | Amount of tokens to withdraw |

### withdrawAll

```solidity
function withdrawAll(address tokenAddress, address recipient) external
```

*Withdraw entire balance of this token Only callable by AssetManager*

#### Parameters

| Name         | Type    | Description       |
| ------------ | ------- | ----------------- |
| tokenAddress | address | Token to withdraw |
| recipient    | address | Recieved by       |

### claimRewards

```solidity
function claimRewards(address tokenAddress, address recipient) external
```

### \_supportsToken

```solidity
function _supportsToken(address tokenAddress) internal view returns (bool)
```

### \_getSupply

```solidity
function _getSupply(address tokenAddress) internal view returns (uint256)
```


# AaveV3Adapter

*The implementation of Aave.Finance MoneyMarket that integrates with AssetManager.*

### assetManager

```solidity
address assetManager
```

*the AssetManager contract address*

### market

```solidity
contract AMarket3 market
```

*Aave Market contract*

### lendingPool

```solidity
contract LendingPool3 lendingPool
```

*Aave Lending pool contract*

### tokenToAToken

```solidity
mapping(address => address) tokenToAToken
```

*Mapping of token to aToken*

### floorMap

```solidity
mapping(address => uint256) floorMap
```

*Mapping of token to floor amount*

### ceilingMap

```solidity
mapping(address => uint256) ceilingMap
```

*Mapping of token to ceiling amount*

### LogSetAssetManager

```solidity
event LogSetAssetManager(address sender, address assetManager)
```

### LogSetFloor

```solidity
event LogSetFloor(address sender, address tokenAddress, uint256 floor)
```

### LogSetCeiling

```solidity
event LogSetCeiling(address sender, address tokenAddress, uint256 ceiling)
```

### \_\_AaveV3Adapter\_init

```solidity
function __AaveV3Adapter_init(address _assetManager, contract LendingPool3 _lendingPool, contract AMarket3 _market) public
```

### TokenNotSupported

```solidity
error TokenNotSupported()
```

### SenderNotAssetManager

```solidity
error SenderNotAssetManager()
```

### checkTokenSupported

```solidity
modifier checkTokenSupported(address tokenAddress)
```

*Check supplied token address is supported*

### onlyAssetManager

```solidity
modifier onlyAssetManager()
```

*Check sender is the asset manager*

### setAssetManager

```solidity
function setAssetManager(address _assetManager) external
```

*Set the asset manager contract address Only callable by the admin*

#### Parameters

| Name           | Type    | Description                   |
| -------------- | ------- | ----------------------------- |
| \_assetManager | address | AssetManager contract address |

### setFloor

```solidity
function setFloor(address tokenAddress, uint256 floor) external
```

*Set floor amount Only callable by the admin*

#### Parameters

| Name         | Type    | Description                               |
| ------------ | ------- | ----------------------------------------- |
| tokenAddress | address | Address of the token to set the floor for |
| floor        | uint256 | Floor amount                              |

### setCeiling

```solidity
function setCeiling(address tokenAddress, uint256 ceiling) external
```

*Set ceiling amount Only callable by the admin*

#### Parameters

| Name         | Type    | Description                                 |
| ------------ | ------- | ------------------------------------------- |
| tokenAddress | address | Address of the token to set the ceiling for |
| ceiling      | uint256 | ceiling amount                              |

### getRate

```solidity
function getRate(address tokenAddress) external view returns (uint256)
```

*Get the underlying market rate*

#### Parameters

| Name         | Type    | Description                  |
| ------------ | ------- | ---------------------------- |
| tokenAddress | address | The underlying token address |

### getSupply

```solidity
function getSupply(address tokenAddress) external view returns (uint256)
```

*Get total supply of this Contracts*

#### Parameters

| Name         | Type    | Description                   |
| ------------ | ------- | ----------------------------- |
| tokenAddress | address | The token to check supply for |

### getSupplyView

```solidity
function getSupplyView(address tokenAddress) external view returns (uint256)
```

*Get total supply of this Contracts including any balance that has been deposited into the underlying market*

#### Parameters

| Name         | Type    | Description                   |
| ------------ | ------- | ----------------------------- |
| tokenAddress | address | The token to check supply for |

### supportsToken

```solidity
function supportsToken(address tokenAddress) external view returns (bool)
```

*Check if this token is supported*

#### Parameters

| Name         | Type    | Description        |
| ------------ | ------- | ------------------ |
| tokenAddress | address | The token to check |

### mapTokenToAToken

```solidity
function mapTokenToAToken(address tokenAddress) external
```

*Add aToken to the token mapping for a supported token*

#### Parameters

| Name         | Type    | Description   |
| ------------ | ------- | ------------- |
| tokenAddress | address | Token address |

### deposit

```solidity
function deposit(address tokenAddress) external
```

*Deposit tokens into the underlying Aave V3 lending pool*

#### Parameters

| Name         | Type    | Description   |
| ------------ | ------- | ------------- |
| tokenAddress | address | Token address |

### withdraw

```solidity
function withdraw(address tokenAddress, address recipient, uint256 tokenAmount) external
```

*Withdraw tokens from this adapter Only callable by the AssetManager*

#### Parameters

| Name         | Type    | Description                  |
| ------------ | ------- | ---------------------------- |
| tokenAddress | address | Token to withdraw            |
| recipient    | address | Recieved by                  |
| tokenAmount  | uint256 | Amount of tokens to withdraw |

### withdrawAll

```solidity
function withdrawAll(address tokenAddress, address recipient) external
```

*Withdraw all tokens from this adapter Only callable by the AssetManager*

#### Parameters

| Name         | Type    | Description       |
| ------------ | ------- | ----------------- |
| tokenAddress | address | Token to withdraw |
| recipient    | address | Recieved by       |

### claimRewards

```solidity
function claimRewards(address tokenAddress, address recipient) external
```

*Claim rewards from the Aave rewards controller*

#### Parameters

| Name         | Type    | Description   |
| ------------ | ------- | ------------- |
| tokenAddress | address | Token address |
| recipient    | address | The recipient |

### \_getSupply

```solidity
function _getSupply(address tokenAddress) internal view returns (uint256)
```

### \_supportsToken

```solidity
function _supportsToken(address tokenAddress) internal view returns (bool)
```


# Governance

{% embed url="<https://github.com/unioncredit/union-v1-contracts>" %}

## Quickstart

Requirements:

* Node @14.x
* Python @3.x (for running Slither)

1. Clone the repo
2. Install dependencies

```
$ yarn install
```

## Testing

To run the entire test suite:

```
$ yarn test
```

To run the unit tests only:

```
$ yarn unitTest
```

To run the deployment script tests:

```
$ yarn deployTest
```

## Coverage

To run tests with coverage:

```
$ yarn coverage
```

## Deployment

To deploy to a supported network:

```
$ yarn deploy {network}
```

Check the deployed params:

```
$ yarn checkDeploy {network}
```


# UnionToken

Mint and distribute UnionTokens.

UNION is an erc20 governance token thats earned by using the Union protocol and is used for controlling the [Timelock](/developers/governance/itimelock)that governs the protocol.

UNION is distributed via an algorithmic mechanism in [Comptroller](/developers/core/comptroller) to the vouchers who stake and vouch for borrowers who don’t default.

## Functions

### `totalSupply() → uint256` <a href="#iuniontoken-totalsupply" id="iuniontoken-totalsupply"></a>

Get total supply

#### Return Values:

* Total supply

### `balanceOf(address account) → uint256` <a href="#iuniontoken-balanceof-address" id="iuniontoken-balanceof-address"></a>

No description

### `transfer(address recipient, uint256 amount) → bool` <a href="#iuniontoken-transfer-address-uint256" id="iuniontoken-transfer-address-uint256"></a>

No description

### `mint(address account, uint256 amount) → bool` <a href="#iuniontoken-mint-address-uint256" id="iuniontoken-mint-address-uint256"></a>

No description

### `getPriorVotes(address account, uint256 blockNumber) → uint256` <a href="#iuniontoken-getpriorvotes-address-uint256" id="iuniontoken-getpriorvotes-address-uint256"></a>

Determine the prior number of votes for an account as of a block number. Block number must be a finalized block or else this function will revert to prevent misinformation.

#### Parameters:

* `account`: The address of the account to check
* `blockNumber`: The block number to get the vote balance at

#### Return Values:

* The number of votes the account had as of the given block

### `permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)` <a href="#iuniontoken-permit-address-address-uint256-uint256-uint8-bytes32-bytes32" id="iuniontoken-permit-address-address-uint256-uint256-uint8-bytes32-bytes32"></a>

Allows to spend owner's Union tokens by the specified spender. The function can be called by anyone, but requires having allowance parameters signed by the owner according to EIP712.

#### Parameters:

* `owner`: The owner's address, cannot be zero address.
* `spender`: The spender's address, cannot be zero address.
* `value`: The allowance amount, in wei.
* `deadline`: The allowance expiration date (unix timestamp in UTC).
* `v`: A final byte of signature (ECDSA component).
* `r`: The first 32 bytes of signature (ECDSA component).
* `s`: The second 32 bytes of signature (ECDSA component).

### `burnFrom(address account, uint256 amount)` <a href="#iuniontoken-burnfrom-address-uint256" id="iuniontoken-burnfrom-address-uint256"></a>

No description

## Events

### `DelegateChanged(address delegator, address fromDelegate, address toDelegate)` <a href="#iuniontoken-delegatechanged-address-address-address" id="iuniontoken-delegatechanged-address-address-address"></a>

No description

### `DelegateVotesChanged(address delegate, uint256 previousBalance, uint256 newBalance)` <a href="#iuniontoken-delegatevoteschanged-address-uint256-uint256" id="iuniontoken-delegatevoteschanged-address-uint256-uint256"></a>

No description

### **`MinterChange(address oldMinter, address newMinter)`**

Minter address change

### **`NewPendingMinter(address oldPendingMinter, address newPendingMinter)`**

Pending minter address change


# ArbUnionWrapper

UNION token wrapper for L2

## Functions

### wrap`(uint256 amount) → uint256` <a href="#iuniontoken-totalsupply" id="iuniontoken-totalsupply"></a>

Wrap union.

**Return Values:**

* whether success

### unwrap`(uint256 amount) → uint256` <a href="#iuniontoken-totalsupply" id="iuniontoken-totalsupply"></a>

Unwrap union.

#### Return Values:

* whether success


# ArbUnion

Inherited from UnionToken


# OpUnion

Inherited from UnionToken. The bridged UNION token on Optimism.


# Governor

Governor is the governance module of the protocol. It allows addresses with more than 1% of the current supply of Union token to propose changes to the protocol. Addresses that hold voting weight, at the start of the proposal, invoked through the `getPriorVotes()` function, can submit their votes during a 3-day voting period. If quorum is met, and the majority of votes are cast for the proposal, the proposal succeeds. It is then queued in the Timelock and can be implemented after 2 days.

## Functions

### `proposalCount() → uint256` <a href="#igovernor-proposalcount" id="igovernor-proposalcount"></a>

No description

### `propose(address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, string description) → uint256` <a href="#igovernor-propose-address-uint256-string-bytes-string" id="igovernor-propose-address-uint256-string-bytes-string"></a>

No description

### `queue(uint256 proposalId)` <a href="#igovernor-queue-uint256" id="igovernor-queue-uint256"></a>

No description

### `execute(uint256 proposalId)` <a href="#igovernor-execute-uint256" id="igovernor-execute-uint256"></a>

No description

### `cancel(uint256 proposalId)` <a href="#igovernor-cancel-uint256" id="igovernor-cancel-uint256"></a>

No description

### `getActions(uint256 proposalId) → address[], uint256[], string[], bytes[]` <a href="#igovernor-getactions-uint256" id="igovernor-getactions-uint256"></a>

No description

### `getReceipt(uint256 proposalId, address voter) → struct IGovernor.Receipt` <a href="#igovernor-getreceipt-uint256-address" id="igovernor-getreceipt-uint256-address"></a>

No description

### `state(uint256 proposalId) → enum IGovernor.ProposalState` <a href="#igovernor-state-uint256" id="igovernor-state-uint256"></a>

No description

### `castVote(uint256 proposalId, uint8 support)` <a href="#igovernor-castvote-uint256-uint8" id="igovernor-castvote-uint256-uint8"></a>

No description

### `castVoteWithReason(uint256 proposalId, uint8 support, string reason)` <a href="#igovernor-castvotewithreason-uint256-uint8-string" id="igovernor-castvotewithreason-uint256-uint8-string"></a>

No description

### `castVoteBySig(uint256 proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s)` <a href="#igovernor-castvotebysig-uint256-uint8-uint8-bytes32-bytes32" id="igovernor-castvotebysig-uint256-uint8-uint8-bytes32-bytes32"></a>

No description

## Events

### `ProposalCreated(uint256 id, address proposer, address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, uint256 startBlock, uint256 endBlock, string description)` <a href="#igovernor-proposalcreated-uint256-address-address-uint256-string-bytes-uint256-uint256-string" id="igovernor-proposalcreated-uint256-address-address-uint256-string-bytes-uint256-uint256-string"></a>

No description

### `UnionTokenSet(address oldUnionToken, address newUnionToken)` <a href="#igovernor-uniontokenset-address-address" id="igovernor-uniontokenset-address-address"></a>

No description

### `TimelockSet(address oldTimelock, address newTimelock)` <a href="#igovernor-timelockset-address-address" id="igovernor-timelockset-address-address"></a>

No description

### `VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay)` <a href="#igovernor-votingdelayset-uint256-uint256" id="igovernor-votingdelayset-uint256-uint256"></a>

No description

### `VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod)` <a href="#igovernor-votingperiodset-uint256-uint256" id="igovernor-votingperiodset-uint256-uint256"></a>

No description

### `ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold)` <a href="#igovernor-proposalthresholdset-uint256-uint256" id="igovernor-proposalthresholdset-uint256-uint256"></a>

No description

### `VoteCast(address voter, uint256 proposalId, uint8 support, uint256 votes, string reason)` <a href="#igovernor-votecast-address-uint256-uint8-uint256-string" id="igovernor-votecast-address-uint256-uint8-uint256-string"></a>

No description

### `ProposalCanceled(uint256 id)` <a href="#igovernor-proposalcanceled-uint256" id="igovernor-proposalcanceled-uint256"></a>

No description

### `ProposalQueued(uint256 id, uint256 eta)` <a href="#igovernor-proposalqueued-uint256-uint256" id="igovernor-proposalqueued-uint256-uint256"></a>

No description

### `ProposalExecuted(uint256 id)` <a href="#igovernor-proposalexecuted-uint256" id="igovernor-proposalexecuted-uint256"></a>

No description

### `NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin)` <a href="#igovernor-newpendingadmin-address-address" id="igovernor-newpendingadmin-address-address"></a>

No description

### `NewAdmin(address oldAdmin, address newAdmin)` <a href="#igovernor-newadmin-address-address" id="igovernor-newadmin-address-address"></a>

No description


# Timelock

Each Union contract allows the Timelock address to modify it. 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. 2 days 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.

## Functions

### isOperation() -> bool

Returns whether an id correspond to a registered operation. This includes both Pending, Ready and Done operations.

### isOperationPending() -> bool

Returns whether an operation is pending or not.

### isOperationReady() -> bool

Returns whether an operation is ready or not.

### isOperationDone() -> bool

Returns whether an operation is done or not.

### getTimestamp() -> uint256

Returns the timestamp at with an operation becomes ready (0 for unset operations, 1 for done operations).

### getMinDelay() -> uint256

Returns the minimum delay for an operation to become valid.

### schedule( address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay )

Schedule an operation containing a single transaction.

### scheduleBatch( address\[] calldata targets, uint256\[] calldata values, bytes\[] calldata datas, bytes32 predecessor, bytes32 salt, uint256 delay )

Schedule an operation containing a batch of transactions.

### cancel(bytes32 id)

Cancel an operation.

### execute( address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt )

Execute an (ready) operation containing a single transaction.

### executeBatch( address\[] calldata targets, uint256\[] calldata values, bytes\[] calldata datas, bytes32 predecessor, bytes32 salt )

Execute an (ready) operation containing a batch of transactions.

## Events

### CallScheduled( bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay )

Emitted when a call is scheduled as part of operation id.

### CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data)

Emitted when a call is performed as part of operation id.

### Cancelled(bytes32 indexed id)

Emitted when operation id is cancelled.

### MinDelayChange(uint256 oldDuration, uint256 newDuration)

Emitted when the minimum delay for future operations is modified.


# Union SDK

A library to help developers build their own contracts that interact with Union protocol.

### Contracts

[BaseUnionMember](https://github.com/unioncredit/union-v1-sdk/blob/master/contracts/BaseUnionMember.sol) - has the basic functions of Union member.

[UnionBorrower](https://github.com/unioncredit/union-v1-sdk/blob/master/contracts/UnionBorrower.sol) - a contract Union member that can borrower from other members.

[UnionVoucher](https://github.com/unioncredit/union-v1-sdk/blob/master/contracts/UnionVoucher.sol) - a contract Union member that can vouch for other members.

## Quickstart

### Installation

```
npm install @unioncredit/v1-sdk
```

#### Imports

```
import "@unioncredit/v1-sdk/contracts/BaseUnionMember.sol";
import "@unioncredit/v1-sdk/contracts/UnionVoucher.sol";
import "@unioncredit/v1-sdk/contracts/UnionBorrower.sol";
```

#### Example Borrower

An example implementation of a contract that is a Union member. Once registered, this contract can borrow DAI and use it to buy [OSQTH](https://www.opyn.co/).

```solidity
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@unioncredit/v1-sdk/contracts/UnionBorrower.sol";

/**
 * @notice A UnionMember that borrows DAI to go long on OSQTH
 */
contract SqueethWithFriends is UnionBorrower {
  address public dai;
  
  constructor(address _dai) {
    dai = _dai;
  }
  
  function borrowAndSqueeth(uint256 _amountInDai) external {
    _borrow(_amountInDai);
    _investInSqueeth(_amountInDai);
  }
  
  function sellAndRepay(uint _amountInSqueeth) external {
    _sellSqueeth(_amountInSqueeth);
    uint balance = IERC20(dai).balanceOf(address(this));
    _repayBorrow(balance);
  }
  
  function _investInSqueeth(uint256 _amountInDai) internal {
    // buy OSQTH with DAI
  }
  
  function _sellSqueeth(uint256 _amountInSqueeth) internal {
    // sell OSQTH for DAI
  }
}
```

#### Example Voucher

An example implementation of a contract that is a Union member. Once registered, this contract can vouch for [frankfrank](https://opensea.io/collection/frankfrank) holders.

```solidity
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@unioncredit/v1-sdk/contracts/UnionVoucher.sol";

/**
 * @notice A UnionMember that vouches for holders of frankfrank
 */
contract VouchForFrankFrank is UnionVoucher {
  uint256 public vouchAmount;
  IERC721 public frank;
  
  constructor(uint _vouchAmount, IERC721 _frank) {
    vouchAmount = _vouchAmount;
    frank = _frank;
  }
  
  function stake() external {
    uint balance = IERC20(dai).balanceOf(address(this));
    _stake(balance);
  }
  
  function vouchForFrankFrank(address holder) external {
    require(frank.balanceOf(holder) > 0, "!holder");
    _updateTrust(holder, vouchAmount);
  }
  
  function cancelPaperHands(address holder) external {
    require(frank.balanceOf(holder) <= 0, "!paper hands");
    _cancelVouch(holder);
  }
}
```


# Union Data

A Javascript package for fetching Union data from the graphQL endpoints.

## Quickstart

### Installation

```
npm install @unioncredit/data
```

### Example

```javascript
import { parseMemberApplications, config } from "@unioncredit/data";

// chain ID defaults to 1 but can be set to 1, 42, or 42161
const chainId = 1;
config.set("chainId", chainId);

async function fetcher() {
  const applications = await parseMemberApplications();
  const firstMemberAddress = applications[0].account;
  ...
} 
```

\_\_


# Documentation

### fetchBorrows(orderBy, orderDirection, where) ⇒ `Promise`

Get Accounts borrows historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, account, amount, fee, timestamp }[]`

| Param          | Type             | Description                          |
| -------------- | ---------------- | ------------------------------------ |
| orderBy        | `string`         | Property to orderBy                  |
| orderDirection | `OrderDirection` | Order in asc or desc                 |
| where          | `object`         | Where object e.g { account: "0x00" } |

### fetchAccountBorrows(account, orderBy, orderDirection) ⇒ `Promise`

Get Account borrows historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, account, amount, fee, timestamp }[]`

| Param          | Type             | Description                               |
| -------------- | ---------------- | ----------------------------------------- |
| account        | `string`         | Account to get borrow historical data for |
| orderBy        | `string`         | Property to orderBy                       |
| orderDirection | `OrderDirection` | Order in asc or desc                      |

### fetchDeposits(orderBy, orderDirection) ⇒ `Promise`

Get AssetManager deposits historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, token, amount, account, marketsTotalSupply }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchMemberApplications(orderBy, orderDirection) ⇒ `Promise`

Get MemberApplications historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, staker, applicant, timestamp }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchRepays(orderBy, orderDirection, where) ⇒ `Promise`

Get Accounts repays historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, account, amount, fee, timestamp }[]`

| Param          | Type             | Description                          |
| -------------- | ---------------- | ------------------------------------ |
| orderBy        | `string`         | Property to orderBy                  |
| orderDirection | `OrderDirection` | Order in asc or desc                 |
| where          | `object`         | Where object e.g { account: "0x00" } |

### fetchAccountRepays(account, orderBy, orderDirection) ⇒ `Promise`

Get Account repays historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, account, amount, fee, timestamp }[]`

| Param          | Type             | Description                              |
| -------------- | ---------------- | ---------------------------------------- |
| account        | `string`         | Account to get repay historical data for |
| orderBy        | `string`         | Property to orderBy                      |
| orderDirection | `OrderDirection` | Order in asc or desc                     |

### fetchStakers(orderBy, orderDirection, where) ⇒ `Promise`

Get stakers historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, account, totalLockedStake, totalFrozen, creditLimit, stakedAmount, timestamp }[]`

| Param          | Type             | Description                          |
| -------------- | ---------------- | ------------------------------------ |
| orderBy        | `string`         | Property to orderBy                  |
| orderDirection | `OrderDirection` | Order in asc or desc                 |
| where          | `object`         | Where object e.g { account: "0x00" } |

### fetchAccountStakes(orderBy, orderDirection) ⇒ `Promise`

Get staker historical data for single account

**Kind**: global function\
**Returns**: `Promise` - `{ id, account, totalLockedStake, totalFrozen, creditLimit, stakedAmount, timestamp }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchTrustlines(orderBy, orderDirection, where) ⇒ `Promise`

Get trustline historical data. when updateTrust is called on the UserManager this trustline data is recorded

**Kind**: global function\
**Returns**: `Promise` - `{ id, staker, borrower, amount, timestamp }[]`

| Param          | Type             | Description                           |
| -------------- | ---------------- | ------------------------------------- |
| orderBy        | `string`         | Property to orderBy                   |
| orderDirection | `OrderDirection` | Order in asc or desc                  |
| where          | `object`         | Where object e.g `{ staker: "0x00" }` |

### fetchAccountTrusted(account, orderBy, orderDirection) ⇒ `Promise`

Get data about accounts that are vouching for `account`

**Kind**: global function\
**Returns**: `Promise` - `{ id, staker, borrower, amount, timestamp }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| account        | `string`         | Account to lookup    |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchAccountTrusting(account, orderBy, orderDirection) ⇒ `Promise`

Get data about accounts that are being trusted by `account`

**Kind**: global function\
**Returns**: `Promise` - `{ id, staker, borrower, amount, timestamp }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| account        | `string`         | Account to lookup    |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchAccountTrustRelationship(borrower, staker, orderBy, orderDirection) ⇒ `Promise`

Get data about related account `borrower` -> `staker`

**Kind**: global function\
**Returns**: `Promise` - `{ id, staker, borrower, amount, timestamp }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| borrower       | `string`         | Account to lookup    |
| staker         | `string`         | Account to lookup    |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchUTokenMeta(orderBy, orderDirection) ⇒ `Promise`

Get UserManager historical state

**Kind**: global function\
**Returns**: `Promise` - `{ id, totalBorrows, totalSupply, totalReserves, totalRedeemable, borrowRate, supplyRate, exchangeRate, timestamp }`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |

### fetchUserManagerMeta(orderBy, orderDirection) ⇒ `Promise`

Get UserManager historical data

**Kind**: global function\
**Returns**: `Promise` - `{ id, totalStaked, totalFrozen, timestamp }[]`

| Param          | Type             | Description          |
| -------------- | ---------------- | -------------------- |
| orderBy        | `string`         | Property to orderBy  |
| orderDirection | `OrderDirection` | Order in asc or desc |


# Peripheral & Fun Contracts

## **UNION Referral & Register Helper**

UNION Referral is a smart contract suite designed to boost the UNION community through a robust referral system. It streamlines community growth by seamlessly integrating access control, referral tracking, and member registration processes.

<https://github.com/unioncredit/union-referral>

## ERC1155 Voucher

A smart contract that vouchers on receipt of an erc1155 token.

<https://github.com/unioncredit/union-v2-contracts/blob/master/contracts/peripheral/ERC1155Voucher.sol>

## Onchain Triggered Vouches

<https://github.com/unioncredit/union-vouchers/tree/master/packages/bulk-voucher>


# GraphQL Endpoints

*Note: all governance is handled on Ethereum Mainnet, so Arbitrum does not have a governance graph endpoint.*

## Optimism

<https://api.studio.thegraph.com/query/78581/union-finance/version/latest>

## Mainnet (Governance)

<https://api.studio.thegraph.com/query/78581/union-v1-mainnet/version/latest>


# Becoming a Member

Onboard as a "member" in order to borrow from, or vouch for, others.\
\
Becoming a member is a simple process. Upon completion, you will have access to credit from your network, be able to extend credit to people whom you trust, and have a voice in your credit system.

Head over to [app.union.finance](http://app.union.finance) and complete the following three steps. If you get stuck, check out the FAQ below or [reach out to the community](https://discord.gg/qu9JYpxKzy) for help.

1. **Stake DAI in the Union protocol**
2. **Pay the membership fee of one UNION token**
3. **Get Vouches from members who trust you**

## **Visual Guide**

#### **1. Stake DAI in the Union protocol**

Staking is a lot like depositing. When you stake DAI in the Union protocol, you earn a proportional amount of UNION governance token each block. And you can use this DAI to underwrite any address that you trust.

####

#### **2. Pay the membership fee of one UNION token**

Once you have earned at least one UNION, claim your rewards.

Burn 1 UNION to become a member, unlock benefits, and start building your credit network. This 'fee' is to mitigate DoS attacks from clogging the network. You should be able to acquire 1 UNION by staking a small sum for about a week.

#### **3. Get  Vouches from members who trust you**

Share your vouch link with Union members in your network. When someone vouches for you, they decide how much DAI they trust you to borrow and repay. And it is this trust that you are literally borrowing against when you borrow.

If you dont know anyone on union who can vouch for you, theres a few ways to get vouches.

1. There's a public-good autonomous underwriter: <https://CreditCub.club> where you are given credit based on your farcaster identity.
2. If you have a specific business need for permissionless credit, you can post to the /union warpcast channel. Just dont spam and be as detailed as possible.


# How Bridging Tokens Works

With the deployment of Union’s protocol on Arbitrum, we plan the maintain UNION based token emissions for stakers on both Ethereum & Arbitrum. As UNION is a non-transferrable token and will be required for governance on Layer-1, we’re required to build some additional features which can allow that process to happen in a clear and simple way.

**The Stages of UNION between Ethereum & L2 Rollups**

1. UNION originates on Ethereum
2. UNION is bridged to L2 via Governance and drip()
3. Users staking DAI on L2 can claim their accumulated l2UNION
4. Holders of l2UNION have the ability to migrate arbUNION to Ethereum via Arbitrum’s native crosschain bridge (We should be promoting the migration of arbUNION to L1)
5. Migrated arbUNION remains in this state until the point where a user unwraps their arbUNION converting it into the final form of UNION

**How a user can manage their UNION tokens through L2 → L1**

* While a users wallet is connected to the Union app on Arbitrum with arbUNION in their wallet, they should have an option to bridge their arbUNION to Layer 1 for Governance purposes with Arbitrum’s native bridge either via a link out or directly within the Union UI (Note potentially high bridging fees)
* If a user has migrated arbUNION to Ethereum using the bridge, they should be notified of the ability to unwrap their arbUNION and convert it into regular UNION. This UNION can then be used for voting in Union Improvement Proposals.

**Related Contracts**

* ArbWrapper

`function wrap(uint256 _amount) public returns (bool)`

`function unwrap(uint256 _amount) external returns (bool)`

<https://github.com/unioncredit/union-v1-contracts/blob/master/contracts/token/ArbUnionWrapper.sol>

* ArbUnion

<https://github.com/unioncredit/union-v1-contracts/blob/master/contracts/token/ArbUnion.sol>

**The bridge UI**

<https://bridge.arbitrum.io/>


# Bridge UNION token from Arbitrum to Ethereum

Here is a detailed outline of how to bridge your arbUNION tokens to Ethereum & unwrap them.

## Arbitrum side of bridge:

Sign into Union’s UI & make sure you are on the Arbitrum network in the top left corner. Next, click on your UNION counter, to the left of your wallet address/ENS

![](/files/a6syxZPIvq2PrNYdJZOx)

In the arbUNION balance modal, click the “Bridge” button in the bottom right corner. You will be redirected to [Arbitrum’s native Bridge](https://bridge.arbitrum.io/).

![](/files/k7Qlo0bSbwy3dxXthI6c)

On the Arbitrum Bridge, you will select the left drop down menu for the token you’d like to bridge. You will manually fill in the token address for arbUNION: 0x6dbde0e7e563e34a53b1130d6b779ec8ed34b4b9

![](/files/vqHJTJIwJhoOtZIELWFm)

![](/files/5Wbil32wFabiBKgIjfE8)

Next, click arbUNION from the search menu. It will appear in the “From: Arbitrum One” modal & you are going to click "max" on the right side of the modal.

Then scroll down to the “Summary” modal & click “Move funds to Mainnet” to initiate the bridging. You will need to confirm the transaction in your wallet extension.

![](/files/VxPNhAac220SdxDIZ1C2)

In the next pop up modal, you will need to select the tab “Use Arbitrum’s Bridge” & mark that you understand it will take 8 days to be able to claim & there will be fees on L1 to finish the bridging.

![](/files/Bshzx04IV19vsF8TdDcR)

**You will confirm the transaction in your wallet extension & wait the 8 days until your arbUNION has been bridged to Ethereum mainnet.**

## Ethereum Side of the Bridge

After the 8 day time period has elapsed, return to the [Arbitrum Bridge ](https://bridge.arbitrum.io/)& connect your wallet. Next click on the 3 horizontal bars in the top right corner.

![](/files/bQrzmBLFLMxAcTIkU033)

Click your wallet address/ENS from the menu. This will lead to your transaction history. Click on withdrawals & you will be able to view your bridging of arbUNION and if it is ready to be claimed. If it is ready, click “Claim” on the right side. You will have to confirm the transaction & gas fees in your wallet extension.

![](/files/V6ZRPdbLDln9ZXQz9bhp)

After the transaction is approved, return to app.union.finance (make sure you are on Ethereum in the top left corner). Click on the UNIOIN token counter in the top right corner of the Union UI, just to the left of your wallet address/ENS.

![](/files/WZqQ1ZAD8NNcsERbrE5n)

In the UNION balance modal, click the “unwrap” button to unwrap your arbUNION. You will confirm the transaction in your wallet extension.

![](/files/nS3rdhwbpkS9RVLc43vV)

Click on your UNION balance modal again & it will have updated to include your new bridged & unwrapped UNION.


# Voting & Delegation

When a Union Improvement Proposal goes live, UNION token holders vote on it. For a proposal to pass, it must reach quorum and have more votes *for* than *against.*

Active proposals can be viewed in the Governance Section of the [Union dapp](https://app.union.finance/governance).

## Delegation

In order to vote, tokens must be delegated to a wallet. Tokens can be delegated to yourself or to others.

If you don't plan on staying up to date with all things Union, including actively participating in governance, it is a good idea to delegate your tokens to a trusted third party.

## Voting

Make sure UNION tokens have been successfully claimed and delegated.

Find an [Active Proposal](https://app.union.finance/governance/proposals/571) and decide to vote for or against it.

![](https://unioncredit.slite.com/api/files/7_MFze-yuMJASe/Screen%20Shot%202022-02-09%20at%203.04.07%20PM.png)

Approve the transaction and voilà, that's it.

![](https://unioncredit.slite.com/api/files/p2gov-StqMa6tK/Screen%20Shot%202022-02-10%20at%203.15.13%20PM.png)


# Delegation from Gnosis

Go to the gnosis-safe app and locate your UNION tokens.

Select "new transaction"

![](/files/yam2kyDQtrA7JiXSKfV7)

Select "contract interaction"

![](/files/yyb9u81JeK55XsozRqkd)

Add the contract address (the UNION token address): "0x5dfe42eea70a3e6f93ee54ed9c321af07a85535c".

Use the drop down to select "delegate"

Enter the address where you wish to delegate your voting power.

Select "review"

![](/files/KAiYnrO5rax8G4k9OvhL)

Submit your transaction:

![](/files/4cvvbwTkW1R3yp639lEz)

Sign the transaction and notify the other signers that your transaction is waiting for signature. The last signer submits the transaction.

![](/files/xB3hCCW53KuJmKtUtElO)


# Vouching & Lending

Vouching is how trust is extended from one party to another. When you vouch for someone, you decide how much of your staked DAI to trust them with. To become a member on L1, a new user must get three vouches. Once you have 3 vouches and are a member of Union, you can vouch for others.

## Guide

#### 1. Vouch for another address

Decide who you would like to vouch for. Make sure it is someone that you trust. Enter their address, an alias, and the vouch amount.

{% embed url="<https://drive.google.com/file/d/1OSs32idNwRyaNtxMZSNQNpfPVFtDOnxK/view?usp=sharing>" %}


# In Progress


# Types of Risk

&#x20;The main forms of risk are:

* **Credit Risk** — You are in control of who you trust in the protocol, and if they don't repay .
* **Contract Risk** — The union protocol is software, and while all efforts were taken to try and write safe software there is the risk of a bug in the code.
* **User Risk** — Unions smart contracts are permissionless and they're deployed on ethereum which is also permissionless, as such any loss of funds due to user error is likely irreversible.


