diff --git a/assets/images/ekoke-supply.png b/assets/images/ekoke-supply.png new file mode 100644 index 0000000..af9583b Binary files /dev/null and b/assets/images/ekoke-supply.png differ diff --git a/docs/README.md b/docs/README.md index c033395..423de7a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,6 +11,15 @@ ## Smart contracts +- [Deferred ERC721](./contracts/Deferred.md) +- [EKOKE ERC20](./contracts/Ekoke.md) +- [Marketplace](./contracts/Marketplace.md) +- [Reward Pool](./contracts/RewardPool.md) + +## EKOKE rewards + +- [Rewards](./reward.md) + ## EKOKE DAO - [DAO](./dao.md) diff --git a/docs/canisters/deferred-data.md b/docs/canisters/deferred-data.md new file mode 100644 index 0000000..b95d354 --- /dev/null +++ b/docs/canisters/deferred-data.md @@ -0,0 +1,85 @@ +# Deferred Data + +- [Deferred Data](#deferred-data) + - [Introduction](#introduction) + - [HTTP Endpoint](#http-endpoint) + - [Get contracts](#get-contracts) + - [Get contract by id](#get-contract-by-id) + +## Introduction + +Deferred **Data** canister takes care of storing sell contracts and provides the following functionalities: + +- **Create contract**: the contract is inserted into the ledger by [deferred-minter](./deferred-minter.md). +- **Close contract**: the contract is closed by [deferred-minter](./deferred-minter.md). +- **Get contract data**: get the data for a contract. Closed contracts are not returned +- **Get all contracts**: get all existing contracts. Closed contracts are not returned +- **Get contract document**: get a contract document with its data and mime type +- **Upload contract document**: The agency can upload documents for a contract +- **Update contract property**: The agency can both update a contract property and restricted property. Mind that when we talk about **contract properties** we don't mean any property, but just those stored in the `properties` and `restricted_properties` fields. + +## HTTP Endpoint + +### Get contracts + +This endpoints gets all the IDs of registered contracts + +```txt +GET /contracts +``` + +Response: + +```json +[ + 1, + 2, + 3 +] +``` + +### Get contract by id + +Get a contract by id + +```txt +GET /contract/:id +``` + +Response: + +```json +{ + "id": 1, + "type": "Sell", + "sellers": [ + { + "address": "0x...", + "quota": 100 + } + ], + "buyers": [ + "0x000", + "0x001", + ], + "installments": 4000, + "value": 400000, + "deposit": 50000, + "currency": "USD", + "properties": [], + "restrictedProperties": [], + "documents": { + "1": { + "accessList": ["agent"], + "mimeType": "application/pdf" + } + }, + "agency": { + ... + }, + "expiration": "2050-01-1", + "closed": false +} +``` + +> Restricted properties are redacted based on your permissions diff --git a/docs/canisters/deferred-minter.md b/docs/canisters/deferred-minter.md index a65dfcd..73e0585 100644 --- a/docs/canisters/deferred-minter.md +++ b/docs/canisters/deferred-minter.md @@ -8,6 +8,8 @@ - [Close a sell contract](#close-a-sell-contract) - [close contract requirements](#close-contract-requirements) - [Close contract](#close-contract) + - [HTTP Endpoint](#http-endpoint) + - [Agents](#agents) ## Introduction @@ -29,7 +31,7 @@ A party involved in the sell process (buyer/seller/agency) must send Ethereum to #### Create contract -At this point the agency can send the `ContractRegistration` data and call the `create_contract` endpoint on the canister. +At this point the **agency** can send the `ContractRegistration` data and call the `create_contract` endpoint on the canister. This endpoint will call `create_contract` on the **Deferred** Ethereum ERC721 which will mint the tokens and after that it will call `create_contract` on **deferred_data** to store the contract on the ledger. @@ -46,10 +48,41 @@ A party involved in the sell process (buyer/seller/agency) must send Ethereum to #### Close contract -At this point the agency can close the contract by calling `close_contract` on the canister. +The **agency** can close the contract by calling `close_contract` on the canister. This will mark the contract as closed both on the ledger and on the ERC721. Once the contract is closed tokens can't be traded anymore and the sell contract is completed. > ❗ The agency must ensure before closing the contract that the buyer owns all the tokens + +## HTTP Endpoint + +### Agents + +```txt +GET /agents +``` + +Returns all the agents registered. + +The response has the following syntax: + +```json +[ + { + "address": "Via roma 12", + "city": "Milan", + "continent": "Europe", + "country": "Italy", + "email": "test@example.com", + "mobile": "3661677509", + "name": "MilanHouses", + "owner": "principal", + "region": "...", + "vat": "", + "website": "", + "zipCode": "33100" + } +] +``` diff --git a/docs/canisters/deferred.md b/docs/canisters/deferred.md deleted file mode 100644 index bfc6a7f..0000000 --- a/docs/canisters/deferred.md +++ /dev/null @@ -1,275 +0,0 @@ -# Deferred Canister - -- [Deferred Canister](#deferred-canister) - - [Introduction](#introduction) - - [Data](#data) - - [Contract](#contract) - - [Roles](#roles) - - [API](#api) - - [register\_contract](#register_contract) - - [sign\_contract](#sign_contract) - - [get\_contract](#get_contract) - - [get\_token](#get_token) - - [get\_signed\_contracts](#get_signed_contracts) - - [get\_agencies](#get_agencies) - - [remove\_agency](#remove_agency) - - [get\_unsigned\_contracts](#get_unsigned_contracts) - - [increment\_contract\_value](#increment_contract_value) - - [update\_contract\_buyers](#update_contract_buyers) - - [update\_contract\_property](#update_contract_property) - - [update\_restricted\_contract\_property](#update_restricted_contract_property) - - [get\_restricted\_contract\_properties](#get_restricted_contract_properties) - - [withdraw\_contract\_deposit](#withdraw_contract_deposit) - - [close\_contract](#close_contract) - - [admin\_set\_ekoke\_reward\_pool\_canister](#admin_set_ekoke_reward_pool_canister) - - [admin\_set\_marketplace\_canister](#admin_set_marketplace_canister) - - [admin\_set\_role](#admin_set_role) - - [admin\_remove\_role](#admin_remove_role) - - [admin\_register\_agency](#admin_register_agency) - - [HTTP API](#http-api) - - [Request protocol](#request-protocol) - - [Request Body](#request-body) - - [HTTP Methods](#http-methods) - - [getContracts](#getcontracts) - - [getContract](#getcontract) - - [getToken](#gettoken) - - [getAgencies](#getagencies) - - [Token Metadata](#token-metadata) - -![Deferred logo](../../assets/images/deferred-logo.png) - -## Introduction - -Deferred is a canister which provides a **Non-fungible Token (NFT)** which implements the **DIP-721** Standard with the `dip721_` namespace. - -The Deferred canister takes care of registering the sell or financing of a real estate between two or more parts, **Buyers** and **Sellers**. This agreement between parts is called **Contract**. - -The Buyer is represented by its Principal, while the Seller is represented by its Principal and its **Quota** in the contract ownership in a total sum of 100. - -Each **Contract** is identified by an **ID** (NAT). - -Each **Contract** will have Token associated, identified by an incremental **TokenIdentifier** (NAT) as specified by the DIP721 standard. - -## Data - -### Contract - -A Contract is identified by the following properties - -- **id**: the contract unique identifier -- **value**: the FIAT value of the contract -- **currency**: the currency used to represent the value -- **agency**: the agency which has created the contract -- **sellers**: the contract sellers. Cannot be empty -- **buyers**: the contract buyers. Cannot be empty. It also contains the deposit account -- **deposit**: buyer deposit amount (FIAT and ICP) -- **is_signed**: if signed the contract tokens can be sold. The token must be signed by custodians (or DAO) -- **type**: the contract type (Sell / Funding) -- **reward**: the reward of EKOKE token given to a NFT buyer -- **expiration**: contract expiration with syntax `YYYY-MM-DD`. -- **properties**: contract properties and metadata -- **restricted_properties**: contract restricted properties - -## Roles - -On the deferred canister the following roles exists: - -- **Custodian**: administrator of the canister, following the DIP721 standard. It can administrate the canister and sign contracts. -- **Agent**: role for agencies. Agents can create contracts, and operate only on contracts created by their agency. - -## API - -See [DID file](../../src/deferred/deferred.did) - -### register_contract - -Register a contract with the provided data. - -**The contract value** MUST be **multiple of installments**. - -### sign_contract - -Approve and sign an existing contract. Once signed, the contract's tokens can be sold on the marketplace. Only an admin or an agent can sign the contract. - -### get_contract - -Get contract by ID - -### get_token - -Get token and its related contract by ID - -### get_signed_contracts - -Get the IDS for all the signed contract - -### get_agencies - -Get all the agencies - -### remove_agency - -Remove an agency. Only admin or the agent himself can call this method - -### get_unsigned_contracts - -Get unsigned contracts. -If called by an agent it returns the unsigned contracts for their agency. -If called by an admin it returns all of them. - -### increment_contract_value - -The seller increments the contract value and mint new NFTs for it - -### update_contract_buyers - -Update the principal of the contract. - -Only the sellers or the buyers can call this method. - -### update_contract_property - -Change a contract property. - -Can be called by Agent, Custodian or seller. - -### update_restricted_contract_property - -Create or change a contract restricted property. - -Can be called by Agent, Custodian or seller. - -### get_restricted_contract_properties - -Get the restricted contract properties. - -The properties returned are those only accessible to the caller - -### withdraw_contract_deposit - -Update endpoint called by the seller once all the NFTs have been bought by the contract buyers. - -The seller provides the contract id he wants to withdraw for and an optional ICRC subaccount. - -If the all the NFTs have been paid by the buyer, the deferred canister will transfer the deposit amount to the caller. - -The seller will receive only `deposit.value_icp / seller.quota` its part of the deposit. - -Each seller must call this method to withdraw their quota of the contract - -### close_contract - -Update endpoint for the agency to close an expired contract. - -All the third party investors are refunded by the liquidity pool with this call, by using the deposit balance. If the deposit balance is not enough, funds from the liquidity pool will be used instead. - -### admin_set_ekoke_reward_pool_canister - -Update ekoke ledger canister principal - -### admin_set_marketplace_canister - -Update marketplace canister - -### admin_set_role - -Set role for principal - -### admin_remove_role - -Remove a role - -### admin_register_agency - -Register a new agency in the canister and give to its principal the role of agent - -## HTTP API - -The deferred canister also exposes an HTTP API - -### Request protocol - -| Method | GET | -|--------------|------------------| -| Content-type | application/json | - -### Request Body - -The request body must be JSON encoded, and must follow this syntax - -| Name | Type | Description | -|--------|--------|----------------------------------------------| -| method | string | HTTP method name | -| params | json | key-value map of optional request parameters | - -### HTTP Methods - -This list contains the http available methods with its parameters and response. - -#### getContracts - -Get all contracts ids - -Body: - -```json -{} -``` - -Response - -```json -[ - 1, - 2, -] -``` - -#### getContract - -Get a contract by ID - -Body: - -```json -{ - "id": 1 -} -``` - -Response: - -See Contract in did. Returns 404 if it doesn't exist. - -#### getToken - -Get a token by ID - -Body: - -```json -{ - "id": 1 -} -``` - -Response: - -See TokenInfo in did. Returns 404 if it doesn't exist. - -#### getAgencies - -Returns all the agencies which have been registered. - -## Token Metadata - -Each NFT has the following properties, following the DIP721 standard. - -- `token:contract_id`: contract id -- `token:value`: fiat value of the contract -- `token:currency`: currency name to represent the value -- `token:ekoke_reward`: reward given when someone buys the contract on the marketplace -- `contract:sellers`: contract sellers principals -- `contract:buyers`: contract buyers principals - -> To these properties the contract properties can be added using the syntax `contract:keyname` diff --git a/docs/canisters/ekoke-erc20-swap-frontend.md b/docs/canisters/ekoke-erc20-swap-frontend.md deleted file mode 100644 index 1848ee4..0000000 --- a/docs/canisters/ekoke-erc20-swap-frontend.md +++ /dev/null @@ -1,10 +0,0 @@ -# EKOKE ERC20 Swap Frontend - -- [EKOKE ERC20 Swap Frontend](#ekoke-erc20-swap-frontend) - - [Introduction](#introduction) - -## Introduction - -This canister provides a ReactJS frontend to allow users to swap their EKOKE tokens from ICRC to ERC20 and viceversa. - -The interface can be accessed from . diff --git a/docs/canisters/ekoke-erc20-swap.md b/docs/canisters/ekoke-erc20-swap.md deleted file mode 100644 index 087c52a..0000000 --- a/docs/canisters/ekoke-erc20-swap.md +++ /dev/null @@ -1,97 +0,0 @@ -# EKOKE ERC20 Swap Canister - -- [EKOKE ERC20 Swap Canister](#ekoke-erc20-swap-canister) - - [Introduction](#introduction) - - [ERC20 Smart Contract](#erc20-smart-contract) - - [swap (erc20)](#swap-erc20) - - [transcribeSwap (erc20)](#transcribeswap-erc20) - - [API](#api) - - [swap (icrc)](#swap-icrc) - - [swap\_fee](#swap_fee) - - [Swap implementation](#swap-implementation) - - [Swap ICRC into ERC20](#swap-icrc-into-erc20) - - [Swap ERC20 into ICRC](#swap-erc20-into-icrc) - - [ckETH / ETH Swap](#cketh--eth-swap) - -## Introduction - -- Ethereum [0x]() -- Sepolia: [0x30eBEE43A1f7Ba89C78Eb4Adde3ada425DAA473d](https://sepolia.etherscan.io/address/0x30eBEE43A1f7Ba89C78Eb4Adde3ada425DAA473d) - -Decimals: 8 -Symbol: **EKOKE** - -The EKOKE ICRC-2 token has a 1:1 token on Ethereum implemented as an ERC20 token, with the same name. -The purpose of this token on the Ethereum blockchain is to make EKOKE more accessible to web3 users. - -## ERC20 Smart Contract - -The smart contract which implements the EKOKE ERC20 has two additional methods to allow bridging between IC and Ethereum blockchains. - -### swap (erc20) - -The swap method allows an Ethereum user to withdraw to convert ERC20 tokens to ICRC tokens. -The swap method takes as argument the **Principal** to withdraw the tokens to and the amount to withdraw. -Once called this method burns the user tokens and emits an event `EkokeSwapped` which is intercepted by the ekoke-erc20-swap canister. - -Once intercepted the canister will withdraw ICRC tokens to the provided principal for the specified amount. - -### transcribeSwap (erc20) - -This method can be called only by the ekoke-erc20-swap canister and it is used once a deposit has been made on the canister, which means that a user has requested to convert his ICRC tokens into ERC20 tokens. - -This method takes as argument the ETH address to mint tokens to and the amount. Once called the ERC20 tokens will be minted to the provided ethereum address. - -## API - -The full API is documented in the [DID](../../src/ekoke_erc20_swap/ekoke-erc20-swap.did). - -### swap (icrc) - -This method can be called by a user who wants to swap a certain amount of ICRC tokens into ERC20 tokens. - -The user provides an ethereum address to swap the tokens to and these will be swapped into the ERC20 token, following the process described below. - -### swap_fee - -Get the swap fee to pay in ckETH to allow the swap. - -## Swap implementation - -### Swap ICRC into ERC20 - -1. Alice wants to swap 100 EKOKE from **ICRC** into **ERC20** -2. Alice calls `swap_fee` on the **erc20-swap canister** -3. The **erc20-swap canister** checks the current gas price and returns the fee Alice must allow the canister to pay for her -4. Alice gives **ckEth** allowance to the **erc20-swap canister** for at least the fee value -5. Alice gives **EKOKE** allowance to the **erc20-swap canister** for the amount she wants to swap -6. Alice calls **swap** on the **erc20-swap canister** providing the Ethereum address she wants to receive the ERC20 tokens to and the amount she will to swap -7. At this point the **erc20-swap canister** verifies - 1. Alice has enough allowance for ckETH to cover the fee costs - 2. Alice has the ekoke balance she wants to swap - 3. Alice has given allowance for the balance she wants to swap -8. **erc20-swap canister** calls `transcribeSwap` on the ERC20 smart contract -9. **erc20-swap canister** transfers the amount from Alice account to its account -10. the ERC20 smart contract mints amount of ekoke tokens to the provided ethereum account -11. Alice can now see her value being transferred from IC to Ethereum. - -### Swap ERC20 into ICRC - -1. Alice wants to swap 100 EKOKE from **ERC20** back into **ICRC** -2. Alice calls `swap` on the ERC20 smart contract, providing her IC principal and the amount she wants to swap -3. the ERC20 sc validates her balance -4. the ERC20 sc burns the token amount she has provided from her account -5. the ERC20 sm emits the `EkokeSwapped` event passing the principal and the amount as arguments -6. **erc20-swap canister** catches the events and transfers the amount from its account to the account specified in the event - -## ckETH / ETH Swap - -Since the conversion from ckETH to ETH is rather complex and requires time, it cannot be done directly during the swap, but it must be scheduled as an async task. - -The task will have to: - -1. Check the ckETH amount on the canister balance using `icrc1_balance`. The balance will have to be 0.03 ckETH at least . -2. Approve the ckETH minter canister to spend the current ckETH amount on the ckETH ledger canister -3. Call `withdraw_eth` on the ckETH minter canister providing the amount of eth we want to withdraw - -For more details: . diff --git a/docs/canisters/ekoke-liquidity-pool.md b/docs/canisters/ekoke-liquidity-pool.md deleted file mode 100644 index 0c03b98..0000000 --- a/docs/canisters/ekoke-liquidity-pool.md +++ /dev/null @@ -1,79 +0,0 @@ -# EKOKE Liquidity Pool Canister - -- [EKOKE Liquidity Pool Canister](#ekoke-liquidity-pool-canister) - - [Introduction](#introduction) - - [Refund to investors](#refund-to-investors) - - [API](#api) - - [withdraw\_refund](#withdraw_refund) - - [HTTP API](#http-api) - - [Request protocol](#request-protocol) - - [Request Body](#request-body) - - [HTTP Methods](#http-methods) - - [liquidityPoolBalance](#liquiditypoolbalance) - - [liquidityPoolAccounts](#liquiditypoolaccounts) - -## Introduction - -In order to guarantee a real value to the $EKOKE token, the EKOKE DAO manages a Liquidity Pool which has currently a ICP account. - -The liquidity pool can be funded by anyone by sending funds to the account returned by `liquidity_pool_accounts` call. - -For each Deferred NFT sold on the marketplace to the contract buyer, the 10% of the amount paid is sent by to the liquidity pool. This guarantees that the value of a $EKOKE is at least 10% of a NFT value. (More or less, there are also other criteria which determines the value of the token). - -## Refund to investors - -In case a contract fails to be paid, the liquidity pool receives the initial deposit sent by the buyer. The liquidity pool funds are also used to refund the third party investors. - -## API - -See full [DID](../../src/ekoke_liquidity_pool/ekoke-liquidity-pool.did) - -### withdraw_refund - -In case a user was a third party investor in a deferred contract and the buyer fails to pay the installments, the contracts get closed by the agent. - -After the contract has been closed, each investor can call the method `withdraw_refund` to withdraw the refund ICP tokens from the liquidity pool providing the subaccount they want to receive the icp tokens to. - -## HTTP API - -The deferred canister also exposes an HTTP API - -### Request protocol - -| Method | GET | -|--------------|------------------| -| Content-type | application/json | - -### Request Body - -The request body must be JSON encoded, and must follow this syntax - -| Name | Type | Description | -|--------|--------|----------------------------------------------| -| method | string | HTTP method name | -| params | json | key-value map of optional request parameters | - -### HTTP Methods - -This list contains the http available methods with its parameters and response. - -#### liquidityPoolBalance - -Returns an object containing the balance for each account in the liquidity pool - -```json -{ - "icp": nat -} -``` - -#### liquidityPoolAccounts - -Returns the account for the liquidity pool - -```json -{ - "icp": { - "owner": "principal" - } -} diff --git a/docs/canisters/marketplace.md b/docs/canisters/marketplace.md deleted file mode 100644 index fff18d1..0000000 --- a/docs/canisters/marketplace.md +++ /dev/null @@ -1,98 +0,0 @@ -# Marketplace - -- [Marketplace](#marketplace) - - [Introduction](#introduction) - - [API](#api) - - [get\_token\_price\_icp](#get_token_price_icp) - - [buy\_token](#buy_token) - - [admin\_set\_ekoke\_ledger\_canister](#admin_set_ekoke_reward_pool_canister) - - [admin\_set\_ekoke\_liquidity\_pool\_canister](#admin_set_ekoke_liquidity_pool_canister) - - [admin\_set\_deferred\_canister](#admin_set_deferred_canister) - - [admin\_set\_xrc\_canister](#admin_set_xrc_canister) - - [admin\_set\_icp\_ledger\_canister](#admin_set_icp_ledger_canister) - - [admin\_set\_admins](#admin_set_admins) - - [admin\_set\_interest\_rate\_for\_buyer](#admin_set_interest_rate_for_buyer) - - [admin\_cycles](#admin_cycles) - - [Buy process](#buy-process) - -## Introduction - -The Marketplace canister is the canister which takes care of intermediate the sell of the Deferred NFTs and to notify the ekoke-ledger canister to send reward to the first buyer of a NFT and to send funds to the ekoke-liquidity-pool canister. - -From a technical perspective the marketplace canister doesn't provide any user interface, but just two call to provide a way to achieve the sell. - -This means that anybody can implement their own interface for the ekoke marketplace. - -## API - -The full API can be seen on the canister [DID](../../src/marketplace/marketplace.did) - -### get_token_price_icp - -Get ICP token price for the provided token - -### buy_token - -See **Buy process** - -### admin_set_ekoke_reward_pool_canister - -set the ekoke ledger canister - -### admin_set_ekoke_liquidity_pool_canister - -set principal for ekoke-liquidity-pool canister - -### admin_set_deferred_canister - -update the principal for the deferred canister - -### admin_set_xrc_canister - -update the canister for XRC. - -### admin_set_icp_ledger_canister - -update the principal of the ICP ledger canister - -### admin_set_admins - -set canister administrators - -### admin_set_interest_rate_for_buyer - -set the interest rate for contract buyer. - -### admin_cycles - -get canister cycles - -## Buy process - -1. The user goes to the marketplace page of a token -2. The user calls with their IC wallet `get_token_icp` to marketplace to get the current price as $ICP token for the token -3. marketplace calls `deferred` to get the contract info for that token -4. deferred returns the contract to marketplace -5. marketplace set `fiat_value` to `token.value` -6. marketplace checks whether the caller is a contract buyer - 1. if so, it multiplies the fiat_value by `1.1` -7. marketplace sets `currency = contract.currency` -8. marketplace checks whether they already have an exchange rate between currency and ICP for the current date - 1. if not, it queries the XRC canister to get the current exchange rate -9. marketplace gets the ICP price for the contract value -10. marketplace sums to the price the ICP canister fee - 1. if the caller is buyer of the contract, it sums the fee twice -11. marketplace returns the ICP price to the user -12. the user gives allowance to the marketplace canister for the ICP value of the token -13. the user calls `buy_token` providing the token id. -14. marketplace verifies the given allowance for ICP is equal to the contract ICP value -15. marketplace checks whether the caller is a contract buyer - 1. if so, marketplace gets the principal of the liquidity pool from `ekoke-liquidity-pool` - 2. if so, marketplace sends the 10% of the ICP price to the liquidity pool calling `icrc2_transfer_from` on the ICP ledger canister -16. the marketplace sends to the current owner of the NFT the ICP value with `icrc2_transfer_from` on the ICP ledger -17. marketplace calls `transfer_from` on deferred to transfer the ownership of the NFT to the caller -18. marketplace verifies whether the token had a previous owner - 1. if true, marketplace calls `send_reward` on `ekoke-ledger` canister and sends the reward to the caller -19. marketplace verifies whether the caller is buyer of the contract - 1. if true, it calls `burn` on deferred canister and burns the NFT. - 2. if true, it transfers the interest on the liquidity pool diff --git a/docs/contracts/Deferred.md b/docs/contracts/Deferred.md new file mode 100644 index 0000000..79cbc72 --- /dev/null +++ b/docs/contracts/Deferred.md @@ -0,0 +1,58 @@ +# Deferred + +- [Deferred](#deferred) + - [Introduction](#introduction) + - [Contract creation](#contract-creation) + - [Lazy minting](#lazy-minting) + - [Token transfers](#token-transfers) + +--- + +## Introduction + +Deferred ERC721 is indeed an implementation in Solidity of a ERC721 standard to be able to implement an NFT for each mortgage installment to pay a real estate on the EKOKE DAO. + +Each NFT URI points to the contract URL on the [deferred-data](../canisters/deferred-data.md) canister. + +## Contract creation + +NFTs on Deferred are minted with the contract creation. + +The `createContract` method can only be called by the [deferred-minter](../canisters/deferred-minter.md) canister, which provides the following data: + +```solidity +struct CreateContractRequest { + /// @dev The id of the contract + uint256 contractId; + /// @dev metadata uri pointing to deferred-data canister uri + string metadataUri; + /// @dev Contract sellers + SellerRequest[] sellers; + /// @dev The contract buyers + address[] buyers; + /// @dev Reward for buying a token + uint256 ekokeReward; + /// @dev The price of the token in USD + uint256 tokenPriceUsd; + /// @dev the amount of tokens to mint + uint256 tokensAmount; +} +``` + +Once called, if the data are valid, the deferred contract will call the [Reward Pool](./RewardPool.md) to reserve `ekokeReward * tokensAmount` EKOKE and finally it will **lazy mint** the tokens. + +### Lazy minting + +Since a contract can thousands of tokens, we can't of course mint all the tokens at the contract creation, so we had to find a way to make it extremely cheap: this method is called Lazy minting. + +How does it work? So basically we know that all the tokens of a contract are actually all the same and all have the same owner. The only thing that can distinguish one token from another, is the ID. + +So instead of actually minting them, we just store the token ID range in the contract data. So the only thing we actually store at the contract creation is the contract data with the ID range for the tokens. + +The tokens are eventually phyisically minted when they are first bought. So when a token is bought, the `transferFrom` method is called and this causes the contract to check whether the contract is already minted or not. If it's not, the contract only at this point is minted to the new owner and so it becomes phyisically minted. + +## Token transfers + +It's important to know that **Deferred ERC721 can't be transferred by the token owner**, but only by the [Marketplace](./Marketplace.md). The marketplace is always allowed to transfer tokens, while the user is never allowed to. It's neither allowed to call `approve` on the contract. + +This is of course done to prevent users from selling the tokens breaking their intrisic price, defined at the contract creation. The **token price will never change**. diff --git a/docs/contracts/Ekoke.md b/docs/contracts/Ekoke.md new file mode 100644 index 0000000..d2d41ee --- /dev/null +++ b/docs/contracts/Ekoke.md @@ -0,0 +1,23 @@ +# EKOKE ERC20 Token + +- [EKOKE ERC20 Token](#ekoke-erc20-token) + - [Introduction](#introduction) + - [Tokenomics](#tokenomics) + +--- + +## Introduction + +The EKOKE ERC20 token is the main token of the DAO and it's used both as a reward for third party investors for buying the Deferred NFTs and as a store of value. + +It's capability of being a store of value is given by the fact that EKOKE tokens are scarce and the amount of minted tokens will decrease quickly in the range of a decade. + +## Tokenomics + +The token has total maximum supply of `888_010_101_000_000` which is **8,880,101.01000000 tokens with 8 decimals**. + +At the creation of the contract the tokens minted will be 0, but tokens can be created both by the contract owner and the [Reward Pool](./RewardPool.md). + +The reward pool will mint up to the 66.6% of the total supply (`592_006_734_000_000`) using the algorithm described in [the reward document](../reward.md); while the remaining 33.3% will be minted by the owners according to this schema: + +![ekoke tokenomics chart](../../assets/images/ekoke-supply.png) diff --git a/docs/contracts/Marketplace.md b/docs/contracts/Marketplace.md new file mode 100644 index 0000000..05475a5 --- /dev/null +++ b/docs/contracts/Marketplace.md @@ -0,0 +1,59 @@ +# Marketplace + +- [Marketplace](#marketplace) + - [Introduction](#introduction) + - [Buy process](#buy-process) + - [The user gives USDT allowance to the Marketplace to buy the desider token](#the-user-gives-usdt-allowance-to-the-marketplace-to-buy-the-desider-token) + - [Buy token](#buy-token) + +--- + +## Introduction + +The marketplace contract has the purpose of being able to buy Deferred ERC721 tokens using USDT. + +The token buyer will also receive EKOKE tokens as a reward as described in the [reward document](../reward.md) with the reward amount established at the contract registration. + +Mind that **ONLY the first token buyer will receive the reward**. + +If the Buyer of the contract buys the token, he will pay the token price plus the interest rate. The interest rate is then transferred to the **Marketplace wallet**, where it will be sealed to back the EKOKE token value. + +## Buy process + +The Marketplace contract provides two functions useful to buy a process, the `buyToken` method and the `tokenPriceForCaller` view method. + +```solidity +function tokenPriceForCaller( + uint256 _tokenId +) external view returns (uint256) {} +``` + +```solidity +function buyToken(uint256 _tokenId) external {} +``` + +So the buy process is divided in two parts. + +### The user gives USDT allowance to the Marketplace to buy the desider token + +1. The user calls `tokenPriceForCaller` providing the ID of the token he wills to buy +2. The contract returns the USDT price to pay for the token + 1. `sellContract.tokenPriceUsd` if the caller is a third party + 2. `sellContract.tokenPriceUsd + interests(sellContract.tokenPriceUsd)` if the caller is a contract buyer +3. The user gives allowance to `usdErc20()` to the address for the returned amount. + +### Buy token + +At this point the user can call `buyToken` providing the ID of the token he got the price for. + +At this point the smart contract will perform the following operations: + +1. Get the contract information related to the provided tokenId +2. Checks whether the caller is a contract buyer +3. Checks whether that token has already been sold on the marketplace +4. `shouldSendReward = contract.ekokeReward > 0 && !tokenAlreadySold` +5. Calculates the pay the caller will pay (as seen before) +6. Checks whether the caller has given allowance for the price to pay +7. The contract transfers USDT for an amount of `tokenPriceUsd` to the current NFT owner +8. If the caller is the deferred contract buyer, the contract transfers the interest rate of `tokenPriceUsd` to the Marketplace wallet +9. The contract transfers the NFT to the caller using `safeTransferFrom` on `Deferred` diff --git a/docs/contracts/RewardPool.md b/docs/contracts/RewardPool.md new file mode 100644 index 0000000..880cbd7 --- /dev/null +++ b/docs/contracts/RewardPool.md @@ -0,0 +1,12 @@ +# Reward Pool + +- [Reward Pool](#reward-pool) + - [Introduction](#introduction) + +--- + +## Introduction + +The reward pool contract is used to track the current reward pools reserved for each contract and to distribute the rewards to the users who buy NFTs on the [Marketplace](./Marketplace.md). + +The Reward Pool contract provides two methods, one to reserve the pool `reservePool`, which is called by [Deferred](./Deferred.md) to reserve the pool when a **Sell contract** is being created, and a method `sendReward` called by [Marketplace](./Marketplace.md) to send the reward to the user who is buying the NFT on the marketplace. diff --git a/docs/canisters/ekoke-reward-pool.md b/docs/reward.md similarity index 58% rename from docs/canisters/ekoke-reward-pool.md rename to docs/reward.md index 7de8522..1aa13bf 100644 --- a/docs/canisters/ekoke-reward-pool.md +++ b/docs/reward.md @@ -1,14 +1,10 @@ -# EKOKE Reward Pool +# EKOKE Rewards -- [EKOKE Reward Pool](#ekoke-reward-pool) +- [EKOKE Rewards](#ekoke-rewards) - [Introduction](#introduction) - - [Supply](#supply) - - [API](#api) - - [reserve\_pool](#reserve_pool) - - [get\_contract\_reward](#get_contract_reward) - - [send\_reward](#send_reward) - - [available\_liquidity](#available_liquidity) + - [When are rewards distributed](#when-are-rewards-distributed) - [Reward Pool](#reward-pool) + - [How to reserve a manual pool](#how-to-reserve-a-manual-pool) - [Reward Deflationary Algorithm](#reward-deflationary-algorithm) - [Reward Multiplier Coefficient](#reward-multiplier-coefficient) - [Avidity](#avidity) @@ -20,46 +16,22 @@ ## Introduction -The EKOKE Reward Pool canister takes care of managing the rewards for user buying Deferred NFTs on the Marketplace. +This document describes how EKOKE manages the EKOKE token rewards. -For each contract registered on Deferred a **Reward pool** is reserved and each time a user buys an NFT from the marketplace, if it's the first time that NFT is sold, the reward for the pool is sent to the user who has bought the NFT. +## When are rewards distributed -## Supply - -At the creation of the SNS Ledger Canister for EKOKE, the following initial supply will be given to the EKOKE reward canister out of the total EKOKE Supply - -**xxxxx** on **8880101.01000000** tokens - -## API - -The full DID can be found [HERE](../../src/ekoke_reward_pool/ekoke-reward-pool.did) - -### reserve_pool - -Given an ID and a certain EKOKE amount, it reserves a pool for the given contract, transferring the EKOKE amount from the caller's account to the reward pool. - -Before calling this method, the caller must have given allowance to the reward-pool canister for at least `amount + ICRC_fee` . - -### get_contract_reward - -This method can only be called by the deferred canister and it is used to get the reward to set for a token associated to a new contract. - -### send_reward - -This method can only be called by the marketplace canister and it is used to send the reward after a token is sold. - -### available_liquidity - -Get the available liquidity in the reward pool +Rewards are automatically given to the buyer of a **Deferred ERC721** when he buys a token on the [Marketplace contract](./contracts/marketplace.md). ## Reward Pool The reward pool can work in two different modes: -- Automatic Mode: If there is still available liquidity in the reward pool, then this mode should be preferred. Basically when a contract is created, the deferred canister will query the reward pool to check whether there is already a pool associated to that contract. If there's not a certain liquidity will be reserved as reward for token buyers following the Reward Pool Algorithm, which you can read about in the next chapter. -- Manual Mode: A user can opt to reserve a EKOKE pool using its EKOKE tokens for a contract before is created. This mode could be preferred to promote the sell of his real-estate or is mandatory in the case the liquidity pool of the reward canister is empty. +- **Automatic Mode:** If there is still available liquidity in the reward pool, then this mode should be preferred. Basically when a contract is created, the deferred canister will query the reward pool to check whether there is already a pool associated to that contract. If there's not a certain liquidity will be reserved as reward for token buyers following the Reward Pool Algorithm, which you can read about in the next chapter. +- **Manual Mode**: A user can opt to reserve a EKOKE pool using its EKOKE tokens for a contract before is created. This mode becomes mandatory in the case the liquidity pool of the reward canister is empty. + +### How to reserve a manual pool -Note that once the reward pool is empty, it basically can never be refilled, unless someone deliberately sends tokens to the canister account. +In order to reserve a manual pool, the user must burn a certain amount of their tokens. Once their tokens are burned, the reward pool can mint those tokens again as rewards. ## Reward Deflationary Algorithm @@ -70,7 +42,7 @@ Once a contract is registered on Deferred, Deferred will call the reward pool ca The reward, if a pool is still not reserved for the contract will be calculated using the following formula: ```txt -reward = rewardPoolLiquidity * RMC * avidity +reward = (rewardPoolLiquidity * RMC * avidity * tokenPrice) / 100 ``` ![reward-formula](../../assets/images/reward-formula.png) @@ -107,7 +79,10 @@ The avidity value follows this schema. ### Token Price -TODO: +The token price is used to proportionate the amount of reward tokens based on the NFT price. The reason behind this is to prevent tokens with for instance value of 1$ to have the same reward as tokens with value of 100$. + +The base reward is set for token price of 100$. So it will be `1000%` if the token price is `1000$` or it will be +10% with a token price of 10$ etc. ### Reward Analysis