├── tutorials ├── create-a-prize-strategy.md ├── networks.md ├── getting-started.md ├── buying-tickets.md ├── withdrawing-from-a-prize-pool.md └── operate-a-prize-pool.md ├── .gitbook └── assets │ ├── 1_cu6o0qf_purcupmqiuv1aa.png │ ├── screen-shot-2020-05-05-at-4.52.53-pm.png │ ├── screen-shot-2020-07-28-at-1.26.44-pm.png │ ├── screen-shot-2021-01-05-at-4.39.03-pm.png │ ├── screen-shot-2021-01-05-at-5.08.06-pm.png │ ├── screen-shot-2021-01-05-at-5.10.29-pm.png │ ├── screen-shot-2021-01-05-at-5.11.24-pm.png │ ├── screen-shot-2021-01-06-at-9.34.16-am.png │ ├── screen-shot-2021-03-11-at-1.54.17-pm.png │ ├── screen-shot-2021-03-11-at-2.03.50-pm.png │ ├── screen-shot-2021-03-11-at-2.50.55-pm.png │ ├── screen-shot-2021-03-11-at-4.43.14-pm.png │ ├── screen-shot-2021-03-11-at-4.44.38-pm.png │ ├── screen-shot-2021-03-29-at-5.02.37-pm.png │ ├── screen-shot-2021-05-05-at-2.20.38-pm.png │ └── prizepool.json ├── protocol ├── tokens │ ├── sponsorship.md │ ├── README.md │ ├── ticket.md │ └── token-listener.md ├── prize-pool │ ├── compound-prize-pool.md │ ├── stake-prize-pool.md │ ├── ticket.md │ ├── custom-yield-sources.md │ ├── yvault-prize-pool.md │ ├── fairness.md │ └── README.md ├── random-number-generator │ ├── blockhash.md │ ├── chainlink-vrf.md │ └── README.md ├── gas-usage.md ├── prize-strategy │ ├── README.md │ └── multiple-winners.md ├── lootbox.md ├── overview.md └── prize-strategy.md ├── faq.md ├── security ├── audits-and-testing.md ├── bug-bounties.md ├── bounties.md └── risks.md ├── migrating-from-v2-to-v3.md ├── resources.md ├── governance └── overview.md ├── SUMMARY.md ├── README.md └── networks.md /tutorials/create-a-prize-strategy.md: -------------------------------------------------------------------------------- 1 | # Create a Prize Strategy 2 | 3 | -------------------------------------------------------------------------------- /.gitbook/assets/1_cu6o0qf_purcupmqiuv1aa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/1_cu6o0qf_purcupmqiuv1aa.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2020-05-05-at-4.52.53-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2020-05-05-at-4.52.53-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2020-07-28-at-1.26.44-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2020-07-28-at-1.26.44-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-01-05-at-4.39.03-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-01-05-at-4.39.03-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-01-05-at-5.08.06-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-01-05-at-5.08.06-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-01-05-at-5.10.29-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-01-05-at-5.10.29-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-01-05-at-5.11.24-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-01-05-at-5.11.24-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-01-06-at-9.34.16-am.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-01-06-at-9.34.16-am.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-03-11-at-1.54.17-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-03-11-at-1.54.17-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-03-11-at-2.03.50-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-03-11-at-2.03.50-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-03-11-at-2.50.55-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-03-11-at-2.50.55-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-03-11-at-4.43.14-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-03-11-at-4.43.14-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-03-11-at-4.44.38-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-03-11-at-4.44.38-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-03-29-at-5.02.37-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-03-29-at-5.02.37-pm.png -------------------------------------------------------------------------------- /.gitbook/assets/screen-shot-2021-05-05-at-2.20.38-pm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pooltogether/documentation/HEAD/.gitbook/assets/screen-shot-2021-05-05-at-2.20.38-pm.png -------------------------------------------------------------------------------- /protocol/tokens/sponsorship.md: -------------------------------------------------------------------------------- 1 | # 🎁 Sponsorship 2 | 3 | Users may "sponsor" the prize pool by depositing funds that don't make them eligible to win. 4 | 5 | This can be useful for the creators of the pool to bootstrap its liquidity. 6 | 7 | -------------------------------------------------------------------------------- /faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## Can I become who I want to be? 4 | 5 | That's a tough question but thankfully, our team is on it. Please bear with us while we're investigating. 6 | 7 | ## How is fairness managed? 8 | 9 | Yes, after a few months we finally found the answer. Sadly, Mike is on vacations right now so I'm afraid we are not able to provide the answer at this point. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tutorials/networks.md: -------------------------------------------------------------------------------- 1 | # 📡 Networks 2 | 3 | ## Networks 4 | 5 | PoolTogether 3.0-alpha is currently deployed to the following networks: 6 | 7 | ### Kovan 8 | 9 | | Contract Name | Address | 10 | | :--- | :--- | 11 | | [CompoundPrizePoolBuilder](../protocol/builders/) | [0x6e12233ac8ccD7cC0d0481787D7e7dd943315984](https://kovan.etherscan.io/address/0x6e12233ac8ccD7cC0d0481787D7e7dd943315984) | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /protocol/tokens/README.md: -------------------------------------------------------------------------------- 1 | # 🎟️ Tokens 2 | 3 | When users deposit into a Prize Pool they receive an ERC20 compatible [ticket](ticket.md). 4 | 5 | External ERC721 and ERC20's can also be [added, controlled and removed](../prize-strategy/multiple-winners.md#external-erc20-and-erc721-awards) by Prize Pools. 6 | 7 | [Sponsorship](sponsorship.md) tokens are created when funds are added to the pool that are not eligible to win any prizes. 8 | 9 | -------------------------------------------------------------------------------- /protocol/prize-pool/compound-prize-pool.md: -------------------------------------------------------------------------------- 1 | # Compound Prize Pool 2 | 3 | The Compound Prize Pool is a Prize Pool that uses [Compound](https://compound.finance) as the yield source. When a Compound Prize Pool is created it is configured with the cToken to use for minting and redeeming. 4 | 5 | ## Retrieving the Underlying cToken 6 | 7 | To access the underlying [cToken](https://compound.finance/docs/ctokens), simply call this function: 8 | 9 | ```javascript 10 | function cToken() returns (address); 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /protocol/random-number-generator/blockhash.md: -------------------------------------------------------------------------------- 1 | # Blockhash 2 | 3 | The Blockhash RNG uses a future blockhash as the random number. This is the least secure method of random number generation, but also the simplest and cheapest. 4 | 5 | When a user request a random number their lock block will be the current block. Their request is considered 'complete' when at least one block has been mined since the lock block. Upon retrieval the last blockhash will be stored as the random number and returned. 6 | 7 | ## Usage 8 | 9 | A prize strategy can use a [RNGBlockhash](../../networks.md) RNG service. No additional work is needed: the blockhash service is free. 10 | 11 | -------------------------------------------------------------------------------- /protocol/prize-pool/stake-prize-pool.md: -------------------------------------------------------------------------------- 1 | # Stake Prize Pool 2 | 3 | The Stake Prize Pool is a prize pool that uses an ERC-20 compatible token as the underlying asset. 4 | 5 | Users's can stake their tokens to become eligible for whatever prize is defined as the prize strategy for that pool. 6 | 7 | This is particularly useful for protocols that are sitting inactively in users's wallets - why not stake them in a pool and become eligible for rewards? 8 | 9 | The returned [ticket](../tokens/ticket.md) can be thought of as a "proof-of-liquidity". 10 | 11 | ### Retrieving the Underlying ERC-20 12 | 13 | The underlying staked asset can be retrieved by calling: 14 | 15 | ```javascript 16 | function token() returns (address); 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /security/audits-and-testing.md: -------------------------------------------------------------------------------- 1 | # Audits & Testing 2 | 3 | The PoolTogether Protocol has undergone three formal professional third party audits. Two have been [conducted by Open Zeppelin](https://blog.openzeppelin.com/pooltogether-v3-audit/), and one by ditCraft. 4 | 5 | Additionally the PoolTogether core team has a long term security relationship with [ConsenSys Diligence](https://diligence.consensys.net/audits/) including monthly code reviews. 6 | 7 | Notwithstanding, portions of the PoolTogether Protocol codebase will continue to evolve and **it should never be expected that 100% of the deployed code has been formally audited.** 8 | 9 | We encourage responsible disclosure of any vulnerabilities in the smart contracts and will pay up to $25,000 for those. See the [Bounties](bounties.md) for more details. 10 | 11 | -------------------------------------------------------------------------------- /protocol/prize-pool/ticket.md: -------------------------------------------------------------------------------- 1 | # Ticket 2 | 3 | The Ticket contract is an [ERC20](https://eips.ethereum.org/EIPS/eip-20)-compatible token that allows users to be selected by a token index. 4 | 5 | The contract organizes the balances into a sum tree data structure, so that each address holds a "range" of tokens. A number can be used as an index within that range, and the holder of the tokens in that range is selected: 6 | 7 | ```javascript 8 | function draw(uint256 randomNumber) public view returns (address) 9 | ``` 10 | 11 | The **randomNumber** will be used as a token index into a specialized data structure that stores the user balances. The randomNumber is constrained to the token supply and modulo bias is corrected. 12 | 13 | The returned address is the user who holds the token index corresponding to the random number. 14 | 15 | -------------------------------------------------------------------------------- /protocol/tokens/ticket.md: -------------------------------------------------------------------------------- 1 | # 🎟️ Ticket 2 | 3 | The Ticket contract is an [ERC20](https://eips.ethereum.org/EIPS/eip-20)-compatible token that allows users to be selected by a token index. 4 | 5 | The contract organizes the balances into a sum tree data structure, so that each address holds a "range" of tokens. A number can be used as an index within that range, and the holder of the tokens in that range is selected: 6 | 7 | ```javascript 8 | function draw(uint256 randomNumber) public view returns (address) 9 | ``` 10 | 11 | The **randomNumber** will be used as a token index into a specialized data structure that stores the user balances. The randomNumber is constrained to the token supply and modulo bias is corrected. 12 | 13 | The returned address is the user who holds the token index corresponding to the random number. 14 | 15 | -------------------------------------------------------------------------------- /protocol/prize-pool/custom-yield-sources.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Create a Prize Pool that Uses a Custom Yield Source 3 | --- 4 | 5 | # Custom Yield Sources 6 | 7 | If you would like to create a Prize Pool that generates prizes with a custom yield source, you'll need to: 8 | 9 | 1. Subclass the [Prize Pool](https://github.com/pooltogether/pooltogether-pool-contracts/blob/master/contracts/prize-pool/PrizePool.sol). 10 | 2. Implement the [Yield Source](https://github.com/pooltogether/pooltogether-pool-contracts/blob/master/contracts/prize-pool/YieldSource.sol) interface 11 | 12 | Once the subclass is written, you'll need to deploy it. The simplest approach will be to follow the [PoolWithMultipleWinnersBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/blob/master/contracts/builders/PoolWithMultipleWinnersBuilder.sol) and create a contract that builds your custom prize pool with the appropriate strategy. 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /security/bug-bounties.md: -------------------------------------------------------------------------------- 1 | # Bug Bounties 2 | 3 | We value contributions from the community to strengthen the security of the core contracts. We want to reward any hackers in good faith who report vulnerabilities. 4 | 5 | The scope of this bounty includes the PoolTogether smart contracts. The determination of the bug severity will be made by the PoolTogether team. 6 | 7 | Payouts will be as follows: 8 | 9 | High: $25,000 DAI 10 | Medium: $10,000 DAI 11 | Low: $1,000 DAI 12 | 13 | The issue must: 14 | 15 | * be a previously unreported, non-public vulnerability. 16 | * include enough detail for us to identify and reproduce the problem 17 | 18 | All reports should start with an email to [hello@pooltogether.us](mailto:hello@pooltogether.us) and they will receive a response within 24 hours. Non-security issues are not eligible for this bounty. 19 | 20 | We determine the severity of an issue according to the [Smart Contract Security Alliance Severity Levels](https://www.smartcontractsecurityalliance.com/) 21 | 22 | Determinations of eligibility and all terms related to this award are at the sole and final discretion of the PoolTogether team. 23 | 24 | -------------------------------------------------------------------------------- /protocol/gas-usage.md: -------------------------------------------------------------------------------- 1 | # ⛽ Gas Usage 2 | 3 | PoolTogether is conscious that to become a truly lossless prize protocol the transaction fees involved must be minimal. The current design utilizes the Minimal Proxy Factory design where possible to reduce gas usage. 4 | 5 | Note that these fees are paid to the Ethereum Network and not to PoolTogether. The amount a transaction costs in USD is calculated as: the amount of gas used \* gasPrice \* USD/ETH. 6 | 7 | Here is a list of common actions and their costs: 8 | 9 | | Function Call | Estimated Gas Cost | $USD \(40 GWei, $600/ETH\) | 10 | | :--- | :--- | :--- | 11 | | **Creating Pools with the Builder** | | | 12 | | createCompoundPoolMultipleWinners\(\) | 1.3M | 33 | 13 | | createStakePoolMultipleWinners\(\) | 1.25M | 30 | 14 | | createVaultPoolMultipleWinners\(\) | 1.2M | 30 | 15 | | **Entering and Leaving Pools** | | | 16 | | depositTo\(\) | 0.5M | 12 | 17 | | withdrawInstantlyFrom\(\) | 0.5M | 12 | 18 | | **Award Process** | | | 19 | | RNG request - [Chainlink VRF](random-number-generator/chainlink-vrf.md) | 2 LINK | 20 \(@ 10 USD/LINK\) | 20 | | startAward\(\) | 200k | 4.8 | 21 | | completeAward\(\) | 250k+ \(variable\) | 6 | 22 | | **Transferring Tickets** | 290k | 7 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /migrating-from-v2-to-v3.md: -------------------------------------------------------------------------------- 1 | # 💱 Migrating from V2 to V3 2 | 3 | Users of PoolTogether V2 can exchange their old tickets for new ones using the [new interface](https://app.pooltogether.com). See the "accounts" section. 4 | 5 | Users can exchange: 6 | 7 | * Dai Pool tickets 8 | * USDC Pool tickets 9 | * Dai Pod tickets 10 | * USDC Pod tickets 11 | 12 | All of these tickets will be exchange 1:1 for new V3 Pool Dai tickets. Note that you will not receive USDC back, you will be **exchanging your USDC Tickets for Dai Tickets**. 13 | 14 | ## Migrating Your Tickets 15 | 16 | 1. Do an ER20 token transfer to the migration contract. 17 | 2. The migration contract will transfer PcDAI to the sender. 18 | 19 | PoolTogether V2 tickets and Pod shares are ERC777 tokens; this token standard triggers a callback on the recipient when the recipient is a contract. When the migration contract is triggered it automatically send PcDAI back to the sender. 20 | 21 | For the latest address see the [Networks](networks.md) page. Look for the Migration contract on mainnet. 22 | 23 | ## Troubleshooting 24 | 25 | The migration contract needs to be stocked with liquidity, so if your ticket exchange fails please contact us on [our Discord](https://discord.gg/hxPhPDW). 26 | 27 | -------------------------------------------------------------------------------- /protocol/random-number-generator/chainlink-vrf.md: -------------------------------------------------------------------------------- 1 | # Chainlink VRF 2 | 3 | **A verifiable random function is a pseudo-random function whose output is unique and can be publicly verified.** 4 | 5 | ChainLink has implemented their VRF using public key cryptography. It works like so: 6 | 7 | 1. The user creates a “seed” value 8 | 2. A ChainLink operator, who has publicly committed to a keypair, uses their secret to sign the seed value. 9 | 3. The user is able to verify that the operator has signed the seed value, and consume the signature as the “random number”. 10 | 11 | **ChainLink VRF Documentation:** [**https://docs.chain.link/docs/chainlink-vrf**](https://docs.chain.link/docs/chainlink-vrf) 12 | 13 | This approach has some benefits in that the operator cannot “lie”: they must sign the seed using the secret they have committed to. The algorithm is also instantaneous: there is no delay or waiting period to get the answer. **** 14 | 15 | ## Usage 16 | 17 | To use the [RNGChainlink](../../networks.md) RNG service, create a new prize pool using the service or set it on an existing pool. 18 | 19 | 🚨🚨🚨 **Chainlink RNG requires 2 LINK tokens per RNG request** 🚨🚨🚨 20 | 21 | 🚨🚨🚨 **You must deposit LINK into the PRIZE STRATEGY** 🚨🚨🚨 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /resources.md: -------------------------------------------------------------------------------- 1 | # 🚰 Resources 2 | 3 | ## Tools 4 | 5 | * [Prize Pool Builder](https://builder.pooltogether.com) ([Source Code](https://github.com/pooltogether/pooltogether-pool-builder-ui)) 6 | * [Prize Pool Reference App](https://reference-app.pooltogether.com) ([Source Code](https://github.com/pooltogether/pooltogether-reference-pool-ui)) 7 | 8 | ## Code 9 | 10 | * [Prize Pool Contracts](https://github.com/pooltogether/pooltogether-pool-contracts) 11 | * [Random Number Generator Contracts](https://github.com/pooltogether/pooltogether-rng-contracts) 12 | * [PoolTogether V3 Subgraph](https://github.com/pooltogether/pooltogether-subgraph-v3) 13 | * [Loot Box Contracts](https://github.com/pooltogether/loot-box) 14 | * [OpenZeppelin Defender Award Autotask](https://github.com/pooltogether/defender-autotask-reward) 15 | * [V2 to V3 Migration Contracts](https://github.com/pooltogether/pooltogether-migrate-v3) 16 | 17 | ## Subgraphs 18 | 19 | * [mainnet Subgraph](https://thegraph.com/explorer/subgraph/pooltogether/pooltogether-v3\_1\_0) 20 | * [mainnet LootBox Subgraph](https://thegraph.com/explorer/subgraph/pooltogether/lootbox-v1\_0\_0) 21 | * [rinkeby Subgraph](https://thegraph.com/explorer/subgraph/pooltogether/rinkeby-v3\_1\_0) 22 | 23 | ## Workshops 24 | 25 | * [ETHOnline Hackathon Custom Prize Strategy](https://github.com/pooltogether/ethonline-workshop) 26 | -------------------------------------------------------------------------------- /governance/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: The Role of Governance 3 | --- 4 | 5 | # 🏛️ Overview 6 | 7 | _The PoolTogether protocol will be transitioning to decentralized governance. In the meantime, the core team serves as the interim governance body._ 8 | 9 | PoolTogether Governance broadly serves two mandates: 10 | 11 | * Protocol improvement 12 | * Prize Pool management 13 | 14 | ## Protocol Improvement 15 | 16 | Governance will guide the evolution of the protocol towards the goal of building products that create financial health. The primary functions of governance are to: 17 | 18 | * Set [rewards](broken-reference) for prize pool users 19 | * Approve & implement new yield sources for prize pools 20 | * Implement new prize strategies 21 | 22 | In addition to these core functions governance may: 23 | 24 | * propose integrations with L2 systems 25 | * Add additional prizes or rewards 26 | * subsidized transactions 27 | * insurance coverage 28 | * Anything else! 29 | 30 | ## Prize Pool Management 31 | 32 | Governance also manages its own set of Prize Pools. These Prize Pools are displayed on the official [PoolTogether App](https://app-v3.pooltogether.com). 33 | 34 | ## Comptroller 35 | 36 | All Prize Pools link to a global protocol [Comptroller](broken-reference). The Comptroller is owned by governance, and determines the reserve rate and rewards Prize Pools. 37 | -------------------------------------------------------------------------------- /protocol/tokens/token-listener.md: -------------------------------------------------------------------------------- 1 | # 👂 Token Listener 2 | 3 | The Token Listener interface allows contracts to "listen" to the complete token lifecycle of mint, transfer and burn. There are two functions that must be implemented: `beforeTokenMint` and `beforeTokenTransfer` 4 | 5 | The beforeTokenMint function must be called whenever a token is minted. 6 | 7 | ```javascript 8 | function beforeTokenMint( 9 | address to, 10 | uint256 amount, 11 | address controlledToken, 12 | address referrer 13 | ) external; 14 | ``` 15 | 16 | | Paramater Name | Parameter Description | 17 | | :--- | :--- | 18 | | to | The address that is receiving the newly minted tokens | 19 | | amount | The amount of new tokens | 20 | | controlledToken | The token being minted | 21 | | referrer | The address that referred the user \(for rewards\) | 22 | 23 | The beforeTokenTransfer function must be called whenever a token is transferred or burned. 24 | 25 | ```javascript 26 | function beforeTokenTransfer( 27 | address from, 28 | address to, 29 | uint256 amount, 30 | address controlledToken 31 | ) external; 32 | ``` 33 | 34 | | Paramater Name | Parameter Description | 35 | | :--- | :--- | 36 | | from | The address that is sending the tokens | 37 | | to | The address that is receiving tokens. May be the zero address if burning. | 38 | | amount | The amount of tokens | 39 | | controlledToken | The token being transferred | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /protocol/prize-strategy/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Customize how a Prize Pool distributes prizes 3 | --- 4 | 5 | # 💸 Prize Strategies 6 | 7 | A Prize Strategy handles prize distribution for a [Prize Pool](../prize-pool/). When a Prize Pool is constructed it is configured with a Prize Strategy. The Prize Strategy has the privileged ability to award tokens from the Prize Pool. 8 | 9 | The most popular Prize Strategy offering is the [Multiple Winner](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/protocol/multiple-winners/) strategy. Earlier versions (< v3.1.0) of the protocol used the Single Random Winner strategy, which is now a trivial subset of Multiple Winners (with `numberofWinners = 1`). 10 | 11 | Prize Strategies must implement the [Token Listener](../tokens/token-listener.md) interface so that they can be aware of the full token lifecycle. 12 | 13 | ## Privileged Actions 14 | 15 | A [Prize Pool's](../prize-pool/) Prize Strategy is able to award tokens held by the Prize Pool contract. The Prize Strategy is able to: 16 | 17 | * [award yield](../prize-pool/#awarding-yield) that has accrued in the Prize Pool 18 | * [award any ERC20 balance](../prize-pool/#awarding-erc-20-s) held by the Prize Pool 19 | * [award any ERC721](../prize-pool/#awarding-erc-721-s-nfts) owned by the Prize Pool 20 | 21 | ## Required Behaviour 22 | 23 | A Prize Strategy must implement the [Token Listener](../tokens/token-listener.md) interface so that it can listen to pool token mint, transfer and burn actions by the Prize Pool. 24 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [✨ Introduction](README.md) 4 | * [📡 Networks](networks.md) 5 | * [🚰 Resources](resources.md) 6 | * [💱 Migrating from V2 to V3](migrating-from-v2-to-v3.md) 7 | 8 | ## Protocol 9 | 10 | * [🌐 Overview](protocol/overview.md) 11 | * [🏆 Prize Pools](protocol/prize-pool/README.md) 12 | * [⚖️ Fairness](protocol/prize-pool/fairness.md) 13 | * [Compound Prize Pool](protocol/prize-pool/compound-prize-pool.md) 14 | * [Stake Prize Pool](protocol/prize-pool/stake-prize-pool.md) 15 | * [yVault Prize Pool](protocol/prize-pool/yvault-prize-pool.md) 16 | * [Custom Yield Sources](protocol/prize-pool/custom-yield-sources.md) 17 | * [💸 Prize Strategies](protocol/prize-strategy/README.md) 18 | * [🤑 Multiple Winners](protocol/prize-strategy/multiple-winners.md) 19 | * [🎟️ Tokens](protocol/tokens/README.md) 20 | * [🎟️ Ticket](protocol/tokens/ticket.md) 21 | * [🎁 Sponsorship](protocol/tokens/sponsorship.md) 22 | * [👂 Token Listener](protocol/tokens/token-listener.md) 23 | * [🎲 Random Number Generator](protocol/random-number-generator/README.md) 24 | * [Blockhash](protocol/random-number-generator/blockhash.md) 25 | * [Chainlink VRF](protocol/random-number-generator/chainlink-vrf.md) 26 | * [🏴‍☠️ Loot Box](protocol/lootbox.md) 27 | * [⛽ Gas Usage](protocol/gas-usage.md) 28 | 29 | ## Governance 30 | 31 | * [🏛️ Overview](governance/overview.md) 32 | 33 | ## Security 34 | 35 | * [Risks](security/risks.md) 36 | * [Audits & Testing](security/audits-and-testing.md) 37 | * [Bounties](security/bounties.md) 38 | -------------------------------------------------------------------------------- /tutorials/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Start Building with PoolTogether 3 | --- 4 | 5 | # Getting Started 6 | 7 | There are many ways to integrate with PoolTogether! Follow the tutorials or browse the resources below. 8 | 9 | ## Tutorials 10 | 11 | * [Depositing into a Prize Pool](buying-tickets.md) 12 | * [Withdraw from a Prize Pool](withdrawing-from-a-prize-pool.md) 13 | * [Create a Prize Pool](creating-a-prize-pool.md) 14 | * Create a Prize Strategy 15 | 16 | ## Resources 17 | 18 | ### Create New Prize Pools 19 | 20 | * Use the [Builder](https://builder.pooltogether.com) user interface. Source code is on [Github](https://github.com/pooltogether/pooltogether-pool-builder-ui). 21 | * Use the core [PoolTogether Pool Contracts](../networks.md). Source code is on [Github](https://github.com/pooltogether/pooltogether-pool-contracts). 22 | 23 | ### Interact With Prize Pools 24 | 25 | * Use the official [PoolTogether App](https://staging-v3.pooltogether.com/) to interact with PoolTogether-managed prize pools 26 | * Pull in the [Current Pool Data](https://github.com/pooltogether/current-pool-data) npm package to programmatically get the list of pools 27 | * Use and fork the [PoolTogether Reference App](https://reference-app.pooltogether.com/) to interact with Prize Pools created by the builder 28 | * Use the [Buidler Console](https://github.com/pooltogether/buidler-console) project to interact with prize pools directly from the command line 29 | * Use the official [Graph Protocol Subgraph](https://github.com/pooltogether/pooltogether-subgraph-v3) to query data. See it on the [Graph Explorer](https://thegraph.com/explorer/subgraph/pooltogether/pooltogether-v3_1_0). 30 | -------------------------------------------------------------------------------- /protocol/prize-pool/yvault-prize-pool.md: -------------------------------------------------------------------------------- 1 | # yVault Prize Pool 2 | 3 | The yVault Prize Pool is a prize pool that uses a yEarn [yVault](https://yearn.finance/vaults) as a yield source. When a yVault Prize Pool is created it is configured with a yVault to deposit and withdraw from. This particular prize pool retains a small reserve in order to cover yVault withdrawal fees. 4 | 5 | ## Prize Pool Reserve 6 | 7 | yVaults may charge users a fee upon withdrawal if the amount exceeds the vault holdings. In order to compensate for this the yVault Prize Pool retains a reserve at a rate matching the fee. For example, if the [yVault fee is 0.5%](https://docs.yearn.finance/products/yvaults#delegated-yvaults) then the yVault Prize Pool will retain 0.5% of the deposits as reserve. The reserve will **not** be exposed to the Prize Strategy for distribution: it will be retained to cover withdrawal fees. 8 | 9 | {% hint style="info" %} 10 | It may appear that users will immediately lose 0.5% upon deposit, but it is important to note that the reserve works in tandem with the [credit system](fairness.md). The credit system ensures users participate long enough to contribute to the prize and the reserve. 11 | {% endhint %} 12 | 13 | The reserve rate can be retrieved using: 14 | 15 | ```javascript 16 | function reserveRateMantissa() returns (uint256); 17 | ``` 18 | 19 | This returns the reserve rate as a 18 decimal fixed point number \(like Ether\). 20 | 21 | The _owner_ of the prize pool can set the reserve rate using the function: 22 | 23 | ```javascript 24 | function setReserveRateMantissa( 25 | uint256 _reserveRateMantissa 26 | ) external onlyOwner 27 | ``` 28 | 29 | ## Retrieving the Underlying yVault 30 | 31 | The underlying yVault address can be retrieved using the function: 32 | 33 | ```javascript 34 | function vault() returns (address); 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ✨ Introduction 2 | 3 | [![PoolTogether Brand](https://github.com/pooltogether/pooltogether--brand-assets/blob/977e03604c49c63314450b5d432fe57d34747c66/logo/pooltogether-logo--purple-gradient.png?raw=true)](https://github.com/pooltogether/pooltogether--brand-assets) 4 | 5 | ## ✨ Introduction 6 | 7 | PoolTogether is a protocol for no-loss prize games on the Ethereum blockchain. The protocol: 8 | 9 | **1\) Enables developers to build their own no-loss prize games 10 | 2\)** **Offers governance-managed no-loss prize games** 11 | 12 | Prize games are pools of funds whose accrued interest is distributed as prizes. The concept is well-established and otherwise known as "[no loss lotteries](http://beniverson.org/papers/MaMa.pdf)" or "[prize savings accounts](https://en.wikipedia.org/wiki/Prize-linked_savings_account)". All prize games created by the protocol share the same key characteristics: 13 | 14 | * No loss of deposited funds 15 | * Ability to withdraw at any time 16 | * Fair prize distribution according to a prize strategy 17 | 18 | Prize games can be differentiated in the following ways: 19 | 20 | * The yield source the prize pool uses to generate no loss return 21 | * The prize strategy used to determine frequency and distribution of prizes 22 | * The additional rewards offered by the prize pool 23 | * The asset type the prize pool accepts for deposits 24 | * The fairness parameters 25 | 26 | ### Governance 27 | 28 | The PoolTogether Protocol governance serves two primary functions. 29 | 30 | * Governing the prize pool creation tools 31 | * Governing a sub-set of prize pools 32 | 33 | The protocol governed prize pools appear on the official [PoolTogether App](https://app-v3.pooltogether.com). Governance is currently the core PoolTogether team, but very soon governance control will be distributed amongst protocol stakeholders. 34 | 35 | 36 | 37 | #### 38 | 39 | #### 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /security/bounties.md: -------------------------------------------------------------------------------- 1 | # Bounties 2 | 3 | We value contributions from the community to strengthen the security of the core contracts. We want to reward any hackers in good faith who report vulnerabilities. 4 | 5 | The scope of this bounty includes the PoolTogether smart contracts. The determination of the bug severity will be made by the PoolTogether team. We determine the severity of an issue according to the [Smart Contract Security Alliance Severity Levels](https://www.smartcontractsecurityalliance.com/) 6 | 7 | Payouts will be as follows: 8 | 9 | High: $25,000 DAI 10 | Medium: $10,000 DAI 11 | Low: $1,000 DAI 12 | 13 | The issue must: 14 | 15 | * be a previously unreported, non-public vulnerability. 16 | * include enough detail for us to identify and reproduce the problem 17 | 18 | All reports should start with an email to [hello@pooltogether.us](mailto:hello@pooltogether.us) and they will receive a response within 24 hours. Non-security issues are not eligible for this bounty. 19 | 20 | Determinations of eligibility and all terms related to this award are at the sole and final discretion of the PoolTogether team. 21 | 22 | ## Past Bounties 23 | 24 | ### PermitAndDepositDai Contract: Unrestricted Sender 25 | 26 | Severity: Medium / High 27 | Date: Thursday, October 22nd, 2020 28 | Reporter: Kevin Foesenek 29 | Payout: $20,000 USD of WETH \([transaction](https://etherscan.io/tx/0xdd9fcf07a29a376b811c775d34cef4ceddf6e720981da34ac7142a8c38e7e7a6)\) 30 | 31 | **Vulnerability** 32 | Just prior to launch a security researcher discovered a flaw in the PermitAndDepositDai contract. This flaw would have allowed an attacker to front-run the "deposit" transaction and take the deposited amount. This would have affected any new deposits to the system. 33 | 34 | **Mitigation** 35 | References to the contract were removed from the user interface, and a fix was immediately deployed to mainnet and published via NPM. 36 | 37 | -------------------------------------------------------------------------------- /protocol/random-number-generator/README.md: -------------------------------------------------------------------------------- 1 | # 🎲 Random Number Generator 2 | 3 | PoolTogether has abstracted the generation of random numbers by creating a request-based Random Number Generator interface. 4 | 5 | It functions like so: 6 | 7 | 1. The user will first get the request fee. The fee will be expressed using an \(address, amount\) pair representing the required ERC20 and amount. 8 | 2. The user will then approve the RNG to spend that ERC20 of the amount 9 | 3. The user will then request the random number. The RNG will transfer the cost into itself and begin the request. The request returns a request identifier. 10 | 4. The user may check to see if the random number is available using the identifier. 11 | 5. When the random number is available the user may retrieve it with the identifier. 12 | 13 | Let's look at these functions in detail. 14 | 15 | ## Get the Request Fee 16 | 17 | Many RNG services require tokens in order to operate. To get the cost of the rng you may do so using: 18 | 19 | ```javascript 20 | function getRequestFee() external view returns (address feeToken, uint256 requestFee); 21 | ``` 22 | 23 | This function returns two values: 24 | 25 | * **feeToken:** the ERC20 that needs to be paid 26 | * **requestFee:** is the amount of the token that needs to be paid 27 | 28 | ## Request a Random Number 29 | 30 | Once the user has approved the RNG service spend, they may request a random number like so: 31 | 32 | ```javascript 33 | function requestRandomNumber() external returns (uint32 requestId, uint32 lockBlock); 34 | ``` 35 | 36 | This function returns two values: 37 | 38 | * **requestId:** the unique id for this RNG request 39 | * **lockBlock:** the commitment block for this RNG request. Users of the RNG request shouldn't make any changes after the lockBlock, otherwise the RNG may be less secure. For example, the Prize Strategy will lock all ticket sales and movements after the lockBlock, as they affect the winner selection. Once the request is complete the Prize Strategy unlocks tickets. 40 | 41 | ## Check if Request is Complete 42 | 43 | The user may check if a request is complete: 44 | 45 | ```javascript 46 | function isRequestComplete(uint32 requestId) external view returns (bool isCompleted) 47 | ``` 48 | 49 | ## Retrieve Random Number 50 | 51 | ```javascript 52 | function randomNumber(uint32 requestId) external returns (uint256 randomNum); 53 | ``` 54 | 55 | ## 56 | 57 | -------------------------------------------------------------------------------- /protocol/lootbox.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: What is a PoolTogether Loot Box? 3 | --- 4 | 5 | # 🏴‍☠️ Loot Box 6 | 7 | ### Overview 8 | 9 | The PoolTogether Loot Box is a permission-less token container. A Loot Box allows wallets to be transferred like an ERC721. 10 | 11 | Many different tokens can be controlled simply by one counterfactual address. The holding contract is created and destroyed within the same transaction. This cheap deployment and immediate destruction of the contract minimizes the gas overhead involved with containerization. 12 | 13 | The code can be found here: [https://github.com/pooltogether/loot-box](https://github.com/pooltogether/loot-box) 14 | 15 | ### How it works 16 | 17 | A LootBox contract ephemerally exists within a transaction. The owner of an `ERC721` owns the LootBox. 18 | 19 | 1. A `ERC721` is created by calling `createERC721Controlled()` on the `ERC721ProxyFactory` by anyone: 20 | 21 | ```javascript 22 | function createERC721Controlled( 23 | string memory name, 24 | string memory symbol, 25 | string memory baseURI 26 | ) external returns (ERC721Controlled) 27 | ``` 28 | 29 | 1. `mint()` can then be called on the `ControlledERC721` which effectively creates a LootBox with an Owner defined by the `to` field: 30 | 31 | ```javascript 32 | function mint(address to) external onlyAdmin returns (uint256) 33 | ``` 34 | 35 | 2\. The LootBox address is calculated by calling: 36 | 37 | ```javascript 38 | computeAddress(address erc721, uint256 tokenId) 39 | ``` 40 | 41 | 3\. Tokens are transferred/minted to this address. In the case of PoolTogether, these are usually external ERC20, ERC721 and ERC1155 rewards for a Prize Period. 42 | 43 | 4\. Anyone can call `plunder()` on the LootBox controller which will transfer all the passed tokens to the LootBox owner. 44 | 45 | ```javascript 46 | function plunder( 47 | address erc721, 48 | uint256 tokenId, 49 | address[] calldata erc20s, 50 | WithdrawERC721[] calldata erc721s, 51 | WithdrawERC1155[] calldata erc1155s 52 | ) 53 | ``` 54 | 55 | where `erc20s` is defined as an array of ERC-20 addresses, 56 | 57 | `erc721s` is defined as: 58 | 59 | ```c 60 | struct WithdrawERC721 { 61 | address token; 62 | uint256[] tokenIds; 63 | } 64 | ``` 65 | 66 | and `erc1155s` as: 67 | 68 | ```c 69 | struct WithdrawERC1155 { 70 | address token; 71 | uint256[] ids; 72 | uint256[] amounts; 73 | bytes data; 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /protocol/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: What are No-Loss Prize Games? 3 | --- 4 | 5 | # 🌐 Overview 6 | 7 | No-loss prize games are pools of funds whose accrued interest is distributed as prizes. 8 | 9 | The high level protocol architecture is outlined below. The code is available on [Github](https://github.com/pooltogether/pooltogether-pool-contracts). 10 | 11 | ## How it works 12 | 13 | 1. Users deposit funds into a Prize Pool. They receive pool tokens in exchange. 14 | 2. The funds earn interest. 15 | 3. The interest is distributed by the Prize Strategy as pool tokens. 16 | 4. Users withdraw their funds at any time by telling the Prize Pool to burn their pool tokens. 17 | 18 | ## Architecture 19 | 20 | ### [Prize Pools](prize-pool/) 21 | 22 | Prize Pools are the central building block of prize games. They pool user funds in a **yield source** and expose the yield to their **Prize Strategy**, which then disburses as desired. 23 | 24 | Prize Pools can be differentiated in four primary ways: 25 | 26 | * The yield source the prize pool uses to generate no loss return 27 | * The prize strategy used to determine frequency and distribution 28 | * The rewards offered by the prize pool 29 | * The asset type the prize pool accepts for deposits 30 | * The fairness parameters 31 | 32 | ### [Prize Strategies](prize-strategy/) 33 | 34 | Prize Strategies determine the prize distribution for the Prize Pool. They can define any logic to allocate tokens that the prize pool accrues. Specifically they can: 35 | 36 | * Award yield in the Prize Pool as pool tokens 37 | * Award ERC20 tokens held by the Prize Pool 38 | * Award ERC721 tokens held by the Prize Pool 39 | 40 | ### [Builders](broken-reference) 41 | 42 | Builders make it easy to create pre-configured prize games. There are currently three Prize Pool types paired with the MultipleWinners, documentation available [here](broken-reference). 43 | 44 | ### [Random Number Generator](random-number-generator/) 45 | 46 | There are many different ways to generate a random number, so we've abstracted them as request-based Random Number Generator services. Each RNG service has a different security profile, so be sure to use the appropriate one for your game. 47 | 48 | ## Conventions 49 | 50 | Fixed point math is used extensively in PoolTogether. We used fixed point math with 18 decimal places for all fractional numbers. You can think of this as being just like Ether and wei: a value of "1" Ether is represented as "1000000000000000000" wei. 51 | 52 | When a number is a fixed point 18 number we always suffix the number with _mantissa._ For example the credit rate is written as _creditRateMantissa_, because it is a fixed point number. 53 | 54 | -------------------------------------------------------------------------------- /security/risks.md: -------------------------------------------------------------------------------- 1 | # Risks 2 | 3 | Using the protocol includes substantial risks of losing some or all of your funds. The PoolTogether core team and community have made every effort to ensure the security of funds. 4 | 5 | This section will help you understand the the types of risk you are taking what has been done to mitigate them and how to mitigate them further. 6 | 7 | ### Protocol Dependency Risk 8 | 9 | The PoolTogether Protocol uses several other protocols. Therefore the first type of risk is the risk that these other integrated protocols can fail. 10 | 11 | Specifically by using PoolTogether you are also taking on the risks of using the Ethereum network, the collateral you are depositing, and the yield service \(currently Compound.Finance\). 12 | 13 | To mitigate this risk the protocol is only integrated with highly reputable and well secured protocols. 14 | 15 | ### Smart Contract Exploit Risk 16 | 17 | The second type of risk is specific to PoolTogether. The risk is that there could be some sort of bug or exploit in the smart contracts that run the PoolTogether Protocol. This is a risk with any product on Ethereum. Depending on what the bug or exploit is, a nefarious person may be able to take some or all of the funds stored in the PoolTogether Protocol. Here’s what we’ve done to mitigate this risk. 18 | 19 | 1. Professional, third party smart contract auditing. PoolTogether has hired companies to professionally review and audit the smart contract code for any bugs or exploits. These auditors have produced reports with their findings. As PoolTogether continues to grow we’re committed to continuing to pay for audits however, it should be understood that at any given time, 100% of the code base has not been professionally audited. 20 | 2. Bug Bounty program. PoolTogether offers payment of up to $25,000 for reports of any bugs in the smart contracts. If someone was to discover a bug, this is a way for them to responsibly disclose it to us and be paid rather than exploit it. 21 | 3. All the smart contract code is open source, meaning it is publicly readable by anyone. At first this may sound strange but it actually makes the protocol more secure as anyone can review it for bugs and submit a bug bounty. 22 | 4. Before we even give our code to auditors we also do extensive internal testing. 23 | 24 | ### Wallet Loss Risk 25 | 26 | This risk doesn’t have anything to do with PoolTogether but we wanted to mention it. Using PoolTogether requires you to use an Ethereum wallet that supports Ethereum apps. If you permanently lose access to this wallet, you will not be able to recover your funds. Different wallets have different recovery mechanisms. It’s important for you to know what those are and be able to recover your wallet. [Argent Wallet](https://www.argent.xyz/) is one example of a wallet with good recovery methods. 27 | 28 | -------------------------------------------------------------------------------- /tutorials/buying-tickets.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: How to Buy Tickets in a PoolTogether Prize Pool 3 | --- 4 | 5 | # Deposit into a Prize Pool 6 | 7 | Let's deposit into a Prize Pool. Each step of the way we'll show you how to do it in both Solidity and Javascript \(using [Ethers.js](https://docs.ethers.io)\). 8 | 9 | First, we'll need to identify what the underlying asset to be deposited is. If it's a Compound Prize Pool, we'll want to check which cToken it is. 10 | 11 | Let's assume we have a Compound Prize Pool that is built against cDai. 12 | 13 | ## Approve 14 | 15 | Let's approve the Prize Pool to spend 1 of our Dai: 16 | 17 | **Solidity** 18 | 19 | ```text 20 | DAI_ERC20.approve(CDAI_PRIZE_POOL, 1 ether); 21 | ``` 22 | 23 | **JavaScript** 24 | 25 | ```javascript 26 | const ethers = require('ethers') 27 | const signer = ethers.Wallet.createRandom() 28 | 29 | const dai = new ethers.Contract( 30 | DAI_ADDRESS, 31 | ERC20_ABI, 32 | signer 33 | ) 34 | 35 | const daiPrizePool = new ethers.Contract( 36 | DAI_PRIZE_POOL_ADDRESS, 37 | PRIZE_POOL_ABI, 38 | signer 39 | ) 40 | 41 | await dai.approve(daiPrizePool.address, ethers.utils.parseEther('1')) 42 | 43 | ``` 44 | 45 | ## Deposit 46 | 47 | Now let's deposit into the Prize Pool. Since we're depositing into a [Compound Prize Pool](../protocol/prize-pool/compound-prize-pool.md) that uses a [Single Random Winner]() strategy, we'll want to mint Tickets so that we're eligible for prizes. 48 | 49 | We can retrieve the Ticket [controlled token](../protocol/prize-pool/#controlled-tokens) address from the Single Random Winner prize strategy: 50 | 51 | **Solidity** 52 | 53 | ```javascript 54 | address ticketAddress = SINGLE_RANDOM_WINNER.ticket(); 55 | ``` 56 | 57 | **JavaScript** 58 | 59 | ```javascript 60 | const strategyAddress = await daiPrizePool.prizeStrategy() 61 | 62 | const singleRandomWinner = new ethers.Contract( 63 | strategyAddress, 64 | SINGLE_RANDOM_WINNER_ABI, 65 | signer 66 | ) 67 | 68 | const ticketAddress = await singleRandomWinner.ticket() 69 | ``` 70 | 71 | Now let's deposit to mint tickets for ourselves: 72 | 73 | **Solidity** 74 | 75 | ```javascript 76 | CDAI_PRIZE_POOL.depositTo( 77 | MY_ADDRESS, 78 | 1 ether, 79 | ticketAddress, 80 | address(0) 81 | ); 82 | ``` 83 | 84 | **JavaScript** 85 | 86 | ```javascript 87 | await daiPrizePool.depositTo( 88 | signer._address, 89 | ethers.utils.parseEther(1), 90 | ticketAddress, 91 | ethers.constants.AddressZero 92 | ) 93 | ``` 94 | 95 | ## Depositing Sponsorship 96 | 97 | If you wish to deposit and receive sponsorship, or any other controlled token, you simply need to pass it in as the `controlledToken` argument. 98 | 99 | ## Depositing for Someone Else 100 | 101 | If you'd like to deposit on someone else's behalf, you can simply change the `to` address in the call to whomever you want to receive the tickets. Note that the caller will not be able to withdraw the funds; those funds can only be withdrawn by the recipient unless they increase your allowance. 102 | 103 | ## Capturing Referral Rewards 104 | 105 | The last parameter to the depositTo function is the referral address. The protocol may drip referral awards globally to Prize Pools. Referrals can earn tokens based on the fraction of referral volume they supply. 106 | 107 | Any interface for a PrizePool will want to pass it's own address as the referrer so that it can capture sweet, sweet rewards. 108 | 109 | For more information see [Rewards]() 110 | 111 | -------------------------------------------------------------------------------- /protocol/prize-strategy.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Customize how a Prize Pool distributes prizes 3 | --- 4 | 5 | # 💸 Prize Strategies 6 | 7 | A Prize Strategy handles prize distribution for a [Prize Pool](prize-pool/). When a Prize Pool is constructed it is configured with a Prize Strategy. 8 | 9 | The Prize Strategy created by the [Compound Prize Pool Builder](builders/) is upgradeable by the governor contract. 10 | 11 | ## Prize Strategy Interface 12 | 13 | ### Calculating the Early Exit Fee 14 | 15 | When a user wishes to redeem their tickets instantly, the prize pool will ask the strategy what the fairness fee should be. 16 | 17 | The function signature is: 18 | 19 | ```javascript 20 | function calculateInstantWithdrawalFee( 21 | address from, 22 | uint256 amount, 23 | address controlledToken 24 | ) 25 | external 26 | returns (uint256 remainingFee, uint256 burnedCredit) 27 | ``` 28 | 29 | The strategy will be given the user, the number of tickets they are attempting to withdraw, and is expected to return the fee amount. 30 | 31 | ### Calculating the Withdrawal Timelock 32 | 33 | When a user wishes to redeem their tickets without paying any fees, then prize pool will ask the strategy what the unlock timestamp is: 34 | 35 | ```javascript 36 | function calculateTimelockDurationAndFee( 37 | address from, 38 | uint256 amount, 39 | address controlledToken 40 | ) 41 | external 42 | returns (uint256 durationSeconds, uint256 burnedCredit) 43 | ``` 44 | 45 | The strategy will be passed the user and the amount of tickets and be expected to return the timestamp after which the user may withdraw. 46 | 47 | ## Prize Strategy Privileges 48 | 49 | Only the Prize Strategy is able to award prizes on its associated [Prize Pool](prize-pool/). See [Awarding Prizes](prize-pool/#awarding-prizes). 50 | 51 | ## Awarding Prizes 52 | 53 | Prizes are awarded in two phases. The first phase locks the Pool and requests a random number from the Random Number Generation service. The second phase retrieves the random number, award the prize, and unlocks the pool. 54 | 55 | The first phase of the award process begins by calling: 56 | 57 | ```javascript 58 | function startAward() external 59 | ``` 60 | 61 | **startAward\(\)** will: 62 | 63 | * Lock the pool: minting and redemption not allowed. 64 | * Request a random number from the RNG service 65 | 66 | Once the random number is available, a user can call: 67 | 68 | ```javascript 69 | function completeAward() external 70 | ``` 71 | 72 | **completeAward\(\)** will: 73 | 74 | * Unlock the pool 75 | * Disburse the prize 76 | * Start the new prize 77 | 78 | Both startAward\(\) and completeAward\(\) have functions to check whether they can be called: 79 | 80 | ```javascript 81 | function canStartAward() external view returns (bool); 82 | ``` 83 | 84 | and 85 | 86 | ```javascript 87 | function canCompleteAward() external view returns (bool); 88 | ``` 89 | 90 | ## Reporting 91 | 92 | ### Current Prize 93 | 94 | To retrieve amount of accrued prize interest so far you may call: 95 | 96 | ```javascript 97 | function currentPrize() external returns (uint256); 98 | ``` 99 | 100 | ### Estimating Prize 101 | 102 | To estimate what the prize will be you can call: 103 | 104 | ```javascript 105 | function estimatePrize() external returns (uint256); 106 | ``` 107 | 108 | ### Time 109 | 110 | To retrieve when the current prize started: 111 | 112 | ```javascript 113 | function prizePeriodStartedAt() external view returns (uint256) 114 | ``` 115 | 116 | To retrieve when the prize will end: 117 | 118 | ```javascript 119 | function prizePeriodEndAt() external view returns (uint256) 120 | ``` 121 | 122 | ## Miscellaneous 123 | 124 | | Function | Description | 125 | | :--- | :--- | 126 | | function sponsorship\(\) returns \([ERC20](https://eips.ethereum.org/EIPS/eip-20)\) | Returns the address of the sponsorship token. | 127 | | function ticket\(\) returns \([Ticket](prize-pool/ticket.md)\) | Returns the address of the ticket token. | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /tutorials/withdrawing-from-a-prize-pool.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: How to Withdraw Funds from a Prize Pool 3 | --- 4 | 5 | # Withdraw from a Prize Pool 6 | 7 | When it comes time to withdraw funds from a Prize Pool you can withdraw funds for yourself, or for others if they have given you an allowance. 8 | 9 | If the funds have [**matured**](https://docs.pooltogether.com/protocol/fairness#credit), then they can be withdrawn fully and instantly. If they haven't, then the funds will first enter a timelock before being unlocked and available to sweep back to the user. Alternatively, you can "pay off" the timelock and forfeit a small fee that goes to the prize. 10 | 11 | Let's see how these two approaches work. 12 | 13 | ## Withdrawing Your Funds With a Timelock 14 | 15 | Assuming you have previously [deposited into a Prize Pool](buying-tickets.md), let's see how to withdraw funds. 16 | 17 | The primary means of withdrawal is using a timelock. Most people will have accrued enough credit such that the timelock duration will be zero, but they still have to call the same function. 18 | 19 | **Solidity** 20 | 21 | ```javascript 22 | uint256 unlockTimestamp = PRIZE_POOL.withdrawWithTimelockFrom( 23 | fromAddress, 24 | amount, 25 | controlledToken 26 | ); 27 | ``` 28 | 29 | * **fromAddress** is the address from which we are withdrawing funds. This address must have a balance of the **controlledToken** greater than the given **amount**. This address must either be the caller, or the caller must an allowance greater than or equal to the amount for the given controlledToken. 30 | * **amount** is the amount of controlledTokens to burn in exchange for funds. Pool tokens are exchanged 1:1 with the underlying asset supplied to [depositTo\(\)](buying-tickets.md) 31 | * **controlledToken** is the address of the pool token you wish to burn. 32 | 33 | **JavaScript** 34 | 35 | ```javascript 36 | const ethers = require('ethers') 37 | const signer = ethers.Wallet.createRandom() 38 | 39 | const daiPrizePool = new ethers.Contract( 40 | DAI_PRIZE_POOL_ADDRESS, 41 | PRIZE_POOL_ABI, 42 | signer 43 | ) 44 | 45 | const controlledTokens = await daiPrizePool.tokens(); 46 | 47 | await daiPrizePool.withdrawWithTimelockFrom( 48 | signer._address, 49 | ethers.utils.parseEther('1'), 50 | controlledTokens[0] 51 | ) 52 | 53 | const unlockTimestamp = await daiPrizePool.balanceAvailableAt(signer._address) 54 | ``` 55 | 56 | ## Withdrawing Funds Instantly 57 | 58 | If the user doesn't have sufficient [credit](../protocol/prize-pool/fairness.md#credit) to cover the timelock they can "pay off" the timelock by contributing to the prize. 59 | 60 | **Solidity** 61 | 62 | ```javascript 63 | uint256 exitFee = PRIZE_POOL.calculateEarlyExitFee( 64 | fromAddress, 65 | controlledToken, 66 | amount 67 | ); 68 | 69 | uint256 actualExitFee = PRIZE_POOL.withdrawInstantlyFrom( 70 | fromAddress, 71 | amount, 72 | controlledToken, 73 | exitFee 74 | ); 75 | ``` 76 | 77 | * **from** is the address to withdraw from. This must be the caller, or the caller must have receive prior approval via the controlled token. 78 | * **amount** is the amount of controlled token to burn. 79 | * **controlledToken** is the pool token the user wishes to burn in exchange for assets. 80 | * **maximumExitFee** allows the caller to declare the maximum fee that is taken from their withdrawal amount. 81 | 82 | This function returns the actual exit fee that was consumed. The exit fee is taken from the withdrawal amount. For example, if the user is withdrawing 100 Dai and the exit fee is 10 Dai, then they will burn 100 pool tokens and be transferred 90 Dai. 83 | 84 | **JavaScript** 85 | 86 | ```javascript 87 | const ethers = require('ethers') 88 | const signer = ethers.Wallet.createRandom() 89 | 90 | const daiPrizePool = new ethers.Contract( 91 | DAI_PRIZE_POOL_ADDRESS, 92 | PRIZE_POOL_ABI, 93 | signer 94 | ) 95 | 96 | const controlledTokens = await daiPrizePool.tokens(); 97 | 98 | const exitFee = await daiPrizePool.callStatic.calculateEarlyExitFee( 99 | controlledTokens[0], 100 | ethers.utils.parseEther('1') 101 | ) 102 | 103 | await daiPrizePool.withdrawInstantlyFrom( 104 | signer._address, 105 | ethers.utils.parseEther('1'), 106 | controlledTokens[0], 107 | exitFee 108 | ) 109 | 110 | ``` 111 | 112 | ## Getting Approval 113 | 114 | Both methods of withdrawal allow users to withdraw on behalf of others, as long as they have sufficient ERC20 allowance for the user and controlled token in question. 115 | 116 | The funds will be transferred back to the original user, not to the caller. 117 | 118 | -------------------------------------------------------------------------------- /tutorials/operate-a-prize-pool.md: -------------------------------------------------------------------------------- 1 | # 🎛️ Operate a Prize Pool 2 | 3 | Now that you've created a Prize Pool, it is time to learn what goes into operating a Prize Pool. Viewing details about any Prize Pool is made easy with the [PoolTogether Reference App](https://reference-app.pooltogether.com/), which displays all the relevant information about any Pool. An example Prize Pool on a testnet can be viewed [here](https://reference-app.pooltogether.com/pools/rinkeby/0x4706856FA8Bb747D50b4EF8547FE51Ab5Edc4Ac2/manage). 4 | 5 | ## Owner Privileges 6 | 7 | When a Prize Pool is created with the Builder, the address that sent the transaction is the owner of both the Prize Pool and default Prize Strategy. These permissions allow the owner to change certain parameters in both the Pool and Strategy contracts once they have been deployed. 8 | 9 | Owner privileges include, but are not limited to: 10 | 11 | * Managing Awards 12 | * Changing the number of winners 13 | * Updating fairness parameters 14 | * Switching to a Custom Prize Strategy 15 | * Transferring ownership to another address 16 | 17 | The [Reference App](https://reference-app.pooltogether.com) has an Admin dashboard which allows for easy access for managing the Prize Pool. Click manage pool and then select the Admin tab to view the different actions. 18 | 19 | ![The admin section of the reference app](https://lh5.googleusercontent.com/pwbhcXJGY5nOFo6jFV76sb1ndeEtzK_1yU26nsLmx-g_1rBYPXaPTZmDPwlge7_-1SWCso_58X2hY-sA4XOUSBC1dj8iatopwarEVueOEZNAM6agpFEdiMnoGHL6WkG_CQeopbS_) 20 | 21 | ## Awarding a Prize 22 | 23 | The default [Multiple Winners](../protocol/prize-strategy/multiple-winners.md) prize strategy awards prizes periodically; the prize can be awarded as soon as the prize period has elapsed. The prize is awarded into two phases: 24 | 25 | 1. **Start Award**: when the prize period has elapsed, anyone can execute the start award transaction. This will cause the prize strategy to make a request for a new random number from the RNG service. The **prize strategy** must have enough tokens to pay for the RNG request if payment is required. 26 | 2. **Complete Award**: when the RNG is ready with the random number, the complete award transaction can be executed by anyone. The prize strategy will use the random number to pick the winners, and start a new prize period. 27 | 28 | ## Awards 29 | 30 | Both the Compound Prize Pool and Stake Prize Pool will award the stake token as a prize if there is excess balance. Excess balance may be due to interest accruing, or from users leaving prematurely and paying the early exit fee. 31 | 32 | Prize Pools can also award any other ERC20 or ERC721 tokens as prizes. These other tokens must be whitelisted in the [Multiple Winners](../protocol/prize-strategy/multiple-winners.md) strategy in order to be awarded. 33 | 34 | * When an ERC20 is whitelisted, the Multiple Winners strategy will award the token as a prize if the **prize pool** has a non-zero balance. 35 | * When an ERC721 token is whitelisted, the Multiple Winners strategy will award the token as a prize to the **first** winner \(if there are multiple winners\). 36 | 37 | ![](https://lh4.googleusercontent.com/9zDHD61JvChxW-CxFQwGQOBp82-NLSo1RHAf2xQ1rrAF4MMFTOuupz63eID8iCKYeyDrWifgknvBfmMkl4wffYOcGNQz-KRk4HI2jsP5UWAGiBXNtF5aekzUzYI2Oqa314qQYvvp) 38 | 39 | ## Number of Winners 40 | 41 | The owner has the ability to increase or decrease the number of winners. The default behaviour of multiple winners differs between the Compound Prize Pool and the Stake Prize Pool: 42 | 43 | **Compound Prize Pool** 44 | 45 | * Any accrued stake tokens are distributed evenly among the winners 46 | * All other ERC20 and ERC721 tokens are given to the first winner 47 | 48 | **Stake Prize Pool** 49 | 50 | * Any accrued stake tokens are distributed evenly among the winners 51 | * All other ERC20 tokens are split evenly among the winners 52 | * All ERC721 tokens go to the first winner. 53 | 54 | ![](https://lh3.googleusercontent.com/Cb0hAWnHo7KA-Ww4zXhj2SP2DIk4PUhjEL3yloFL2xGKmtqnTtY_PwC0L-Hnm7zz2MS_i4mvCXIeUgl_G42xm1WPsBqsJNLe_rnJWQ1JrIU8tf4LvFG98y52gsGDLWFqjwd8g3yq) 55 | 56 | ## Fairness 57 | 58 | The early exit fee must be paid when a user withdraws from a Prize Pool. The fee is a percentage of the withdrawal amount that they must contribute to the prize. However, the fee will decay to zero over time so that users can withdraw without paying any fee. 59 | 60 | Here we can see the early exit fee is 1% and the decay time is 14 days. If a user deposits 100 Dai then immediately withdraws, they will need to contribute 1 Dai to the prize. However, if the user waits 14 days after depositing then they can withdraw their full amount. 61 | 62 | **Please view the** [**Fairness**](https://docs.pooltogether.com/protocol/prize-pool/fairness) **section of the documentation for more details.** 63 | 64 | ![](../.gitbook/assets/screen-shot-2021-01-06-at-9.34.16-am.png) 65 | 66 | -------------------------------------------------------------------------------- /protocol/prize-pool/fairness.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: How Prize Pools Ensure Fair Play 3 | --- 4 | 5 | # ⚖️ Fairness 6 | 7 | When users play a game they want it to be fair. In PoolTogether, this means that everyone has contributed the same amount of interest to prizes they are eligible to win. Interest accrues over time, so the Prize Pool needs to measure and enforce the time that funds are held. Without this mechanism, it would be very easy to game the system by depositing right before a prize, having a chance to win, and withdrawing right after. 8 | 9 | Prize Pools measure the duration of time funds are held by accruing **credit** for each user at the **credit rate**. The longer a user holds tokens, the more credit they accrue. 10 | 11 | Prize Pools enforce the duration of time funds are held by setting a **credit limit**. Once a credit limit is reached a user can withdraw instantly with no loss. If the credit limit has not been reached the user can either use a withdrawal **timelock** or pay an early exit contribution to the prize. 12 | 13 | ## Credit 14 | 15 | After a user deposits funds they begin to accrue credit according to the credit rate. The credit rate is expressed in tokens per second. 16 | 17 | For example: if the user deposits 100 DAI and the credit rate is 0.1, then they will have accrued 1 DAI in credit after 10 seconds. Note that they cannot withdraw the 1 DAI credit; it's simply a measure of their contribution. 18 | 19 | Users will accrue credit up until the **credit limit**. The credit limit is a fraction, so a users credit limit is that fraction of their entire balance. For example, if the user holds 100 DAI and the credit limit is 0.1, then they will accrue a maximum of 10 DAI in credit. 20 | 21 | Once a deposit has accrued maximum credit, it is considered **matured**. 22 | 23 | ## Timelock 24 | 25 | A deposit can be withdrawn instantly from the Prize Pool if it has matured. Otherwise, upon withdrawal the deposit will be timelocked until it has matured, at which point the funds can be swept back to the user by anyone. 26 | 27 | The timelock duration is calculated based on the spare credit the user has. The spare credit for a withdrawal is their credit balance _less the credit limit for their remaining balance of tokens_. For example: let's say a user has 100 DAI and is attempting to withdraw 10 DAI. They currently have 9 DAI in credit and the credit limit is 0.1. The user's spare credit is 9 - \(100 - 10\) \* 0.1 = 0 DAI. If the user was instead withdrawing 50 DAI, then they would have 9 - \(100 - 50\) \* 0.1 = 4 DAI in spare credit. 28 | 29 | The duration of the timelock is the time it takes for the withdrawal to mature less the spare credit. For example: if the withdrawal amount is 100 DAI, and the user has 5 DAI in spare credit, and the credit rate is 0.1, then the timelock will be \(\(100 \* 0.1\) - 5\) / 0.1 = 50 seconds. The spare credit is burned and the funds are placed in a timelock that can be swept after the duration has elapsed. 30 | 31 | ### Paying Off the Timelock 32 | 33 | It's possible for a user to withdraw their funds instantly. Instead of a timelock, the Prize Pool will capture the remaining contribution directly from the withdrawal amount. The user will receive the withdrawal amount less the remaining contribution. Their spare credit will be burned. We call this an **instant withdrawal.** 34 | 35 | ## What should the credit rate and credit limit for a pool be? 36 | 37 | In principal, we want the timelock to be as short as possible and most users should never encounter it. We are trying to prevent abuse of the system by a small subset of users while keeping the smoothest experience for the majority of users. 38 | 39 | At first glance the credit limit should simply be equal to the amount of interest a deposit would contribute over a prize period. But there are several factors that can change the cost / benefit balance for depositors, specifically: 40 | 41 | * Any subsidies to the prize \(whether through sponsored deposits or direct additions\) 42 | * Any rewards given to deposits through the token drips 43 | * Fluctuations in the yield rate 44 | * Total amount of outstanding tickets for a given prize 45 | * Gas fees of entering and exiting the pool 46 | 47 | To find the ideal credit limit it is best to estimate the **effective APR** a pool is offering. 48 | 49 | **Example** 50 | 51 | Assume a pool has a yield source that returns 5% APR. The pool awards prizes weekly, which means that each week approximately 5% / 52 = 0.096% accrues. A fair credit limit could be 0.1%: if the user decides to game the prize, they will need to contribute 0.1% of their deposit. 52 | 53 | However, we want users who have been in the pool since the beginning to not have to pay anything. Let's say we wish for users to accrue 0.1% credit per week, so that they can withdraw losslessly. The credit rate is applied per second and does not compound, so we can calculate the credit rate as the credit limit / seconds in a week, or 0.1% / 86400 = 0.0000011574074074074074. 54 | 55 | This means that users will need to stay in the pool for a week, otherwise they'll need to pay an early exit fee of 0.1%. Note, however, that this fee diminishes over time. 56 | 57 | -------------------------------------------------------------------------------- /protocol/prize-strategy/multiple-winners.md: -------------------------------------------------------------------------------- 1 | # 🤑 Multiple Winners 2 | 3 | The Multiple Winners prize strategy periodically selects a predefined number of winners and awards to them an equal share of the prizes available in the Prize Pool. 4 | 5 | ## Initialization 6 | 7 | A Multiple Winners prize strategy is initialized with: 8 | 9 | **Prize Period Start:** the timestamp at which the prize period should start 10 | 11 | **Prize Period Seconds**: the duration of time between prizes 12 | 13 | **PrizePool Address**: the address of the [prize pool](../prize-pool/) that implements the pool functionality such as deposit and withdraw 14 | 15 | [**Ticket**](../tokens/ticket.md)**:** The interface to use to select winners 16 | 17 | \*\*\*\*[**Sponsorship**](../tokens/sponsorship.md)**:** The token that represents sponsorship 18 | 19 | \*\*\*\*[**Random Number Generator**](../random-number-generator/): used to generate random numbers for winner selection 20 | 21 | **Number of Winners**: the number of winners in a prize period. This can be later changed by the owner calling set number of winners. 22 | 23 | ## Strategy Settings 24 | 25 | ### Set Number of Winners 26 | 27 | The number of winners in a prize period can be set by calling: 28 | 29 | ```javascript 30 | function setNumberOfWinners(uint256 count) 31 | external onlyOwner requireAwardNotInProgress 32 | ``` 33 | 34 | * Requires that the Award process is not in progress 35 | * `count` must be greater than 0 36 | 37 | ### View Number of Winners 38 | 39 | The number of winners setting can be viewed by calling: 40 | 41 | ```javascript 42 | function numberOfWinners() external view returns (uint256 43 | ``` 44 | 45 | ### Set Split External ERC-20 Awards 46 | 47 | The `SplitExternalErc20Awards` flag can be set by calling: 48 | 49 | ```javascript 50 | function setSplitExternalErc20Awards(bool _splitExternalErc20Awards) 51 | external onlyOwner requireAwardNotInProgress 52 | ``` 53 | 54 | This controls how externally added ERC-20's are distributed. Setting to `true` results in the ERC-20's paid out uniformly \(similar to the main prize\), while `false` does not pay out external ERC-20's for that award. 55 | 56 | ### Set Random Number Generation Service 57 | 58 | The [Random Number Generation](../random-number-generator/) Service can be set when the award process has not started by the Prize Pool owner by calling: 59 | 60 | ```javascript 61 | function setRngService(RNGInterface rngService) 62 | external onlyOwner requireAwardNotInProgress 63 | ``` 64 | 65 | ### Set Random Number Generator Request Timeout 66 | 67 | The RNG request timeout parameter can be set \(in seconds\) when the award process has not started by the Prize Pool owner by calling: 68 | 69 | ```javascript 70 | function setRngRequestTimeout(uint32 _rngRequestTimeout) 71 | external onlyOwner requireAwardNotInProgress { 72 | ``` 73 | 74 | ## Prize Period Information 75 | 76 | #### View if the Prize Period is Over 77 | 78 | To check if the prize period is finished call: 79 | 80 | ```javascript 81 | function isPrizePeriodOver() external view returns (bool) 82 | ``` 83 | 84 | Returns `true` if the prize period is over, `false` otherwise. 85 | 86 | #### View when the Prize Period Finishes 87 | 88 | To check the unix time when the prize period ends call: 89 | 90 | ```javascript 91 | function prizePeriodEndAt() external view returns (uint256) 92 | ``` 93 | 94 | #### View Estimate of Number of Blocks to Prize Block 95 | 96 | To estimate the remaining blocks until the prize given a number of seconds per block call `estimateRemainingBlocksToPrize` with `secondPerBlockMantissa` set to 15 seconds for Ethereum mainnet: 97 | 98 | ```javascript 99 | function estimateRemainingBlocksToPrize(uint256 secondsPerBlockMantissa) public 100 | view returns (uint256) 101 | ``` 102 | 103 | #### View Prize Period Remaining Time \(in seconds\) 104 | 105 | To get the number of seconds remaining until the prize can be awarded call: 106 | 107 | ```javascript 108 | function prizePeriodRemainingSeconds() external view returns (uint256) 109 | ``` 110 | 111 | #### View Next Prize Period Start Time 112 | 113 | To get the Unix timestamp of when the next prize period will start call `calculateNextPrizePeriodStartTime` with `currentTime` set to the current Unix time: 114 | 115 | ```javascript 116 | function calculateNextPrizePeriodStartTime(uint256 currentTime) 117 | external view returns (uint256) 118 | ``` 119 | 120 | ## Award Process 121 | 122 | At the end of the prize period, anyone can begin the award process. This happens in two main stages - `startAward` and `completeAward`. `startAward` triggers the configured Random Number Generator request, which will take some blocks. `completeAward` can then be called, which selects the winners using the RNG result and pushes the tokens out to the winners. 123 | 124 | ### Start Award 125 | 126 | The award process can be started by calling `startAward`. This function starts the award process by starting the configured random number request. The prize period must have ended. The RNG-Request-Fee is expected to be held within this contract before calling this function. 127 | 128 | ```text 129 | function startAward() external requireCanStartAward 130 | ``` 131 | 132 | Upon completion this function fires the following event: 133 | 134 | ```csharp 135 | event PrizePoolAwardStarted( 136 | address indexed operator, 137 | address indexed prizePool, 138 | uint32 indexed rngRequestId, 139 | uint32 rngLockBlock 140 | ); 141 | ``` 142 | 143 | ### Complete Award 144 | 145 | The award process can be finished by calling `completeAward`. The random number must have been requested and now available \(is can be checked by calling `isRngCompleted()`\). 146 | 147 | ```javascript 148 | function completeAward() external requireCanCompleteAward 149 | ``` 150 | 151 | This function fires two events upon completion: 152 | 153 | ```csharp 154 | event PrizePoolAwarded( 155 | address indexed operator, 156 | uint256 randomNumber 157 | ); 158 | ``` 159 | 160 | Since Prize Pools are continuously rolling the next prize period is now open: 161 | 162 | ```csharp 163 | event PrizePoolOpened( 164 | address indexed operator, 165 | uint256 indexed prizePeriodStartedAt 166 | ); 167 | ``` 168 | 169 | ### Cancel Award 170 | 171 | This function can be called by anyone to unlock the tickets if the RNG has timed out: 172 | 173 | ```javascript 174 | function cancelAward() public 175 | ``` 176 | 177 | This function will fire the event: 178 | 179 | ```csharp 180 | event PrizePoolAwardCancelled( 181 | address indexed operator, 182 | address indexed prizePool, 183 | uint32 indexed rngRequestId, 184 | uint32 rngLockBlock 185 | ); 186 | ``` 187 | 188 | ### Listeners 189 | 190 | A prize strategy can have both a [token listener](https://github.com/pooltogether/pooltogether-pool-contracts/blob/master/contracts/token/TokenListener.sol) and a periodic prize strategy listener in order execute code for certain callbacks \(event hooks\). 191 | 192 | #### Set Token Listener 193 | 194 | The token listener can be set by the prize pool owner when the award process is not in progress by calling `setTokenListener` with the address of the new `tokenList` : 195 | 196 | ```javascript 197 | function setTokenListener(TokenListenerInterface _tokenListener) 198 | external onlyOwner requireAwardNotInProgress 199 | ``` 200 | 201 | #### Set Periodic Prize Strategy Listener 202 | 203 | The periodic prize strategy listener can be set by the prize pool owner when the award process is not in progress by calling `setPeriodicPrizeStrategyListener` with the address of the new `PeriodicPrizeStrategyListener`: 204 | 205 | ```javascript 206 | function setPeriodicPrizeStrategyListener(PeriodicPrizeStrategyListenerInterface _periodicPrizeStrategyListener) 207 | external onlyOwner requireAwardNotInProgress 208 | ``` 209 | 210 | This function will ensure the Listener Interface is implementing using ERC-165 introspection, and upon completion fire the following event: 211 | 212 | ```csharp 213 | event PeriodicPrizeStrategyListenerSet( 214 | PeriodicPrizeStrategyListenerInterface indexed periodicPrizeStrategyListener 215 | ); 216 | ``` 217 | 218 | ### External ERC20 and ERC721 Awards 219 | 220 | External awards can be added to the pool. This is particularly useful in the case of the stake pool. Although still possible for either the token listener or the owner to manually add or remove ERC-20's and ERC-721's, it is recommended to add a single [LootBox](../lootbox.md) per prize period and direct the external awards to this LootBox address. 221 | 222 | The pool owner or the token listener can add/remove ERC721's by calling: 223 | 224 | ```javascript 225 | function addExternalErc721Award(IERC721Upgradeable _externalErc721, 226 | uint256[] calldata _tokenIds) 227 | external onlyOwnerOrListener requireAwardNotInProgress 228 | ``` 229 | 230 | ```javascript 231 | function removeExternalErc721Award( 232 | IERC721Upgradeable _externalErc721, 233 | IERC721Upgradeable _prevExternalErc721) 234 | external onlyOwner requireAwardNotInProgress 235 | ``` 236 | 237 | The pool owner or the token listener can add/remove ERC20's by calling: 238 | 239 | ```javascript 240 | function addExternalErc20Awards(IERC20Upgradeable[] calldata _externalErc20s) 241 | external onlyOwnerOrListener requireAwardNotInProgress 242 | ``` 243 | 244 | ```javascript 245 | function removeExternalErc20Award( 246 | IERC20Upgradeable _externalErc20, 247 | IERC20Upgradeable _prevExternalErc20) 248 | external onlyOwner requireAwardNotInProgress 249 | ``` 250 | 251 | Corresponding events are fired for each ERC type added or removed: 252 | 253 | ```csharp 254 | event ExternalErc721AwardAdded( 255 | IERC721Upgradeable indexed externalErc721, 256 | uint256[] tokenIds 257 | ); 258 | 259 | event ExternalErc20AwardAdded( 260 | IERC20Upgradeable indexed externalErc20 261 | ); 262 | 263 | event ExternalErc721AwardRemoved( 264 | IERC721Upgradeable indexed externalErc721Award 265 | ); 266 | 267 | event ExternalErc20AwardRemoved( 268 | IERC20Upgradeable indexed externalErc20Award 269 | ); 270 | ``` 271 | 272 | -------------------------------------------------------------------------------- /networks.md: -------------------------------------------------------------------------------- 1 | # 📡 Networks 2 | 3 | _This document was generated_ [_automatically_](https://github.com/pooltogether/generate-networks-doc) 4 | 5 | ## Mainnet 6 | 7 | ### PoolTogether Pools & Supporting Contracts 8 | 9 | **@pooltogether/current-pool-data ^3.1.5** [**npm**](https://www.npmjs.com/package/@pooltogether/current-pool-data) 10 | 11 | | Contract | Address | 12 | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | 13 | | Dai Prize Pool | [0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a](https://etherscan.io/address/0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a) | 14 | | Dai Prize Strategy | [0x178969A87a78597d303C47198c66F68E8be67Dc2](https://etherscan.io/address/0x178969A87a78597d303C47198c66F68E8be67Dc2) | 15 | | UNI Prize Pool | [0x0650d780292142835F6ac58dd8E2a336e87b4393](https://etherscan.io/address/0x0650d780292142835F6ac58dd8E2a336e87b4393) | 16 | | UNI Prize Strategy | [0xe8726B85236a489a8E84C56c95790d07a368f913](https://etherscan.io/address/0xe8726B85236a489a8E84C56c95790d07a368f913) | 17 | | USDC Prize Pool | [0xde9ec95d7708b8319ccca4b8bc92c0a3b70bf416](https://etherscan.io/address/0xde9ec95d7708b8319ccca4b8bc92c0a3b70bf416) | 18 | | USDC Prize Strategy | [0x3d9946190907ada8b70381b25c71eb9adf5f9b7b](https://etherscan.io/address/0x3d9946190907ada8b70381b25c71eb9adf5f9b7b) | 19 | | Loot Box ERC721 | [0x4d695c615a7AACf2d7b9C481B66045BB2457Dfde](https://etherscan.io/address/0x4d695c615a7AACf2d7b9C481B66045BB2457Dfde) | 20 | | Loot Box Prize Strategy Listener | [0xfe7205DF55BA42c8801e44B55BF05F06cCe8565E](https://etherscan.io/address/0xfe7205DF55BA42c8801e44B55BF05F06cCe8565E) | 21 | 22 | ### RNG Contracts 23 | 24 | **@pooltogether/pooltogether-rng-contracts ^1.0.0** [**npm**](https://www.npmjs.com/package/@pooltogether/pooltogether-rng-contracts) 25 | 26 | | Contract | Address | Artifact | 27 | | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | 28 | | [RNGBlockhash](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/contracts/RNGBlockhash.sol) | [0xb1D89477d1b505C261bab6e73f08fA834544CD21](https://etherscan.io/address/0xb1D89477d1b505C261bab6e73f08fA834544CD21) | [Artifact](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/deployments/mainnet/RNGBlockhash.json) | 29 | | [RNGChainlink](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/contracts/RNGChainlink.sol) | [0xB2DC5571f477b1C5b36509a71013BFedD9Cc492F](https://etherscan.io/address/0xB2DC5571f477b1C5b36509a71013BFedD9Cc492F) | [Artifact](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/deployments/mainnet/RNGChainlink.json) | 30 | 31 | ### Loot Box Contracts 32 | 33 | **@pooltogether/loot-box ^1.0.0** [**npm**](https://www.npmjs.com/package/@pooltogether/loot-box) 34 | 35 | | Contract | Address | Artifact | 36 | | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | 37 | | [ERC721ControlledFactory](https://github.com/pooltogether/loot-box/tree/main/contracts/ERC721ControlledFactory.sol) | [0x4E869b3A0978fA61DAbd7Da8F9B272AADc745Fb3](https://etherscan.io/address/0x4E869b3A0978fA61DAbd7Da8F9B272AADc745Fb3) | [Artifact](https://github.com/pooltogether/loot-box/tree/main/deployments/mainnet/ERC721ControlledFactory.json) | 38 | | [LootBoxController](https://github.com/pooltogether/loot-box/tree/main/contracts/LootBoxController.sol) | [0x2c2a966b7F5448A36EC9f896088DfB99B21d8A24](https://etherscan.io/address/0x2c2a966b7F5448A36EC9f896088DfB99B21d8A24) | [Artifact](https://github.com/pooltogether/loot-box/tree/main/deployments/mainnet/LootBoxController.json) | 39 | | [LootBoxPrizeStrategyListenerFactory](https://github.com/pooltogether/loot-box/tree/main/contracts/LootBoxPrizeStrategyListenerFactory.sol) | [0x25e6a78D93D2935A638fDbd684e7b39565d0B7eA](https://etherscan.io/address/0x25e6a78D93D2935A638fDbd684e7b39565d0B7eA) | [Artifact](https://github.com/pooltogether/loot-box/tree/main/deployments/mainnet/LootBoxPrizeStrategyListenerFactory.json) | 40 | 41 | ### V2-to-V3 Migration Contract 42 | 43 | **@pooltogether/migrate-v3 ^0.1.3** [**Github**](https://github.com/pooltogether/pooltogether-migrate-v3) 44 | 45 | | Contract | Address | Artifact | 46 | | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | 47 | | [MigrateV2ToV3](https://github.com/pooltogether/pooltogether-migrate-v3/tree/master/contracts/MigrateV2ToV3.sol) | [0x801B4872a635dCCc7E679eEaf04bEf08E562972a](https://etherscan.io/address/0x801B4872a635dCCc7E679eEaf04bEf08E562972a) | [Artifact](https://github.com/pooltogether/pooltogether-migrate-v3/tree/master/deployments/mainnet/MigrateV2ToV3.json) | 48 | 49 | ## Rinkeby 50 | 51 | ### PoolTogether Pools & Supporting Contracts 52 | 53 | **@pooltogether/current-pool-data ^3.1.5** [**npm**](https://www.npmjs.com/package/@pooltogether/current-pool-data) 54 | 55 | | Contract | Address | 56 | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | 57 | | Dai Prize Pool | [0x4706856FA8Bb747D50b4EF8547FE51Ab5Edc4Ac2](https://rinkeby.etherscan.io/address/0x4706856FA8Bb747D50b4EF8547FE51Ab5Edc4Ac2) | 58 | | Dai Prize Strategy | [0x5E0A6d336667EACE5D1b33279B50055604c3E329](https://rinkeby.etherscan.io/address/0x5E0A6d336667EACE5D1b33279B50055604c3E329) | 59 | | USDC Prize Pool | [0xde5275536231eCa2Dd506B9ccD73C028e16a9a32](https://rinkeby.etherscan.io/address/0xde5275536231eCa2Dd506B9ccD73C028e16a9a32) | 60 | | USDC Prize Strategy | [0x1b92BC2F339ef25161711e4EafC31999C005aF21](https://rinkeby.etherscan.io/address/0x1b92BC2F339ef25161711e4EafC31999C005aF21) | 61 | | BAT Prize Pool | [0xab068F220E10eEd899b54F1113dE7E354c9A8eB7](https://rinkeby.etherscan.io/address/0xab068F220E10eEd899b54F1113dE7E354c9A8eB7) | 62 | | BAT Prize Strategy | [0x41CF0758b7Cc2394b1C2dfF6133FEbb0Ef317C3b](https://rinkeby.etherscan.io/address/0x41CF0758b7Cc2394b1C2dfF6133FEbb0Ef317C3b) | 63 | | Loot Box ERC721 | [0xfbC6677806253dB9739d0F6CBD89b9e7Ed4A5c66](https://rinkeby.etherscan.io/address/0xfbC6677806253dB9739d0F6CBD89b9e7Ed4A5c66) | 64 | 65 | ### Core Contracts 66 | 67 | **@pooltogether/pooltogether-contracts ^3.1.0** [**npm**](https://www.npmjs.com/package/@pooltogether/pooltogether-contracts) 68 | 69 | | Contract | Address | Artifact | 70 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | 71 | | [CompoundPrizePoolBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/builders/CompoundPrizePoolBuilder.sol) | [0xA8F32475438733B974CD4F19Ba8f97484EeB95a3](https://rinkeby.etherscan.io/address/0xA8F32475438733B974CD4F19Ba8f97484EeB95a3) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/CompoundPrizePoolBuilder.json) | 72 | | [Comptroller](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/comptroller/Comptroller.sol) | [0xaF00636E7D943a62CCb87E8153c1C97bF657F11D](https://rinkeby.etherscan.io/address/0xaF00636E7D943a62CCb87E8153c1C97bF657F11D) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/Comptroller.json) | 73 | | [ControlledTokenBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/builders/ControlledTokenBuilder.sol) | [0x239fC7c69Ba8079ebEC07156F13a6d78d234Fa6B](https://rinkeby.etherscan.io/address/0x239fC7c69Ba8079ebEC07156F13a6d78d234Fa6B) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/ControlledTokenBuilder.json) | 74 | | [MultipleWinnersBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/builders/MultipleWinnersBuilder.sol) | [0x32e8D4c9d1B711BC958d0Ce8D14b41F77Bb03a64](https://rinkeby.etherscan.io/address/0x32e8D4c9d1B711BC958d0Ce8D14b41F77Bb03a64) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/MultipleWinnersBuilder.json) | 75 | | [PermitAndDepositDai](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/permit/PermitAndDepositDai.sol) | [0x80768c51cDEd011B64A24Ba91b6d4471bB3Da150](https://rinkeby.etherscan.io/address/0x80768c51cDEd011B64A24Ba91b6d4471bB3Da150) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/PermitAndDepositDai.json) | 76 | | [PoolWithMultipleWinnersBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/builders/PoolWithMultipleWinnersBuilder.sol) | [0x47a5ABfAcDebf5af312B034B3b748935A0259136](https://rinkeby.etherscan.io/address/0x47a5ABfAcDebf5af312B034B3b748935A0259136) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/PoolWithMultipleWinnersBuilder.json) | 77 | | [Reserve](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/reserve/Reserve.sol) | [0x10f61a36e1327036E5E416D52ff0f4b5c9EfAAA3](https://rinkeby.etherscan.io/address/0x10f61a36e1327036E5E416D52ff0f4b5c9EfAAA3) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/Reserve.json) | 78 | | ReserveRegistry | [0xAD1C620137FA76f520f9a39daAcD7B008D7d2F2D](https://rinkeby.etherscan.io/address/0xAD1C620137FA76f520f9a39daAcD7B008D7d2F2D) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/ReserveRegistry.json) | 79 | | [StakePrizePoolBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/builders/StakePrizePoolBuilder.sol) | [0xdd4d117723C257CEe402285D3aCF218E9A8236E1](https://rinkeby.etherscan.io/address/0xdd4d117723C257CEe402285D3aCF218E9A8236E1) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/StakePrizePoolBuilder.json) | 80 | | [VaultPrizePoolBuilder](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/contracts/builders/VaultPrizePoolBuilder.sol) | [0xd89a09084555a7D0ABe7B111b1f78DFEdDd638Be](https://rinkeby.etherscan.io/address/0xd89a09084555a7D0ABe7B111b1f78DFEdDd638Be) | [Artifact](https://github.com/pooltogether/pooltogether-pool-contracts/tree/version-3/deployments/rinkeby/VaultPrizePoolBuilder.json) | 81 | 82 | ### RNG Contracts 83 | 84 | **@pooltogether/pooltogether-rng-contracts ^1.0.0** [**npm**](https://www.npmjs.com/package/@pooltogether/pooltogether-rng-contracts) 85 | 86 | | Contract | Address | Artifact | 87 | | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | 88 | | [RNGBlockhash](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/contracts/RNGBlockhash.sol) | [0xA932e74d5263A754Ea04432E5c53658434b0484B](https://rinkeby.etherscan.io/address/0xA932e74d5263A754Ea04432E5c53658434b0484B) | [Artifact](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/deployments/rinkeby/RNGBlockhash.json) | 89 | | [RNGChainlink](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/contracts/RNGChainlink.sol) | [0x11D94431718934868C4339aFc5ea27585F46C99A](https://rinkeby.etherscan.io/address/0x11D94431718934868C4339aFc5ea27585F46C99A) | [Artifact](https://github.com/pooltogether/pooltogether-rng-contracts/tree/master/deployments/rinkeby/RNGChainlink.json) | 90 | 91 | ### Loot Box Contracts 92 | 93 | **@pooltogether/loot-box ^1.0.0** [**npm**](https://www.npmjs.com/package/@pooltogether/loot-box) 94 | 95 | | Contract | Address | Artifact | 96 | | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | 97 | | [ERC721ControlledFactory](https://github.com/pooltogether/loot-box/tree/main/contracts/ERC721ControlledFactory.sol) | [0x1D90F79a8515F63881075Ec2C212e18272aD9E38](https://rinkeby.etherscan.io/address/0x1D90F79a8515F63881075Ec2C212e18272aD9E38) | [Artifact](https://github.com/pooltogether/loot-box/tree/main/deployments/rinkeby/ERC721ControlledFactory.json) | 98 | | [LootBoxController](https://github.com/pooltogether/loot-box/tree/main/contracts/LootBoxController.sol) | [0xb1EAc75da9bc31B078742C5AF9EDe62EFE31299D](https://rinkeby.etherscan.io/address/0xb1EAc75da9bc31B078742C5AF9EDe62EFE31299D) | [Artifact](https://github.com/pooltogether/loot-box/tree/main/deployments/rinkeby/LootBoxController.json) | 99 | | [LootBoxPrizeStrategyListenerFactory](https://github.com/pooltogether/loot-box/tree/main/contracts/LootBoxPrizeStrategyListenerFactory.sol) | [0xadB4D93D84b18b5D82063aCf58b21587c92fdfb5](https://rinkeby.etherscan.io/address/0xadB4D93D84b18b5D82063aCf58b21587c92fdfb5) | [Artifact](https://github.com/pooltogether/loot-box/tree/main/deployments/rinkeby/LootBoxPrizeStrategyListenerFactory.json) | 100 | -------------------------------------------------------------------------------- /protocol/prize-pool/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Pool deposits and award accrued interest periodically as a prize 3 | --- 4 | 5 | # 🏆 Prize Pools 6 | 7 | ## Introduction 8 | 9 | Prize Pools allow funds to be pooled together into a no-loss yield source, such as Compound, and have the yield safely exposed to a separate Prize Strategy. They are the primary way through which users interact with PoolTogether prize games. 10 | 11 | Prize Pools provide controls to the owner so that participation can be made fair. See [Fairness](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/protocol/fairness.md) for more information. 12 | 13 | There is a different type of prize pool for each yield source. For example, if you wish to use Compound you will use the Compound Prize Pool. 14 | 15 | All Prize Pools share the functionality below. 16 | 17 | ## Owner 18 | 19 | When a Prize Pool is created, the creator is set as the pool's "owner". The owner is able to: 20 | 21 | * Add additional pool tokens 22 | * Change the Prize Strategy 23 | * Set the [credit rate and credit limit](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/protocol/fairness.md#credit) 24 | * Shutdown the prize pool 25 | * Transfer ownership 26 | * Renounce ownership 27 | 28 | **The prize pool is not upgradeable and therefore the owner can never seize the funds deposited into the prize pool** 29 | 30 | ## Limits 31 | 32 | When a Prize Pool is created it is initialized with some hard-coded limits to protect users. See [Fairness](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/protocol/fairness.md) for more details. 33 | 34 | ### **Maximum Timelock Duration** 35 | 36 | The maximum timelock duration ensures that a user has to wait at most X amount of time to withdraw their funds loss-lessly. If the owner of a pool sets the credit rate to be way too low, this limit ensures users will still be able to withdraw. 37 | 38 | If using the Single Random Winner Prize Strategy, it would make sense to set the maximum timelock duration to 2x the prize period. That way the owner has some flexibility when adjusting the credit limit and credit rate. 39 | 40 | ### **Maximum Credit Limit** 41 | 42 | The maximum credit limit ensures that the credit limit cannot be set higher than this number. This prevents the owner of the Prize Pool from capturing \*all\* of a user's deposit at withdrawal time. 43 | 44 | ### **Maximum Liquidity Limit** 45 | 46 | The maximum liquidity limit allows the PrizePool owner to set a cap on the amount of liquidity the pool can hold. This can be set by calling: 47 | 48 | ```javascript 49 | function setLiquidityCap(uint256 _liquidityCap) external override onlyOwner 50 | ``` 51 | 52 | ## Token Model 53 | 54 | A Prize Pool accepts a single type of ERC20 token for deposits. This token depends on the implementation: for a Compound Prize Pool bound to cDai it will be Dai, for a yEarn yUSDC vault it will be USDC. This is the underlying **asset** of the Prize Pool. 55 | 56 | Prize Pools use **Controlled Tokens** for their internal accounting. These tokens are minted when depositing or awarding prizes. Controlled Tokens are burned when users withdraw. They are exchanged at a ratio of 1:1 to the asset. 57 | 58 | The tokens associated with a PrizePool can be seen by calling: 59 | 60 | ```javascript 61 | function tokens() external override view returns (address[] memory) 62 | ``` 63 | 64 | ### Controlled Tokens 65 | 66 | A Controlled Token is a standard ERC20 that is bound to a **Token Controller**. 67 | 68 | The Token Controller has the privileged ability to mint and burn tokens on user's behalf, and has a callback that listens for token transfers. Controlled Tokens are expected to trigger this callback on any mint, burns or transfers. 69 | 70 | The Prize Pool must be the Token Controller for the controlled tokens that it is initialized with at construction. 71 | 72 | The default [Compound Prize Pool Builder](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/builders/) creates a Ticket controlled token and a [Sponsorship](../tokens/sponsorship.md) controlled token. 73 | 74 | A Controlled Token can added by the PrizePool owner by calling: 75 | 76 | ```javascript 77 | function addControlledToken(ControlledTokenInterface _controlledToken) 78 | external override onlyOwner 79 | ``` 80 | 81 | ### Minting 82 | 83 | When a user deposits into a Prize Pool they must request what type of controlled token they receive in exchange. This token will be minted to them at an exchange rate of 1:1 for the asset. 84 | 85 | ### Burning 86 | 87 | When a user wishes to withdraw from a Prize Pool they must burn controlled tokens. 88 | 89 | ## Depositing 90 | 91 | Users can deposit into the Prize Pool using the **depositTo** function. A user is instantly minted tokens upon deposit. 92 | 93 | ```javascript 94 | function depositTo( 95 | address to, 96 | uint256 amount, 97 | address controlledToken, 98 | address referrer 99 | ) external; 100 | ``` 101 | 102 | | Parameter | Description | 103 | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 104 | | to | The address to whom the controlled tokens should be minted | 105 | | amount | The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. | 106 | | controlledToken | The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. | 107 | | referrer | The address that should receive [referral awards](https://app.gitbook.com/s/governance/untitled.md#referral-volume-drips), if any. | 108 | 109 | Depositing fires the event: 110 | 111 | ```javascript 112 | event Deposited( 113 | address indexed operator, 114 | address indexed to, 115 | address indexed token, 116 | uint256 amount 117 | ); 118 | ``` 119 | 120 | | Event Data | Description | 121 | | ---------- | --------------------------------------------------------------------------------------------- | 122 | | operator | The caller that made the deposit | 123 | | to | The address that received the minted tokens | 124 | | token | The address of the controlled token that was minted | 125 | | amount | The amount of both the underlying asset that was transferred and the tokens that were minted. | 126 | 127 | ### Depositing Using Timelocked Funds 128 | 129 | If a user wishes to re-deposit their timelocked funds, they can do so using this function: 130 | 131 | ```javascript 132 | function timelockDepositTo( 133 | address to, 134 | uint256 amount, 135 | address controlledToken 136 | ) external; 137 | ``` 138 | 139 | | Parameter | Description | 140 | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 141 | | to | The address to whom the controlled tokens should be minted | 142 | | amount | The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. | 143 | | controlledToken | The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. | 144 | 145 | ## Withdrawing 146 | 147 | When a user withdraws they may need to contribute to the prize according to the [fairness rules](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/protocol/fairness.md). They may either cover the contribution by time-locking their funds, or cover the contribution explicitly using funds. 148 | 149 | ### **Withdraw with Timelock** 150 | 151 | Funds can be withdrawn losslessly by time-locking the funds. The withdrawal amount will be unlocked at a later date at which point the funds can be swept back to the user. The timelock duration is calculated based on the users accrued credit, the credit rate, and the fairness fee. 152 | 153 | If the user has sufficient credit, the unlockTimestamp may be "now" and the funds are instantly swept to the `from` address. 154 | 155 | Tip: You can call this function in a constant way to see when the users funds will be unlocked. 156 | 157 | To start a lossless withdrawal a user may call: 158 | 159 | ```javascript 160 | function withdrawWithTimelockFrom( 161 | address from, 162 | uint256 amount, 163 | address controlledToken 164 | ) external returns (uint256 unlockTimestamp); 165 | ``` 166 | 167 | | Parameter Name | Parameter Description | 168 | | --------------- | -------------------------------------------------------------------------------------------------------------------------------- | 169 | | from | The user from whom to withdraw. This means you may withdraw on another user's behalf if they have given you an ERC20 allowance. | 170 | | amount | The amount of collateral to withdraw. | 171 | | controlledToken | The type of controlled token to withdraw. | 172 | 173 | ### Checking Timelock Balances 174 | 175 | To see how many funds have been timelocked for a `user` call: 176 | 177 | ```javascript 178 | function timelockBalanceOf(address user) external view returns (uint256) 179 | ``` 180 | 181 | After funds have been time-locked, you can see at what timestamp they'll be available: 182 | 183 | ```javascript 184 | function timelockBalanceAvailableAt(address user) external view returns (uint256) 185 | ``` 186 | 187 | ### Checking Timelock Duration 188 | 189 | To calculate a timelocked withdrawal duration and credit consumption call: 190 | 191 | ```javascript 192 | function calculateTimelockDuration(address from, address controlledToken, uint256 amount) 193 | external override returns (uint256 durationSeconds,uint256 burnedCredit) 194 | ``` 195 | 196 | | Parameter Name | Description | 197 | | --------------- | ----------------------------------------- | 198 | | from | The user who is withdrawing. | 199 | | amount | The amount the user is withdrawing. | 200 | | controlledToken | The type of controlled token to withdraw. | 201 | 202 | **returns**: 203 | 204 | | Returned Parameter Name | Description | 205 | | ----------------------- | ----------------------------------------- | 206 | | `durationSeconds` | The duration of the timelock in seconds | 207 | | `burned` | The amount of credit that would be burned | 208 | 209 | ### Estimating Credit Accrual Time 210 | 211 | Similarly it is also possible to calculate how long a user must keep their funds in the pool: 212 | 213 | ```javascript 214 | function estimateCreditAccrualTime(address _controlledToken, 215 | uint256 _principal, 216 | uint256 _interest) 217 | external override view returns (uint256 durationSeconds) 218 | ``` 219 | 220 | | Parameter Name | Parameter Description | 221 | | ----------------- | --------------------------------------------------- | 222 | | \_controlledToken | The type of controlled token. | 223 | | \_principal | The principal amount on which interest is accruing. | 224 | | \_interest | The amount of interest that must accrue. | 225 | 226 | ### Sweeping Timelocked Funds 227 | 228 | When a user's withdrawal timelocks have ended, the funds may be swept to their wallets: 229 | 230 | ```javascript 231 | function sweepTimelockBalances( 232 | address[] memory users 233 | ) external returns (uint256 totalWithdrawal); 234 | ``` 235 | 236 | The function accepts an array of addresses and will attempt to sweep the time-locked funds for each one. The funds will be transferred back to the users wallets. 237 | 238 | ### Withdraw Instantly 239 | 240 | If a user would like their tickets right away, they may pay an early exit fee to the prize. The early exit fee is determined by the [Prize Strategy](https://app.gitbook.com/s/-M58QPye9-PujrSjSWqv-887967055/prize-strategy/). 241 | 242 | The instant withdrawal function returns the amount of the withdrawal that was retained as payment. This means you can call this function in a constant way to check to see what the exit fee will be. When it comes time to run the tx, that exit fee can be passed as the `maximumExitFee` to ensure it doesn't exceed the expected limit. 243 | 244 | ```javascript 245 | function withdrawInstantlyFrom( 246 | address from, 247 | uint256 amount, 248 | address controlledToken, 249 | uint256 maximumExitFee 250 | ) 251 | external 252 | returns (uint256 exitFee); 253 | ``` 254 | 255 | | Parameter Name | Parameter Description | 256 | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------- | 257 | | from | The address to withdraw from. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. | 258 | | amount | The amount to withdraw | 259 | | controlledToken | The controlled token to withdraw from | 260 | | maximumExitFee | The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on-the-fly. | 261 | 262 | This early exit fee can also be calculated by calling: 263 | 264 | ```javascript 265 | function calculateEarlyExitFee(address from, address controlledToken, uint256 amount) 266 | external override returns (uint256 exitFee, uint256 burnedCredit) 267 | ``` 268 | 269 | | Parameter Name | Parameter Description | 270 | | --------------- | ------------------------------------- | 271 | | from | The address to withdraw from | 272 | | controlledToken | The controlled token to withdraw from | 273 | | amount | The amount to withdraw | 274 | 275 | returns the `exitFee` that would be paid along with the credit that would be burned (`burnedCredit`). 276 | 277 | ## Awarding 278 | 279 | Only the Prize Strategy can call the award functions. These functions allow prizes to be disbursed to users. 280 | 281 | ### Awarding Yield 282 | 283 | Yield that accrues in the Prize Pool can be awarded by the Prize Strategy. The yield must first be **captured** and then it can be **awarded.** 284 | 285 | To capture the yield the prize strategy can call the `captureAwardBalance` function: 286 | 287 | ```javascript 288 | function captureAwardBalance() external onlyPrizeStrategy returns (uint256); 289 | ``` 290 | 291 | This function will: 292 | 293 | * add the current yield balance to the available award balance 294 | * capture a portion for the reserve 295 | * return the total available award balance. 296 | 297 | To award the captured yield to an address, the Prize strategy uses the `award` function. The yield must be awarded as one of the controlled tokens configured in the Prize Pool. 298 | 299 | ```javascript 300 | function award( 301 | address to, 302 | uint256 amount, 303 | address controlledToken 304 | ) external onlyPrizeStrategy; 305 | ``` 306 | 307 | | Parameter Name | Parameter Description | 308 | | --------------- | ---------------------------------------------- | 309 | | to | The address to receive the newly minted tokens | 310 | | amount | The amount of tokens to mint | 311 | | controlledToken | The type of token to mint | 312 | 313 | ### Awarding ERC20s 314 | 315 | The Prize Strategy can award ERC20 tokens that are held by the Prize Pool. 316 | 317 | ```javascript 318 | function awardExternalERC20( 319 | address to, 320 | address externalToken, 321 | uint256 amount 322 | ) external onlyPrizeStrategy; 323 | ``` 324 | 325 | However, some tokens are be blacklisted if they need to be held to generate yield (i.e. Compound cTokens). 326 | 327 | | Parameter Name | Parameter Description | 328 | | -------------- | ----------------------------------- | 329 | | to | The address to receive the transfer | 330 | | externalToken | The ERC20 to transfer | 331 | | amount | The amount of tokens to transfer | 332 | 333 | ### Awarding ERC721s (NFTs) 334 | 335 | The Prize Strategy can award ERC721 tokens that are held by the Prize Pool. 336 | 337 | ```javascript 338 | function awardExternalERC721( 339 | address to, 340 | address externalToken, 341 | uint256[] calldata tokenIds 342 | ) 343 | external 344 | onlyPrizeStrategy; 345 | ``` 346 | 347 | | Parameter Name | Parameter Description | 348 | | -------------- | ------------------------------- | 349 | | to | The address to receive the NFTs | 350 | | externalToken | The ERC721 contract address | 351 | | tokenIds | The NFT token ids to transfer. | 352 | 353 | ## Credit 354 | 355 | Credit accrues differently for each of the Prize Pool's controlled tokens, so each token will have its own credit rate and credit limit. 356 | 357 | ### Credit Balance 358 | 359 | To get a users credit balance for a controlled token: 360 | 361 | ```javascript 362 | function balanceOfCredit( 363 | address user, 364 | address controlledToken 365 | ) external returns (uint256); 366 | ``` 367 | 368 | | Parameter Name | Parameter Description | 369 | | --------------- | ------------------------------------------------------- | 370 | | user | The user whose credit balance should be returned | 371 | | controlledToken | The token for which the credit balance should be pulled | 372 | 373 | ### Credit Rate 374 | 375 | The credit rate for a controlled token can be checked like so: 376 | 377 | ```javascript 378 | function creditRateOf( 379 | address controlledToken 380 | ) external view returns ( 381 | uint128 creditLimitMantissa, 382 | uint128 creditRateMantissa 383 | ); 384 | ``` 385 | 386 | | Parameter Name | Parameter Description | 387 | | --------------- | -------------------------------------------------------------------- | 388 | | controlledToken | The controlled token whose credit limit and rate should be returned. | 389 | 390 | Note that the returned values are "mantissas": i.e. fixed point numbers with 18 decimal places. 391 | 392 | ### Credit Plan 393 | 394 | The credit plan associated with a `controlledToken` can be found by calling: 395 | 396 | ```javascript 397 | function creditPlanOf(address controlledToken) external override view returns (uint128 creditLimitMantissa, uint128 creditRateMantissa) 398 | ``` 399 | 400 | ## Prizes 401 | 402 | ### Current Award Balance 403 | 404 | The following function returns the amount calculated by `captureAwardBalance()`: 405 | 406 | ```javascript 407 | function awardBalance() external override view returns (uint256) 408 | ``` 409 | 410 | ### Total Balances 411 | 412 | The total of all controlled tokens (including timelocked) can be obtained by calling: 413 | 414 | ```javascript 415 | function accountedBalance() external override view returns (uint256) 416 | ``` 417 | 418 | The total underlying balance of all assets (including both principal and interest) can be obtained by calling: 419 | 420 | ```javascript 421 | function balance() external returns (uint256) 422 | ``` 423 | 424 | ## External Prizes 425 | 426 | ### Adding Tokens 427 | 428 | The owner can add "external" ERC20 tokens as prizes. The strategy will award the entire balance held by the Prize Pool to the winner. 429 | 430 | ```javascript 431 | function addExternalErc20Award(address _externalErc20) external onlyOwner; 432 | ``` 433 | 434 | The owner can add "external" ERC721 tokens as prizes. These tokens will be transferred to the winner. 435 | 436 | ```javascript 437 | function addExternalErc721Award( 438 | address _externalErc721, 439 | uint256[] calldata _tokenIds 440 | ) external onlyOwner 441 | ``` 442 | 443 | ### Checking Tokens 444 | 445 | Checks with the Prize Pool if a specific token type (`_externalToken`) may be awarded as an external prize: 446 | 447 | ```javascript 448 | function canAwardExternal(address _externalToken) external view returns (bool) 449 | ``` 450 | 451 | ## Prize Time Periods 452 | 453 | To retrieve when the current prize started: 454 | 455 | ```javascript 456 | function prizePeriodStartedAt() external view returns (uint256) 457 | ``` 458 | 459 | To retrieve when the prize will end: 460 | 461 | ```javascript 462 | function prizePeriodEndAt() external view returns (uint256) 463 | ``` 464 | 465 | ## Reserve 466 | 467 | ### Calculate Reserve Fee 468 | 469 | Calculates the reserve portion of the given `amount` of funds. If there is no reserve address, the Reserve fee portion will be zero. 470 | 471 | ```javascript 472 | function calculateReserveFee(uint256 amount) public view returns (uint256) 473 | ``` 474 | 475 | ## Prize Strategy 476 | 477 | ### Set the Prize Strategy 478 | 479 | The associated Prize Strategy can be set by calling: 480 | 481 | ```javascript 482 | function setPrizeStrategy(TokenListenerInterface _prizeStrategy) external override onlyOwner 483 | ``` 484 | 485 | Only the Prize Pool owner can call this function. 486 | 487 | -------------------------------------------------------------------------------- /.gitbook/assets/prizepool.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "winner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "token", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "amount", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Awarded", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "winner", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "token", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "amount", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "AwardedExternalERC20", 50 | "type": "event" 51 | }, 52 | { 53 | "anonymous": false, 54 | "inputs": [ 55 | { 56 | "indexed": true, 57 | "internalType": "address", 58 | "name": "winner", 59 | "type": "address" 60 | }, 61 | { 62 | "indexed": true, 63 | "internalType": "address", 64 | "name": "token", 65 | "type": "address" 66 | }, 67 | { 68 | "indexed": false, 69 | "internalType": "uint256[]", 70 | "name": "tokenIds", 71 | "type": "uint256[]" 72 | } 73 | ], 74 | "name": "AwardedExternalERC721", 75 | "type": "event" 76 | }, 77 | { 78 | "anonymous": false, 79 | "inputs": [ 80 | { 81 | "indexed": true, 82 | "internalType": "address", 83 | "name": "token", 84 | "type": "address" 85 | } 86 | ], 87 | "name": "ControlledTokenAdded", 88 | "type": "event" 89 | }, 90 | { 91 | "anonymous": false, 92 | "inputs": [ 93 | { 94 | "indexed": true, 95 | "internalType": "address", 96 | "name": "user", 97 | "type": "address" 98 | }, 99 | { 100 | "indexed": true, 101 | "internalType": "address", 102 | "name": "token", 103 | "type": "address" 104 | }, 105 | { 106 | "indexed": false, 107 | "internalType": "uint256", 108 | "name": "amount", 109 | "type": "uint256" 110 | } 111 | ], 112 | "name": "CreditBurned", 113 | "type": "event" 114 | }, 115 | { 116 | "anonymous": false, 117 | "inputs": [ 118 | { 119 | "indexed": true, 120 | "internalType": "address", 121 | "name": "user", 122 | "type": "address" 123 | }, 124 | { 125 | "indexed": true, 126 | "internalType": "address", 127 | "name": "token", 128 | "type": "address" 129 | }, 130 | { 131 | "indexed": false, 132 | "internalType": "uint256", 133 | "name": "amount", 134 | "type": "uint256" 135 | } 136 | ], 137 | "name": "CreditMinted", 138 | "type": "event" 139 | }, 140 | { 141 | "anonymous": false, 142 | "inputs": [ 143 | { 144 | "indexed": false, 145 | "internalType": "address", 146 | "name": "token", 147 | "type": "address" 148 | }, 149 | { 150 | "indexed": false, 151 | "internalType": "uint128", 152 | "name": "creditLimitMantissa", 153 | "type": "uint128" 154 | }, 155 | { 156 | "indexed": false, 157 | "internalType": "uint128", 158 | "name": "creditRateMantissa", 159 | "type": "uint128" 160 | } 161 | ], 162 | "name": "CreditPlanSet", 163 | "type": "event" 164 | }, 165 | { 166 | "anonymous": false, 167 | "inputs": [ 168 | { 169 | "indexed": true, 170 | "internalType": "address", 171 | "name": "operator", 172 | "type": "address" 173 | }, 174 | { 175 | "indexed": true, 176 | "internalType": "address", 177 | "name": "to", 178 | "type": "address" 179 | }, 180 | { 181 | "indexed": true, 182 | "internalType": "address", 183 | "name": "token", 184 | "type": "address" 185 | }, 186 | { 187 | "indexed": false, 188 | "internalType": "uint256", 189 | "name": "amount", 190 | "type": "uint256" 191 | }, 192 | { 193 | "indexed": false, 194 | "internalType": "address", 195 | "name": "referrer", 196 | "type": "address" 197 | } 198 | ], 199 | "name": "Deposited", 200 | "type": "event" 201 | }, 202 | { 203 | "anonymous": false, 204 | "inputs": [], 205 | "name": "EmergencyShutdown", 206 | "type": "event" 207 | }, 208 | { 209 | "anonymous": false, 210 | "inputs": [ 211 | { 212 | "indexed": false, 213 | "internalType": "address", 214 | "name": "trustedForwarder", 215 | "type": "address" 216 | }, 217 | { 218 | "indexed": false, 219 | "internalType": "address", 220 | "name": "reserve", 221 | "type": "address" 222 | }, 223 | { 224 | "indexed": false, 225 | "internalType": "uint256", 226 | "name": "maxExitFeeMantissa", 227 | "type": "uint256" 228 | }, 229 | { 230 | "indexed": false, 231 | "internalType": "uint256", 232 | "name": "maxTimelockDuration", 233 | "type": "uint256" 234 | } 235 | ], 236 | "name": "Initialized", 237 | "type": "event" 238 | }, 239 | { 240 | "anonymous": false, 241 | "inputs": [ 242 | { 243 | "indexed": true, 244 | "internalType": "address", 245 | "name": "operator", 246 | "type": "address" 247 | }, 248 | { 249 | "indexed": true, 250 | "internalType": "address", 251 | "name": "from", 252 | "type": "address" 253 | }, 254 | { 255 | "indexed": true, 256 | "internalType": "address", 257 | "name": "token", 258 | "type": "address" 259 | }, 260 | { 261 | "indexed": false, 262 | "internalType": "uint256", 263 | "name": "amount", 264 | "type": "uint256" 265 | }, 266 | { 267 | "indexed": false, 268 | "internalType": "uint256", 269 | "name": "redeemed", 270 | "type": "uint256" 271 | }, 272 | { 273 | "indexed": false, 274 | "internalType": "uint256", 275 | "name": "exitFee", 276 | "type": "uint256" 277 | } 278 | ], 279 | "name": "InstantWithdrawal", 280 | "type": "event" 281 | }, 282 | { 283 | "anonymous": false, 284 | "inputs": [ 285 | { 286 | "indexed": false, 287 | "internalType": "uint256", 288 | "name": "liquidityCap", 289 | "type": "uint256" 290 | } 291 | ], 292 | "name": "LiquidityCapSet", 293 | "type": "event" 294 | }, 295 | { 296 | "anonymous": false, 297 | "inputs": [ 298 | { 299 | "indexed": true, 300 | "internalType": "address", 301 | "name": "previousOwner", 302 | "type": "address" 303 | }, 304 | { 305 | "indexed": true, 306 | "internalType": "address", 307 | "name": "newOwner", 308 | "type": "address" 309 | } 310 | ], 311 | "name": "OwnershipTransferred", 312 | "type": "event" 313 | }, 314 | { 315 | "anonymous": false, 316 | "inputs": [ 317 | { 318 | "indexed": true, 319 | "internalType": "address", 320 | "name": "prizeStrategy", 321 | "type": "address" 322 | } 323 | ], 324 | "name": "PrizeStrategySet", 325 | "type": "event" 326 | }, 327 | { 328 | "anonymous": false, 329 | "inputs": [ 330 | { 331 | "indexed": true, 332 | "internalType": "address", 333 | "name": "to", 334 | "type": "address" 335 | }, 336 | { 337 | "indexed": true, 338 | "internalType": "address", 339 | "name": "token", 340 | "type": "address" 341 | }, 342 | { 343 | "indexed": false, 344 | "internalType": "uint256", 345 | "name": "amount", 346 | "type": "uint256" 347 | } 348 | ], 349 | "name": "ReserveFeeCaptured", 350 | "type": "event" 351 | }, 352 | { 353 | "anonymous": false, 354 | "inputs": [ 355 | { 356 | "indexed": true, 357 | "internalType": "address", 358 | "name": "token", 359 | "type": "address" 360 | } 361 | ], 362 | "name": "ReserveFeeControlledTokenSet", 363 | "type": "event" 364 | }, 365 | { 366 | "anonymous": false, 367 | "inputs": [ 368 | { 369 | "indexed": true, 370 | "internalType": "address", 371 | "name": "operator", 372 | "type": "address" 373 | }, 374 | { 375 | "indexed": true, 376 | "internalType": "address", 377 | "name": "to", 378 | "type": "address" 379 | }, 380 | { 381 | "indexed": true, 382 | "internalType": "address", 383 | "name": "token", 384 | "type": "address" 385 | }, 386 | { 387 | "indexed": false, 388 | "internalType": "uint256", 389 | "name": "amount", 390 | "type": "uint256" 391 | } 392 | ], 393 | "name": "TimelockDeposited", 394 | "type": "event" 395 | }, 396 | { 397 | "anonymous": false, 398 | "inputs": [ 399 | { 400 | "indexed": true, 401 | "internalType": "address", 402 | "name": "operator", 403 | "type": "address" 404 | }, 405 | { 406 | "indexed": true, 407 | "internalType": "address", 408 | "name": "from", 409 | "type": "address" 410 | }, 411 | { 412 | "indexed": true, 413 | "internalType": "address", 414 | "name": "token", 415 | "type": "address" 416 | }, 417 | { 418 | "indexed": false, 419 | "internalType": "uint256", 420 | "name": "amount", 421 | "type": "uint256" 422 | }, 423 | { 424 | "indexed": false, 425 | "internalType": "uint256", 426 | "name": "unlockTimestamp", 427 | "type": "uint256" 428 | } 429 | ], 430 | "name": "TimelockedWithdrawal", 431 | "type": "event" 432 | }, 433 | { 434 | "anonymous": false, 435 | "inputs": [ 436 | { 437 | "indexed": true, 438 | "internalType": "address", 439 | "name": "operator", 440 | "type": "address" 441 | }, 442 | { 443 | "indexed": true, 444 | "internalType": "address", 445 | "name": "from", 446 | "type": "address" 447 | }, 448 | { 449 | "indexed": false, 450 | "internalType": "uint256", 451 | "name": "amount", 452 | "type": "uint256" 453 | }, 454 | { 455 | "indexed": false, 456 | "internalType": "uint256", 457 | "name": "redeemed", 458 | "type": "uint256" 459 | } 460 | ], 461 | "name": "TimelockedWithdrawalSwept", 462 | "type": "event" 463 | }, 464 | { 465 | "anonymous": false, 466 | "inputs": [ 467 | { 468 | "indexed": true, 469 | "internalType": "address", 470 | "name": "to", 471 | "type": "address" 472 | }, 473 | { 474 | "indexed": true, 475 | "internalType": "address", 476 | "name": "token", 477 | "type": "address" 478 | }, 479 | { 480 | "indexed": false, 481 | "internalType": "uint256", 482 | "name": "amount", 483 | "type": "uint256" 484 | } 485 | ], 486 | "name": "TransferredExternalERC20", 487 | "type": "event" 488 | }, 489 | { 490 | "inputs": [], 491 | "name": "accountedBalance", 492 | "outputs": [ 493 | { 494 | "internalType": "uint256", 495 | "name": "", 496 | "type": "uint256" 497 | } 498 | ], 499 | "stateMutability": "view", 500 | "type": "function" 501 | }, 502 | { 503 | "inputs": [ 504 | { 505 | "internalType": "address", 506 | "name": "_controlledToken", 507 | "type": "address" 508 | } 509 | ], 510 | "name": "addControlledToken", 511 | "outputs": [], 512 | "stateMutability": "nonpayable", 513 | "type": "function" 514 | }, 515 | { 516 | "inputs": [ 517 | { 518 | "internalType": "address", 519 | "name": "to", 520 | "type": "address" 521 | }, 522 | { 523 | "internalType": "uint256", 524 | "name": "amount", 525 | "type": "uint256" 526 | }, 527 | { 528 | "internalType": "address", 529 | "name": "controlledToken", 530 | "type": "address" 531 | } 532 | ], 533 | "name": "award", 534 | "outputs": [], 535 | "stateMutability": "nonpayable", 536 | "type": "function" 537 | }, 538 | { 539 | "inputs": [], 540 | "name": "awardBalance", 541 | "outputs": [ 542 | { 543 | "internalType": "uint256", 544 | "name": "", 545 | "type": "uint256" 546 | } 547 | ], 548 | "stateMutability": "view", 549 | "type": "function" 550 | }, 551 | { 552 | "inputs": [ 553 | { 554 | "internalType": "address", 555 | "name": "to", 556 | "type": "address" 557 | }, 558 | { 559 | "internalType": "address", 560 | "name": "externalToken", 561 | "type": "address" 562 | }, 563 | { 564 | "internalType": "uint256", 565 | "name": "amount", 566 | "type": "uint256" 567 | } 568 | ], 569 | "name": "awardExternalERC20", 570 | "outputs": [], 571 | "stateMutability": "nonpayable", 572 | "type": "function" 573 | }, 574 | { 575 | "inputs": [ 576 | { 577 | "internalType": "address", 578 | "name": "to", 579 | "type": "address" 580 | }, 581 | { 582 | "internalType": "address", 583 | "name": "externalToken", 584 | "type": "address" 585 | }, 586 | { 587 | "internalType": "uint256[]", 588 | "name": "tokenIds", 589 | "type": "uint256[]" 590 | } 591 | ], 592 | "name": "awardExternalERC721", 593 | "outputs": [], 594 | "stateMutability": "nonpayable", 595 | "type": "function" 596 | }, 597 | { 598 | "inputs": [], 599 | "name": "balance", 600 | "outputs": [ 601 | { 602 | "internalType": "uint256", 603 | "name": "", 604 | "type": "uint256" 605 | } 606 | ], 607 | "stateMutability": "nonpayable", 608 | "type": "function" 609 | }, 610 | { 611 | "inputs": [ 612 | { 613 | "internalType": "address", 614 | "name": "user", 615 | "type": "address" 616 | }, 617 | { 618 | "internalType": "address", 619 | "name": "controlledToken", 620 | "type": "address" 621 | } 622 | ], 623 | "name": "balanceOfCredit", 624 | "outputs": [ 625 | { 626 | "internalType": "uint256", 627 | "name": "", 628 | "type": "uint256" 629 | } 630 | ], 631 | "stateMutability": "nonpayable", 632 | "type": "function" 633 | }, 634 | { 635 | "inputs": [ 636 | { 637 | "internalType": "address", 638 | "name": "from", 639 | "type": "address" 640 | }, 641 | { 642 | "internalType": "address", 643 | "name": "to", 644 | "type": "address" 645 | }, 646 | { 647 | "internalType": "uint256", 648 | "name": "amount", 649 | "type": "uint256" 650 | } 651 | ], 652 | "name": "beforeTokenTransfer", 653 | "outputs": [], 654 | "stateMutability": "nonpayable", 655 | "type": "function" 656 | }, 657 | { 658 | "inputs": [ 659 | { 660 | "internalType": "address", 661 | "name": "from", 662 | "type": "address" 663 | }, 664 | { 665 | "internalType": "address", 666 | "name": "controlledToken", 667 | "type": "address" 668 | }, 669 | { 670 | "internalType": "uint256", 671 | "name": "amount", 672 | "type": "uint256" 673 | } 674 | ], 675 | "name": "calculateEarlyExitFee", 676 | "outputs": [ 677 | { 678 | "internalType": "uint256", 679 | "name": "exitFee", 680 | "type": "uint256" 681 | }, 682 | { 683 | "internalType": "uint256", 684 | "name": "burnedCredit", 685 | "type": "uint256" 686 | } 687 | ], 688 | "stateMutability": "nonpayable", 689 | "type": "function" 690 | }, 691 | { 692 | "inputs": [ 693 | { 694 | "internalType": "uint256", 695 | "name": "amount", 696 | "type": "uint256" 697 | } 698 | ], 699 | "name": "calculateReserveFee", 700 | "outputs": [ 701 | { 702 | "internalType": "uint256", 703 | "name": "", 704 | "type": "uint256" 705 | } 706 | ], 707 | "stateMutability": "view", 708 | "type": "function" 709 | }, 710 | { 711 | "inputs": [ 712 | { 713 | "internalType": "address", 714 | "name": "from", 715 | "type": "address" 716 | }, 717 | { 718 | "internalType": "address", 719 | "name": "controlledToken", 720 | "type": "address" 721 | }, 722 | { 723 | "internalType": "uint256", 724 | "name": "amount", 725 | "type": "uint256" 726 | } 727 | ], 728 | "name": "calculateTimelockDuration", 729 | "outputs": [ 730 | { 731 | "internalType": "uint256", 732 | "name": "durationSeconds", 733 | "type": "uint256" 734 | }, 735 | { 736 | "internalType": "uint256", 737 | "name": "burnedCredit", 738 | "type": "uint256" 739 | } 740 | ], 741 | "stateMutability": "nonpayable", 742 | "type": "function" 743 | }, 744 | { 745 | "inputs": [ 746 | { 747 | "internalType": "address", 748 | "name": "_externalToken", 749 | "type": "address" 750 | } 751 | ], 752 | "name": "canAwardExternal", 753 | "outputs": [ 754 | { 755 | "internalType": "bool", 756 | "name": "", 757 | "type": "bool" 758 | } 759 | ], 760 | "stateMutability": "view", 761 | "type": "function" 762 | }, 763 | { 764 | "inputs": [], 765 | "name": "captureAwardBalance", 766 | "outputs": [ 767 | { 768 | "internalType": "uint256", 769 | "name": "", 770 | "type": "uint256" 771 | } 772 | ], 773 | "stateMutability": "nonpayable", 774 | "type": "function" 775 | }, 776 | { 777 | "inputs": [ 778 | { 779 | "internalType": "address", 780 | "name": "controlledToken", 781 | "type": "address" 782 | } 783 | ], 784 | "name": "creditPlanOf", 785 | "outputs": [ 786 | { 787 | "internalType": "uint128", 788 | "name": "creditLimitMantissa", 789 | "type": "uint128" 790 | }, 791 | { 792 | "internalType": "uint128", 793 | "name": "creditRateMantissa", 794 | "type": "uint128" 795 | } 796 | ], 797 | "stateMutability": "view", 798 | "type": "function" 799 | }, 800 | { 801 | "inputs": [ 802 | { 803 | "internalType": "address", 804 | "name": "to", 805 | "type": "address" 806 | }, 807 | { 808 | "internalType": "uint256", 809 | "name": "amount", 810 | "type": "uint256" 811 | }, 812 | { 813 | "internalType": "address", 814 | "name": "controlledToken", 815 | "type": "address" 816 | }, 817 | { 818 | "internalType": "address", 819 | "name": "referrer", 820 | "type": "address" 821 | } 822 | ], 823 | "name": "depositTo", 824 | "outputs": [], 825 | "stateMutability": "nonpayable", 826 | "type": "function" 827 | }, 828 | { 829 | "inputs": [], 830 | "name": "emergencyShutdown", 831 | "outputs": [], 832 | "stateMutability": "nonpayable", 833 | "type": "function" 834 | }, 835 | { 836 | "inputs": [ 837 | { 838 | "internalType": "address", 839 | "name": "_controlledToken", 840 | "type": "address" 841 | }, 842 | { 843 | "internalType": "uint256", 844 | "name": "_principal", 845 | "type": "uint256" 846 | }, 847 | { 848 | "internalType": "uint256", 849 | "name": "_interest", 850 | "type": "uint256" 851 | } 852 | ], 853 | "name": "estimateCreditAccrualTime", 854 | "outputs": [ 855 | { 856 | "internalType": "uint256", 857 | "name": "durationSeconds", 858 | "type": "uint256" 859 | } 860 | ], 861 | "stateMutability": "view", 862 | "type": "function" 863 | }, 864 | { 865 | "inputs": [ 866 | { 867 | "internalType": "address", 868 | "name": "_trustedForwarder", 869 | "type": "address" 870 | }, 871 | { 872 | "internalType": "contract ReserveInterface", 873 | "name": "_reserve", 874 | "type": "address" 875 | }, 876 | { 877 | "internalType": "address[]", 878 | "name": "_controlledTokens", 879 | "type": "address[]" 880 | }, 881 | { 882 | "internalType": "uint256", 883 | "name": "_maxExitFeeMantissa", 884 | "type": "uint256" 885 | }, 886 | { 887 | "internalType": "uint256", 888 | "name": "_maxTimelockDuration", 889 | "type": "uint256" 890 | } 891 | ], 892 | "name": "initialize", 893 | "outputs": [], 894 | "stateMutability": "nonpayable", 895 | "type": "function" 896 | }, 897 | { 898 | "inputs": [], 899 | "name": "isShutdown", 900 | "outputs": [ 901 | { 902 | "internalType": "bool", 903 | "name": "", 904 | "type": "bool" 905 | } 906 | ], 907 | "stateMutability": "view", 908 | "type": "function" 909 | }, 910 | { 911 | "inputs": [ 912 | { 913 | "internalType": "address", 914 | "name": "forwarder", 915 | "type": "address" 916 | } 917 | ], 918 | "name": "isTrustedForwarder", 919 | "outputs": [ 920 | { 921 | "internalType": "bool", 922 | "name": "", 923 | "type": "bool" 924 | } 925 | ], 926 | "stateMutability": "view", 927 | "type": "function" 928 | }, 929 | { 930 | "inputs": [], 931 | "name": "liquidityCap", 932 | "outputs": [ 933 | { 934 | "internalType": "uint256", 935 | "name": "", 936 | "type": "uint256" 937 | } 938 | ], 939 | "stateMutability": "view", 940 | "type": "function" 941 | }, 942 | { 943 | "inputs": [], 944 | "name": "maxExitFeeMantissa", 945 | "outputs": [ 946 | { 947 | "internalType": "uint256", 948 | "name": "", 949 | "type": "uint256" 950 | } 951 | ], 952 | "stateMutability": "view", 953 | "type": "function" 954 | }, 955 | { 956 | "inputs": [], 957 | "name": "maxTimelockDuration", 958 | "outputs": [ 959 | { 960 | "internalType": "uint256", 961 | "name": "", 962 | "type": "uint256" 963 | } 964 | ], 965 | "stateMutability": "view", 966 | "type": "function" 967 | }, 968 | { 969 | "inputs": [], 970 | "name": "owner", 971 | "outputs": [ 972 | { 973 | "internalType": "address", 974 | "name": "", 975 | "type": "address" 976 | } 977 | ], 978 | "stateMutability": "view", 979 | "type": "function" 980 | }, 981 | { 982 | "inputs": [], 983 | "name": "prizeStrategy", 984 | "outputs": [ 985 | { 986 | "internalType": "contract TokenListenerInterface", 987 | "name": "", 988 | "type": "address" 989 | } 990 | ], 991 | "stateMutability": "view", 992 | "type": "function" 993 | }, 994 | { 995 | "inputs": [], 996 | "name": "renounceOwnership", 997 | "outputs": [], 998 | "stateMutability": "nonpayable", 999 | "type": "function" 1000 | }, 1001 | { 1002 | "inputs": [], 1003 | "name": "reserve", 1004 | "outputs": [ 1005 | { 1006 | "internalType": "contract ReserveInterface", 1007 | "name": "", 1008 | "type": "address" 1009 | } 1010 | ], 1011 | "stateMutability": "view", 1012 | "type": "function" 1013 | }, 1014 | { 1015 | "inputs": [], 1016 | "name": "reserveFeeControlledToken", 1017 | "outputs": [ 1018 | { 1019 | "internalType": "address", 1020 | "name": "", 1021 | "type": "address" 1022 | } 1023 | ], 1024 | "stateMutability": "view", 1025 | "type": "function" 1026 | }, 1027 | { 1028 | "inputs": [ 1029 | { 1030 | "internalType": "address", 1031 | "name": "_controlledToken", 1032 | "type": "address" 1033 | }, 1034 | { 1035 | "internalType": "uint128", 1036 | "name": "_creditRateMantissa", 1037 | "type": "uint128" 1038 | }, 1039 | { 1040 | "internalType": "uint128", 1041 | "name": "_creditLimitMantissa", 1042 | "type": "uint128" 1043 | } 1044 | ], 1045 | "name": "setCreditPlanOf", 1046 | "outputs": [], 1047 | "stateMutability": "nonpayable", 1048 | "type": "function" 1049 | }, 1050 | { 1051 | "inputs": [ 1052 | { 1053 | "internalType": "uint256", 1054 | "name": "_liquidityCap", 1055 | "type": "uint256" 1056 | } 1057 | ], 1058 | "name": "setLiquidityCap", 1059 | "outputs": [], 1060 | "stateMutability": "nonpayable", 1061 | "type": "function" 1062 | }, 1063 | { 1064 | "inputs": [ 1065 | { 1066 | "internalType": "contract TokenListenerInterface", 1067 | "name": "_prizeStrategy", 1068 | "type": "address" 1069 | } 1070 | ], 1071 | "name": "setPrizeStrategy", 1072 | "outputs": [], 1073 | "stateMutability": "nonpayable", 1074 | "type": "function" 1075 | }, 1076 | { 1077 | "inputs": [ 1078 | { 1079 | "internalType": "address", 1080 | "name": "controlledToken", 1081 | "type": "address" 1082 | } 1083 | ], 1084 | "name": "setReserveFeeControlledToken", 1085 | "outputs": [], 1086 | "stateMutability": "nonpayable", 1087 | "type": "function" 1088 | }, 1089 | { 1090 | "inputs": [ 1091 | { 1092 | "internalType": "address[]", 1093 | "name": "users", 1094 | "type": "address[]" 1095 | } 1096 | ], 1097 | "name": "sweepTimelockBalances", 1098 | "outputs": [ 1099 | { 1100 | "internalType": "uint256", 1101 | "name": "", 1102 | "type": "uint256" 1103 | } 1104 | ], 1105 | "stateMutability": "nonpayable", 1106 | "type": "function" 1107 | }, 1108 | { 1109 | "inputs": [ 1110 | { 1111 | "internalType": "address", 1112 | "name": "user", 1113 | "type": "address" 1114 | } 1115 | ], 1116 | "name": "timelockBalanceAvailableAt", 1117 | "outputs": [ 1118 | { 1119 | "internalType": "uint256", 1120 | "name": "", 1121 | "type": "uint256" 1122 | } 1123 | ], 1124 | "stateMutability": "view", 1125 | "type": "function" 1126 | }, 1127 | { 1128 | "inputs": [ 1129 | { 1130 | "internalType": "address", 1131 | "name": "user", 1132 | "type": "address" 1133 | } 1134 | ], 1135 | "name": "timelockBalanceOf", 1136 | "outputs": [ 1137 | { 1138 | "internalType": "uint256", 1139 | "name": "", 1140 | "type": "uint256" 1141 | } 1142 | ], 1143 | "stateMutability": "view", 1144 | "type": "function" 1145 | }, 1146 | { 1147 | "inputs": [ 1148 | { 1149 | "internalType": "address", 1150 | "name": "to", 1151 | "type": "address" 1152 | }, 1153 | { 1154 | "internalType": "uint256", 1155 | "name": "amount", 1156 | "type": "uint256" 1157 | }, 1158 | { 1159 | "internalType": "address", 1160 | "name": "controlledToken", 1161 | "type": "address" 1162 | } 1163 | ], 1164 | "name": "timelockDepositTo", 1165 | "outputs": [], 1166 | "stateMutability": "nonpayable", 1167 | "type": "function" 1168 | }, 1169 | { 1170 | "inputs": [], 1171 | "name": "timelockTotalSupply", 1172 | "outputs": [ 1173 | { 1174 | "internalType": "uint256", 1175 | "name": "", 1176 | "type": "uint256" 1177 | } 1178 | ], 1179 | "stateMutability": "view", 1180 | "type": "function" 1181 | }, 1182 | { 1183 | "inputs": [], 1184 | "name": "token", 1185 | "outputs": [ 1186 | { 1187 | "internalType": "contract IERC20", 1188 | "name": "", 1189 | "type": "address" 1190 | } 1191 | ], 1192 | "stateMutability": "view", 1193 | "type": "function" 1194 | }, 1195 | { 1196 | "inputs": [], 1197 | "name": "tokens", 1198 | "outputs": [ 1199 | { 1200 | "internalType": "address[]", 1201 | "name": "", 1202 | "type": "address[]" 1203 | } 1204 | ], 1205 | "stateMutability": "view", 1206 | "type": "function" 1207 | }, 1208 | { 1209 | "inputs": [ 1210 | { 1211 | "internalType": "address", 1212 | "name": "to", 1213 | "type": "address" 1214 | }, 1215 | { 1216 | "internalType": "address", 1217 | "name": "externalToken", 1218 | "type": "address" 1219 | }, 1220 | { 1221 | "internalType": "uint256", 1222 | "name": "amount", 1223 | "type": "uint256" 1224 | } 1225 | ], 1226 | "name": "transferExternalERC20", 1227 | "outputs": [], 1228 | "stateMutability": "nonpayable", 1229 | "type": "function" 1230 | }, 1231 | { 1232 | "inputs": [ 1233 | { 1234 | "internalType": "address", 1235 | "name": "newOwner", 1236 | "type": "address" 1237 | } 1238 | ], 1239 | "name": "transferOwnership", 1240 | "outputs": [], 1241 | "stateMutability": "nonpayable", 1242 | "type": "function" 1243 | }, 1244 | { 1245 | "inputs": [], 1246 | "name": "trustedForwarder", 1247 | "outputs": [ 1248 | { 1249 | "internalType": "address", 1250 | "name": "", 1251 | "type": "address" 1252 | } 1253 | ], 1254 | "stateMutability": "view", 1255 | "type": "function" 1256 | }, 1257 | { 1258 | "inputs": [], 1259 | "name": "versionRecipient", 1260 | "outputs": [ 1261 | { 1262 | "internalType": "string", 1263 | "name": "", 1264 | "type": "string" 1265 | } 1266 | ], 1267 | "stateMutability": "view", 1268 | "type": "function" 1269 | }, 1270 | { 1271 | "inputs": [ 1272 | { 1273 | "internalType": "address", 1274 | "name": "from", 1275 | "type": "address" 1276 | }, 1277 | { 1278 | "internalType": "uint256", 1279 | "name": "amount", 1280 | "type": "uint256" 1281 | }, 1282 | { 1283 | "internalType": "address", 1284 | "name": "controlledToken", 1285 | "type": "address" 1286 | }, 1287 | { 1288 | "internalType": "uint256", 1289 | "name": "maximumExitFee", 1290 | "type": "uint256" 1291 | } 1292 | ], 1293 | "name": "withdrawInstantlyFrom", 1294 | "outputs": [ 1295 | { 1296 | "internalType": "uint256", 1297 | "name": "", 1298 | "type": "uint256" 1299 | } 1300 | ], 1301 | "stateMutability": "nonpayable", 1302 | "type": "function" 1303 | }, 1304 | { 1305 | "inputs": [ 1306 | { 1307 | "internalType": "address", 1308 | "name": "from", 1309 | "type": "address" 1310 | }, 1311 | { 1312 | "internalType": "uint256", 1313 | "name": "amount", 1314 | "type": "uint256" 1315 | }, 1316 | { 1317 | "internalType": "address", 1318 | "name": "controlledToken", 1319 | "type": "address" 1320 | } 1321 | ], 1322 | "name": "withdrawWithTimelockFrom", 1323 | "outputs": [ 1324 | { 1325 | "internalType": "uint256", 1326 | "name": "", 1327 | "type": "uint256" 1328 | } 1329 | ], 1330 | "stateMutability": "nonpayable", 1331 | "type": "function" 1332 | } 1333 | ] --------------------------------------------------------------------------------