diff --git a/.github/workflows/github_pages.yml b/.github/workflows/github_pages.yml deleted file mode 100644 index fd32bf6cd..000000000 --- a/.github/workflows/github_pages.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Documentation - -on: - pull_request: - branches: [main] - push: - branches: [main] - -jobs: - checks: - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: '16.x' - - name: Test Build - run: | - cd docs - if [ -e yarn.lock ]; then - yarn install --frozen-lockfile - elif [ -e package-lock.json ]; then - npm ci - else - npm i - fi - npm run build - gh-deploy: - if: github.event_name == 'push' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - ref: ob-docs - - uses: actions/setup-node@v1 - with: - node-version: '16.x' - - uses: webfactory/ssh-agent@v0.5.4 - with: - ssh-private-key: ${{ secrets.GH_PAGES_DEPLOY }} - - name: Update ob-docs branch - env: - USE_SSH: true - GIT_USER: git - run: | - git config --global user.email "actions@github.com" - git config --global user.name "gh-actions" - git remote set-url origin git@github.com:Brushfam/openbrush-contracts.git - git merge origin/main - git push - - name: Release to GitHub Pages - env: - USE_SSH: true - GIT_USER: git - run: | - cd docs - git config --global user.email "actions@github.com" - git config --global user.name "gh-actions" - if [ -e yarn.lock ]; then - yarn install --frozen-lockfile - elif [ -e package-lock.json ]; then - npm ci - else - npm i - fi - npm run deploy \ No newline at end of file diff --git a/.github/workflows/github_pages_release.yml b/.github/workflows/github_pages_release.yml deleted file mode 100644 index 4f7ff99b0..000000000 --- a/.github/workflows/github_pages_release.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Update and deploy new documentation version - -on: - release: - types: [published] - branches: [main] - -jobs: - gh-deploy-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - ref: ob-docs - - uses: actions/setup-node@v1 - with: - node-version: '16.x' - - uses: webfactory/ssh-agent@v0.5.4 - with: - ssh-private-key: ${{ secrets.GH_PAGES_DEPLOY }} - - name: Release to GitHub Pages - env: - USE_SSH: true - GIT_USER: git - run: | - cd docs - git config --global user.email github-actions@github.com - git config --global user.name github-actions - if [ -e yarn.lock ]; then - yarn install --frozen-lockfile - elif [ -e package-lock.json ]; then - npm ci - else - npm i - fi - npm run docusaurus docs:version ${{ github.event.release.tag_name }} - npm run build - npm run deploy - git remote set-url origin git@github.com:Brushfam/openbrush-contracts.git - git checkout -b gh-deploy-release-branch - git add -A - git commit -m "New docs version" - git checkout ob-docs - git merge gh-deploy-release-branch - git push \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 3e3f03844..f35862258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ edition = "2018" license = "MIT" readme = "README.md" repository = "https://github.com/Brushfam/openbrush-contracts" -documentation = "https://docs.openbrush.io" -homepage = "https://727.ventures" +documentation = "https://learn.brushfam.io/docs/openbrush" +homepage = "https://brushfam.io/" description = "OpenBrush library for smart contract development on ink!." keywords = ["wasm", "brushfam", "smart-contracts", "blockchain", "ink"] categories = ["no-std", "embedded"] diff --git a/README.md b/README.md index 485e19812..03638ff23 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ to simplify unit testing of you code. functionality for your code. - Read our **documentation** in [doc](https://learn.brushfam.io/docs/openbrush). - Go through our **examples** in [examples](examples) to check hot to use the library and ink!. -- Check the [**example of project struct**](https://github.com/Brushfam/openbrush-contracts/tree/main/example_project_structure) and [according documentation](https://docs.openbrush.io/smart-contracts/example/overview). +- Check the [**example of project struct**](https://github.com/Brushfam/openbrush-contracts/tree/main/example_project_structure) and [according documentation](https://learn.brushfam.io/docs/OpenBrush/smart-contracts/example/overview). Not sure where to start? Use [the interactive generator](https://openbrush.io) to bootstrap your contract and learn about the components offered in OpenBrush. diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 271c4b757..c0e3c096e 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" license = "MIT" readme = "README.md" repository = "https://github.com/Brushfam/openbrush-contracts" -documentation = "https://docs.openbrush.io" -homepage = "https://727.ventures" +documentation = "https://learn.brushfam.io/docs/openbrush" +homepage = "https://brushfam.io/" description = "Reusable implementations of contracts and traits for interaction with them." keywords = ["wasm", "brushfam", "smart-contracts", "blockchain", "ink"] categories = ["no-std", "embedded"] diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index b2d6de306..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -# Dependencies -/node_modules - -# Production -/build - -# Generated files -.docusaurus -.cache-loader - -# Misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 55d0c3ef4..000000000 --- a/docs/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Website - -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. - -### Installation - -``` -$ yarn -``` - -### Local Development - -``` -$ yarn start -``` - -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. - -### Build - -``` -$ yarn build -``` - -This command generates static content into the `build` directory and can be served using any static contents hosting service. - -### Deployment - -``` -$ GIT_USER= USE_SSH=true yarn deploy -``` - -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/docs/babel.config.js b/docs/babel.config.js deleted file mode 100644 index e00595dae..000000000 --- a/docs/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], -}; diff --git a/docs/docs/assets/20220604_183027_go-to-polkadot.gif b/docs/docs/assets/20220604_183027_go-to-polkadot.gif deleted file mode 100644 index 8a68a513a..000000000 Binary files a/docs/docs/assets/20220604_183027_go-to-polkadot.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_122254_upload-contract.gif b/docs/docs/assets/20220605_122254_upload-contract.gif deleted file mode 100644 index 9e95814e6..000000000 Binary files a/docs/docs/assets/20220605_122254_upload-contract.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_124705_balance-of.gif b/docs/docs/assets/20220605_124705_balance-of.gif deleted file mode 100644 index ab2eeca7a..000000000 Binary files a/docs/docs/assets/20220605_124705_balance-of.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_125943_contracts-node.gif b/docs/docs/assets/20220605_125943_contracts-node.gif deleted file mode 100644 index b01f7d0fb..000000000 Binary files a/docs/docs/assets/20220605_125943_contracts-node.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_132655_shibuya_testnet.gif b/docs/docs/assets/20220605_132655_shibuya_testnet.gif deleted file mode 100644 index bc81558e4..000000000 Binary files a/docs/docs/assets/20220605_132655_shibuya_testnet.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_132803_transfer-shibuya.gif b/docs/docs/assets/20220605_132803_transfer-shibuya.gif deleted file mode 100644 index fc6ff98e0..000000000 Binary files a/docs/docs/assets/20220605_132803_transfer-shibuya.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_133034_check-balance-of-shibuya.gif b/docs/docs/assets/20220605_133034_check-balance-of-shibuya.gif deleted file mode 100644 index afa8045b5..000000000 Binary files a/docs/docs/assets/20220605_133034_check-balance-of-shibuya.gif and /dev/null differ diff --git a/docs/docs/assets/20220605_155001_create-wallet.gif b/docs/docs/assets/20220605_155001_create-wallet.gif deleted file mode 100644 index 187a4b247..000000000 Binary files a/docs/docs/assets/20220605_155001_create-wallet.gif and /dev/null differ diff --git a/docs/docs/deployment.md b/docs/docs/deployment.md deleted file mode 100644 index 17f5c4fba..000000000 --- a/docs/docs/deployment.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -sidebar_position: 4 -title: Deployment -sidebar_label: Deployment ---- - -- Deployment of ink! based smart contracts - -This document contains description of how to deploy and test smart contracts locally and in testnet. - -### Ecosystem - -Polkadot doesn't support smart contract execution, only parachains can provide this functionality. More information -about how it works you can find on [official wiki](https://wiki.polkadot.network/docs/en/build-smart-contracts). - -The list of standalone blockchain/parachains that support ink! smart contracts: - -* [Astar](https://astar.network/) - -### Overview - -- To deploy contract you should build your own contract or get some example from [Openbrush](https://github.com/Brushfam/openbrush-contracts/tree/main/examples). You can find instruction how to build ink! smart contract in [docs](https://ink.substrate.io/getting-started/building-your-contract) -- You have to choose substrate network to deploy your contract. - There are several option you have: - - - Local substrate node with pallet contracts. - - `Canvas` network - - `Shibuya` - Astar testnet - - `Shiden` - Astar canary network - - `Astar` main network (will support pallet contracts in near futures) - - Other networks which supports pallet contracts -- Be sure that you have installed `polkadot.js.org` [wallet](#install-polkadot-extention-for-your-browser-and-create-account) extenstion for your browser -- Here you can find how to [Build](https://ink.substrate.io/cargo-contract-cli/#usage) **ink!** smart contract -- Let's [deploy to local network](#deployment-on-local-network) -- You can manuly [call](#call-the-smart-contract) our deployed contract -- [Canvas](https://github.com/paritytech/cumulus#canvas-) - a Smart Contracts [Parachain](https://wiki.polkadot.network/docs/learn-parachains) which was deployed on [Polkadot](https://polkadot.network/ru/) test network - [Rococo](https://polkadot.network/tag/rococo/). You need to get free `ROC` token using [faucet](#rococo-faucet) to deploy contract to Canvas network. Finally deploy your ink! smart contract to [canvas](#deploy-to-=anvas) -- [Astar](#astar) - [WASM](https://webassembly.org/) + [EVM](https://ethereum.org/en/developers/docs/evm/) Hub on [Polkadot](https://polkadot.network/). More info about astar [here](https://docs.astar.network/) -- You can deploy **ink!** smart contract to [Shibuya](#deploy-to-shibuya) (astar test network). How to get free `SBY` using [faucet](https://docs.astar.network/integration/testnet-faucet) - -### Build - -- navigate to `./openbrush/examples/psp22` -- build ink! contract using: - -``` -cargo contract build -``` - -Once the compilation is completed, a target folder is created. In this folder, under the ink subdirectory, you will be able to see a `my_psp22.wasm` file and a `metadata.json` file. `my_psp22.wasm` is your contract that has been compiled to web assembly and the `metadata.json` is a JSON abstraction of your contract. - -You will find 3 files in folder `./openbrush/examples/psp22/target/ink` - -- `my_psp22.contract` (code + metadata) -- `my_psp22.wasm` (the contract’s code) -- `metadata.json` (the contract’s metadata) - -### Install polkadot extention for your browser and create account - -- Navigate to [Polkadot.js.org](https://polkadot.js.org/extension/) extention tab and install to your browser. You need wallet extention to sign and submit transaction for deployment contract and manual testing via UI -- Create or import polkadot account. You need account and some tokens on that account to deploy and test contracts on test network like `Canvas`, `Shibuya` or main network like `Shiden` and `Astar` in near futures. How to get free tokens for test net you will find [there](https://paritytech.github.io/polkadot-testnet-faucet/) -- Please write down your wallet's mnemonic seed and keep it in a safe place. The mnemonic can be used to restore your wallet. Keep it carefully to not lose your assets. - -![](assets/20220605_155001_create-wallet.gif) - -### Deployment on local network - -- Substrate framework pre requisites [guide](https://ink.substrate.io/getting-started/setup/#substrate-framework-pre-requisites) -- Run a Substrate Node [guide](https://ink.substrate.io/getting-started/running-substrate) -- Navigate to the [Polkadot.js.org](https://polkadot.js.org) in a web browser -- Verify that you are connected to the [Local Node](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer). - -![](assets/20220604_183027_go-to-polkadot.gif) - -- Upload and deploy contract - - Click `Developer` -> `Contracts` -> `Upload & deploy code`. Specify the user account to use for deployment. Any fees will be deducted from deployment account. Upload `*.contract` file. `*.contract` file contains the `ABI` for the `WASM` code. The `ABI` is required and stored for future operations such as sending messages. Type a descriptive name for the smart contract. Set value `1000` for `totalSupply` when initialize the contract using constructor. And finally click `Sign and Submit` transaction.![](assets/20220605_122254_upload-contract.gif) - -The Polkadot UI displays information about the content of the smart contract. - -Depending on the account you used, you might be prompted for the account password. If you used a predefined account, you won’t need to provide a password. - -### Call the smart contract - -Now that your contract has been deployed on the blockchain, you can interact with it. Our deployed smart contract has functions — `totalSupply()` and `balanceOf()` — and you can use the Polkadot UI to try them out. - -To test the `balanceOf()` function: - -Select any account from the Account list. - -This contract doesn’t place restrictions on who is allowed to send the `balanceOf()` request. - -- Click `Read`. Verify that the value `1,000,000,000,000,000` is returned in the Call Results. - -![](assets/20220605_124705_balance-of.gif) - -### Rococo Faucet - -**Canvas** - parachain on **Rococo** ‒ a testnet for **Polkadot and Kusama parachains**. -As a first step, you should create an account. [See here for a detailed guide.](https://wiki.polkadot.network/docs/learn-account-generation) - -As a second step, you have to get `ROC` testnet tokens through the [Rococo Faucet](https://wiki.polkadot.network/docs/learn-DOT#getting-rococo-tokens). This is a chat room in which you need to write: - -`!drip YOUR_SS_58_ADDRESS:1002` - -send message to [#rococo-faucet:matrix.org](https://matrix.to/#/#rococo-faucet:matrix.org) - -The number `1002` is the parachain id of **Canvas on Rococo**, by supplying it the faucet will teleport `ROC` tokens directly to your account on the parachain - -### Deploy to Canvas - -- Navigate to the [Polkadot.js.org](https://polkadot.js.org/appshttps://paritytech.github.io/contracts-u) in a web browser. -- Verify that you are connected to the **Contracts Node**. - -![](assets/20220605_125943_contracts-node.gif) - -- Upload `my_psp22.contract` file the same way as to local node but we need some `ROC` tokens -- Use wallet which contains `ROC` tokens - -### Astar - -* **Astar** - Astar is a multi-chain smart contract platform that supports multiple - blockchains and virtual machines. -* **Astar/Shiden Network Family:** - Before starting the deployment, it's important to understand Astar/Shiden Network family. You should change the network based on what you want to do. Currently, there are 3 networks available, **Shiden**, **Shibuya**, and **Local** network. All networks support own standard Substrate RPC and EVM RPC. -* **Astar and Shiden**: - Astar is the network that aims to be the parachain of Polkadot. Shiden is the sister network of Astar which is the parachain of Kusama. Basically, Astar and Shiden share the same code base. The biggest difference is the economic impact. - -Please note that Shiden has its real economic value. So you need to pay in SDN, the native token of Shiden, when you execute transactions. You can buy SDN on crypto exchanges. - -* **Shibuya**: - Shibuya is the test network of Shiden and is connected to our own Relaychain. So Shibuya behaves almost the same as Shiden. Any new features are tested on Shibuya first and then deployed on Shiden. SBY, the native token of Shibuya, has no economic value and is available through our [faucet](https://docs.astar.network/integration/testnet-faucet). The best practice is to testing smart contract on Shibuya before deploying it on Shiden to check whether your smart contract works well or not. -* **Astar local Network**: - Here is [tutorial](https://docs.astar.network/tutorial/develop-and-deploy-your-first-smart-contract-on-aster-shiden-evm/running-local-network) how to run local network - -### Deploy to Shibuya - -- Build smart contract the same way as for [local node](#build) -- Be sure that you have polkadot [wallet](https://docs.astar.network/stake2earn-festival/how-to-make-a-kusama-polkadot-address#recommend-polkadot-.js-browser-plugin) exension in your browser -- [Create polkadot account](https://docs.astar.network/tutorial/how-to/how-to-make-a-kusama-polkadot-address#create-account) if not have yet -- Use **Faucet** to get free **SBY** [token](https://docs.astar.network/integration/testnet-faucet) -- Go to [polkadot.js.org](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.shibuya.astar.network#/explorer) -- Switch network to **Shibuya** and deploy contract - -![](assets/20220605_132655_shibuya_testnet.gif) - -We use **“messages”** to communicate with smart contracts. - -There are 2 types of messages: - -- messages that change a smart contract’s state should be sent as transactions -- messages that don’t change a state can be made by using RPC calls - -Next, let’s change the smart contract state by sending a transaction that calls the `transfer()` function. - -![](assets/20220605_132803_transfer-shibuya.gif) - -As expected, the value that was stored in the smart contract changed from `0` to `1` after the `transfer()` transaction is successfully executed - -![](assets/20220605_133034_check-balance-of-shibuya.gif) - -Congratulations, you deployed and test your first L1 Smart Contract to **Shibuya** network! diff --git a/docs/docs/evm-wasm-smart-contracts.md b/docs/docs/evm-wasm-smart-contracts.md deleted file mode 100644 index 3debbcab7..000000000 --- a/docs/docs/evm-wasm-smart-contracts.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -sidebar_position: 5 -title: EVM vs WASM Smart Contracts ---- - -This short article describes differences between EVM Smart Contracts & Substrate WASM Smart contracts. - -## EVM - -### Background -Ethereum was the first Turing-complete blockchain and was mainly developed because of limitations of Bitcoin's script. In order to execute smart contracts, a sandbox environment is needed. The sandbox environment where Smart contracts are executed in Ethereum is the **Ethereum virtual machine** (*EVM*) - -### Bytecode -EVM uses a stack-based bytecode language called EVM bytecode. EVM bytecode is a series of OP_CODEs (instructions) that are executed by the EVM. These OP_CODEs offer rather limited instructions compared to a full-blown language (Java, WASM, ..) - -The primitive of the bytecode is *256-bit* integer, which can be a big limitation, as it is needed to use *256-bit* integers to do calculations on even small numbers. Moreover, it will take 256 bits of memory for any value that is stored in persistent storage. - -The main OP_CODEs are **SLOAD** to load data, **SSTORE** to write a *256-bit* integer to storage and **CALL** to call another contract [full list of OP_CODES](https://github.com/crytic/evm-opcodes). It's via these instructions that EVMs interact with the chain. - -![image info](pictures/eth-1.png) - -### Gas model -The incentive model of interacting with EVM is gas. It acts like a fee that is calculated for each instruction someone executes. Every instruction has a certain pre-calculated fee amount, and the total gas spent is the sum of all the instructions that were executed. - -Please check the entire table of [fees](https://blockgeeks.com/wp-content/uploads/2018/03/image2-2.png) -A gas fee is only due when interacting with EVM. There is no charge for the space you are using for storage, and it results in having a ton of dead code (or non-used contracts) stay on Ethereum chain forever. - -### Evm process - -![image info](pictures/eth2.png) - -## Substrate Contract-Pallet - -### Intro -`pallet-contract` is a module in Substrate (the framework to build blockchain on `Polkadot`). Its purpose is a sandbox environment, which provides WASM interpreter and allows executing WASM smart contracts. - -![image info](pictures/WASM1.png) - -### WASM interpreter - -At the moment, contract pallet uses [wasmi](https://github.com/paritytech/wasmi) as the interpreter. Wasmi is a pure interpreter-type WASM virtual machine. It is used because execution of smart contract needs a high degree of correctness. - -### Storage rent & Gas - -In order to incentive the deletion of unused code on chain, pallet-contract implemented a storage-rent principle. - -Every smart contract will have a rent to pay for its code and on-chain storage (small amount at every block). When the smart contract fails to pay the rent, the contract will become a Tombstone (its storage will be deleted). - -The gas system of ethereum (price depending on the complexity of the computation) is still present in Substrate, but the gas is charged after the call is executed, as it is basically a fee on the time of execution (the more time it takes for the node to execute a call, the more caller will pay). The `pallet-contract` defines the [amount of gas](https://substrate.dev/docs/en/knowledgebase/smart-contracts/contracts-pallet) - -### Contract code & instance are decoupled - -Even though `pallet-contract` uses an account model for its contracts (like ethereum does), there is still one big difference: - -When you deploy a WASM smart contract, it will only create a hash of the WASM code, meaning that this contract will have no address, nor will it have some associated storage (so it is impossible to interact with this contract). In Ethereum, though, every contract code deployed on chain has a unique address (instance) and its associated storage. - -After the WASM hash is deployed, one can create as many instances of this contract as they want. Each instance will have a unique address to interact with, as well as its own associated storage. - -Why is it useful: -- Different contract instances with different **constructor parameters** can be instantiated using the same uploaded code, which reduces the space needed on chain to store WASM code -- Storage and balance are decoupled from contract code logic, which provides ability **patch or upgrade** the underlying contract code - -## EVM vs contract-pallet - -- Common point: they both are a sandbox to execute smart contracts -- The engine which executes contracts is different. In Ethereum, as it was the first blockchain to implement a sandbox environment, it is rather limited and slow, compared to a wasm interpreter - Storage rent has been introduced in contract-pallet to incentivize the deletion of unused code -- Contract pallet integrates a [two-step-deployment](https://substrate.dev/docs/en/knowledgebase/smart-contracts/contracts-pallet#two-step-deployment) to decouple contract code and contract instances - - -## Comparing WASM Smart contracts to EVM smart contracts - -- WASM is broadly adopted, while EVM bytecode is only used in EVMs -> There is way more tools available for WASM development -- EVM bytecode can only be compiled from Solidity or Vyper, while WASM can be compiled from a lot of Popular languages (Rust, C/C++, C#, Java, Typescript, Haxe, Kotlin and even from Solidity) -- Excellent integration of Rust to compile in WASM -- Lightweight: it produces lightweight binaries that can ship easily -- performance near native code (2x faster than Javascipt) -- Continually developed by major companies such as Google, Apple, Microsoft, Mozilla, and Facebook. - -[more info here](https://paritytech.github.io/ink-docs/why-webassembly-for-smart-contracts) \ No newline at end of file diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md deleted file mode 100644 index 127732613..000000000 --- a/docs/docs/getting-started.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -sidebar_position: 1 -slug: / -title: OpenBrush documentation -sidebar_label: Getting started ---- - -Welcome to OpenBrush documentation! This documentation aims to guide you through the usage of OpenBrush library. - -## What is OpenBrush -OpenBrush is a library for smart contract development on ink! -It provides standard contracts ([based on PSP](https://github.com/w3f/PSPs)), -as well as useful contracts and macros to help you build ink! smart contracts. - -## Why OpenBrush -OpenBrush attempts to analogize OpenZeppelin perfectly with Rust’s paradigm, -enabling users to import contracts implemented by another user without problems -and reuse the code. -There was a need to have a library that can provide base implementations of -PSPs and to import/reuse them by customizing their own logic. Also, OpenBrush provides other useful -traits and macros to help you build ink! smart contracts in easier way. \ No newline at end of file diff --git a/docs/docs/pictures/WASM1.png b/docs/docs/pictures/WASM1.png deleted file mode 100644 index dcf1e1c30..000000000 Binary files a/docs/docs/pictures/WASM1.png and /dev/null differ diff --git a/docs/docs/pictures/eth-1.png b/docs/docs/pictures/eth-1.png deleted file mode 100644 index aa2d854f2..000000000 Binary files a/docs/docs/pictures/eth-1.png and /dev/null differ diff --git a/docs/docs/pictures/eth2.png b/docs/docs/pictures/eth2.png deleted file mode 100644 index 537bd8e75..000000000 Binary files a/docs/docs/pictures/eth2.png and /dev/null differ diff --git a/docs/docs/smart-contracts/PSP22-Pallet/Extensions/burnable.md b/docs/docs/smart-contracts/PSP22-Pallet/Extensions/burnable.md deleted file mode 100644 index 3c6fbe810..000000000 --- a/docs/docs/smart-contracts/PSP22-Pallet/Extensions/burnable.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -sidebar_position: 3 -title: PSP22 Pallet Burnable ---- - -This example shows how you can reuse the implementation of -[PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet) token with [PSP22Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet/extensions/burnable.rs) extension via `pallet-assets` chain extension. - -## How to use this extension - -First, you should implement basic version of [PSP22 Pallet](../psp22-pallet.md). - -After you can just add implementation of PSP22PalletBurnable via `#[openbrush::implementation(PSP22PalletBurnable)]` attribute. - -```rust -#[openbrush::implementation(PSP22Pallet, PSP22PalletBurnable)] -#[openbrush::contract] -pub mod my_psp22_pallet { - ... -``` - -And that's it! Your `PSP22 Pallet` is now extended by the `PSP22Burnable` extension and ready to use its functions! diff --git a/docs/docs/smart-contracts/PSP22-Pallet/Extensions/metadata.md b/docs/docs/smart-contracts/PSP22-Pallet/Extensions/metadata.md deleted file mode 100644 index bd86f6e72..000000000 --- a/docs/docs/smart-contracts/PSP22-Pallet/Extensions/metadata.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -sidebar_position: 1 -title: PSP22 Pallet Metadata ---- - -This example shows how you can reuse the implementation of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet) token with the [PSP22Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet/extensions/metadata.rs) extension via `pallet-assets` chain extension. - -First, you should implement basic version of [PSP22 Pallet](../psp22-pallet.md). - -## Step 1: Implement features - -- Use `openbrush::contract` macro instead of `ink::contract`. -- Implement `PSP22PalletMetadata` via `#[openbrush::implementation]. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::contract] -#[openbrush::implementation(PSP22Pallet, PSP22PalletMetadata)] -pub mod my_psp22_pallet { - ... -} -``` - -## Step 2: Define storage - -Declare storage struct and declare the field related to the metadata module data structure. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. Deriving this trait allows you to reuse the -`PSP22Metadata` extension in your `PSP22 Pallet` implementation. - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct Contract { - #[storage_field] - pallet: psp22_pallet::Data, -} -``` - -## Step 3: Define constructor - -Define constructor. Your `PSP22Metadata` contract is ready! - -```rust -impl Contract { - /// During instantiation of the contract, you need to pass native tokens as a deposit - /// for asset creation. - #[ink(constructor)] - #[ink(payable)] - pub fn new( - asset_id: u32, - min_balance: Balance, - total_supply: Balance, - name: String, - symbol: String, - decimal: u8, - ) -> Self { - let mut instance = Self::default(); - - psp22_pallet::Internal::_create(&mut instance, asset_id, Self::env().account_id(), min_balance) - .expect("Should create an asset"); - instance.pallet.asset_id.set(&asset_id); - instance.pallet.origin.set(&Origin::Caller); - instance - .pallet - .pallet_assets - .get_or_default() - .set_metadata(asset_id, name.into(), symbol.into(), decimal) - .expect("Should set metadata"); - psp22_pallet::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22Pallet, PSP22PalletMetadata)] -#[openbrush::contract] -pub mod my_psp22_pallet_metadata { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - pallet: psp22_pallet::Data, - } - - impl Contract { - /// During instantiation of the contract, you need to pass native tokens as a deposit - /// for asset creation. - #[ink(constructor)] - #[ink(payable)] - pub fn new( - asset_id: u32, - min_balance: Balance, - total_supply: Balance, - name: String, - symbol: String, - decimal: u8, - ) -> Self { - let mut instance = Self::default(); - - psp22_pallet::Internal::_create(&mut instance, asset_id, Self::env().account_id(), min_balance) - .expect("Should create an asset"); - instance.pallet.asset_id.set(&asset_id); - instance.pallet.origin.set(&Origin::Caller); - instance - .pallet - .pallet_assets - .get_or_default() - .set_metadata(asset_id, name.into(), symbol.into(), decimal) - .expect("Should set metadata"); - psp22_pallet::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP22-Pallet/Extensions/mintable.md b/docs/docs/smart-contracts/PSP22-Pallet/Extensions/mintable.md deleted file mode 100644 index 997e3db5d..000000000 --- a/docs/docs/smart-contracts/PSP22-Pallet/Extensions/mintable.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -sidebar_position: 2 -title: PSP22 Pallet Mintable ---- - -This example shows how you can reuse the implementation of -[PSP22 Pallet](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet) token with [PSP22Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet/extensions/mintable.rs) extension via `pallet-assets` chain extension. - -## How to use this extension - -First, you should implement basic version of [PSP22 Pallet](../psp22-pallet.md). - -After you can just add implementation of PSP22PalletMintable via `#[openbrush::implementation(PSP22PalletMintable)]` attribute. - -# Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22Pallet, PSP22PalletMintable)] -#[openbrush::contract] -pub mod my_psp22_pallet_mintable { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - pallet: psp22_pallet::Data, - } - - impl Contract { - /// During instantiation of the contract, you need to pass native tokens as a deposit - /// for asset creation. - #[ink(constructor)] - #[ink(payable)] - pub fn new(asset_id: u32, min_balance: Balance, total_supply: Balance) -> Self { - let mut instance = Self::default(); - let caller = instance.env().caller(); - - psp22_pallet::Internal::_create(&mut instance, asset_id, Self::env().account_id(), min_balance) - .expect("Should create an asset"); - instance.pallet.asset_id.set(&asset_id); - instance.pallet.origin.set(&Origin::Caller); - psp22_pallet::Internal::_mint_to(&mut instance, caller, total_supply).expect("Should mint_to"); - - instance - } - } -} -``` - -And that's it! Your `PSP22 Pallet` is now extended by the `PSP22Mintable` extension and ready to use its functions! diff --git a/docs/docs/smart-contracts/PSP22-Pallet/_category.json b/docs/docs/smart-contracts/PSP22-Pallet/_category.json deleted file mode 100644 index 195e1036f..000000000 --- a/docs/docs/smart-contracts/PSP22-Pallet/_category.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "PSP22 Pallet", - "collapsed": false, - "position": 9 -} \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP22-Pallet/psp22-pallet.md b/docs/docs/smart-contracts/PSP22-Pallet/psp22-pallet.md deleted file mode 100644 index f9d849deb..000000000 --- a/docs/docs/smart-contracts/PSP22-Pallet/psp22-pallet.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -sidebar_position: 9 -title: PSP22 Pallet ---- - -This example shows how you can reuse the implementation of [PSP22 Pallet](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22_pallet) via `pallet-assets` chain extension. Also, this example shows how you can customize the logic, for example, to get current `asset_id`. - -## Step 1: Implement features - -With [default `Cargo.toml`](../overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `psp22-pallet` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](../overview.md/#reuse-implementation-of-traits-from-openbrush). - -Use `psp22_pallet` storage and implement `PSP22` for your contract. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22Pallet)] -#[openbrush::contract] -pub mod my_psp22_pallet { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - pallet: psp22_pallet::Data, - } -} -``` - -## Step 3: Add constructor - -Add constructor for your contract, create asset and mint tokens to caller. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22Pallet)] -#[openbrush::contract] -pub mod my_psp22_pallet { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - pallet: psp22_pallet::Data, - } - - impl Contract { - /// During instantiation of the contract, you need to pass native tokens as a deposit - /// for asset creation. - #[ink(constructor)] - #[ink(payable)] - pub fn new(asset_id: u32, min_balance: Balance, total_supply: Balance) -> Self { - let mut instance = Self::default(); - - psp22_pallet::Internal::_create(&mut instance, asset_id, Self::env().account_id(), min_balance) - .expect("Should create an asset"); - instance.pallet.asset_id.set(&asset_id); - instance.pallet.origin.set(&Origin::Caller); - psp22_pallet::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } - - /// Asset id of the asset in the `pallet-assets` - #[ink(message)] - pub fn asset_id(&self) -> u32 { - self.pallet.asset_id.get_or_default() - } - } -} -``` - -You can check an example of the usage of [PSP22 Pallet](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_pallet). - -Also you can use extensions for PSP22 token: - -[PSP22 Pallet Metadata](Extensions/metadata.md): metadata for PSP22 Pallet. - -[PSP22 Pallet Mintable](Extensions/mintable.md): creation of new tokens. - -[PSP22 Pallet Burnable](Extensions/burnable.md): destruction of own tokens. diff --git a/docs/docs/smart-contracts/PSP22/Extensions/burnable.md b/docs/docs/smart-contracts/PSP22/Extensions/burnable.md deleted file mode 100644 index d1efe770e..000000000 --- a/docs/docs/smart-contracts/PSP22/Extensions/burnable.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -sidebar_position: 3 -title: PSP22 Burnable ---- - -This example shows how you can reuse the implementation of -[PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/burnable.rs) extension. - -## How to use this extension - -First, you should implement basic version of [PSP22](../psp22.md). - -After you can just add implementation of PSP22Burnable via `#[openbrush::implementation(PSP22Burnable)]` attribute. - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Burnable)] -#[openbrush::contract] -pub mod my_psp22_burnable { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance) -> Self { - let mut instance = Self::default(); - - psp22::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } - - #[ink(message)] - pub fn burn_from_many(&mut self, accounts: Vec<(AccountId, Balance)>) -> Result<(), PSP22Error> { - for account in accounts.iter() { - PSP22Burnable::burn(self, account.0, account.1)?; - } - Ok(()) - } - } -} -``` - -And that's it! Your `PSP22` is now extended by the `PSP22Burnable` extension and ready to use its functions! -You can check an example of the usage of [PSP22 Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_extensions/burnable). diff --git a/docs/docs/smart-contracts/PSP22/Extensions/capped.md b/docs/docs/smart-contracts/PSP22/Extensions/capped.md deleted file mode 100644 index 460b314f0..000000000 --- a/docs/docs/smart-contracts/PSP22/Extensions/capped.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -sidebar_position: 6 -title: PSP22 Capped ---- - -This example shows how you can reuse the implementation of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token with the [PSP22Capped](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/capped.rs) extension. - -First, you should implement basic version of [PSP22](../psp22.md). - -## Step 1: Add imports and enable unstable feature - -- Use `openbrush::contract` macro instead of `ink::contract`. -- Use `openbrush::implementation` macro to inherit implementations of `PSP22` and `PSP22Capped` traits. - -## Step 2: Define storage - -Declare storage struct and declare the field related to the capped module data structure. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. Deriving this trait allows you to reuse the -`PSP22Capped` extension in your `PSP22` implementation. - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct Contract { - ... - #[storage_field] - cap: capped::Data, -} -``` - -## Step 3: Inherit logic - -You can customize (override) methods using `#[overrider]` attribute. - -Override `psp22::Transfer` to check is the cap exceeded before minting. - -```rust -#[overrider(psp22::Internal)] -fn _before_token_transfer( - &mut self, - from: Option<&AccountId>, - _: Option<&AccountId>, - amount: &Balance, -) -> Result<(), PSP22Error> { - // `is_none` means that it is minting - if from.is_none() && capped::Internal::_is_cap_exceeded(self, amount) { - return Err(PSP22Error::Custom(String::from("Cap exceeded"))) - } - Ok(()) -} -``` - -## Step 4: Define constructor - -Define constructor. Your `PSP22Capped` contract is ready! - -```rust -impl Contract { - /// Constructor which mints `initial_supply` of the token to sender - /// Will set the token's cap to `cap` - #[ink(constructor)] - pub fn new(inital_supply: Balance, cap: Balance) -> Self { - let mut instance = Self::default(); - - assert!(capped::Internal::_init_cap(&mut instance, cap).is_ok()); - assert!(PSP22Mintable::mint(&mut instance, Self::env().caller(), inital_supply).is_ok()); - - instance - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Capped, PSP22Mintable)] -#[openbrush::contract] -pub mod my_psp22_capped { - use openbrush::traits::{ - Storage, - String, - }; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - cap: capped::Data, - } - - #[overrider(psp22::Internal)] - fn _before_token_transfer( - &mut self, - from: Option<&AccountId>, - _: Option<&AccountId>, - amount: &Balance, - ) -> Result<(), PSP22Error> { - // `is_none` means that it is minting - if from.is_none() && capped::Internal::_is_cap_exceeded(self, amount) { - return Err(PSP22Error::Custom(String::from("Cap exceeded"))) - } - Ok(()) - } - - impl Contract { - /// Constructor which mints `initial_supply` of the token to sender - /// Will set the token's cap to `cap` - #[ink(constructor)] - pub fn new(inital_supply: Balance, cap: Balance) -> Self { - let mut instance = Self::default(); - - assert!(capped::Internal::_init_cap(&mut instance, cap).is_ok()); - assert!(PSP22Mintable::mint(&mut instance, Self::env().caller(), inital_supply).is_ok()); - - instance - } - } -} -``` - -You can check an implementation example of [PSP22 Capped](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_extensions/capped). diff --git a/docs/docs/smart-contracts/PSP22/Extensions/flashmint.md b/docs/docs/smart-contracts/PSP22/Extensions/flashmint.md deleted file mode 100644 index 7a1f0e7b9..000000000 --- a/docs/docs/smart-contracts/PSP22/Extensions/flashmint.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -sidebar_position: 5 -title: PSP22 FlashMint ---- - -This example shows how you can reuse the implementation of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22FlashMint](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/flashmint.rs) extension, which allows the user to perform a flash loan on the token by minting the borrowed amount and then burning it along with fees for the loan. - -## 1. Implement the FlashMint extension - -First, you should implement basic version of [PSP22](../psp22.md). - -After you can just add implementation of PSP22Flashmint via `#[openbrush::implementation(PSP22Flashmint)]` attribute. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, Flashmint)] -#[openbrush::contract] -pub mod my_psp22_flashmint { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - } - - /// Override `get_fee` function to add 1% fee to the borrowed `amount` - #[overrider(flashmint::Internal)] - fn _get_fee(&self, amount: Balance) -> Balance { - amount / 100 - } - - impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance) -> Self { - let mut instance = Self::default(); - psp22::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } - } -} -``` - -And that's it! Your `PSP22` is now extended by the `PSP22FlashMint` extension and ready to use its functions! -You can check the full example of the implementation of this extension [here](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_extensions/flashmint). diff --git a/docs/docs/smart-contracts/PSP22/Extensions/metadata.md b/docs/docs/smart-contracts/PSP22/Extensions/metadata.md deleted file mode 100644 index 840b42c27..000000000 --- a/docs/docs/smart-contracts/PSP22/Extensions/metadata.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -sidebar_position: 1 -title: PSP22 Metadata ---- - -This example shows how you can reuse the implementation of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token with the [PSP22Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/metadata.rs) extension. - -First, you should implement basic version of [PSP22](../psp22.md). - -## Step 1: Add imports and enable unstable feature - -Use `openbrush::contract` macro instead of `ink::contract`. Import **everything** from `openbrush::contracts::psp22::extensions::metadata`. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Metadata)] -#[openbrush::contract] -pub mod my_psp22 { - ... -``` - -## Step 2: Define storage - -Declare storage struct and declare the field related to the metadata module data structure. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. Deriving this trait allows you to reuse the -`PSP22Metadata` extension in your `PSP22` implementation. - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct Contract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - metadata: metadata::Data, -} -``` - -## Step 3: Define constructor - -Define constructor. Your `PSP22Metadata` contract is ready! - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance, name: Option, symbol: Option, decimal: u8) -> Self { - let mut instance = Self::default(); - let caller = instance.env().caller(); - - instance.metadata.name.set(&name); - instance.metadata.symbol.set(&symbol); - instance.metadata.decimals.set(&decimal); - - psp22::Internal::_mint_to(&mut instance, caller, total_supply).expect("Should mint total_supply"); - - instance - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Metadata)] -#[openbrush::contract] -pub mod my_psp22 { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - metadata: metadata::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance, name: Option, symbol: Option, decimal: u8) -> Self { - let mut instance = Self::default(); - let caller = instance.env().caller(); - - instance.metadata.name.set(&name); - instance.metadata.symbol.set(&symbol); - instance.metadata.decimals.set(&decimal); - - psp22::Internal::_mint_to(&mut instance, caller, total_supply).expect("Should mint total_supply"); - - instance - } - } -} -``` - -You can check an example of the usage of [PSP22 Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_extensions/metadata). diff --git a/docs/docs/smart-contracts/PSP22/Extensions/mintable.md b/docs/docs/smart-contracts/PSP22/Extensions/mintable.md deleted file mode 100644 index 2622d1a3c..000000000 --- a/docs/docs/smart-contracts/PSP22/Extensions/mintable.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -sidebar_position: 2 -title: PSP22 Mintable ---- - -This example shows how you can reuse the implementation of -[PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/mintable.rs) extension. - -## How to use this extension - -First, you should implement basic version of [PSP22](../psp22.md). - -After you can just add implementation of PSP22Mintable via `#[openbrush::implementation(PSP22Mintable)]` attribute. - -## Final implementation - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Mintable)] -#[openbrush::contract] -pub mod my_psp22_mintable { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance) -> Self { - let mut instance = Self::default(); - - psp22::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } - } -} - -``` - -You can check an example of the usage of [PSP22 Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_extensions/mintable). - -And that's it! Your `PSP22` is now extended by the `PSP22Mintable` extension and ready to use its functions! diff --git a/docs/docs/smart-contracts/PSP22/Extensions/wrapper.md b/docs/docs/smart-contracts/PSP22/Extensions/wrapper.md deleted file mode 100644 index 469633115..000000000 --- a/docs/docs/smart-contracts/PSP22/Extensions/wrapper.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -sidebar_position: 4 -title: PSP22 Wrapper ---- - -This example shows how you can reuse the implementation of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token with [PSP22 Wrapper](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/extensions/wrapper.rs) extension, which allows you to wrap your `PSP22` token in a `PSP22Wrapper` token which can be used for example for governance. - -First, you should implement basic version of [PSP22](../psp22.md). - -## Step 1: Add imports and enable unstable feature - -- Use `openbrush::contract` macro instead of `ink::contract`. -- Use `openbrush::implementation` macro to inherit implementations of `PSP22` and `PSP22Wrapper` traits. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Wrapper)] -#[openbrush::contract] -pub mod my_psp22_wrapper { - ... -``` - -## Step 2: Define storage - -Declare storage struct and declare the field related to the wrapper module data structure. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. Deriving this trait allows you to reuse the -`PSP22Wrapper` extension in your `PSP22` implementation. - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct Contract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - wrapper: wrapper::Data, -} -``` - -## Step 3: Define constructor - -Define constructor where you init address of wrapper fungible token(PSP22) and `recover` message. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(token_address: AccountId) -> Self { - let mut instance = Self::default(); - - Internal::_init(&mut instance, token_address); - - instance - } - - /// Exposes the `_recover` function for message caller - #[ink(message)] - pub fn recover(&mut self) -> Result { - Internal::_recover(self, Self::env().caller()) - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22, PSP22Wrapper)] -#[openbrush::contract] -pub mod my_psp22_wrapper { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - wrapper: wrapper::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(token_address: AccountId) -> Self { - let mut instance = Self::default(); - - Internal::_init(&mut instance, token_address); - - instance - } - - /// Exposes the `_recover` function for message caller - #[ink(message)] - pub fn recover(&mut self) -> Result { - Internal::_recover(self, Self::env().caller()) - } - } -} - -``` - -You can check an example of the usage of [PSP22 Wrapper](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_extensions/wrapper). diff --git a/docs/docs/smart-contracts/PSP22/Utils/token-timelock.md b/docs/docs/smart-contracts/PSP22/Utils/token-timelock.md deleted file mode 100644 index 914344a5b..000000000 --- a/docs/docs/smart-contracts/PSP22/Utils/token-timelock.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -sidebar_position: 1 -title: PSP22 Token Timelock ---- - -This example shows how you can reuse the implementation of [PSP22 Token Timelock](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22/src/utils/token_timelock.rs) utility for [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22). This contract will lock user's `PSP22` tokens until the time specified, when they can withdraw them. - -## Step 1: Implement features - -- Use `openbrush::contract` macro instead of `ink::contract`. -- Use `openbrush::implementation` macro to inherit the implementation of the `PSP22TokenTimelock` trait. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::contract] -#[openbrush::implementation(PSP22TokenTimelock)] -pub mod my_psp22_token_timelock { -... -``` - -## Step 2: Define storage - -Declare storage struct and declare the field related to the timelock module data structure. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. Deriving this trait allows you to reuse the -`PSP22TokenTimelock`. - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct Contract { - #[storage_field] - timelock: token_timelock::Data, -} -``` - -## Step 3: Inherit logic - -You can customize (override) methods using `#[openbrush::overrider]` macro. - -## Step 4: Define constructor - -Define constructor. Your implementation of `PSP22TokenTimelock` contract is ready! - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(token_address: AccountId, beneficiary: AccountId, release_time: Timestamp) -> Self { - let mut instance = Self::default(); - - assert!(instance._init(token_address, beneficiary, release_time).is_ok()); - - instance - } -} -``` - -## Final code -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP22TokenTimelock)] -#[openbrush::contract] -pub mod my_psp22_token_timelock { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - timelock: token_timelock::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(token_address: AccountId, beneficiary: AccountId, release_time: Timestamp) -> Self { - let mut instance = Self::default(); - - token_timelock::Internal::_init(&mut instance, token_address, beneficiary, release_time) - .expect("Should init"); - - instance - } - } -} - -``` - -You can check an example of the usage of [PSP22 Token Timelock](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22_utils/token_timelock). - -You can also check the documentation for the basic implementation of [PSP22](../psp22.md). \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP22/_category_.json b/docs/docs/smart-contracts/PSP22/_category_.json deleted file mode 100644 index 3b80cb91d..000000000 --- a/docs/docs/smart-contracts/PSP22/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "PSP22", - "collapsed": false, - "position": 8 -} diff --git a/docs/docs/smart-contracts/PSP22/psp22.md b/docs/docs/smart-contracts/PSP22/psp22.md deleted file mode 100644 index f5dc054d9..000000000 --- a/docs/docs/smart-contracts/PSP22/psp22.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -sidebar_position: 1 -title: PSP22 ---- - -This example shows how you can reuse the implementation of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22) token. Also, this example shows how you can customize the logic, for example, to reject transferring tokens to `hated_account`. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](../overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `psp22` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](../overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `PSP22`. - -## Step 2: Define constructor - -Define constructor where you mint tokens to caller. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance) -> Self { - let mut instance = Self { - psp22: Default::default(), - hated_storage: HatedStorage { - hated_account: [255; 32].into(), - }, - }; - - Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } -} -``` - -## Step 3: Customize your contract - -Customize it by adding hated account logic. It will contain two public methods `set_hated_account` and `get_hated_account`. -Also we will override `_before_token_transfer` method in the `PSP22` implementation(that methods defined in `Transfer` trait), -and we will add the `hated_account: AccountId` field to the structure. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -// pub use my_psp22::*; -pub use openbrush::traits::{ - AccountId, - Storage, -}; - -// we need to expand this struct before the contract macro is expanded -// that is why we declare it here for this example -#[ink::storage_item] -#[openbrush::accessors(HatedStorageAccessors)] -#[derive(Debug)] -pub struct HatedStorage { - #[get] - #[set] - pub hated_account: AccountId, -} - -#[openbrush::implementation(PSP22)] -#[openbrush::contract] -pub mod my_psp22 { - use crate::*; - use openbrush::traits::String; - - #[ink(storage)] - #[derive(Storage)] - pub struct Contract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - hated_storage: HatedStorage, - } - - #[overrider(psp22::Internal)] - fn _before_token_transfer( - &mut self, - _from: Option<&AccountId>, - to: Option<&AccountId>, - _amount: &Balance, - ) -> Result<(), PSP22Error> { - if to == Some(&self.hated_storage.hated_account) { - return Err(PSP22Error::Custom(String::from("I hate this account!"))) - } - Ok(()) - } - - impl HatedStorageAccessors for Contract {} - - impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance) -> Self { - let mut instance = Self { - psp22: Default::default(), - hated_storage: HatedStorage { - hated_account: [255; 32].into(), - }, - }; - - Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - - instance - } - } -} - -``` - -You can check an example of the usage of [PSP22](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp22). - -Also you can use extensions for PSP22 token: - -[PSP22Metadata](Extensions/metadata.md): metadata for PSP22. - -[PSP22Mintable](Extensions/mintable.md): creation of new tokens. - -[PSP22Burnable](Extensions/burnable.md): destruction of own tokens. - -[PSP22Wrapper](Extensions/wrapper.md): token wrapper for PSP22. - -[PSP22FlashMint](Extensions/flashmint.md): extension which allows the user to perform flashloans on the token by minting and burning the token. - -Check out the utilities for PSP22 token: - -[PSP22TokenTimelock](Utils/token-timelock.md): utility for locking PSP22 tokens for a specified time. diff --git a/docs/docs/smart-contracts/PSP34/Extensions/burnable.md b/docs/docs/smart-contracts/PSP34/Extensions/burnable.md deleted file mode 100644 index ac774dbc6..000000000 --- a/docs/docs/smart-contracts/PSP34/Extensions/burnable.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -sidebar_position: 3 -title: PSP34 Burnable ---- - -This example shows how you can reuse the implementation of [PSP34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/burnable.rs) extension. - -## How to use this extension - -First, you should implement basic version of [PSP34](../psp34.md). - -After you can just add implementation of PSP34Burnable via `#[openbrush::implementation(PSP34Burnable)]` attribute. - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Burnable)] -#[openbrush::contract] -pub mod my_psp34_burnable { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp34: psp34::Data, - } - - impl Contract { - /// The constructor - #[ink(constructor)] - pub fn new() -> Self { - let mut instance = Self::default(); - - psp34::Internal::_mint_to(&mut instance, Self::env().caller(), Id::U8(0u8)) - .expect("Should mint token with id 0"); - psp34::Internal::_mint_to(&mut instance, Self::env().caller(), Id::U8(1u8)) - .expect("Should mint token with id 1"); - psp34::Internal::_mint_to(&mut instance, Self::env().caller(), Id::U8(2u8)) - .expect("Should mint token with id 2"); - - instance - } - } -} - -``` - -And that's it! Your `PSP34` is now extended by the `PSP34Burnable` extension and ready to use its functions! -You can check an example of the usage of [PSP34 Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp34_extensions/burnable). \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP34/Extensions/enumerable.md b/docs/docs/smart-contracts/PSP34/Extensions/enumerable.md deleted file mode 100644 index d596dc70b..000000000 --- a/docs/docs/smart-contracts/PSP34/Extensions/enumerable.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -sidebar_position: 3 -title: PSP34 Enumerable ---- - -This example shows how you can reuse the implementation of [PSP34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Enumerable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/enumerable.rs) extension. - -First, you should implement basic version of [PSP34](../psp34.md). - -## Step 1: Implement features - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Mintable, PSP34Burnable, PSP34Enumerable)] -#[openbrush::contract] -pub mod my_psp34 { -... -``` - -## Step 2: Define storage - -Pass `enumerable::Balances` into `psp34::Data` to be able to use `PSP34Enumerable` extension -in your `PSP34` implementation. - -```rust -#[derive(Default, Storage)] -#[ink(storage)] -pub struct Contract { - #[storage_field] - psp34: psp34::Data, - #[storage_field] - enumerable: enumerable::Data, -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Mintable, PSP34Burnable, PSP34Enumerable)] -#[openbrush::contract] -pub mod my_psp34_enumerable { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp34: psp34::Data, - #[storage_field] - enumerable: enumerable::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - } -} -``` - -And that's it! Your `PSP34` is now extended by the `PSP34Enumerable` extension and ready to use its functions! -You can check an example of the usage of [PSP34 Enumerable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp34_extensions/enumerable). diff --git a/docs/docs/smart-contracts/PSP34/Extensions/metadata.md b/docs/docs/smart-contracts/PSP34/Extensions/metadata.md deleted file mode 100644 index 22686225d..000000000 --- a/docs/docs/smart-contracts/PSP34/Extensions/metadata.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -sidebar_position: 1 -title: PSP34 Metadata ---- - -This example shows how you can reuse the implementation of [PSP34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/metadata.rs) extension. - -First, you should implement basic version of [PSP34](../psp34.md). - -## Step 1: Implement features - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Metadata)] -#[openbrush::contract] -pub mod my_psp34_metadata { -... -``` - -## Step 2: Define storage - -Declare storage struct and declare the field related to the metadata module data structure. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. Deriving this trait allows you to reuse the -`PSP34Metadata` extension in your `PSP34` implementation. - -```rust -#[derive(Default, Storage)] -#[ink(storage)] -pub struct Contract { - #[storage_field] - psp34: psp34::Data, - #[storage_field] - metadata: metadata::Data, -} -``` - -## Step 3: Define constructor - -Define constructor. Your `PSP34Metadata` contract is ready! - -```rust -impl Contract { - /// A constructor which mints the first token to the owner - #[ink(constructor)] - pub fn new(id: Id, name: String, symbol: String) -> Self { - let mut instance = Self::default(); - - let name_key = String::from("name"); - let symbol_key = String::from("symbol"); - metadata::Internal::_set_attribute(&mut instance, id.clone(), name_key, name); - metadata::Internal::_set_attribute(&mut instance, id, symbol_key, symbol); - - instance - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Metadata)] -#[openbrush::contract] -pub mod my_psp34_metadata { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp34: psp34::Data, - #[storage_field] - metadata: metadata::Data, - } - - impl Contract { - /// A constructor which mints the first token to the owner - #[ink(constructor)] - pub fn new(id: Id, name: String, symbol: String) -> Self { - let mut instance = Self::default(); - - let name_key = String::from("name"); - let symbol_key = String::from("symbol"); - metadata::Internal::_set_attribute(&mut instance, id.clone(), name_key, name); - metadata::Internal::_set_attribute(&mut instance, id, symbol_key, symbol); - - instance - } - } -} -``` - -You can check an example of the usage of [PSP34 Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp34_extensions/metadata). - diff --git a/docs/docs/smart-contracts/PSP34/Extensions/mintable.md b/docs/docs/smart-contracts/PSP34/Extensions/mintable.md deleted file mode 100644 index f6bdc63c5..000000000 --- a/docs/docs/smart-contracts/PSP34/Extensions/mintable.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -sidebar_position: 2 -title: PSP34 Mintable ---- - -This example shows how you can reuse the implementation of [PSP34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34) token with [PSP34Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34/extensions/mintable.rs) extension. - -## How to use this extension - -First, you should implement basic version of [PSP34](../psp34.md). - -After you can just add implementation of PSP34Mintable via `#[openbrush::implementation(PSP34Mintable)]` attribute. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Mintable)] -#[openbrush::contract] -pub mod my_psp34_mintable { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp34: psp34::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - } -} -``` - -And that's it! Your `PSP34` is now extended by the `PSP34Mintable` extension and ready to use its functions! -You can check an example of the usage of [PSP34 Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp34_extensions/mintable). diff --git a/docs/docs/smart-contracts/PSP34/_category_.json b/docs/docs/smart-contracts/PSP34/_category_.json deleted file mode 100644 index 28258720c..000000000 --- a/docs/docs/smart-contracts/PSP34/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "PSP34", - "collapsed": false, - "position": 10 -} \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP34/psp34.md b/docs/docs/smart-contracts/PSP34/psp34.md deleted file mode 100644 index e2a29bb70..000000000 --- a/docs/docs/smart-contracts/PSP34/psp34.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -sidebar_position: 1 -title: PSP34 ---- - -This example shows how you can reuse the implementation of [PSP34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34) token. Also, this example shows how you can customize the logic, for example, to track the number of tokens minted with `next_id`, increasing it with each new token minted, securing a unique id for each token. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](../overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `psp34` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](../overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `PSP34`. - -## Step 2: Define constructor - -Define empty constructor. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } -} -``` - -## Step 3: Customize your contract - -Customize it by adding logic for tracking the number of minted tokens. -It will contain a custom `mint_token` function which will handle the id of the -newly minted token. Also, we will add the `next_id: u8` field to the structure, -which will be increased with each newly minted token. This way we will make sure -there will always be added a token with a unique id. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34)] -#[openbrush::contract] -pub mod my_psp34 { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp34: psp34::Data, - next_id: u8, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[ink(message)] - pub fn mint_token(&mut self) -> Result<(), PSP34Error> { - psp34::Internal::_mint_to(self, Self::env().caller(), Id::U8(self.next_id))?; - self.next_id += 1; - Ok(()) - } - - #[ink(message)] - pub fn mint(&mut self, id: Id) -> Result<(), PSP34Error> { - psp34::Internal::_mint_to(self, Self::env().caller(), id) - } - } -} - -``` - -You can check an example of the usage of [PSP34](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp34). -Also you can use extensions for psp34 token: - -[PSP34Metadata](Extensions/metadata.md): metadata for PSP34. - -[PSP34Mintable](Extensions/mintable.md): creation of new tokens. - -[PSP34Burnable](Extensions/burnable.md): destruction of contract's tokens. - -[PSP34Enumerable](Extensions/enumerable.md): iterating over contract's tokens. diff --git a/docs/docs/smart-contracts/PSP37/Extensions/batch.md b/docs/docs/smart-contracts/PSP37/Extensions/batch.md deleted file mode 100644 index 3d0d605c6..000000000 --- a/docs/docs/smart-contracts/PSP37/Extensions/batch.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -sidebar_position: 2 -title: PSP37 Batch ---- - -This example shows how you can reuse the implementation of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) token with [PSP37Batch](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37/extensions/batch.rs) extension, which allows batch transferring of PSP37 tokens. - -## How to use this extension - -First, you should implement basic version of [PSP37](../psp37.md). -After you can just add implementation of PSP37Batch via `#[openbrush::implementation(PSP37Batch)]` attribute. - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37, PSP37Batch)] -#[openbrush::contract] -pub mod my_psp37 { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[ink(message)] - pub fn mint(&mut self, to: AccountId, ids_amounts: Vec<(Id, Balance)>) -> Result<(), PSP37Error> { - psp37::Internal::_mint_to(self, to, ids_amounts) - } - } -} -``` - -And that's it! Your `PSP37` is now extended by the `PSP37Batch` extension and ready to use its functions! -You can check an example of the usage of [PSP37 Batch](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp37_extensions/batch). diff --git a/docs/docs/smart-contracts/PSP37/Extensions/burnable.md b/docs/docs/smart-contracts/PSP37/Extensions/burnable.md deleted file mode 100644 index b67747d89..000000000 --- a/docs/docs/smart-contracts/PSP37/Extensions/burnable.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -sidebar_position: 3 -title: PSP37 Burnable ---- - -This example shows how you can reuse the implementation of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) token with [PSP37Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37/extensions/burnable.rs) extension. - -## How to use this extension - -First, you should implement basic version of [PSP37](../psp37.md). -After you can just add implementation of PSP37Burnable via `#[openbrush::implementation(PSP37Burnable)]` attribute. - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37, PSP37Burnable)] -#[openbrush::contract] -pub mod my_psp37 { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[ink(message)] - pub fn mint_to(&mut self, to: AccountId, ids_amounts: Vec<(Id, Balance)>) -> Result<(), PSP37Error> { - psp37::Internal::_mint_to(self, to, ids_amounts) - } - } -} - -``` - -And that's it! Your `PSP37` is now extended by the `PSP37Burnable` extension and ready to use its functions! -You can check an example of the usage of [PSP37 Burnable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp37_extensions/burnable). \ No newline at end of file diff --git a/docs/docs/smart-contracts/PSP37/Extensions/enumerable.md b/docs/docs/smart-contracts/PSP37/Extensions/enumerable.md deleted file mode 100644 index 6fd09f541..000000000 --- a/docs/docs/smart-contracts/PSP37/Extensions/enumerable.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -sidebar_position: 1 -title: PSP37 Enumerable ---- - -This example shows how you can reuse the implementation of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) token with [PSP37Enumerable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37/extensions/enumerable.rs) extension. - -First, you should implement basic version of [PSP37](../psp37.md). - -## Step 1: Implement PSP37Enumerable - -Import **everything** from `openbrush::contracts::psp37::extensions::enumerable`. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::contract] -#[openbrush::implementation(..., PSP37, PSP37Enumerable, ...)] -pub mod my_psp37 { -... -``` - -## Step 2: Define storage - -```rust -#[derive(Default, Storage)] -#[ink(storage)] -pub struct Contract { - #[storage_field] - psp37: psp37::Data, - #[storage_field] - enumerable: enumerable::Data, -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37, PSP37Batch, PSP37Burnable, PSP37Mintable, PSP37Enumerable)] -#[openbrush::contract] -pub mod my_psp37_enumerable { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - #[storage_field] - enumerable: enumerable::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - } -} -``` - -And that's it! Your `PSP37` is now extended by the `PSP37Enumerable` extension and ready to use its functions! -You can check an example of the usage of [PSP37 Enumerable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp37_extensions/enumerable). diff --git a/docs/docs/smart-contracts/PSP37/Extensions/metadata.md b/docs/docs/smart-contracts/PSP37/Extensions/metadata.md deleted file mode 100644 index 802bcb81a..000000000 --- a/docs/docs/smart-contracts/PSP37/Extensions/metadata.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -sidebar_position: 1 -title: PSP37 Metadata ---- - -This example shows how you can reuse the implementation of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) token with [PSP37Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37/extensions/metadata.rs) extension. - -First, you should implement basic version of [PSP37](../psp37.md). - -## Step 1: Implement PSP37Metadata - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37, PSP37Metadata)] -#[openbrush::contract] -pub mod my_psp37 { -... -``` - -## Step 2: Define storage - -Declare storage struct and declare the field related to the metadata module. -Then you need to derive the `Storage` trait and mark the corresponding field with -the `#[storage_field]` attribute. -Deriving this trait allows you to reuse the `PSP37Metadata` extension in your -`PSP37` implementation. - -```rust -#[derive(Default, Storage)] -#[ink(storage)] -pub struct Contract { - ... - #[storage_field] - metadata: metadata::Data, -} -``` - -## Step 3: Define constructor - -Define constructor. Your `PSP37Metadata` contract is ready! - -```rust -impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37, PSP37Metadata)] -#[openbrush::contract] -pub mod my_psp37 { - use openbrush::traits::{ - Storage, - String, - }; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - #[storage_field] - metadata: metadata::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[ink(message)] - pub fn set_attribute(&mut self, id: Id, key: String, data: String) -> Result<(), PSP37Error> { - metadata::Internal::_set_attribute(self, &id, &key, &data) - } - } -} - -``` - -You can check an example of the usage of [PSP37 Metadata](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp37_extensions/metadata). diff --git a/docs/docs/smart-contracts/PSP37/Extensions/mintable.md b/docs/docs/smart-contracts/PSP37/Extensions/mintable.md deleted file mode 100644 index 216ee6703..000000000 --- a/docs/docs/smart-contracts/PSP37/Extensions/mintable.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -sidebar_position: 2 -title: PSP37 Mintable ---- - -This example shows how you can reuse the implementation of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) token with [PSP37Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37/extensions/mintable.rs) extension. - -## How to use this extension - -First, you should implement basic version of [PSP37](../psp37.md). - -For your smart contract to use this extension, you only need to implement the -`PSP37Mintable` via `#[openbrush::implementation(PSP37Mintable)]` attribute. - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37, PSP37Mintable)] -#[openbrush::contract] -pub mod my_psp37 { - use openbrush::traits::Storage; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - } -} -``` - -And that's it! Your `PSP37` is now extended by the `PSP37Mintable` extension and ready to use its functions! -You can check an example of the usage of [PSP37 Mintable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp37_extensions/mintable). diff --git a/docs/docs/smart-contracts/PSP37/_category_.json b/docs/docs/smart-contracts/PSP37/_category_.json deleted file mode 100644 index 2b5920cff..000000000 --- a/docs/docs/smart-contracts/PSP37/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "PSP37", - "collapsed": false, - "position": 11 -} diff --git a/docs/docs/smart-contracts/PSP37/psp37.md b/docs/docs/smart-contracts/PSP37/psp37.md deleted file mode 100644 index d67e3f5dd..000000000 --- a/docs/docs/smart-contracts/PSP37/psp37.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -sidebar_position: 1 -title: PSP37 ---- - -This example shows how you can reuse the implementation of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) -token. Also, this example shows how you can customize the logic, for example, to -track the number of token types with `unique_ids`, adding a new token type with the `add_type` function. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](../overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `psp37` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](../overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `PSP37`. - -## Step 2: Define constructor - -Define empty constructor. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } -} -``` - -## Step 3: Customize your contract - -Customize it by adding logic for denying of minting some tokens. -We can deny minting of token with id by `deny` function. -Id will be added to `denied_ids` map. -If someone tries to mint token with denied id, we will reject transaction. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP37)] -#[openbrush::contract] -pub mod my_psp37 { - use ink::prelude::vec; - use openbrush::{ - storage::Mapping, - traits::{ - Storage, - String, - }, - }; - - #[derive(Default, Storage)] - #[ink(storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - denied_ids: Mapping, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[ink(message)] - pub fn deny(&mut self, id: Id) { - self.denied_ids.insert(&id, &()); - } - - #[ink(message)] - pub fn mint_tokens(&mut self, id: Id, amount: Balance) -> Result<(), PSP37Error> { - if self.denied_ids.get(&id).is_some() { - return Err(PSP37Error::Custom(String::from("Id is denied"))) - } - psp37::Internal::_mint_to(self, Self::env().caller(), vec![(id, amount)]) - } - } -} -``` -You can check an example of the usage of [PSP37](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/psp37). - -Also you can use extensions for PSP37 token: - -[PSP37Metadata](Extensions/metadata.md): metadata for PSP37. - -[PSP37Mintable](Extensions/mintable.md): creation of new tokens. - -[PSP37Burnable](Extensions/burnable.md): destruction of contract's tokens. - -[PSP37Batch](Extensions/batch.md): transfer batch of tokens. - -[PSP37Enumerable](Extensions/enumerable.md): iterates over contract's tokens. \ No newline at end of file diff --git a/docs/docs/smart-contracts/_category_.json b/docs/docs/smart-contracts/_category_.json deleted file mode 100644 index 104fda350..000000000 --- a/docs/docs/smart-contracts/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Smart Contracts", - "position": 2, - "collapsed": false -} diff --git a/docs/docs/smart-contracts/access-control/Extensions/enumerable.md b/docs/docs/smart-contracts/access-control/Extensions/enumerable.md deleted file mode 100644 index 60c40285f..000000000 --- a/docs/docs/smart-contracts/access-control/Extensions/enumerable.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -sidebar_position: 1 -title: AccessControl Enumerable ---- - -This example shows how you can reuse the implementation of [AccessControl](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/access/access_control/access_control.rs) with [AccessControlEnumerable](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/access/access_control/extensions/enumerable.rs) extension, which enables an easier overview of access control roles. - -First, you should implement basic version of [AccessControl](../access-control.md). - -## Step 1: Add implemenation of AccessControlEnumerable - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::contract] -#[openbrush::implementation(AccessControl, AccessControlEnumerable)] -pub mod my_access_control { - ... -``` - -## Step 2: Define storage - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct Contract { - #[storage_field] - enumerable: enumerable::Data, -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(AccessControl, AccessControlEnumerable)] -#[openbrush::contract] -pub mod my_access_control { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - enumerable: enumerable::Data, - } - - // You can manually set the number for the role. - // But better to use a hash of the variable name. - // It will generate a unique identifier of this role. - // And will reduce the chance to have overlapping roles. - const MINTER: RoleType = ink::selector_id!("MINTER"); - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - let mut instance = Self::default(); - - let caller = Self::env().caller(); - access_control::Internal::_init_with_admin(&mut instance, Some(caller)); - // We grant minter role to caller in constructor, so he can mint/burn tokens - AccessControl::grant_role(&mut instance, MINTER, Some(caller)).expect("Should grant MINTER role"); - assert_eq!(AccessControlEnumerable::get_role_member_count(&instance, MINTER), 1); - - instance - } - } -} -``` - -And that's it! Your `AccessControl` is now extended by the `AccessControlEnumerable` extension and ready to use its functions! -You can check an example of the usage of [AccessControl Enumerable](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/access/access_control/extensions/enumerable.rs). - -You can also check the documentation for the basic implementation of [AccessControl](../access-control.md). \ No newline at end of file diff --git a/docs/docs/smart-contracts/access-control/_category_.json b/docs/docs/smart-contracts/access-control/_category_.json deleted file mode 100644 index 7305de50c..000000000 --- a/docs/docs/smart-contracts/access-control/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Access Control", - "collapsed": false, - "position": 2 -} diff --git a/docs/docs/smart-contracts/access-control/access-control.md b/docs/docs/smart-contracts/access-control/access-control.md deleted file mode 100644 index 807c6f3ad..000000000 --- a/docs/docs/smart-contracts/access-control/access-control.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -sidebar_position: 1 -title: Access Control ---- - -This example shows how you can use the implementation of [access-control](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/access/access_control) to provide rights for usage of specific smart contract functions. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](../overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `access-control` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](../overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `AccessControl`. - -## Step 2: Define constructor - -Define constructor where you grant `MINTER` role(or any another role) to the caller. - -```rust -// You can manually set the number for the role. -// But better to use a hash of the variable name. -// It will generate a unique identifier of this role. -// And will reduce the chance to have overlapping roles. -const MINTER: RoleType = ink::selector_id!("MINTER"); - -impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - let mut instance = Self::default(); - - let caller = instance.env().caller(); - access_control::Internal::_init_with_admin(&mut instance, Some(caller)); - // We grant minter role to caller in constructor, so he can mint/burn tokens - AccessControl::grant_role(&mut instance, MINTER, Some(caller)).expect("Should grant MINTER role"); - - instance - } -} -``` - -## Step 3: Customize your contract - -Customize it by adding access control logic. We will add a `restricted_function` to `Contract` implementation, -which will use the `only_role` modifier with `MINTER` parameter, which verifies that the caller has the `MINTER` role. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PSP34, PSP34Burnable, PSP34Mintable, AccessControl)] -#[openbrush::contract] -pub mod my_access_control { - use openbrush::{ - modifiers, - traits::Storage, - }; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp34: psp34::Data, - #[storage_field] - access: access_control::Data, - } - - // You can manually set the number for the role. - // But better to use a hash of the variable name. - // It will generate a unique identifier of this role. - // And will reduce the chance to have overlapping roles. - const MINTER: RoleType = ink::selector_id!("MINTER"); - - #[default_impl(PSP34Burnable)] - #[modifiers(only_role(MINTER))] - fn burn() {} - - #[default_impl(PSP34Mintable)] - #[modifiers(only_role(MINTER))] - fn mint() {} - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - let mut instance = Self::default(); - - let caller = instance.env().caller(); - access_control::Internal::_init_with_admin(&mut instance, Some(caller)); - // We grant minter role to caller in constructor, so he can mint/burn tokens - AccessControl::grant_role(&mut instance, MINTER, Some(caller)).expect("Should grant MINTER role"); - - instance - } - } -} - -``` - -You can check an example of the usage of [Access Control](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/access_control). \ No newline at end of file diff --git a/docs/docs/smart-contracts/assets/20220715_130335_47FD0F8D-60F3-4FDF-82F4-672402FDC5D1.jpeg b/docs/docs/smart-contracts/assets/20220715_130335_47FD0F8D-60F3-4FDF-82F4-672402FDC5D1.jpeg deleted file mode 100644 index 75bca967c..000000000 Binary files a/docs/docs/smart-contracts/assets/20220715_130335_47FD0F8D-60F3-4FDF-82F4-672402FDC5D1.jpeg and /dev/null differ diff --git a/docs/docs/smart-contracts/assets/20220715_130416_DD058578-67E2-4832-9F75-CA18C3B3921C_4_5005_c.jpeg b/docs/docs/smart-contracts/assets/20220715_130416_DD058578-67E2-4832-9F75-CA18C3B3921C_4_5005_c.jpeg deleted file mode 100644 index e8664d852..000000000 Binary files a/docs/docs/smart-contracts/assets/20220715_130416_DD058578-67E2-4832-9F75-CA18C3B3921C_4_5005_c.jpeg and /dev/null differ diff --git a/docs/docs/smart-contracts/assets/20220719_075309_F016550A-65D2-4DCD-A60A-D1A70B38D813.jpeg b/docs/docs/smart-contracts/assets/20220719_075309_F016550A-65D2-4DCD-A60A-D1A70B38D813.jpeg deleted file mode 100644 index 5554a2dc5..000000000 Binary files a/docs/docs/smart-contracts/assets/20220719_075309_F016550A-65D2-4DCD-A60A-D1A70B38D813.jpeg and /dev/null differ diff --git a/docs/docs/smart-contracts/diamond/Extensions/loupe.md b/docs/docs/smart-contracts/diamond/Extensions/loupe.md deleted file mode 100644 index 5a505392b..000000000 --- a/docs/docs/smart-contracts/diamond/Extensions/loupe.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_position: 1 -title: Diamond Loupe ---- - -This example shows how you can reuse the implementation of [Diamond Standard](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/upgradeability/diamond) with [Diamond Loupe](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/upgradeability/diamond/extensions/diamond_loupe.rs) extension, which allows you to iterate over diamond contract's facets and available functions. - -## How to use this extension - -First, you should implement basic version of [Diamond standard](../diamond.md). - -After you can just add implementation of DiamondLoupe via `#[openbrush::implementation(Diamond, DiamondLoupe)]` attribute. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(Diamond, DiamondLoupe)] -#[openbrush::contract] -pub mod my_diamond_loupe { - ... -``` - -## Find result - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(Ownable, Diamond, DiamondLoupe)] -#[openbrush::contract] -pub mod diamond { - use openbrush::{ - modifiers, - traits::Storage, - }; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - ownable: ownable::Data, - #[storage_field] - diamond: diamond::Data, - #[storage_field] - loupe: diamond_loupe::Data, - } - - #[default_impl(Diamond)] - #[modifiers(only_owner)] - fn diamond_cut() {} - - impl Contract { - #[ink(constructor)] - pub fn new(owner: AccountId) -> Self { - let mut instance = Self::default(); - ownable::Internal::_init_with_owner(&mut instance, owner); - - instance - } - - #[ink(message, payable, selector = _)] - pub fn forward(&self) { - diamond::Internal::_fallback(self) - } - } -} -``` - -And that's it! Your `Diamond` is now extended by the `DiamondLoupe` extension and ready to use its functions! -You can check an example of the usage of [Diamond Loupe](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/diamond). diff --git a/docs/docs/smart-contracts/diamond/_category_.json b/docs/docs/smart-contracts/diamond/_category_.json deleted file mode 100644 index 428495556..000000000 --- a/docs/docs/smart-contracts/diamond/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Diamond Standard", - "collapsed": false, - "position": 4 -} diff --git a/docs/docs/smart-contracts/diamond/diamond.md b/docs/docs/smart-contracts/diamond/diamond.md deleted file mode 100644 index 4615010e0..000000000 --- a/docs/docs/smart-contracts/diamond/diamond.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -sidebar_position: 1 -title: Diamond Standard ---- - -This example shows how you can use the implementation of [diamond standard](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/upgradeability/diamond) to implement diamond standard pattern for upgradeable and unlimited contracts. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](../overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `diamond` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](../overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait are `Ownable` and `Diamond`. - -## Step 2: Define constructor - -Define the constructor and initialize the owner with the contract initiator. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(owner: AccountId) -> Self { - let mut instance = Self::default(); - ownable::Internal::_init_with_owner(&mut instance, owner); - - instance - } -} -``` - -## Step 3: Define forward function - -Define the forward function to make delegate calls of facet contracts through the diamond contract. - -```rust -impl Contract { - #[ink(message, payable, selector = _)] - pub fn forward(&self) { - diamond::Internal::_fallback(self) - } -} -``` - -## Step 4: Customize your contract - -You can add more basic functionality for your diamond contract by adding functions to `Contract` implemenation, -but the point of the Diamond standard is not to increase the size of your contract, -and to add upgradeable functionality to your contract via so called facets. - -When you create a new contract (facet), which you want to make delegate calls from your -diamond contract to, you will call the `diamond_cut` function on your diamond contract, -with the code hash of your new facet and the selectors of all the functions from this -facet you want to use. The diamond will register them and anytime you call this function -on your diamond contract, it will make the delegate call to the facet the function belongs to. -You can add, remove or replace these functions anytime with the `diamond_cut` function, -some of limitations are, that you can not add functions with the same selectors, -when replacing functions, the new function needs to be from a different contract, -then currently in use, and when removing functions, the function needs to be registered in the diamond contract. - -You can check an example of the usage of [Diamond](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/diamond). diff --git a/docs/docs/smart-contracts/example/_category_.json b/docs/docs/smart-contracts/example/_category_.json deleted file mode 100644 index 722f0084d..000000000 --- a/docs/docs/smart-contracts/example/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "OpenBrush Example", - "collapsed": false, - "position": 11 -} diff --git a/docs/docs/smart-contracts/example/contract.md b/docs/docs/smart-contracts/example/contract.md deleted file mode 100644 index e23a16ad4..000000000 --- a/docs/docs/smart-contracts/example/contract.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -sidebar_position: 9 -title: Lending contract ---- - -The main logic of the `LendingContract` is defined in the `impls/lending` directory. -In this file, we only need to "inherit" it. - -## Add dependencies - -`LendingContract` instantiates the `SharesContract` and `LoanContract`, so we -should import them as `ink-as-dependency`. Also we want to use the `AccessControl` -and `Pausable` from OpenBrush, so we import them too. We also want to "inherit" the -implementation of `Lending` and `LendingPermissioned` traits defined in the `lending_project` crate. - -```toml -[package] -name = "lending_contract" -version= "4.0.0-beta" -authors = ["Brushfam "] -edition = "2021" - -[dependencies] -ink = { version = "4.2.1", default-features = false } -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } - -# These dependencies -shares_contract = { path = "../shares", default-features = false, features = ["ink-as-dependency"] } -loan_contract = { path = "../loan", default-features = false, features = ["ink-as-dependency"] } -lending_project = { path = "../..", default-features = false } -openbrush = { git = "https://github.com/Brushfam/openbrush-contracts", branch = "develop", default-features = false, features = ["pausable", "access_control"] } - -[lib] -name = "lending_contract" -path = "lib.rs" - - -[features] -default = ["std"] -std = [ - "ink/std", - "scale/std", - "scale-info", - "scale-info/std", - - # These dependencies - "loan_contract/std", - "shares_contract/std", - "openbrush/std", -] -ink-as-dependency = [] - -[profile.dev] -codegen-units = 16 - -[profile.release] -overflow-checks = false -``` - -## Define the contract storage - -As described earlier, we want our smart contract to be paused by the Manager account. -To do that, we need our contract to be `Pausable` and we need a manager role. -We can do this with the `AccessControl`. Also, we want to use the data from lending that we have declared. -So we will declare a struct and derive all the needed traits. - -```rust -#[ink(storage)] -#[derive(Default, Storage)] -pub struct LendingContract { - #[storage_field] - access: access_control::Data, - #[storage_field] - pause: pausable::Data, - #[storage_field] - lending: lending::data::Data, -} -``` - -## Implement traits - -We need to "inherit" the implementation of `AccessControll`, `Pausable`, `Lending`, -`LendingPermissioned` and `lending::Internal`. - -```rust -impl lending::Internal for LendingContract {} - -impl LendingImpl for LendingContract {} - -impl Lending for LendingContract { - #[ink(message)] - fn total_asset(&self, asset_address: AccountId) -> Result { - LendingImpl::total_asset(self, asset_address) - } - // other methods should be implemented here as the one above -} - -impl LendingPermissionedImpl for LendingContract {} - -impl LendingPermissioned for LendingContract { - #[ink(message)] - fn deposit(&mut self, asset_address: AccountId, amount: Balance) -> Result<(), LendingError> { - LendingPermissionedImpl::deposit(self, asset_address, amount) - } - // other methods should be implemented here as the one above -} - -impl lending::Instantiator for LendingContract { - fn _instantiate_shares_contract(&self, contract_name: &str, contract_symbol: &str) -> AccountId { - let code_hash = self.lending.shares_contract_code_hash; - - let salt = (::env().block_timestamp(), contract_name).encode(); - - let hash = xxh32(&salt, 0).to_le_bytes(); - - let contract = - SharesContractRef::new(Some(String::from(contract_name)), Some(String::from(contract_symbol))) - .endowment(0) - .code_hash(code_hash) - .salt_bytes(&hash[..4]) - .instantiate(); - contract.to_account_id() - } -} -``` - -Now the `LendingContract` has functionality of all that traits. - -## Define the constructor - -Finally, we will add a constructor, in which we will initiate the admin of -the contract, to whom we will also grant the manager role declared before, -and we will also instantiate the `LoanContract` here and store its AccountId -in `LendingContract`. - -```rust -impl LendingContract { - /// constructor with name and symbol - #[ink(constructor, payable)] - pub fn new(shares_hash: Hash, loan_hash: Hash) -> Self { - - let mut instance = Self::default(); - - let caller = Self::env().caller(); - instance._init_with_admin(caller); - instance.grant_role(MANAGER, caller).expect("Can not set manager role"); - instance.lending.shares_contract_code_hash = shares_hash; - // instantiate NFT contract and store its account id - let nft = LoanContractRef::new() - .endowment(0) - .code_hash(loan_hash) - .salt_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]) - .instantiate() - .unwrap(); - instance.lending.loan_account = nft.to_account_id(); - - instance - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/data.md b/docs/docs/smart-contracts/example/data.md deleted file mode 100644 index 6cec7bbe9..000000000 --- a/docs/docs/smart-contracts/example/data.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -sidebar_position: 6 -title: Data and derive macro ---- - -## Data segregation - -Rust doesn't have inheritance like OOP languages. -If you want to "inherit" some fields, you can use structural composition. -If you want to "inherit" some implementation, you can use traits. -Traits can have a [default implementation](https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations) or a [generic implementation](https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods). -The traits in Rust can't contain fields, it is pure interfaces. - -Based on that information we propose you the following concept of smart contract -development: - -### Storage trait - -Extract the logic of data storing into a separate trait to have the ability to -define the default implementation without knowing what contract will inherit that. -You can use that separate trait as a bound in your generic implementation(below we will describe). - -You can define your own storage trait like: -```rust -pub trait PointStorage { - fn get(&self) -> & PointData; - fn get_mut(&mut self) -> &mut PointData; -} -``` - -Or you can use `openbrush::traits::Storage` trait from OpenBrush. - -`Storage` is a generic trait, so you can use it to work with different storage. -For example, if in your default implementation you need to have `psp22::extensions::metadata::Data` and `psp22::Data`, -you can add bounds `T: Storage + Storage`. -It allows you to work with two independent storage. - -### Data of the trait - -That trait returns some data with fields that can be used in the implementation. -The data is a simple struct with fields. Later that struct can be embedded into the contract struct. -```rust -pub struct PointData { - pub x: u32, - pub y: u32, -} -``` - -Also, you can use the `openbrush::storage_item` macro that implements that trait by default, -and also prepare the storage to be upgradeable. - -```rust -#[openbrush::storage_item] -pub struct PointData { - pub x: u32, - pub y: u32, -} -``` - -### Upgradeable struct -For struct to be upgradeable, every field that is not Mapping or other lazy loaded type, -it should be marked with `#[lazy]` attribute. -```rust - -#[openbrush::storage_item] -pub struct PointData { - #[lazy] - pub x: u32, - #[lazy] - pub y: u32, -} -``` - -### Default implementation - -Define the default or generic implementation for your main trait with the restriction that `Self` -should also implement storage trait. - -A default implementation with impl trait: -```rust -pub trait PointImpl: PointStorage { - fn x(&self) -> u32 { - PointStorage::get(self).x - } - - fn y(&self) -> u32 { - PointStorage::get(self).y - } - - fn name(&self) -> String { - "AlphaPoint".to_string() - } -} -``` -or a generic implementation: -```rust - -pub trait Point: PointImpl { - fn x(&self) -> u32; - - fn y(&self) -> u32; - - fn name(&self) -> String; -} -``` - -A default implementation with `openbrush::traits::Storage`: -```rust -pub trait Point: openbrush::traits::Storage { - fn x(&self) -> u32 { - self.data().x - } - - fn y(&self) -> u32 { - self.data().y - } - - fn name(&self) -> String { - "AlphaPoint".to_string() - } -} -``` -or a generic implementation with `openbrush::traits::Storage`: -```rust - -pub trait Point { - fn x(&self) -> u32; - - fn y(&self) -> u32; - - fn name(&self) -> String; -} -``` - -### "Inheritance" of the implementation - -When someone wants to "inherit" implementation and fields, he can embed the data structure, -implement the storage trait, and define an impl section of the main trait: -```rust -struct PointContract { - point: PointData, -} - -impl PointStorage for PointContract { - fn get(&self) -> & PointData { - &self.point - } - fn get_mut(&mut self) -> &mut PointData { - &mut self.point - } -} - -impl PointImpl for PointContract {} - -impl Point for PointContract { - #[ink(message)] - fn x(&self) -> u32 { - PointImpl::x(self) - } - - #[ink(message)] - fn y(&self) -> u32 { - PointImpl::y(self) - } - - #[ink(message)] - fn name(&self) -> String { - PointImpl::name(self) - } -} -``` - -If you are using `openbrush::traits::Storage` trait, then you can use derive macro to automate the implementation of the trait. -Each field for which you want to implement the `Storage` trait should be marked with `#[storage_field]`. - -```rust -use openbrush::traits::Storage; - -#[derive(Storage)] -struct PointContract { - #[storage_field] - point: PointData, -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/errors.md b/docs/docs/smart-contracts/example/errors.md deleted file mode 100644 index facfcade0..000000000 --- a/docs/docs/smart-contracts/example/errors.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -sidebar_position: 8 -title: Errors ---- - -We will define errors thrown by the lending contract at end of `traits/lending.rs` -because only that contract returns its own errors. But if you have more than one error definition, -better to create a separate `traits/errors.rs` file for them(or a directory `traits/errors/`). -In that file(directory) you can define the errors that will be returned by your contracts, -and implement conversion between different errors. -In the project, we implement the conversion for some errors from OpenBrush. - -## Define errors - -```rust -/// Enum of errors raised by our lending smart contract -#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum LendingError { - PSP22Error(PSP22Error), - PSP34Error(PSP34Error), - AccessControlError(AccessControlError), - PausableError(PausableError), - - /// This error will be thrown when the lender does not have enough allowance - /// to transfer the lending asset to the contract - InsufficientAllowanceToLend, - /// This error will be thrown when the lender tries to lend more amount of asset than they own - InsufficientBalanceToLend, - /// This error will be thrown when the borrower does not have enough allowance - /// to transfer the borrowed asset to the contract - InsufficientAllowanceToRepay, - /// This error will be thrown when the borrower tries to repay more amount of asset than they own - InsufficientBalanceToRepay, - /// This error will be thrown when the borrower does not have enough allowance - /// to transfer the collateral asset to the contract - InsufficientAllowanceForCollateral, - /// This error will be thrown when the borrower tries to use more amount of asset as collateral than they own - InsufficientCollateralBalance, - // This error will be thrown if the amount of borrowed assets is greater than or equal to the liquidation price of deposited collateral - AmountNotSupported, - // This error will be thrown if the user wants to borrow or withdraw more assets than there currently are in the contract - InsufficientBalanceInContract, - /// This error will be thrown if the user tries to lend or borrow asset which is not supported by the lending contract - /// or if a user tries to use an usupported asset as a collateral - AssetNotSupported, - /// This error will be thrown if the user tries to allow an asset which is already allowed - AssetSupported, - /// This error will be thrown if the user tries to repay a loan he does not own - NotTheOwner, - /// This error will be thrown if the loan we try to liquidate was already liquidated - LoanLiquidated, - /// This error will be thrown if the loan we try to liquidate is not below liquidation price - CanNotBeLiquidated, - /// This error will be thrown if an user wants to disallow lending of an asset which is still present in the contract - AssetsInTheContract, -} -``` - -## Implement conversion from OpenBrush errors - -```rust -impl From for LendingError { - fn from(access: AccessControlError) -> Self { - LendingError::AccessControlError(access) - } -} - -impl From for LendingError { - fn from(access: PausableError) -> Self { - LendingError::PausableError(access) - } -} - -impl From for LendingError { - fn from(error: PSP22Error) -> Self { - LendingError::PSP22Error(error) - } -} - -impl From for LendingError { - fn from(error: PSP34Error) -> Self { - LendingError::PSP34Error(error) - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/implementation.md b/docs/docs/smart-contracts/example/implementation.md deleted file mode 100644 index 8d283e74f..000000000 --- a/docs/docs/smart-contracts/example/implementation.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -sidebar_position: 10 -title: Notes about methods ---- - -In this section, we describe the implementation of the functions of our lending -contract. - -## Instantiating contracts - -Each asset that we will accept to be lent will have two underlying tokens: -the shares token and the reserves token. The shares token will represent a -user's share of the lent asset which they can then withdraw and the reserves -token will represent the amount of asset lent since we don't want to keep -track of all addresses and amounts which have borrowed the assets. We will -simply take this amount from the total supply of the underlying reserve token. -So when we are accepting an asset for lending, we need to create a new token -contract for shares and for reserves. We will define an internal function for -this: - -```rust -fn _instantiate_shares_contract(&self, contract_name: &str, contract_symbol: &str) -> AccountId { - let code_hash = self.lending.shares_contract_code_hash; - let salt = (::env().block_timestamp(), contract_name).encode(); - let hash = xxh32(&salt, 0).to_le_bytes(); - - let contract = - SharesContractRef::new(Some(String::from(contract_name)), Some(String::from(contract_symbol))) - .endowment(0) - .code_hash(code_hash) - .salt_bytes(&hash[..4]) - .instantiate() - .unwrap(); - contract.to_account_id() -} -``` - -This function will instantiate our `SharesContract` contract and return -the `AccountId` of the instantiated contract. We will call this function -when allowing assets. - -## Simulating oracle - -As mentioned before, we will not be using a price oracle in our example, -but we will use our own simulated oracle. And by simulated we mean adding -some storage fields which hold the info about price of an asset and a function -only callable by the account with `MANAGER` role, which will set the price of -the asset. For that we define these functions: - -```rust -#[modifiers(only_role(MANAGER))] -fn set_asset_price( - &mut self, - asset_in: AccountId, - asset_out: AccountId, - price: Balance, -) -> Result<(), LendingError> { - set_asset_price(self, &asset_in, &asset_out, &price); - Ok(()) -} - -/// this internal function will be used to set price of `asset_in` when we deposit `asset_out` -/// we are using this function in our example to simulate an oracle -pub fn set_asset_price(instance: &mut T, asset_in: &AccountId, asset_out: &AccountId, price: &Balance) -where - T: Storage, -{ - instance.data().asset_price.insert(&(asset_in, asset_out), price); -} -``` - -## Allowing assets - -If we just started lending and borrowing random assets or using random assets -as collateral there would be chaos in our smart contract. -Regarding lending, it would not be a big problem, since if somebody is -willing to borrow an asset, it would generate a profit for the lender. -But if we started accepting random assets as collateral, anyone could just -throw a random coin as collateral and then just for example rug pull it and -also keep the borrowed assets. Because of this we will only accept certain -assets for lending and using as collateral. For an asset to be accepted, an -account with the `MANAGER` role needs to allow it with the `allow_asset` function. -We will use a modifier from OpenBrush, which serves similarly to Solidity's -function modifiers. The function will look like this: - -```rust -#[modifiers(only_role(MANAGER))] -fn allow_asset(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_lending(asset_address) { - return Err(LendingError::AssetSupported) - } - - // instantiate the shares of the lended assets - let shares_address = self._instantiate_shares_contract("LendingShares", "LS"); - // instantiate the reserves of the borrowed assets - let reserves_address = self._instantiate_shares_contract("LendingReserves", "LR"); - // accept the asset and map shares and reserves to it - - accept_lending(self, asset_address, shares_address, reserves_address); - Ok(()) -} -``` - -## Lending assets - -For lending the assets we will use the function `lend_assets(asset_address, amount)`, -where `asset_address` is the address of `PSP22` we want to deposit and `amount` -is the amount of asset deposited. Some checks need to be checked to assure the correct -behavior of our contract. The asset deposited needs to be recognized by our contract -(manager must have approved it). If it is not accepted, an error will be returned. -Then the user must have approved the asset to spent by our contract and the user's -balance must be greater than or equal to `amount`. So we will transfer the asset from -the user to the contract, mint shares to the user. To perform a cross contract call -we will be using the references to contracts `SharesRef`. -We will also add `when_not_paused` modifier to this function, -so it can be only called when the contract is not paused. -The code will look like this: - -```rust -#[modifiers(when_not_paused)] -fn lend_assets(&mut self, asset_address: AccountId, amount: Balance) -> Result<(), LendingError> { - // we will be using these often so we store them in variables - let lender = Self::env().caller(); - let contract = Self::env().account_id(); - // ensure the user gave allowance to the contract - if PSP22Ref::allowance(&asset_address, lender, contract) < amount { - return Err(LendingError::InsufficientAllowanceToLend) - } - // ensure the user has enough assets - if PSP22Ref::balance_of(&asset_address, lender) < amount { - return Err(LendingError::InsufficientBalanceToLend) - } - // how much assets is already in the contract - // if the asset is not accepted by the contract, this function will return an error - let total_asset = self.total_asset(asset_address)?; - // transfer the assets from user to the contract| - PSP22Ref::transfer_from_builder(&asset_address, lender, contract, amount, Vec::::new()) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap()?; - // if no assets were deposited yet we will mint the same amount of shares as deposited `amount` - let new_shares = if total_asset == 0 { - amount - } else { - // else we calculate how much shares will belong us after depositing the `amount` - (amount * self.total_shares(asset_address)?) / total_asset - }; - let reserve_asset = get_reserve_asset(self, &asset_address)?; - // mint the shares token to the user - SharesRef::mint(&reserve_asset, lender, new_shares)?; - Ok(()) -} -``` - -## Borrowing assets - -The `borrow_assets(asset_address, collateral_address, amount)` function will -serve for the users to borrow assets from the smart contract. -`asset_address` is the account id of the asset we want to borrow, -`collateral_address` is the account id of asset which the user wants -to use as collateral, and `amount` is the amount of collateral deposited. -Our contract will calculate the value of the deposited collateral and -will give the borrower 70% of the collateral value. For pricing, we would -use an oracle, but in this example, we will use our 'simulated oracle' - -we will just store the price info in our contract and the admin will -be able to change it. The liquidation price of the loan will be calculated -at 75% of the collateral value. First of all the contract must not be paused, -for which we use modifier `when_not_paused`. After that, for the borrowing -to succeed, the `collateral_address` must be accepted by the contract, -the contract needs to have enough allowance to spend the borrower's collateral -token, borrower's collateral balance must be equal to or greater than `amount` -and finally, the `asset_address` must be accepted for borrowing in the -smart contract. After we calculate the liquidation price and borrow amount, -we ensure the contract has enough assets to provide for the borrower, -and we also want the liquidation price of the collateral to be higher than -the borrowed amount. Since we are dealing with integers, entering a very -low amount (below 10) of collateral may result in the liquidation price being -the same as the borrowed amount, which could be exploited. We can surely -handle it in many different ways, but again, it is not the purpose of this -example so we will deal with it this way. When everything is alright, we will -transfer the collateral to the contract, mint an NFT, which stores the -information about the loan, to the borrower, then transfer the asset to the -borrower, and finally, mint the reserve token. We will mint the same amount -that we lent, and we will burn it after the loan is repaid or liquidated. -This reserve token will be used to track the amount of the asset which is -currently borrowed. - -```rust -#[modifiers(when_not_paused)] -fn borrow_assets( - &mut self, - asset_address: AccountId, - collateral_address: AccountId, - amount: Balance, -) -> Result<(), LendingError> { - // we will be using these often so we store them in variables - let borrower = Self::env().caller(); - let contract = Self::env().account_id(); - // ensure this asset is accepted as collateral - if !self.is_accepted_collateral(collateral_address) { - return Err(LendingError::AssetNotSupported) - } - // ensure the user gave allowance to the contract - if PSP22Ref::allowance(&collateral_address, borrower, contract) < amount { - return Err(LendingError::InsufficientAllowanceForCollateral) - } - // ensure the user has enough collateral assets - if PSP22Ref::balance_of(&collateral_address, borrower) < amount { - return Err(LendingError::InsufficientCollateralBalance) - } - let reserve_asset = get_reserve_asset(self, &asset_address)?; - - // we will find out the price of deposited collateral - let price = get_asset_price(self, &amount, &collateral_address, &asset_address); - // we will set the liquidation price to be 75% of current price - let liquidation_price = (price * 75) / 100; - // borrow amount is 70% of collateral - let borrow_amount = (price * 70) / 100; - // ensure the liquidation price is greater than borrowed amount to avoid misuses - if borrow_amount >= liquidation_price { - return Err(LendingError::AmountNotSupported) - } - // ensure we have enough assets in the contract - if PSP22Ref::balance_of(&asset_address, contract) < borrow_amount { - return Err(LendingError::InsufficientBalanceInContract) - } - // we will transfer the collateral to the contract - PSP22Ref::transfer_from_builder(&collateral_address, borrower, contract, amount, Vec::::new()) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap()?; - // create loan info - let loan_info = LoanInfo { - borrower, - collateral_token: collateral_address, - collateral_amount: amount, - borrow_token: asset_address, - borrow_amount, - liquidation_price, - timestamp: Self::env().block_timestamp(), - liquidated: false, - }; - - let load_account = self.data::().loan_account; - LoanRef::create_loan(&load_account, loan_info)?; - // transfer assets to borrower - PSP22Ref::transfer(&asset_address, borrower, borrow_amount, Vec::::new())?; - // mint `borrow_amount` of the reserve token - SharesRef::mint(&reserve_asset, contract, borrow_amount)?; - Ok(()) -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/impls.md b/docs/docs/smart-contracts/example/impls.md deleted file mode 100644 index 8dbb4d377..000000000 --- a/docs/docs/smart-contracts/example/impls.md +++ /dev/null @@ -1,782 +0,0 @@ ---- -sidebar_position: 7 -title: Lending impls ---- - -The lending contract implementation consists of two traits: -- `Lending` - contains methods that can be called by anyone. - These methods can be used to lend, borrow, liquidate assets and get some information about them. -- `LendingPermissioned` - contains methods with restrictions. These methods can be called by a manager. - These methods allow defining the list of allowed tokens, setting price and etc. - -We will define everything stuff from the previous chapter for "inheritable" contracts: -- Both traits in `traits/lending.rs` -- A data structure in `impls/lending/data.rs` -- A generic implementation for trait `Lending` in `impls/lending/lending.rs` -- A generic implementation for trait `LendingPermissioned` in `impls/lending/lending_permissioned.rs` - -## Definition of traits - -In the `traits/lending.rs`, we will define `Lending` and `LendingPermissioned` traits. -We plan that `LendingContract` also will implement `AccessControl` and `Pausable`, so -`LendingContractRef` is defined like a combination of `AccessControl`, `Pausable`, `LendingPermissioned` and `Lending`. -That wrapper describes all methods available in the `LendingContract`. - -```rust -use openbrush::{ - contracts::traits::{ - access_control::*, - pausable::*, - psp22::PSP22Error, - psp34::{ - Id, - PSP34Error, - }, - }, - traits::{ - AccountId, - Balance, - }, -}; - -/// Combination of all traits of the contract to simplify calls to the contract -#[openbrush::wrapper] -pub type LendingContractRef = dyn Lending + LendingPermissioned + AccessControl + Pausable; - -#[openbrush::wrapper] -pub type LendingRef = dyn Lending; - -#[openbrush::trait_definition] -pub trait Lending { - /// This function will return the total amount of assets available to borrow - /// along with amount of the same asset borrowed - /// - /// Returns `AssetNotSupported` error if we try to get amount of asset not supported by our contract - #[ink(message)] - fn total_asset(&self, asset_address: AccountId) -> Result; - - /// This function will return the total amount of shares minted for an asset - /// - /// Returns `AssetNotSupported` error if we try to get shares of asset not supported by our contract - #[ink(message)] - fn total_shares(&self, asset_address: AccountId) -> Result; - - /// This function will return the address of shares - /// which is bound to `asset_address` asset token - #[ink(message)] - fn get_asset_shares(&self, asset_address: AccountId) -> Result; - - /// This function will return true if the asset is accepted by the contract - #[ink(message)] - fn is_accepted_lending(&self, asset_address: AccountId) -> bool; - - /// This function will return true if the asset is accepted for using as collateral - #[ink(message)] - fn is_accepted_collateral(&self, asset_address: AccountId) -> bool; - - /// This function is called by a user who wants to lend tokens and gain interest - /// - /// `asset_address` is the AccountId of the PSP22 token to be deposited - /// `amount` is the amount to be deposited - /// - /// Returns `InsufficientAllowanceToLend` if the caller does not have enough allowance - /// Returns `InsufficientBalanceToLend` if the caller does not have enough balance - /// Returns `AssetNotSupported` if the asset is not supported for lending - #[ink(message)] - fn lend_assets(&mut self, asset_address: AccountId, amount: Balance) -> Result<(), LendingError>; - - /// This function is called by a user who wants to borrow tokens. In order to do that, - /// they need to deposit collateral. The value of borrowed assets will be equal to 70% - /// of the value of deposited collateral. - /// - /// `asset_address` is the AccountId of the PSP22 token to be borrowed - /// `collateral_address` is the AccountId of the PSP22 token used as collateral - /// `amount` is the amount to be deposited - /// - /// Returns `AssetNotSupported` if `asset_address` is not supported for using as collateral - /// Returns `InsufficientAllowanceForCollateral` if the caller does not have enough allowance - /// Returns `InsufficientCollateralBalance` if the caller does not have enough balance - /// Returns `AssetNotSupported` if the borrowing asset is not supported for borrowing - /// Returns `AmountNotSupported` if the liquidation price is less than or equal to the borrowed amount - /// Returns `InsufficientBalanceInContract` if there is not enough amount of assets in the contract to borrow - #[ink(message)] - fn borrow_assets( - &mut self, - asset_address: AccountId, - collateral_address: AccountId, - amount: Balance, - ) -> Result<(), LendingError>; - - /// This function is called by the user who borrowed some asset. User needs to deposit borrowed amount along with interest - /// They can either repay the full amount or just a portion of the amount. If they repay the full amount, they will get all deposited - /// collateral back, another way they will get back the same portion of collateral as the repay portion (eg. if they deposit 80% of - /// the loan + interests, they will get 80% of collateral back). If the loan was liquidated, the user does not get their collateral - /// back and the NFT will be burned - /// - /// `loan_id` is the id of the loan to be repaid - /// `repay_amount` is the amount of borrowed asset to be repaid - /// - /// Returns true if the loan was repaid successfuly, false if the loan was already liquidated and can not be repaid - /// Returns `NotTheOwner` error if the initiator is not the owner of the loan token - /// Returns `InsufficientAllowanceToRepay` error if the initiator did not give allowance to the contract - /// Returns `InsufficientBalanceToRepay` error if the initiator tries to repay more tokens than their balance - #[ink(message)] - fn repay(&mut self, loan_id: Id, repay_amount: Balance) -> Result; - - /// This function is called by the user who wants to withdraw assets they deposited for lending. They will deposit their - /// share tokens and get back their share of the asset mapped to this share token - /// - /// `shares_address` account id of the shares token which is binded to the asset - /// `shares_amount` amount of shares being withdrawn - /// - /// Returns `InsufficientBalanceInContract` if there is currently not enough assets in the contract - #[ink(message)] - fn withdraw_asset(&mut self, shares_address: AccountId, shares_amount: Balance) -> Result<(), LendingError>; - - /// This function will liquidate the loan with `loan_id`. In this example contract the tokens will be kept in the smart - /// contract and the liquidator gets 1% of the liquidated assets. In a real implementation we would swap the collateral - /// for the borrowed asset so we would be able to cover the shares of lenders. - /// - /// `loan_id` id of loan to be liquidated - /// - /// Returns `LoanLiquidated` error if the loan was already liquidated - /// Returns `CanNotBeLiquidated` error if the price of collateral is not below the liquidation price - #[ink(message)] - fn liquidate_loan(&mut self, loan_id: Id) -> Result<(), LendingError>; -} - -#[openbrush::wrapper] -pub type LendingPermissionedRef = dyn LendingPermissioned; - -#[openbrush::trait_definition] -pub trait LendingPermissioned { - /// This function will allow an asset to be accepted by the contract - /// It will also create the contracts for the shares token and lended reserves token - #[ink(message, payable)] - fn allow_asset(&mut self, asset_address: AccountId) -> Result<(), LendingError>; - - /// This function will disallow lending and borrowing of asset - /// To do this all assets of this asset must be repaid and all of the asset must be withdrawn - #[ink(message)] - fn disallow_lending(&mut self, asset_address: AccountId) -> Result<(), LendingError>; - - /// This function will allow an asset to be accepted as collateral - #[ink(message)] - fn allow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError>; - - /// This function will disallow an asset to be accepted as collateral - #[ink(message)] - fn disallow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError>; - - /// This function will set price of `asset_in` in `asset_out` to `amount` in our simulated oracle - #[ink(message)] - fn set_asset_price( - &mut self, - asset_in: AccountId, - asset_out: AccountId, - price: Balance, - ) -> Result<(), LendingError>; -} -``` - -## Data and storage trait - -In the `impls/lending/data.rs` we will define the data related to the lending contract. -Also there we will define some helper functions. - -In this example we will not be using price oracles, we will do -our own simulated oracle. Since oracles are not the point of this example, -it will be enough for us. We will store prices info in our data struct. - -```rust -// Importing everything publicly from traits allows you to import -// every stuff related to lending by one import -use crate::traits::lending::*; -use openbrush::{ - storage::{ - Mapping, - TypeGuard, - }, - traits::{ - AccountId, - Balance, - Hash, - }, -}; - -use openbrush::traits::Storage; - -#[derive(Default, Debug)] -#[ink::storage_item] -/// define the struct with the data that our smart contract will be using -/// this will isolate the logic of our smart contract from its storage -pub struct Data { - /// mapping from asset address to lended asset address - /// when X amount of asset is lended, X amount of asset it is mapped to is minted - /// so the contract knows how much of asset it has and how much of the asset was lended - pub assets_lended: Mapping, - /// mapping from asset address to shares asset address - /// the lended asset is mapped to a shares asset which represents - /// the total share of the mapping asset - /// example: if a user has X% of the total supply of the asset A', they - /// are eligible to withdraw X% of the asset A tracked by this contract - pub asset_shares: Mapping, - /// mapping from share token to asset token - pub shares_asset: Mapping, - /// mapping from asset address to bool - /// maps to `true` if an asset is accepted for using as collateral - pub collateral_accepted: Mapping, - /// mapping from tuple of two assets to balance - /// mapped balance represents the amount of assets of tuple.1 we get - /// when we deposit 1 unit of tuple.0 - /// we are using this just to simulate an oracle in our example - /// in the example the returned balance will be amount of stable coin for an asset - pub asset_price: Mapping<(AccountId, AccountId), Balance, AssetPriceKey>, - /// code hash of the `SharesContract` - pub shares_contract_code_hash: Hash, - /// the `AccountId` of the `Loan` - pub loan_account: AccountId, -} - -pub struct AssetPriceKey; - -impl<'a> TypeGuard<'a> for AssetPriceKey { - type Type = &'a (&'a AccountId, &'a AccountId); -} - -/// this internal function will be used to set price of `asset_in` when we deposit `asset_out` -/// we are using this function in our example to simulate an oracle -pub fn set_asset_price(instance: &mut T, asset_in: &AccountId, asset_out: &AccountId, price: &Balance) -where - T: Storage, -{ - instance.data().asset_price.insert(&(asset_in, asset_out), price); -} - -/// this internal function will be used to set price of `asset_in` when we deposit `asset_out` -/// we are using this function in our example to simulate an oracle -pub fn get_asset_price(instance: &T, amount_in: &Balance, asset_in: &AccountId, asset_out: &AccountId) -> Balance -where - T: Storage, -{ - let price = instance.data().asset_price.get(&(asset_in, asset_out)).unwrap_or(0); - price * amount_in -} - -/// Internal function which will return the address of the shares token -/// which are minted when `asset_address` is borrowed -pub fn get_reserve_asset(instance: &T, asset_address: &AccountId) -> Result -where - T: Storage, -{ - instance - .data() - .asset_shares - .get(&asset_address) - .ok_or(LendingError::AssetNotSupported) -} - -/// internal function which will return the address of asset -/// which is bound to `shares_address` shares token -pub fn get_asset_from_shares(instance: &T, shares_address: &AccountId) -> Result -where - T: Storage, -{ - instance - .data() - .shares_asset - .get(shares_address) - .ok_or(LendingError::AssetNotSupported) -} - -``` - -## A generic implementation of `LendingPermissioned` trait - -The all methods in `LendingPermissioned` are restricted and requires `#[modifiers(only_role(MANAGER))]`. -That means that only accounts with `MANAGER` role can execute these methods. -Usage of `only_role` modifier from [access_control](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/access/access_control/mod.rs#L30) -requires that the contract should implement `Storage`. -For that we also require the same restriction on the generic type. - -In the implementation of `LendingPermissioned`, we want to use methods from -`Lending`. For that, the set of restrictions for generic in the `Lending` implementation -should be a subset(<=) of restrictions for generic in the `LendingPermissioned` implementation. -The `Lending` implementation requires `Storage` and `Storage` to use `when_paused` -modifier from [pausable](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/security/pausable/mod.rs#L24). -So we should have the same restriction in our generic implementation. - -In the logic of the trait `LendingPermissioned` we need to instantiate -the `SharesContract`. But we can't import `SharesContract` into `lending_project` -crate, because `SharesContract` also depends on `lending_project`. -That will cause cyclic dependencies. -To avoid that we will import `SharesContract` into `LendingContract` and in `LendingContract` we will define -`_instantiate_shares_contract` method, that will instantiate `SharesCotnract`. - -```rust -impl lending::Internal for LendingContract { - fn _instantiate_shares_contract(&self, contract_name: &str, contract_symbol: &str) -> AccountId { - let code_hash = self.lending.shares_contract_code_hash; - - let salt = (::env().block_timestamp(), contract_name).encode(); - - let hash = xxh32(&salt, 0).to_le_bytes(); - - let contract = - SharesContractRef::new(Some(String::from(contract_name)), Some(String::from(contract_symbol))) - .endowment(0) - .code_hash(code_hash) - .salt_bytes(&hash[..4]) - .instantiate() - .unwrap(); - contract.to_account_id() - } -} -``` - -For that we defined the `Internal` trait in `lending` module with `_instantiate_shares_contract` method. -Then, we define the default implementaion of `LendingPermissioned` trait which we call `LendingPermissionedImpl` -and which restricts the type for which it is implemented by `Storage`, `Storage`, `Storage`, `lending::Internal` traits. -That allows us to use methods from these traits and define the implementation. - -```rust -use super::{ - data, - data::*, -}; -use crate::traits::lending::*; -use openbrush::{ - contracts::{ - access_control::*, - pausable::*, - traits::psp22::PSP22Ref, - }, - modifiers, - traits::{ - AccountId, - Balance, - OccupiedStorage, - Storage, - }, -}; - -pub const MANAGER: RoleType = ink::selector_id!("MANAGER"); - -pub trait LendingPermissionedImpl: - access_control::Internal + Storage + lending_internal::Internal + Lending + Instantiator -{ - #[modifiers(only_role(MANAGER))] - fn allow_asset(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_lending(asset_address) { - return Err(LendingError::AssetSupported) - } - - // instantiate the shares of the lended assets - let shares_address = self._instantiate_shares_contract("LendingShares", "LS"); - // instantiate the reserves of the borrowed assets - let reserves_address = self._instantiate_shares_contract("LendingReserves", "LR"); - // accept the asset and map shares and reserves to it - - accept_lending(self, asset_address, shares_address, reserves_address); - Ok(()) - } - - #[modifiers(only_role(MANAGER))] - fn disallow_lending(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - let reserve_asset = get_reserve_asset(self, &asset_address)?; - if PSP22Ref::balance_of(&asset_address, Self::env().account_id()) > 0 - || PSP22Ref::balance_of(&reserve_asset, Self::env().account_id()) > 0 - { - return Err(LendingError::AssetsInTheContract) - } - disallow_lending(self, asset_address); - Ok(()) - } - - #[modifiers(only_role(MANAGER))] - fn allow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_collateral(asset_address) { - return Err(LendingError::AssetSupported) - } - set_collateral_accepted(self, asset_address, true); - Ok(()) - } - - #[modifiers(only_role(MANAGER))] - fn disallow_collateral(&mut self, asset_address: AccountId) -> Result<(), LendingError> { - // we will ensure the asset is not accepted already - if self.is_accepted_collateral(asset_address) { - set_collateral_accepted(self, asset_address, false); - } - Ok(()) - } - - #[modifiers(only_role(MANAGER))] - fn set_asset_price( - &mut self, - asset_in: AccountId, - asset_out: AccountId, - price: Balance, - ) -> Result<(), LendingError> { - set_asset_price(self, &asset_in, &asset_out, &price); - Ok(()) - } -} - -pub trait Internal { - /// Internal function which instantiates a shares contract and returns its AccountId - fn _instantiate_shares_contract(&self, contract_name: &str, contract_symbol: &str) -> AccountId; -} - -fn accept_lending>( - instance: &mut T, - asset_address: AccountId, - share_address: AccountId, - reserve_address: AccountId, -) { - instance.data().asset_shares.insert(&asset_address, &share_address); - instance.data().shares_asset.insert(&share_address, &asset_address); - instance.data().assets_lended.insert(&asset_address, &reserve_address); -} - -fn disallow_lending>(instance: &mut T, asset_address: AccountId) { - if let Some(share_address) = instance - .data() - .asset_shares - .get(&asset_address) { - instance.data().asset_shares.remove(&asset_address); - instance.data().shares_asset.remove(&share_address); - instance.data().assets_lended.remove(&asset_address); - } -} - -/// this function will accept `asset_address` for using as collateral -fn set_collateral_accepted>(instance: &mut T, asset_address: AccountId, accepted: bool) { - instance.data().collateral_accepted.insert(&asset_address, &accepted); -} -``` - -## A generic implementation of `Lending` trait - -The same logic is used during definition of the implementation for `Lending` trait. - -The `Storage` restriction is required to use `when_paused`, `when_not_paused` modifiers -from [pausable](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/security/pausable/mod.rs#L24). - -```rust -// Importing everything publicly from traits allows you to import -// every stuff related to lending by one import -pub use crate::{ - impls::lending::{ - data, - data::*, - lending, - lending_permissioned::{ - Internal, - *, - }, - *, - }, - traits::lending::*, -}; - -use crate::traits::{ - loan::{ - LoanInfo, - LoanRef, - }, - shares::SharesRef, -}; -use ink::prelude::vec::Vec; -use openbrush::{ - contracts::{ - pausable::*, - traits::{ - psp22::PSP22Ref, - psp34::Id, - }, - }, - modifiers, - traits::{ - AccountId, - Balance, - Storage, - Timestamp, - }, -}; - -pub const YEAR: Timestamp = 60 * 60 * 24 * 365; - -pub trait LendingImpl: Storage + lending_internal::Internal + Storage { - fn total_asset(&self, asset_address: AccountId) -> Result { - // get asset from mapping - if let Some(mapped_asset) = self - .data::() - .assets_lended - .get(&asset_address){ - let contract = Self::env().account_id(); - let available = PSP22Ref::balance_of(&asset_address, contract); - let unavailable = PSP22Ref::balance_of(&mapped_asset, contract); - Ok(available + unavailable) - }else { - // return error if the asset is not supported - Err(LendingError::AssetNotSupported) - } - } - - fn total_shares(&self, asset_address: AccountId) -> Result { - // get asset from mapping - if let Some(mapped_asset) = self - .data::() - .asset_shares - .get(&asset_address) { - Ok(PSP22Ref::total_supply(&mapped_asset)) - } else { - Err(LendingError::AssetNotSupported) - } - } - - fn get_asset_shares(&self, asset_address: AccountId) -> Result { - self - .data::() - .asset_shares - .get(&asset_address) - .ok_or(LendingError::AssetNotSupported) - } - - fn is_accepted_lending(&self, asset_address: AccountId) -> bool { - self - .data::() - .asset_shares - .get(&asset_address) - .is_some() - } - - fn is_accepted_collateral(&self, asset_address: AccountId) -> bool { - self.data::() - .collateral_accepted - .get(&asset_address) - .unwrap_or(false) - } - - #[modifiers(when_not_paused)] - fn lend_assets(&mut self, asset_address: AccountId, amount: Balance) -> Result<(), LendingError> { - // we will be using these often so we store them in variables - let lender = Self::env().caller(); - let contract = Self::env().account_id(); - // ensure the user gave allowance to the contract - if PSP22Ref::allowance(&asset_address, lender, contract) < amount { - return Err(LendingError::InsufficientAllowanceToLend) - } - // ensure the user has enough assets - if PSP22Ref::balance_of(&asset_address, lender) < amount { - return Err(LendingError::InsufficientBalanceToLend) - } - // how much assets is already in the contract - // if the asset is not accepted by the contract, this function will return an error - let total_asset = self.total_asset(asset_address)?; - // transfer the assets from user to the contract| - PSP22Ref::transfer_from_builder(&asset_address, lender, contract, amount, Vec::::new()) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap()?; - // if no assets were deposited yet we will mint the same amount of shares as deposited `amount` - let new_shares = if total_asset == 0 { - amount - } else { - // else we calculate how much shares will belong us after depositing the `amount` - (amount * self.total_shares(asset_address)?) / total_asset - }; - let reserve_asset = get_reserve_asset(self, &asset_address)?; - // mint the shares token to the user - SharesRef::mint(&reserve_asset, lender, new_shares)?; - Ok(()) - } - - #[modifiers(when_not_paused)] - fn borrow_assets( - &mut self, - asset_address: AccountId, - collateral_address: AccountId, - amount: Balance, - ) -> Result<(), LendingError> { - // we will be using these often so we store them in variables - let borrower = Self::env().caller(); - let contract = Self::env().account_id(); - // ensure this asset is accepted as collateral - if !self.is_accepted_collateral(collateral_address) { - return Err(LendingError::AssetNotSupported) - } - // ensure the user gave allowance to the contract - if PSP22Ref::allowance(&collateral_address, borrower, contract) < amount { - return Err(LendingError::InsufficientAllowanceForCollateral) - } - // ensure the user has enough collateral assets - if PSP22Ref::balance_of(&collateral_address, borrower) < amount { - return Err(LendingError::InsufficientCollateralBalance) - } - let reserve_asset = get_reserve_asset(self, &asset_address)?; - - // we will find out the price of deposited collateral - let price = get_asset_price(self, &amount, &collateral_address, &asset_address); - // we will set the liquidation price to be 75% of current price - let liquidation_price = (price * 75) / 100; - // borrow amount is 70% of collateral - let borrow_amount = (price * 70) / 100; - // ensure the liquidation price is greater than borrowed amount to avoid misuses - if borrow_amount >= liquidation_price { - return Err(LendingError::AmountNotSupported) - } - // ensure we have enough assets in the contract - if PSP22Ref::balance_of(&asset_address, contract) < borrow_amount { - return Err(LendingError::InsufficientBalanceInContract) - } - // we will transfer the collateral to the contract - PSP22Ref::transfer_from_builder(&collateral_address, borrower, contract, amount, Vec::::new()) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap()?; - // create loan info - let loan_info = LoanInfo { - borrower, - collateral_token: collateral_address, - collateral_amount: amount, - borrow_token: asset_address, - borrow_amount, - liquidation_price, - timestamp: Self::env().block_timestamp(), - liquidated: false, - }; - - let load_account = self.data::().loan_account; - LoanRef::create_loan(&load_account, loan_info)?; - // transfer assets to borrower - PSP22Ref::transfer(&asset_address, borrower, borrow_amount, Vec::::new())?; - // mint `borrow_amount` of the reserve token - SharesRef::mint(&reserve_asset, contract, borrow_amount)?; - Ok(()) - } - - fn repay(&mut self, loan_id: Id, repay_amount: Balance) -> Result { - // REPAYING (borrower: B, nft, repayAmount: X): - let initiator = Self::env().caller(); - let contract = Self::env().account_id(); - let loan_account = self.data::().loan_account; - let apy = 1000; - // initiator must own the nft - if LoanRef::owner_of(&loan_account, loan_id.clone()) != Some(initiator) { - return Err(LendingError::NotTheOwner) - } - let loan_info = LoanRef::get_loan_info(&loan_account, loan_id.clone())?; - if loan_info.liquidated { - LoanRef::delete_loan(&loan_account, initiator, loan_id.clone())?; - return Ok(false) - } - - // ensure initiator has enough allowance - if PSP22Ref::allowance(&loan_info.borrow_token, initiator, contract) < repay_amount { - return Err(LendingError::InsufficientAllowanceToRepay) - } - // ensure initiator has enough balance - if PSP22Ref::balance_of(&loan_info.borrow_token, initiator) < repay_amount { - return Err(LendingError::InsufficientBalanceToRepay) - } - let time_passed = Self::env().block_timestamp() - loan_info.timestamp; - let total_apy = (apy * time_passed as Balance) / YEAR as Balance; - let to_repay = (((loan_info.borrow_amount) * (10000 + total_apy)) / 10000) + 1; - let reserve_asset = get_reserve_asset(self, &loan_info.borrow_token)?; - if repay_amount >= to_repay { - PSP22Ref::transfer_from_builder(&loan_info.borrow_token, initiator, contract, to_repay, Vec::::new()) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap()?; - PSP22Ref::transfer( - &loan_info.collateral_token, - initiator, - loan_info.collateral_amount, - Vec::::new(), - )?; - LoanRef::delete_loan(&loan_account, initiator, loan_id)?; - SharesRef::burn(&reserve_asset, Self::env().caller(), loan_info.borrow_amount)?; - } else { - PSP22Ref::transfer_from_builder( - &loan_info.borrow_token, - initiator, - contract, - repay_amount, - Vec::::new(), - ) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap()?; - let to_return = (repay_amount * loan_info.collateral_amount) / to_repay; - PSP22Ref::transfer(&loan_info.collateral_token, initiator, to_return, Vec::::new())?; - SharesRef::mint( - &reserve_asset, - contract, - to_repay - repay_amount, - )?; - LoanRef::update_loan( - &loan_account, - loan_id.clone(), - to_repay - repay_amount, - Self::env().block_timestamp(), - loan_info.collateral_amount - to_return, - )?; - } - Ok(true) - } - - fn withdraw_asset( - &mut self, - shares_address: AccountId, - shares_amount: Balance, - ) -> Result<(), LendingError> { - let withdraw_asset = get_asset_from_shares(self, &shares_address)?; - let withdraw_amount = - (shares_amount * self.total_asset(withdraw_asset)?) / PSP22Ref::total_supply(&shares_address); - if withdraw_amount > PSP22Ref::balance_of(&withdraw_asset, Self::env().account_id()) { - return Err(LendingError::InsufficientBalanceInContract) - } - - SharesRef::burn(&shares_address, Self::env().caller(), shares_amount)?; - PSP22Ref::transfer(&withdraw_asset, Self::env().caller(), withdraw_amount, Vec::::new())?; - Ok(()) - } - - fn liquidate_loan(&mut self, loan_id: Id) -> Result<(), LendingError> { - let loan_account = self.data::().loan_account; - let loan_info = LoanRef::get_loan_info(&loan_account, loan_id.clone())?; - - if loan_info.liquidated { - return Err(LendingError::LoanLiquidated) - } - - let price = get_asset_price( - self, - &loan_info.collateral_amount, - &loan_info.collateral_token, - &loan_info.borrow_token, - ); - - if price <= loan_info.liquidation_price { - // if we swapped the collateral to borrow asset we would burn the reserve tokens - // let reserve_asset = self._get_reserve_asset(borrow_asset); - // PSP22BurnableRef::burn(&reserve_asset, borrow_amount) - let reward = (loan_info.collateral_amount * 1000) / 100000; - PSP22Ref::transfer( - &loan_info.collateral_token, - Self::env().caller(), - reward, - Vec::::new(), - )?; - LoanRef::liquidate_loan(&loan_account, loan_id.clone())?; - } else { - return Err(LendingError::CanNotBeLiquidated) - } - Ok(()) - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/loan.md b/docs/docs/smart-contracts/example/loan.md deleted file mode 100644 index 28bc24306..000000000 --- a/docs/docs/smart-contracts/example/loan.md +++ /dev/null @@ -1,303 +0,0 @@ ---- -sidebar_position: 5 -title: Loan contract ---- - -In our project we will also implement [PSP34](../PSP34/psp34.md) -token. This token will represent a loan of a user who borrowed some assets. -Upon borrowing assets the contract will mint an NFT to them, which will hold -the information about their loan, namely the user who borrowed the assets, -address of the asset which was used as collateral, how much collateral was -deposited, what asset was borrowed, and how much, the liquidation price of -the loan, timestamp of when was the loan performed, and information whether -the loan is liquidated or not. This data will be stored in a separate storage -trait, which we will derive in our NFT contract. We do this to separate storage -from the logic, and we will do this in the lending contract as well. -We do not want anybody to just mint and burn these, so we will implement -the [Ownable](../ownable.md) extension in our NFT. The mint and burn -logic will be covered differently, we will not be using the mintable and -burnable extensions. - -The `LoanContract` will contain several methods defined in the `Loan` trait. -These methods are restricted and can be called only by an owner of the contract. -There is not too much logic to split it, so everything will be implemented -in the body of the contract. - -## Definition of the `Loan` trait - -In the `traits/loan.rs`, we will define a `Loan` trait. -That trait contains three super traits: `PSP34`, `PSP34Metadata`, and `Ownable`. -Also, the trait contains several methods, and the definition of the `LoanInfo` -(that structure is used during interacting with the contract -so it is defined in the `traits` instead of the body of the contract). -`LoanRef` can be used by other developers to do a cross contract call to `LoanContract`. - -```rust -use openbrush::{ - contracts::traits::{ - ownable::*, - psp34::{ - extensions::metadata::*, - *, - }, - }, - traits::{ - AccountId, - Balance, - Timestamp, - }, -}; - -#[cfg(feature = "std")] -use ink::storage::traits::StorageLayout; - -#[derive(Default, Debug, Clone, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(StorageLayout, scale_info::TypeInfo))] -pub struct LoanInfo { - pub borrower: AccountId, - pub collateral_token: AccountId, - pub collateral_amount: Balance, - pub borrow_token: AccountId, - pub borrow_amount: Balance, - pub liquidation_price: Balance, - pub timestamp: Timestamp, - pub liquidated: bool, -} - -#[openbrush::wrapper] -pub type LoanRef = dyn Loan + PSP34 + PSP34Metadata + Ownable; - -#[openbrush::trait_definition] -pub trait Loan: PSP34 + PSP34Metadata + Ownable { - /// This function initalizes data of a loan and mint token inside it - #[ink(message)] - fn create_loan(&mut self, loan_info: LoanInfo) -> Result<(), PSP34Error>; - - /// This function frees data of a loan and burn token inside it - #[ink(message)] - fn delete_loan(&mut self, initiator: AccountId, loan_id: Id) -> Result<(), PSP34Error>; - - /// This function will be used when the user repays their loan only partially - #[ink(message)] - fn update_loan( - &mut self, - loan_id: Id, - new_borrow_amount: Balance, - new_timestamp: Timestamp, - new_collateral_amount: Balance, - ) -> Result<(), PSP34Error>; - - /// This function will set a loan to liquidated - #[ink(message)] - fn liquidate_loan(&mut self, loan_id: Id) -> Result<(), PSP34Error>; - - /// Function returns `LoanInfo` by `Id` - #[ink(message)] - fn get_loan_info(&self, loan_id: Id) -> Result; -} -``` - -## Add dependencies - -In addition to the dependencies imported in the [PSP34](../PSP34/psp34.md) -documentation, we will also add the `ownable` dependency the same way as in the -[ownable](../ownable.md) documentation. We will be using `LoanContract` -as a dependency in our lending contract to instantiate it. So we need to also add -the `"rlib"` crate type to have the ability to import the `LoanContract` as a dependency. - -## Implement the contract - -We want a basic [PSP34](../PSP34/psp34.md) token with metadata and ownable extensions, -so we will add these to our contract. We will add a `openbrush::contract` macro to our contract and add some imports: - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -/// This contract will represent the loan of a user -#[openbrush::implementation(Ownable, PSP34, PSP34Metadata)] -#[openbrush::contract] -pub mod loan { - use openbrush::traits::String; - use lending_project::traits::loan::*; - use openbrush::{ - modifiers, - storage::Mapping, - traits::Storage, - }; -``` - -> *Note*: If some default OpenBrush implementation is added to contract as a part of `#[openbrush::implementation]` macro, you -> don't have to add imports on your module. For example, if you add `PSP34` to your contract, you don't have to add -> `use openbrush::traits::psp34::*;` to your module. - -## Define the storage - -We will derive the storage traits related to `PSP34`, `PSP34Metadata`, and -`Ownable` and declare the fields related to these traits. Also, we will declare -fields related to `Loan` itself. - -```rust -/// Define the storage for PSP34 data, Metadata data and Ownable data -#[ink(storage)] -#[derive(Storage)] -pub struct LoanContract { - #[storage_field] - psp34: psp34::Data, - #[storage_field] - ownable: ownable::Data, - #[storage_field] - metadata: metadata::Data, - - // Fields of current contract - /// mapping from token id to `LoanInfo` - loan_info: Mapping, - /// the id of last loan - last_loan_id: Id, -} -``` - -## Implement the extension traits - -We will be using these extensions in our NFT token, so we will implement them inside `#[openbrush::implementation]` macro. - -```rust -#[openbrush::implementation(Ownable, PSP34, PSP34Metadata)] -#[openbrush::contract] -mod contract { - ... -} -``` - -## Implement the Loan trait - -We will implement the `Loan` trait. -All functions except one are restricted by the `only_owner` modifier. - -```rust -impl Loan for LoanContract { - #[modifiers(only_owner)] - #[ink(message)] - fn create_loan(&mut self, mut loan_info: LoanInfo) -> Result<(), PSP34Error> { - let loan_id = self._get_next_loan_id_and_increase()?; - if self.loan_info.get(&loan_id).is_some() { - return Err(PSP34Error::Custom(String::from("This loan id already exists!"))) - } - loan_info.liquidated = false; - self.loan_info.insert(&loan_id, &loan_info); - self._mint_to(loan_info.borrower, loan_id) - } - - #[modifiers(only_owner)] - #[ink(message)] - fn delete_loan(&mut self, initiator: AccountId, loan_id: Id) -> Result<(), PSP34Error> { - self.loan_info.remove(&loan_id); - self._burn_from(initiator, loan_id) - } - - #[modifiers(only_owner)] - #[ink(message)] - fn update_loan( - &mut self, - loan_id: Id, - new_borrow_amount: Balance, - new_timestamp: Timestamp, - new_collateral_amount: Balance, - ) -> Result<(), PSP34Error> { - self._update_loan(loan_id, new_borrow_amount, new_timestamp, new_collateral_amount) - } - - #[modifiers(only_owner)] - #[ink(message)] - fn liquidate_loan(&mut self, loan_id: Id) -> Result<(), PSP34Error> { - self._liquidate_loan(loan_id) - } - - #[ink(message)] - fn get_loan_info(&self, loan_id: Id) -> Result { - let loan_info = self.loan_info.get(&loan_id); - if loan_info.is_none() { - return Err(PSP34Error::Custom(String::from("Loan does not exist"))) - } - Ok(loan_info.unwrap()) - } -} -``` - -## Define the constructor and add functions - -Finally, we will define the constructor where we will set the name and -the symbol of the token and then initialize the owner of the token -(that owner will be able to mint and burn the tokens). -We will also add several helper functions. - -```rust -impl LoanContract { - /// constructor with name and symbol - #[ink(constructor, payable)] - pub fn new() -> Self { - let mut instance = Self::default(); - - instance.last_loan_id = Id::U128(1); - instance._set_attribute( - Id::U8(1u8), - String::from("LoanContract NFT"), - String::from("L-NFT"), - ); - instance._init_with_owner(Self::env().caller()); - - instance - } - - /// Internal function to update data of a loan - fn _update_loan( - &mut self, - loan_id: Id, - new_borrow_amount: Balance, - new_timestamp: Timestamp, - new_collateral_amount: Balance, - ) -> Result<(), PSP34Error> { - let loan_info = self.loan_info.get(&loan_id); - - if loan_info.is_none() { - return Err(PSP34Error::Custom(String::from("This loan does not exist!"))) - } - - let mut loan_info = loan_info.unwrap(); - loan_info.collateral_amount = new_collateral_amount; - loan_info.borrow_amount = new_borrow_amount; - loan_info.timestamp = new_timestamp; - - self.loan_info.insert(&loan_id, &loan_info); - - Ok(()) - } - - /// Internal function to set loan to liquidated - fn _liquidate_loan(&mut self, loan_id: Id) -> Result<(), PSP34Error> { - let loan_info = self.loan_info.get(&loan_id); - - if loan_info.is_none() { - return Err(PSP34Error::Custom(String::from("This loan does not exist!"))) - } - - let mut loan_info = loan_info.unwrap(); - loan_info.liquidated = true; - - self.loan_info.insert(&loan_id, &loan_info); - - Ok(()) - } - - /// Internal function to return the id of a new loan and to increase it in the storage - fn _get_next_loan_id_and_increase(&mut self) -> Result { - match &mut self.last_loan_id { - Id::U128(id) => { - let result = Id::U128(id.clone()); - *id += 1; - Ok(result) - } - _ => Err(PSP34Error::Custom(String::from("Not expected Id!"))), - } - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/overview.md b/docs/docs/smart-contracts/example/overview.md deleted file mode 100644 index bb4bfc905..000000000 --- a/docs/docs/smart-contracts/example/overview.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -sidebar_position: 1 -title: Overview ---- - -This example will show you how you can reuse OpenBrush smart contracts and macros in your -project to ease the development process. We will also pay attention to the project -structure to keep the maintenance and future development of the project simple. - -We will be implementing a simple lending protocol, in which users can lend -[PSP22](../PSP22/psp22.md) tokens, borrow them against a collateral token, -repay their loans with interest, and of course withdraw the deposited assets. -We will create a [PSP22](../PSP22/psp22.md) implementation which will be used -for a stable coin and a collateral token, another [PSP22](../PSP22/psp22.md) -token which will represent the shares of assets in the contract, -[PSP34](../PSP34/psp34.md) token which will represent the loans and the -lending contract itself. The simple [PSP22](../PSP22/psp22.md) token -implementation will be created just for this example and to test the contract's functions. -The contract will have the following features: - -## Lending of assets accepted by the smart contract - -Users can lend [PSP22](../PSP22/psp22.md) tokens, which are accepted by the -contract. The allowance of lending specific tokens is decided in the smart contract -by the accounts which have the Manager role. Upon lending the user gets a -[PSP22](../PSP22/psp22.md) token representing their share of the asset pool. - -## Borrowing of assets by depositing accepted assets as collateral - -Users can borrow [PSP22](../PSP22/psp22.md) tokens, which are available in -the contract. To borrow an asset, the user has to deposit an accepted -[PSP22](../PSP22/psp22.md) token as collateral. The allowance of specific -tokens being used as collateral is decided in the smart contract by the accounts -which have the Manager role. The value of the borrowed assets can be equal at most -to 70% of the value of the deposited collateral. If the value of the deposited -collateral drops to or below 75% of the original value, the loan can be liquidated. -Upon borrowing the assets user gets a [PSP34](../PSP34/psp34.md) token -representing info about their loan (how much assets were borrowed, when did they -borrow, what asset was borrowed, what asset was used as collateral, amount of -collateral asset deposited, the liquidation price of the loan and if it was liquidated -or not). This NFT token can be then used to repay the loan and get the collateral back. - -## Repaying the loan - -Users can repay their loan by depositing the borrowed amount of the borrowed assets -with the interest which is calculated by the contract. Our contract has an interest -rate of 10% per year. Users can repay the whole loan or a portion of the loan. The -user will use their NFT to repay the loan. If the loan was liquidated in the meantime, -they do not get their collateral back and the NFT is burned. - -## Withdraw deposited assets - -Users will deposit their share tokens to the smart contract and get back the deposited -assets along with the interest generated if any. - -## Liquidate a loan - -Users can liquidate a loan which's collateral value is below or equal to 75% of the -original value of the collateral. After the loan is liquidated, the liquidator -gets 1% of the liquidated assets. - -## Allow an asset for lending or being used as a collateral - -Users with the Manager role can allow an asset to be available for lending and -borrowing or for being used as collateral. - -## Pause the contract - -Users with the Manager role can pause the contract. When the contract is paused, -users can not deposit new assets for lending or borrowing assets. Users can still -repay their loans, liquidate loans or withdraw their deposits when paused. \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/psp22.md b/docs/docs/smart-contracts/example/psp22.md deleted file mode 100644 index 9db39991b..000000000 --- a/docs/docs/smart-contracts/example/psp22.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -sidebar_position: 3 -title: Implement PSP22 contract ---- - -First, we will cover the implementation of [PSP22](../PSP22/psp22.md) -token used by our smart contract, which will represent the stable coin that we will be -lending and another [PSP22](../PSP22/psp22.md) token which we will be -using as collateral. These are used just to test our example, you will not be creating -an actual [PSP22](../PSP22/psp22.md) implementation of stable coin or collateral -token in your lending protocol, but this will also showcase how to implement -a basic implementation of a fungible token with OpenBrush. - -## Definition of the `StableCoin` trait - -In the `traits/stable_coin.rs`, we will define a `StableCoin` trait. -That trait contains only two super traits: `PSP22` and `PSP22Metadata`, without any other method. -That shows that `StableCoin` is simple `PSP22`. In the implementation of the contract -we will implement that trait to be sure that all super traits are also implemented. -`StableCoinRef` can be used by other developers to do a cross contract call to `StableCoinContract`. - -```rust -use openbrush::contracts::traits::psp22::{ - extensions::metadata::*, - extensions::mintable::*, - *, -}; - -#[openbrush::wrapper] -pub type StableCoinRef = dyn PSP22 + PSP22Metadata + PSP22Mintable; - -#[openbrush::trait_definition] -pub trait StableCoin: PSP22 + PSP22Metadata + PSP22Mintable {} -``` - -## Add dependencies - -First we will add the dependencies used in our [PSP22](../PSP22/psp22.md) -contract to the `Cargo.toml` file. You will import the same dependencies as in -the [PSP22](../PSP22/psp22.md) documentation, so we will not show -it here to keep it simple. - -## Implement the contract - -We want a basic [PSP22](../PSP22/psp22.md) token with metadata, -so we will add the [PSP22 Metadata](../PSP22/Extensions/metadata.md) -extension to our contract. We will add a `openbrush::contract` macro to our contract -and add some imports: - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -/// This is a simple `PSP22` which will be used as a stable coin and a collateral token in our lending contract -#[openbrush::implementation(PSP22, PSP22Metadata, PSP22Mintable)] -#[openbrush::contract] -pub mod token { - use openbrush::traits::String; - use lending_project::traits::stable_coin::*; - use openbrush::traits::Storage; -``` - -## Define the storage - -We will derive the storage traits related to `PSP22` and `PSP22Metadata` and declare -the fields related to these traits. - -```rust -/// Define the storage for PSP22 data and Metadata data -#[ink(storage)] -#[derive(Default, Storage)] -pub struct StableCoinContract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - metadata: metadata::Data, -} -``` - -## Implement the PSP22 and PSP22Metadata traits and define the constructor - -We will implement the `PSP22Metadata` trait and define the constructor where we -will set the `name` and the `symbol` for our token. Also, we will mint the -initial supply of tokens to the caller of the constructor. - -`PSP22Metadata` and similar traits imported from OpenBrush should be implemented with `#[openbrush::implementation]` macro. - -```rust -#[openbrush::implementation(PSP22, PSP22Metadata, PSP22Mintable)] -#[openbrush::contract] -mod contract { - ... -} -``` - -Below we are implementing `StableCoin` trait itself and defining the constructor. - -```rust - -// It forces the compiler to check that you implemented all super traits -impl StableCoin for StableCoinContract {} - -impl StableCoinContract { - /// Constructor with name and symbol - #[ink(constructor)] - pub fn new(name: Option, symbol: Option) -> Self { - let mut instance = Self::default(); - - instance.metadata.name = name; - instance.metadata.symbol = symbol; - instance.metadata.decimals = 18; - let total_supply = 1_000_000 * 10_u128.pow(18); - assert!(instance._mint_to(Self::env().caller(), total_supply).is_ok()); - - instance - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/example/setup_project.md b/docs/docs/smart-contracts/example/setup_project.md deleted file mode 100644 index cf8065ca4..000000000 --- a/docs/docs/smart-contracts/example/setup_project.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -sidebar_position: 2 -title: Setup the project ---- - -In the first step, we will define the structure of the project. -We suggest using that structure during development for the following reasons: -- The interface of the contracts is defined separately from the contracts. That allows others to communicate with these contracts without knowledge about the implementation and these interfaces can easily be imported to another project(that allows others to communicate with these contracts). -- Resolves the problem with cyclic dependencies across the project. To call the methods of the contract from the project you enough to have an interface. -- The usage of the `ink-as-dependency` feature is minimized. That can resolve a lot of headaches in the future. -- The implementation of big contracts can be split into small parts to simplify the development. -- The body of the contract doesn't contain the whole implementation of the contract. That improves the readability of the contracts. - -The project will contain the following directories: -- `traits` - contains all traits(interfaces) of the contracts developed in the project. -Traits describe the functionality of each contract and allow to do cross-contracts calls -without knowledge about the implementation(no need to import the contract, using a trait is enough). -- `impls` - contains the implementations of traits for the contracts. -If the contract contains several simple functions, better to implement -them in the body of the contract. But if the contract contains a lot of logic -and methods, better to move(and maybe split on parts) the implementation to that directory. -Better to store the implementation of one contract in its own directory and not mix it with others. -- `contracts` - contains the bodies of the contracts. Each contract should be defined -in its own crate(it is a rule of the ink!). Each folder in that directory is a -crate(contract). These contracts can have the implementation inside themselves -or they can import the implementation from `impls`. - -In that structure `traits` and `impls` directories are the parts of on `PROJECT_NAME` crate. -Each contract in the `contracts` directory imports the crate `PROJECT_NAME` and use it inside. - -Based on the rules above the structure will look like the following: -```shell -├── traits -│   ├── lending.rs -│   ├── loan.rs -│   ├── mod.rs -│   ├── shares.rs -│   └── stable_coin.rs -├── impls -│   ├── lending -│   │   ├── data.rs -│   │   ├── lending.rs -│   │   ├── lending_permissioned.rs -│   │   └── mod.rs -│   └── mod.rs -├── contracts -│   ├── lending -│   │   ├── Cargo.toml -│   │   └── lib.rs -│   ├── loan -│   │   ├── Cargo.toml -│   │   └── lib.rs -│   ├── shares -│   │   ├── Cargo.toml -│   │   └── lib.rs -│   └── stable_coin -│   ├── Cargo.toml -│   └── lib.rs -├── lib.rs -├── Cargo.toml -``` - -`traits` directory contains 4 traits with logic from the [overview](overview.md). -In the example: -- `LendingContract` is a big contract and we moved the main logic into `impls/lending` folder. That logic is split into two traits(`Lending` and `LendingPermissione`) to show how it can be done. -- `LoanContract` contains few methods, so the implementation is defined directly in the body of the contract. -- `SharesContract` is `PSP22` token with public `mint` and `burn` functions that require ownership. -- `StableCoinContract` is a pure `PSP22` token. diff --git a/docs/docs/smart-contracts/example/shares.md b/docs/docs/smart-contracts/example/shares.md deleted file mode 100644 index d727b0874..000000000 --- a/docs/docs/smart-contracts/example/shares.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -sidebar_position: 4 -title: Shares contract ---- - -Similarly, we will implement another [PSP22](../PSP22/psp22.md) token -which will represent the ownership of assets available by the smart contract -to be lent. In this token, we will need [PSP22Metadata](../PSP22/Extensions/metadata.md) -and we will also need to mint and burn this token. We only want our contract(lending contract) to -perform these actions, so we will also add the [Ownable](../ownable.md) extension. - -## Definition of the `Shares` trait - -In the `traits/shares.rs`, we will define a `Shares` trait. -That trait contains the next super traits: `PSP22`, `PSP22Mintable`, `PSP22Burnable`, `PSP22Metadata`, and `Ownable`, without any other method. -That shows that `Shares` is `PSP22` with mint and burn methods that can be called only by the owner. -In the implementation of the contract, we will implement that trait to be sure that all super traits are also implemented. -`SharesRef` can be used by other developers to do a cross contract call to `SharesContract`. - -```rust -use openbrush::contracts::traits::{ - ownable::*, - psp22::{ - extensions::{ - burnable::*, - metadata::*, - mintable::*, - }, - *, - }, -}; - -#[openbrush::wrapper] -pub type SharesRef = dyn PSP22 + PSP22Mintable + PSP22Burnable + PSP22Metadata + Ownable; - -#[openbrush::trait_definition] -pub trait Shares: PSP22 + PSP22Mintable + PSP22Burnable + PSP22Metadata + Ownable {} -``` - -## Add dependencies - -In addition to the dependencies imported in the [PSP22](../PSP22/psp22.md) -documentation, we will also add the `ownable` dependency the same way as in the -[ownable](../ownable.md) documentation. We will be using `SharesContract` -as a dependency in our lending contract to instantiate it. So we need to also add -the `"rlib"` crate type to have the ability to import the `SharesContract` as a dependency. - -## Implement the contract - -Implementing our shares contract will follow the same steps as implementing -the basic `PSP22` contract in the previous step, but we will do some small -changes for the token to be mintable, burnable, and for these functions to -be restricted. Therefore, on top of the imports in the previous contract, -we also need these imports: - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -/// This contract will be used to represent the shares of a user -/// and other instance of this contract will be used to represent -/// the amount of borrowed tokens -#[openbrush::implementation(PSP22, PSP22Mintable, PSP22Burnable, PSP22Metadata, Ownable)] -#[openbrush::contract] -pub mod shares { - use openbrush::traits::String; - use lending_project::traits::shares::*; - use openbrush::{ - modifiers, - traits::Storage, - }; -``` - -## Define the storage - -In this storage, we will also derive the storage trait related to `Ownable` -and declare the field related to this trait. - -```rust -/// Define the storage for PSP22 data, Metadata data and Ownable data -#[ink(storage)] -#[derive(Default, Storage)] -pub struct SharesContract { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - ownable: ownable::Data, - #[storage_field] - metadata: metadata::Data, -} -``` - -## Implement the extension traits - -We will be using these extensions in our token, so we will implement them for -our storage. - -```rust -// It forces the compiler to check that you implemented all super traits -impl Shares for SharesContract {} -``` - -## Implement the Burnable and Mintable traits - -Now we will implement the `PSP22Burnable` and `PSP22Mintable` traits. -These are a little different so we are doing it in a separate section. -We don't want anybody to mint or burn the tokens, we only want the owner, -in this case, our lending contract, to do it. So we will add the `PSP22Burnable` -and `PSP22Mintable` and mark the functions of these traits with the `only_owner` -restriction. Here we are using the `#[default_impl]` macro to mark, that we want to use default implementation of the trait's -method but to use some modifiers or add other attributes to the method. - -```rust -/// override the `mint` function to add the `only_owner` modifier -#[default_impl(PSP22Mintable)] -#[modifiers(only_owner)] -fn mint() {} - -/// override the `burn` function to add the `only_owner` modifier -#[default_impl(PSP22Burnable)] -#[modifiers(only_owner)] -fn burn() {} -``` - -This will restrict accounts other than the owner of the token (which will be the lending contract) -from calling these functions. - -## Define the constructor - -Finally, we will define the constructor where we will set the name and the symbol -of the token and then initialize the owner of the token -(which then will be able to mint and burn the tokens). - -```rust -impl SharesContract { - /// constructor with name and symbol - #[ink(constructor)] - pub fn new(name: Option, symbol: Option) -> Self { - let mut instance = Self::default(); - - let caller = Self::env().caller(); - instance.metadata.name = name; - instance.metadata.symbol = symbol; - instance.metadata.decimals = 18; - instance._init_with_owner(caller); - - instance - } -} -``` \ No newline at end of file diff --git a/docs/docs/smart-contracts/overview.md b/docs/docs/smart-contracts/overview.md deleted file mode 100644 index 1669843f1..000000000 --- a/docs/docs/smart-contracts/overview.md +++ /dev/null @@ -1,326 +0,0 @@ ---- -sidebar_position: 1 -title: Overview ---- - -This doc contains description of how the OpenBrush library can be imported and used. - -The OpenBrush is using ink! version `4.2.1` at the moment. -You will need to use the same version of ink! in your project. -If you use a different version of ink, you need to use a different version of OpenBrush which uses the same version of ink!. -OpenBrush had several significant changes in API, so you check the [Wizard](https://openbrush.io) -to study how to use different versions of OpenBrush. - -The documentation describes the latest available OpenBrush and how to use it. -It doesn't contain [versioning](https://github.com/supercolony-net/openbrush-contracts/issues/127) yet. - -#### The default `toml` of your project with OpenBrush: - -```toml -[package] -name = "name_of_contract" -version = "1.0.0" -authors = ["The best developer ever"] -edition = "2023" - -[dependencies] -# Import ink! -ink = { version = "4.2.1", default-features = false} - -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } - -# OpenBrush dependency -openbrush = { git = "https://github.com/Brushfam/openbrush-contracts", branch = "develop", default-features = false } - -[lib] -name = "name_of_contract" -path = "lib.rs" - -[features] -default = ["std"] -std = [ - "ink/std", - "scale/std", - "scale-info/std", - # OpenBrush dependency - "openbrush/std", -] -ink-as-dependency = [] -``` - -By default, the `openbrush` crate provides [macros](https://github.com/Brushfam/openbrush-contracts/blob/main/lang/macro/src/lib.rs) -for simplification of the development and [traits](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/traits) of -contracts (you can implement them by yourself, and you can use them for cross-contract calls). - -OpenBrush also provides the default implementation of traits that can be enabled via crate features. -A list of all available features you can find [here](https://github.com/Brushfam/openbrush-contracts/blob/main/Cargo.toml#L51). -The default features are implemented by a `#[openbrush::implentation]` macro, by providing the trait name you want to implement, and functions from the default implementation can be overriden using the `#[overrider]` attribute. If you want to use the default implementation of a function, while adding some modifier to the function, you can do so with the `#[default_impl]` attribute. Both of these attribute take the name of the trait we are overriding the method in as argument. Some default implementations come with several traits containing methods that can be overriden. We can override any function in any trait with these attributes. An example PSP22 with some overriden functions would look like this: - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -// This will add the default implementation of PSP22 and PSP22Mintable -#[openbrush::implementation(PSP22, PSP22Mintable, Ownable)] -// This macro will collect the traits and override them. Make sure it comes after the implementation macro! -#[openbrush::contract] -pub mod psp22_example { - // derive macro which implements traits needed for a proper Storage manipulation within OB standards - use openbrush::traits::Storage; - use ink::storage::traits::ManualKey; - use ink::storage::traits::Lazy; - #[ink(storage)] // needed for the ink! contract storage struct - // this will implement traits needed for OB standards to work with the contract storage struct - #[derive(Storage, Default)] - pub struct PSP22Example { - // we have to add the data structs needed to work with the implemented traits to the storage - // the fields need to be marked with this attribute in order for the contract to implement neede traits - #[storage_field] - psp22: psp22::Data, - #[storage_field] - ownable: ownable::Data, - // here we can add any other fields needed for our contract - // we will add logic which bans a user from transferring the token - // we will make it lazy and set it a manual storage key so we can upgrade this contract in future - banned_account: Lazy> - } - - #[default_impl(PSP22Mintable)] // we will add some attributes to the mint function in PSP22Mintable - #[modifiers(ownable::only_owner)] // this will be moved to the PSP22Mintable::mint along with any other attributes - fn mint() { - // the default_impl attribute only cares about the function name and the trait name - // in which we want to override the method, therefore we can omit all parameters and return types. - // default_impl macro will use the original body of the function, so here we can keep it empty as well. - } - - #[overrider(psp22::Internal)] // we want to override psp22::Internal::_before_token_transfer method - fn _before_token_transfer( - &mut self, - from: Option<&AccountId>, - _to: Option<&AccountId>, - _amount: &Balance, - ) -> Result<(), PSP22Error> { - if from == self.banned_account.get() { - return Err(PSP22Error::InsufficientAllowance) - } - Ok(()) - } - - impl Contract { - #[ink(constructor)] - pub fn new(total_supply: Balance) -> Self { - let mut instance = Self::default(); - - psp22::Internal::_mint_to(&mut instance, Self::env().caller(), total_supply).expect("Should mint"); - ownable::Internal::_init_with_owner(&mut instance, Self::env().caller()); - // private key of 0x0 is known, so we ban transfers from this account and users can safely use it as burn address! - instance.banned_account.set([0u8; 32]); - - instance - } - } -} -``` - -:::note - -ink! requires to put `#![cfg_attr(not(feature = "std"), no_std, no_main)]` at the top of root crate. - -::: - -:::note - -The standards implemented in OpenBrush support events, but user has to specify this in their contract. ink! events have to be defined in the contract mod, meaning you have to emit them in the contract mod. You can do this by overriding the default _emit_xxx_event methods in different standards. There is currently a [PR in ink!](https://github.com/paritytech/ink/pull/1827) which will allow us to define events anywhere, and we will reflect this change in OB as well! - -::: - -#### Reuse implementation of traits from OpenBrush - -The doc contains links to the examples of how to reuse and customize the default implementation of traits. - -All default implementations of the traits provided by OpenBrush have the same pattern. -Consequently, the re-usage of each implementation in your contract also has the same pattern. - -Each implementation of the contract has its module and its feature that enables that -module. A list of available modules you can find [here](https://github.com/Brushfam/openbrush-contracts/blob/main/contracts/src/lib.rs#L33), -a list of available features [here](https://github.com/Brushfam/openbrush-contracts/blob/main/Cargo.toml#L51). -Each module can be reached via the `openbrush::contracts::` namespace. For example, -to use the `psp22` module, you need to import `openbrush::contracts::psp22`; -to use the `ownable` module, you need to import `openbrush::contracts::ownable`. It is not needed to import the modules when using the `implementation` macro, the macro will do it for you. - -Before importing each module (manually or with the macro), first you need to enable the corresponding feature in your `Cargo.toml`. -The name of the feature is the same as the name of the module. For example: - -To enable `psp22`: - -```toml -openbrush = { git = "https://github.com/Brushfam/openbrush-contracts", branch = "develop", default-features = false, features = ["psp22"] } -``` - -To enable `ownable`: - -```toml -openbrush = { git = "https://github.com/Brushfam/openbrush-contracts", branch = "develop", default-features = false, features = ["ownable"] } -``` - -To enable both: - -```toml -openbrush = { git = "https://github.com/Brushfam/openbrush-contracts", branch = "develop", default-features = false, features = ["psp22, ownable"] } -``` - -After enabling the feature and importing the corresponding module, you need to embed the module -data structure into your contract as a field and implement the `openbrush::traits::Storage` -trait for that field. In most cases, the data structure of each module is named `Data`. -If importing several modules, you can specify which data you want to use via namespaces like -`psp22::Data` or `ownable::Data`. - -Embedding of data structures looks like: - -```rust -use openbrush::contracts::ownable::*; -use openbrush::contracts::psp22::*; - -#[ink(storage)] -pub struct Contract { - foo: psp22::Data, - bar: ownable::Data, -} -``` - -Each contract that wants to reuse implementation should implement the -`openbrush::traits::Storage` with the corresponding data structure. -The easiest way to implement that trait is via the derive macro by adding -`#[derive(Storage)]` and marking the corresponding fields with the `#[storage_field]` -attribute. - -```rust -use openbrush::contracts::ownable::*; -use openbrush::contracts::psp22::*; -use openbrush::traits::Storage; - -#[ink(storage)] -#[derive(Storage)] -pub struct Contract { - #[storage_field] - foo: psp22::Data, - #[storage_field] - bar: ownable::Data, -} -``` - -Now your contract has access to default implementation on the Rust level. -It is on the Rust level so you can call methods only inside your contract -(in the example, it is methods of `PSP22`, `psp22::Internal`, `Ownable`, and -`ownable::Internal` traits). You can implement the traits yourself if you need -a custom behavior of OpenBrush-defined standard. In most cases, you want to -inherit the behavior of OpenBrush standard and do some modifications in its behavior. -You can do it with the `#[openbrush::implementation]` macro. We can omit the imports -for ownable and psp22 as they will be imported with the macro. - -```rust -#[openbrush::implementation(PSP22, Ownable)] -#[openbrush::contract] -pub mod my_psp22 { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Storage)] - pub struct Contract { - #[storage_field] - foo: psp22::Data, - #[storage_field] - bar: ownable::Data, - } -} -``` - -Remember, only traits with `#[ink(message)]` methods can be public. `psp22::Internal` -and `ownable::Internal` can't be exposed. It is for internal usage only. - -The implementation in OpenBrush is called "default" because you can customize (override) it. -You can override any method from any trait with the `#[overrider]` macro, by passing the -name of trait the method belongs to. For example: - -```rust -#[openbrush::implementation(PSP22, Ownable)] -#[openbrush::contract] -pub mod my_psp22 { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Storage)] - pub struct Contract { - #[storage_field] - foo: psp22::Data, - #[storage_field] - bar: ownable::Data, - } - - #[overrider(PSP22)] - fn balance_of(&self, owner: AccountId) -> Balance { - // For example you can break `balance_of` method and return always zero - return 0 - } - - #[overrider(Ownable)] - fn owner(&self) -> Option { - // For example you can return always zero owner - None - } - - #[overrider(psp22::Internal)] - fn _mint(&mut self, account: AccountId, amount: Balance) -> Result<(), PSP22Error> { - return Err(PSP22Error::Custom("I don't want to mint anything".to_string())); - } - - #[overrider(ownable::Internal)] - fn _init_with_owner(&mut self, owner: AccountId) { - // Maybe you want to change something during initialization of the owner - } -``` - -Work with each module has the same pattern. The difference is only in the naming of -the module and main trait. Some contract extensions require additional steps, so below, -you can find instructions on how to work with them: - -- [PSP22](PSP22/psp22.md) is an example of how you can reuse the implementation of - [psp22](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp22). You also can find examples of how to reuse extensions. - - [PSP22Metadata](PSP22/Extensions/metadata.md): metadata for PSP22. - - [PSP22Mintable](PSP22/Extensions/mintable.md): creation of new tokens. - - [PSP22Burnable](PSP22/Extensions/burnable.md): destruction of own tokens. - - [PSP22Wrapper](PSP22/Extensions/wrapper.md): wrapper for PSP22 token (useful for governance tokens etc.). - - [PSP22FlashMint](PSP22/Extensions/flashmint.md): extension which allows performing flashloans of the token by minting and burning the token. - - [PSP22Pausable](pausable.md): example of using pausable extension in the PSP22 contract. - - [PSP22TokenTimelock](PSP22/Utils/token-timelock.md): Utility which allows token holders to lock their tokens for a specified amount of time. -- [PSP34](PSP34/psp34.md) is an example of how you can reuse the implementation of - [psp34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34). You also can find examples of how to reuse extensions. - - [PSP34Metadata](PSP34/Extensions/metadata.md): metadata for PSP34. - - [PSP34Mintable](PSP34/Extensions/mintable.md): creation of new tokens. - - [PSP34Burnable](PSP34/Extensions/burnable.md): destruction of own tokens. - - [PSP34Enumerable](PSP34/Extensions/enumerable.md): iterating over contract's tokens. -- [PSP37](PSP37/psp37.md) is an example of how you can reuse the implementation of - [psp37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37). You also can find examples of how to reuse extensions. - - [PSP37Metadata](PSP37/Extensions/metadata.md): metadata for PSP37. - - [PSP37Mintable](PSP37/Extensions/mintable.md): creation of new tokens. - - [PSP37Burnable](PSP37/Extensions/burnable.md): destruction of own tokens. - - [PSP37Batch](PSP37/Extensions/batch.md): batch transferring of tokens. - - [PSP37Enumerable](PSP37/Extensions/enumerable.md): iterating over contract's tokens. -- [Access Control](access-control/access-control.md) shows how you can use the implementation of - [access-control](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/access/access_control) and - [psp34](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/token/psp34) together to provide rights to mint and burn NFT tokens. - - [AccessControlEnumerable](access-control/Extensions/enumerable.md): iterating over contract's roles. -- [Ownable](ownable.md) shows how you can use the implementation of - [ownable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/access/ownable) and - [psp37](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/token/psp37) together to provide rights to mint and burn tokens. -- [ReentrancyGuard](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/security/reentrancy_guard) - modifier to prevent reentrancy during certain functions. -- [Pausable](pausable.md) shows how you can use the implementation of - [pausable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/security/pausable) - contract and modifiers. -- [TimelockController](timelock-controller.md) shows how you can use the implementation of - [timelock-controller](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/governance/timelock_controller) - to execute a transaction with some delay via governance. -- [PaymentSplitter](payment-splitter.md) shows how you can use the implementation of - [payment-splitter](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/finance/payment_splitter) - to split received native tokens between participants of the contract. diff --git a/docs/docs/smart-contracts/ownable.md b/docs/docs/smart-contracts/ownable.md deleted file mode 100644 index 3ebdabbdf..000000000 --- a/docs/docs/smart-contracts/ownable.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -sidebar_position: 3 -title: Ownable ---- - -This example shows how you can use the implementation of [ownable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/access/ownable) to provide `only owner` rights for contract's functions. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `ownable` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `Ownable`. - -## Step 2: Define constructor - -Define the constructor and initialize the owner with the contract initiator. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - let mut instance = Self::default(); - ownable::Internal::_init_with_owner(&mut instance, Self::env().caller()); - instance - } -} -``` - -## Step 3: Customize your contract - -Customize it by adding ownable logic. We will add a `owner_function` to `MyOwnable` implementation -and add the `only_owner` modifier, which will verify that the caller of the function is the owner. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(Ownable, PSP37, PSP37Burnable, PSP37Mintable)] -#[openbrush::contract] -pub mod ownable { - use openbrush::{ - modifiers, - traits::Storage, - }; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - psp37: psp37::Data, - #[storage_field] - ownable: ownable::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - let mut instance = Self::default(); - ownable::Internal::_init_with_owner(&mut instance, Self::env().caller()); - instance - } - } - - #[default_impl(PSP37Mintable)] - #[modifiers(only_owner)] - fn mint(&mut self) {} - - #[default_impl(PSP37Burnable)] - #[modifiers(only_owner)] - fn burn(&mut self) {} -} - -``` - -You can check an example of the usage of [Ownable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/ownable). diff --git a/docs/docs/smart-contracts/pausable.md b/docs/docs/smart-contracts/pausable.md deleted file mode 100644 index e5d722cfb..000000000 --- a/docs/docs/smart-contracts/pausable.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -sidebar_position: 6 -title: Pausable ---- - -This example shows how you can reuse the implementation of -[pausable](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/security/pausable) in `Flipper` contract to `flip` only if the contract is not paused. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `pausable` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `Pausable`. - -## Step 2: Define constructor - -Define constructor with default value(paused variable is `false` in that case). - -```rust -impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } -} -``` - -## Step 3: Customize your contract - -Customize it by adding flipper logic. We will implement `flip` method marked with `when_not_paused` modifier. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(Pausable)] -#[openbrush::contract] -pub mod my_pausable { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - pause: pausable::Data, - flipped: bool, - } - - impl Contract { - #[ink(constructor)] - pub fn new() -> Self { - Self::default() - } - - #[ink(message)] - #[openbrush::modifiers(when_not_paused)] - pub fn flip(&mut self) -> Result<(), PausableError> { - self.flipped = !self.flipped; - Ok(()) - } - - #[ink(message)] - pub fn pause(&mut self) -> Result<(), PausableError> { - Internal::_pause(self) - } - - #[ink(message)] - pub fn unpause(&mut self) -> Result<(), PausableError> { - Internal::_unpause(self) - } - - #[ink(message)] - pub fn change_state(&mut self) -> Result<(), PausableError> { - Internal::_switch_pause(self) - } - } -} -``` - -You can check an example of the usage of [Pausable](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/pausable). diff --git a/docs/docs/smart-contracts/payment-splitter.md b/docs/docs/smart-contracts/payment-splitter.md deleted file mode 100644 index 09796eb7a..000000000 --- a/docs/docs/smart-contracts/payment-splitter.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -sidebar_position: 7 -title: Payment Splitter ---- - -This example shows how you can reuse the implementation of -[payment-splitter](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/finance/payment_splitter). - -## Step 1: Import default implementation - -With [default `Cargo.toml`](overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable `payment_splitter` feature, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main trait is `PaymentSplitter`. - -## Step 2: Define constructor - -Define constructor where you init payees and shares. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(payees_and_shares: Vec<(AccountId, Balance)>) -> Self { - let mut instance = Self::default(); - payment_splitter::Internal::_init(&mut instance, payees_and_shares).expect("Should init"); - instance - } -} -``` - -You can check an example of the usage of [PaymentSplitter](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/payment_splitter). - -## Step 3 (Optional): Customize your contract - -The `PaymentSplitter` trait defines and has default implementations for the core payment splitter functions. -Additional functionality with *some* predefined functions is available through the `payment_splitter::Internal` trait. -Likely the most common function to use from this internal trait will be `_release_all`. This allows you to payout all -`payees` stored in the contract at once. To add this function to your contract, simply define a new publicly dispatchable -function (i.e. `#[ink(message)]`) called `release_all` and have it call the internal `_release_all` function using `self`. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(PaymentSplitter)] -#[openbrush::contract] -pub mod my_payment_splitter { - use ink::prelude::vec::Vec; - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - splitter: payment_splitter::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(payees_and_shares: Vec<(AccountId, Balance)>) -> Self { - let mut instance = Self::default(); - payment_splitter::Internal::_init(&mut instance, payees_and_shares).expect("Should init"); - instance - } - - /// Payout all payees at once. - /// Delete this method if you don't want this functionality in your version of the payment splitter. - #[ink(message)] - pub fn release_all(&mut self) -> Result<(), PaymentSplitterError> { - // `_release_all()` is an internal method defined by the `payment_splitter::Internal` trait - payment_splitter::Internal::_release_all(self) - } - } -} - -``` -The `_add_payee` function is also available in the `payment_splitter::Internal` trait and can be added to -your contract in the same way as `_release_all`. \ No newline at end of file diff --git a/docs/docs/smart-contracts/proxy.md b/docs/docs/smart-contracts/proxy.md deleted file mode 100644 index 349463955..000000000 --- a/docs/docs/smart-contracts/proxy.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -sidebar_position: 3 -title: Proxy ---- - -This example shows how you can use the implementation of [proxy](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/upgradeability/proxy) to to implement proxy pattern for upgradeable contracts. - -## Disclaimer - -Delegate calls [were marked](https://github.com/paritytech/ink/pull/1331#discussion_r953736863) as a possible attack vector in ink! Therefore the `Proxy` pattern will not work within OpenBrush until this is reimplemented in ink! 4. -You can use this tutorial for general understanding of `Proxy` pattern. - -## Step 1: Import default implementation - -With [default `Cargo.toml`](overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable corresponding features, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main traits are `Ownable` and `Proxy`. - -## Step 2: Define constructor - -Define the constructor where you initialize the owner with the contract initiator -and passing code hash of the logic layer. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(forward_to: Hash) -> Self { - let mut instance = Self::default(); - - let caller = Self::env().caller(); - instance._init_with_forward_to(forward_to); - instance._init_with_owner(caller); - - instance - } -} -``` - -## Step 3: Define forward function - -Define the forward function to make delegate calls of upgradeable contract through proxy contract. - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(Proxy, Ownable)] -#[openbrush::contract] -pub mod proxy { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - proxy: proxy::Data, - #[storage_field] - ownable: ownable::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(forward_to: Hash) -> Self { - let mut instance = Self::default(); - proxy::Internal::_init_with_forward_to(&mut instance, forward_to); - ownable::Internal::_init_with_owner(&mut instance, Self::env().caller()); - - instance - } - #[ink(message, payable, selector = _)] - pub fn forward(&self) { - proxy::Internal::_fallback(self) - } - } -} -``` - -You can check an example of the usage of [Proxy](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/proxy). diff --git a/docs/docs/smart-contracts/timelock-controller.md b/docs/docs/smart-contracts/timelock-controller.md deleted file mode 100644 index 96ebe3333..000000000 --- a/docs/docs/smart-contracts/timelock-controller.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -sidebar_position: 8 -title: Timelock Controller ---- - -This example shows how you can reuse the implementation of -[timelock-controller](https://github.com/Brushfam/openbrush-contracts/tree/main/contracts/src/governance/timelock_controller). - -## Step 1: Import default implementation - -With [default `Cargo.toml`](overview.md/#the-default-toml-of-your-project-with-openbrush), -you need to enable corresponding features, embed modules data structures and implement them via `#[openbrush::implementation]` macro -as described in [that section](overview.md/#reuse-implementation-of-traits-from-openbrush). - -The main traits are `AccessControl` and `TimelockController`. - -## Step 2: Define constructor - -Define constructor where you init admin of the contract. - -```rust -impl Contract { - #[ink(constructor)] - pub fn new(min_delay: Timestamp, proposers: Vec, executors: Vec) -> Self { - let mut instance = Self::default(); - - let caller = Self::env().caller(); - // `TimelockController` and `AccessControl` have `_init_with_admin` methods. - // You need to call it for each trait separately, to initialize everything for these traits. - access_control::Internal::_init_with_admin(instance, caller); - timelock_controller::Internal::_init_with_admin(instance, caller, min_delay, proposers, executors); - - instance - } -} -``` - -## Final code - -```rust -#![cfg_attr(not(feature = "std"), no_std, no_main)] - -#[openbrush::implementation(AccessControl, TimelockController)] -#[openbrush::contract] -pub mod my_timelock_controller { - use ink::prelude::vec::Vec; - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct Contract { - #[storage_field] - access_control: access_control::Data, - #[storage_field] - timelock: timelock_controller::Data, - } - - impl Contract { - #[ink(constructor)] - pub fn new(min_delay: Timestamp, proposers: Vec, executors: Vec) -> Self { - let mut instance = Self::default(); - - let caller = Self::env().caller(); - // `TimelockController` and `AccessControl` have `_init_with_admin` methods. - // You need to call it for each trait separately, to initialize everything for these traits. - access_control::Internal::_init_with_admin(&mut instance, Some(caller)); - timelock_controller::Internal::_init_with_admin( - &mut instance, - Some(caller), - min_delay, - proposers, - executors, - ); - - instance - } - } -} - -``` - -You can check an example of the usage of [TimelockController](https://github.com/Brushfam/openbrush-contracts/tree/main/examples/timelock_controller). \ No newline at end of file diff --git a/docs/docs/smart-contracts/upgradeable.md b/docs/docs/smart-contracts/upgradeable.md deleted file mode 100644 index 791bdd2c8..000000000 --- a/docs/docs/smart-contracts/upgradeable.md +++ /dev/null @@ -1,640 +0,0 @@ ---- -sidebar_position: 1 -title: Upgradeable contract ---- - -# Upgradeable contract - -## Overview - -Code of a smart contract deployed on chain is immutable, however, we can update the code hash of a contract to point to a different code (therefore changing the code of the smart contract). This functionality can be used for bug fixing and potential product improvements. To do this, we need to first deploy a smart contract with the new code to register its code hash to the chain, and then call the `ink::env::set_code_hash` function. This function needs to be exposed in the smart contract API. You can check an example [here](https://github.com/paritytech/ink/tree/master/examples/upgradeable-contracts/set-code-hash). Openbrush also provides `Upgradeable` trait, which exposes this function. When using this, be sure to add some access modifiers, so only the admin of the contract can call the function. - -Upgradeability allows experimenting and deploying the product at the early stage, always leaving the chance to fix vulnerabilities and progressively add features. Upgradeable contracts are not a Bug if they are developed consciously with decentralization in mind. - -Decentralization can be achieved by providing the right to upgrade only to decentralized authority like governance, multisig, or another analog. - -There is also a possibility of smart contract upgradeability via `Proxy` and `Diamond` patterns, which use `DelegateCall` to perform operations over own storage with code of a different deployed contract. - -## Storage layout - -### How storage works - -Contracts use key-value storage to persist data. Each field of the contract may have -its key and occupy storage cell. It is called storage layout. - -![](assets/20220719_075309_F016550A-65D2-4DCD-A60A-D1A70B38D813.jpeg) - -During compilation ink! inserts code to work with storage and ink! knows how to store -each type in which storage cell. How exactly it works is not a part of this tutorial, -but you can read about that [here](https://use.ink/basics/upgradeable-contracts). -The main point is that each type knows how to operate with each field and operate with -storage, because of a unique identifier of type `u32`. - -So, each data is stored under its unique identifier - the storage key. The value of the -key is the sequence of bytes - serialized (by SCALE codec) data type. The logic layer -knows how to serialize and deserialize each data type. So during the execution, the -logic layer deserializes all data by their storage keys and returns the -filled contract's storage variable. The developer works with that variable, and before -the end of the execution, the logic layer serializes data into sequences of bytes and -stores them in their storage cells. - -### Rules for upgradeable storage layout - -The contract can have several logic layers (like in the `Diamond` pattern). -So we will define rules in terms of several layers, but it is also applicable -for upgradeable contracts with the `Proxy` pattern or `set_code_hash`. - -1. The data stored under the storage key should use the same serialization and -deserialization mechanism across all logic layers. Otherwise, some logic layers will not -be able to deserialize the data type and fail. -1. Each logic unit (in most cases, it is a type) should occupy the same storage key across -all logic layers. For example, you have several logic layers that use the -`Mapping` to track users' balances. You should use the same storage -key if you want to interact with the same mapping. Otherwise, you will work with different mappings. -1. Each field that occupies some storage key should be used only in its usage flow. -For example, you have `Mapping` to track users' balances of token -A in one logic layer. You can't use it in another logic layer to track users' -balances of token B. Otherwise, one logic layer can overwrite another. - -Following those simple rules will save you from corrupting the storage. -Those rules are applicable for upgraded logic layers too. - -If you use the same storage layout across all logic layers, and you don't plan to have -unique fields per layer(so you don't plan to modify the storage layout in future upgrades). -Then you already follow those rules with automatically calculated storage keys. -But if you want to use a unique layout per layer or -plan to modify the layout in the future, the next section will help you. - -### Suggestions on how follow the rules - -#### Approach description - -The manual setting of the storage key for each field allows following the rules but -makes development harder. ink! allows you to manually implement all utility traits and -specify the storage key you want to use for each field. If your contract has 20 fields, -then you need to set 20 storage keys. - -The main suggestion is to design your storage layout as a bunch of logic units and -assign a unique storage key to logic unit. The logic unit can be one field or a bunch -of fields. In the scope of the logic unit, you can use automatically calculated keys -offset with the storage key of the logic unit, or you can use the same approach -again and split logic into more units. - -Remember to use unique storage keys so the storage spaces don't overlap. - -OpenBrush provides [`openbrush::storage_item`](https://github.com/727-Ventures/openbrush-contracts/blob/main/lang/macro/src/lib.rs#L447) -attribute macro that implements the required traits for a struct, as well as automatically generating unqiue storage keys for each of the struct's -fields which are either marked as `#[lazy]` or are of type `Mapping`/`MultiMapping`. You can access those storage keys as consts in the generated code. -The format of the storage key is `STORAGE_KEY_{struct_name}_{field_name}`, where `struct_name` and `field_name` are uppercase. -So, this way, `#[openbrush::storage_item]` macro will generate keys for all types, that are lazily loaded from storage, it is -making this fields upgradeable, but to for struct to be upgradeable, every field should be lazily loaded. - -> **Note**: Each logic unit should have a unique storage key. -The storage key should be used only once in the contract. -Those two requirements mean that your type(logic unit) can be used only once in the contract. For example, -`psp22::Data` is a logic unit. You can have only one field of that type. -If you have several fields with that type, you will use the same storage -key several times, which is a collision. - -#### Logic unit per business use case - -You can include all fields into logic unit, like this: -```rust -#[openbrush::storage_item] -pub struct Data { - balances: Mapping, - #[lazy] - total_owners: u128, -} -``` - -And in the result, you will have the following expanded code: -```rust -#[ink::storage_item] -pub struct Data { - balances: Mapping>, - total_owners: ::ink::Lazy>, -} - -pub const STORAGE_KEY_DATA_BALANCES: u32 = 2312123323; -pub const STORAGE_KEY_DATA_TOTAL_OWNERS: u32 = 13123212132; -``` - -It makes your code readable and segregated by business logic. -But it will add some limitations to future upgrades. - -#### Logic unit per each field - -You can create a unique type for each field like: - -```rust -#[openbrush::storage_item] -pub struct Balances(openbrush::storage::Mapping); - -#[openbrush::storage_item] -pub struct TotalOwners(u128); -``` - -You have no limitations, but you made your code harder to read, -and maybe you have a lot of unique structures :D - -#### Unique storage key - -The storage key should be unique per each logic unit. You can assign each key manually or -use some hash function to automate it. - -OpenBrush provides [`openbrush::storage_unique_key!`](https://github.com/Brushfam/openbrush-contracts/blob/main/lang/src/macros.rs#L25) -macro that generates a storage key based on the path to the structure. -It has one required input argument - the name of the structure. - -```rust -#[openbrush::storage_item] -pub struct Data { - balances: Mapping, - total_owners: u128, -} -``` - -or - -```rust -#[openbrush::storage_item] -pub struct Data { - balances: Mapping, - total_owners: u128, -} -``` - -## Constructor and initialization - -### Disclaimer - -The following information describes `Proxy` and `Diamond` patterns of upgradeable storage. - -Uploading your contract on the blockchain with `contract-pallet` has two phases: -- Deploy - deploys source code to the blockchain. After deploying, the network uses the hash of the source code as an identifier for future instantiation of the contract. Now anyone can instantiate the contract by source code hash. -- Instantiation - creates the instance of the contract on the blockchain that uses source code by its hash. After that, anyone can interact with the contract. - -So, deploy - uploading a logic layer to the blockchain; instantiation - reservation of -the storage that belongs to the contract and creation of the entity to -interact(contract with `AccountId`) with that storage via logic layers. - -Each logic layer can require initialization to set up initial variables for correct -work. In the typical scenario, when the contract is not upgradeable, you have only -one logic layer, which should be initialized only once during the instantiation of -the contract. It is called a constructor. You still can have several constructors, -but you can call only one during instantiation. - -Constructors can still be used to initialize upgradeable contracts that use the -`set_code_hash` function. But that approach doesn't work for logic layers of -`Proxy` and `Diamond` patterns. - -### Initialization method - -`Proxy` and `Diamond` pattern contracts have their constructor but only initialize -variables related to forwarding calls to corresponding logic layers. - -#### Delegate call - -Those contracts use [`delegate_call`](https://github.com/paritytech/substrate/issues/10566) to forward calls to logic layers. -The delegate call accepts the logic layer's code hash and executes the source code, corresponding to the hash, -in the context of the current contract. The source code works with the current contract state in that case. -You can't call the constructor during a delegate call, so you can't naturally initialize the contract. - -#### Workaround - -Instead of using the constructors as the primary way to initialize the logic -units(each logic layer has its logic unit or a bunch of logic units), -you can add a separate initializer method and leave the constructor empty(with -resolving [issue](https://github.com/paritytech/ink/issues/1187) you can not -have constructor at all). That initialize method can accept any arguments the -same as a typical constructor. - -For example, for an upgradeable `PSP22` contract, you can add the `init_with_supply` method: - -```rust -#[ink(message)] -pub fn init_with_supply(&mut self, total_supply: Balance) -> Result<(), PSP22Error> { - self._mint_to(Self::env().caller(), total_supply) -} -``` - -You can add as many methods as you wish with any arguments (the same as constructors). -The problem is that anyone can call all those methods unlimited times when the -constructor can be called once by the creator of the contract. -In most cases, you want the same behavior as a constructor. -So you need to add the check that initialization already was called and -restrict the set of people that can do that. - -#### Initialization state per logic unit - -Each logic unit can store boolean variable that shows the state of initialization. - -```rust -#[ink(message)] -pub fn init_with_supply(&mut self, total_supply: Balance) -> Result<(), PSP22Error> { - if self.initialized { - return Err(PSP22Error::Custom(String::from("Already initialized"))); - } - self._mint_to(Self::env().caller(), total_supply) -} -``` - -#### Permission to initialize - -Also, you can use some logic to manage permission. In an upgradeable contract, -you should already use some logic to manage upgrades. You can reuse it here. -If you use `Ownable` then the code can look like this: - -```rust -#[ink(message)] -#[openbrush::modifiers(only_owner)] -pub fn init_with_supply(&mut self, total_supply: Balance) -> Result<(), PSP22Error> { - if self.initialized { - return Err(PSP22Error::Custom(String::from("Already initialized"))); - } - self._mint_to(Self::env().caller(), total_supply) -} -``` - -OpenBrush doesn't provide any utils for initialization right now because, -in most cases, you have a unique way to initialize the contract. -OpenBrush team doesn't want to add overhead - boolean variables per logic unit. - -But for managing the permission, you can use `Ownable` or `AccessControl` default implementation. - -### Small optimization for all upgradeable contract - -Most contracts require initialization, but in most cases, only once. -After initialization, you can always upgrade your contract and exclude the -initialization logic. It will make your contracts safer, improve performance, -and reduce gas consumption. - -## Types of upgradeable contracts - -There are 3 types of Upgradeable contract. - -1. **Proxy** pattern - * Pros - * Basic pattern where it's hard to introduce a bug - * Cons - * Necessity to deploy extra contract and additional overhead for every singe call -2. Usage of **set_code_hash** method - * Pros - * Easy to make your contract upgradeable, you only need to expose the `set_code_hash` method - * Cons - * If you forget to expose it during the update, you will lose the ability to do upgrades -3. **Diamond standard** pattern - * Pros - * Allows splitting your contract on facets(logic layers) to optimize performance of your contract and overcome contract size limits - * Allows upgrading facets(logic layers) separately and use different governance rules per logic layer - * Cons - * More overhead for particular overlapping logic units - * More likely to break the storage - * Requires good deploy management - -### The `Proxy` Pattern - -`Proxy` pattern has two contracts. The first contract is a simple wrapper - -a "proxy" that users interact with directly and is in charge of forwarding calls to -the second contract - the logic layer. The logic layer can be replaced while the proxy -can not. To upgrade the logic layer, you must replace the code hash of logic layer with a new one. - -The proxy contract is not upgradeable and straightforward. You can reuse implementation from OpenBrush to create your proxy. -The logic layer is better to follow the rules described above. - -This is the illustration how `Proxy` contract with delegate_call looks like: - -![](assets/20220715_130416_DD058578-67E2-4832-9F75-CA18C3B3921C_4_5005_c.jpeg) - -OpenBrush provides default implementation for `Proxy` pattern. -It has `proxy::Data` logic unit that stores `forward_to` inside. -The storage unit is wrapped in `Lazy` and occupies the `proxy::STORAGE_KEY_DATA_FORWARD_TO` storage key. -The `forward_to` is the code hash of the logic layer's source code. It also contains -`change_delegate_call` method to update code hash for `forward_to` value inside -the contract. Only the owner is able to call the `change_delegate_call` method. - -```rust -#[derive(Debug)] -#[openbrush::storage_item] -pub struct Data { - #[lazy] - pub forward_to: Hash, -} -``` - -For more details on how to reuse the default `Proxy` implementation, you can check [Proxy](proxy.md). - -The logic layer for `Proxy` is the same as the definition of the facet for the `Diamond` -contract, but you have only one facet. You can read about it in [that section](#logic-units-for-facet). - -### Usage of `set_code_hash` method - -ink! has the `ink::env::set_code_hash` method, which allows replacing the code hash -of the current contract. So you can change the logic layer by specifying a new code -hash on demand. After setting a new code hash, the next call to your contract will execute updated logic. - -You only need to expose that method somehow, and your common contract is upgradeable. -For example, you can add that method, and it is done: - -```rust -#[ink(message)] -pub fn upgrade_my_contract(&mut self, new_code_hash: Hash) { - ink::env::set_code_hash(&new_code_hash) -} -``` - -You need to consider the permission system because only a restricted set of people -should be able to call that function. - -All suggestions described above are applicable for that kind of upgradeable contracts. -Better to have an upgradeable storage layout, initialization function for new versions -of your contract, permission system, etc. - -### The `Diamond` Standard - -### Disclaimer -Right now, you can't use diamond standard in OpenBrush because of the problems with delegate calls. - -Using `Diamond` Standard you can add support for several facets(logic layers) that -can be upgraded. [This standard](https://eips.ethereum.org/EIPS/eip-2535) came -from the ethereum network. It works in the same way in ink! but instead of the -address of the logic layer, you need to use the code hash of the logic layer. - -This is the illustration of the flow of the `Diamond` pattern: - -![](assets/20220715_130335_47FD0F8D-60F3-4FDF-82F4-672402FDC5D1.jpeg) - -Each method in the smart contract has a selector. It is used as an identifier during the -smart contract call to execute the right logic. -Each facet has a list of selectors that describe which methods are supported by the facet. -Each selector is unique and belongs only to one facet. So selectors of facets can't overlap. -`Diamond` contract knows which facet is responsible for which selector and forwards each -call to the smart contract to the corresponding facet (logic layer). -`Diamond` contract has a function `diamond_cut` that allows registering each facet. - -All suggestions above ideally describe how to develop an upgradeable contract -with multi-logic layers and many logic units. -So here we will describe how to write facets (logic layers) with OpenBrush. - -#### Logic units for facet - -Each facet (logic layer) can have zero, one, or many logic units that work -with storage. - -Each logic unit should have a unique storage key and be upgradeable (support initialization -on demand, use storage key as an offset for all inner fields). It can be a struct with -one or many fields (structs without fields are useless) or an enum with at least two -variants (an enum with one variant is a structure). - -As an example, we will define logic units for `PSP22` and `Ownable` facets. - -```rust -// OpenBrush uses the same logic unit for the default implementation of the `PSP22` trait. -#[openbrush::storage_item] -pub struct PSP22Data { - // Total supply of the `PSP22` - #[lazy] - pub supply: Balance, - // Balance of each user - pub balances: Mapping, - // Allowance to send tokens from one user to another - pub allowances: Mapping<(AccountId, AccountId), Balance>, -} - -// OpenBrush uses the same logic unit for the default implementation of the `Ownable` trait. -// It simply stores the `AccountId` of the owner of the contract. -#[openbrush::storage_item] -pub struct OwnableData { - // Owner of the contract - #[lazy] - pub owner: AccountId, -} -``` - -`PSP22Data` and `OwnableData` have their storage keys - -#### Definition of the facet (logic layer) - -The definition of the facet is the same as the definition of the contract. You need -to combine your logic units in the contract as fields. Leave the constructor empty, -add the initialization method and methods related to business logic. - -Example uses logic units defined in the previous section. - -```rust -#[openbrush::implementation(PSP22, Ownable)] -#[openbrush::contract] -pub mod facet_a { - use openbrush::traits::Storage; - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct FacetA { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - ownable: ownable::Data, - } - - // Your own implementation of `PSP22` trait. - #[openbrush::overrider(PSP22)] - pub fn balance_of(&self) {...} - - - impl FacetA { - #[ink(constructor)] - pub fn new() -> Self { - // Empty constructor, we do here nothing - Self::default() - } - - // Initialization method to grant `total_supply` tokens to someone. - #[ink(message)] - pub fn init_with_supply(&mut self, total_supply: Balance) { - assert_eq!(Self::env().caller(), self.ownable.owner, "Only owner can init contract"); - ... - } - } -} -``` - -You can deploy the code of the facet to the blockchain. After that, you can -register your facet in `Diamond` via the `diamond_cut` method or in `Proxy` via -`change_delegate_call` method. - -#### Interaction between facets - -During development, you can have cases when one logic layer (for example `FacetA`) needs to interact with -another logic unit or logic layer (for example `FacetB`). -You have two options for how to do that: - -1. The contract can send a cross-contract call to itself and execute the public function(method marked with `#[ink(message)]`) of your contract. -1. Embed logic unit into your contract what you want to use into another facet and interact with it. - -##### Cross-contract call to itself - -If your `FacetA` implements some trait, then you can use the -[wrapper around trait](https://github.com/Brushfam/openbrush-contracts#wrapper-around-traits) -feature of OpenBrush to do cross-contract call. - -> **Note**: The trait should be defined with `openbrush::trait_definition`. - -```rust -#[openbrush::implementation(PSP22, Ownable)] -#[openbrush::contract] -pub mod facet_b { - ... - - impl FacetB { - ... - - - #[ink(message)] - fn balance_of_owner_in_facet_a(&self, owner: AccountId) -> Balance { - let address_of_current_contract = Self::env().account_id(); - // It does a cross-contract call to itself with `owner` as an argument. - // It needs to allow reentrancy if it wants to execute itself. - PSP22Ref::balance_of_builder(&address_of_current_contract, owner) - .call_flags(ink::env::CallFlags::default().set_allow_reentry(true)) - .try_invoke() - .unwrap(); - - } - } -} -``` - -The important thing is that you should allow reentrancy during that call. -You can also import the code of `FacetA` and use the native `Ref` feature for -cross-contract calls of ink!. - -Better to avoid the usage of cross-contract calls and work directly with the logic unit. -But it depends on the complexity of the logic layer. - -##### Embed logic unit - -If you use OpenBrush and follow suggestions above, your logic units are independent. -It allows you to embed any logic unit into any facet(logic layer) without corruption of the storage. - -```rust -#[openbrush::implementation(PSP22, Ownable)] -#[openbrush::contract] -pub mod facet_b { - ... - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct FacetB { - // You embed `PSP22Data` logic unit from `FacetA`. - // It works with the same storage as `FacetA`. - // So you have access to data of `FacetA`. - #[storage_field] - psp22: psp22::Data, - // Some data for `FacetB`. - #[storage_field] - foo: BarData, - } - - impl FacetB { - #[ink(constructor)] - pub fn new() -> Self { - // Empty constructor, we do here nothing - Self::default() - } - - #[ink(message)] - fn balance_of_owner_in_facet_a(&self, owner: AccountId) -> Balance { - // It accesses to the balance of `owner` that is managed by `FacetA`. - self.psp22.balances.get(&owner).unwrap_or_default() - } - } -} -``` - -Embedding of the logic unit grants access to its storage. - -##### Remark about logic units and OpenBrush - -All data structures for contracts provided by OpenBrush are upgradeable logic units. -So contracts support upgradeability by default. - -You can access the default implementation when you embed OpenBrush data structures -into your contract. You can use default implementation internally, or you can make -it public. - -For example, when you embed `psp22::Data` into your `Contract`, you can already -internally use the methods of the `PSP22` trait. Implementation of the `PSP22` makes your method public. - -```rust -impl PSP22 for Contract {} // That line makes your method publicly available. -``` - -So, if you embed `psp22:Data` in your `FacetB` contract. Then you can call `self.balance_of(owner)` and it will use the default implementation without a cross-contract call. - -```rust -// Code of facet A -#[openbrush::contract] -#[openbrush::implementation(PSP22, Ownable)] -pub mod facet_a { - ... - - #[ink(storage)] - #[derive(Default, Storage)] - pub struct FacetA { - #[storage_field] - psp22: psp22::Data, - #[storage_field] - ownable: ownable::Data, - } - - impl FacetA { - #[ink(constructor)] - pub fn new() -> Self { - // Empty constructor, we do here nothing - Self::default() - } - - // Initialization method to grant `total_supply` tokens to someone. - #[ink(message)] - pub fn init_with_supply(&mut self, total_supply: Balance) { - assert_eq!(Self::env().caller(), self.ownable.owner, "Only owner can init contract"); - ... - } - } -} - -// Code of facet B -#[openbrush::implementation(PSP22, Ownable)] -#[openbrush::contract] -pub mod facet_b { - ... - - #[ink(storage)] - pub struct FacetB { - // The same logic unit is used in `FacetA`. - #[storage_field] - psp22: psp22::Data, - // Some data for `FacetB`. - #[storage_field] - foo: BarData, - } - - impl FacetB { - #[ink(constructor)] - pub fn new() -> Self { - // Empty constructor, we do here nothing - Self::default() - } - - #[ink(message)] - fn balance_of_owner_in_facet_a(&self, owner: AccountId) -> Balance { - // Use default implementation for `psp22::Data`. - // It is not public, because it doesn't have `impl PSP22 for FacetB {}` - self.balance_of(owner) - } - - ... - } -} -``` \ No newline at end of file diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js deleted file mode 100644 index 19e4e9b46..000000000 --- a/docs/docusaurus.config.js +++ /dev/null @@ -1,74 +0,0 @@ -const lightCodeTheme = require('prism-react-renderer/themes/vsLight') -const darkCodeTheme = require('prism-react-renderer/themes/vsDark') - -/** @type {import('@docusaurus/types').DocusaurusConfig} */ -module.exports = { - title: 'OpenBrush', - tagline: 'OpenBrush contracts documentation', - url: 'https://docs.openbrush.io', - baseUrl: '/openbrush-contracts/', - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', - favicon: 'img/favicon.svg', - organizationName: '727-Ventures', - projectName: 'openbrush-contracts', - themeConfig: { - colorMode: { - defaultMode: 'dark' - }, - navbar: { - logo: { - alt: 'OpenBrush', - src: 'img/logo.svg', - srcDark: 'img/logo-dark.svg' - }, - items: [ - { - to: 'smart-contracts/overview', - position: 'right', - label: 'Examples', - activeBasePath: 'smart-contracts' - }, - { - to: 'deployment', - position: 'right', - label: 'Deployment' - }, - { - href: 'https://twitter.com/brushfam_io', - className: 'header-twitter-link', - position: 'right' - }, - { - href: 'https://github.com/727-Ventures/openbrush-contracts', - className: 'header-github-link', - position: 'right' - } - ] - }, - footer: { - copyright: `Copyright © ${new Date().getFullYear()} OpenBrush, Supercolony.net.` - }, - prism: { - theme: lightCodeTheme, - darkTheme: darkCodeTheme, - additionalLanguages: ['toml', 'rust'] - } - }, - plugins: ['docusaurus-plugin-sass'], - presets: [ - [ - '@docusaurus/preset-classic', - { - docs: { - routeBasePath: '/', - sidebarPath: require.resolve('./sidebars.js'), - editUrl: 'https://github.com/727-Ventures/openbrush-contracts/tree/main/docs' - }, - theme: { - customCss: [require.resolve('./src/css/custom.scss')] - } - } - ] - ] -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 5d5dc0ae5..000000000 --- a/docs/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "openbrush", - "version": "4.0.0-beta", - "private": true, - "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" - }, - "dependencies": { - "@docusaurus/core": "^2.4.1", - "@docusaurus/preset-classic": "^2.4.1", - "@docusaurus/theme-search-algolia": "^2.4.1", - "@mdx-js/react": "^1.6.21", - "@svgr/webpack": "^8.0.1", - "clsx": "^1.1.1", - "docusaurus-plugin-sass": "^0.2.1", - "file-loader": "^6.2.0", - "prism-react-renderer": "^1.2.1", - "react": "^17.0.1", - "react-dom": "^17.0.1", - "sass": "^1.39.0", - "url-loader": "^4.1.1" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "^2.4.1", - "@tsconfig/docusaurus": "^1.0.3", - "@types/react": "^17.0.14", - "@types/react-helmet": "^6.1.2", - "@types/react-router-dom": "^5.1.8", - "typescript": "^4.3.5" - }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/docs/sidebars.js b/docs/sidebars.js deleted file mode 100644 index 981a73cd7..000000000 --- a/docs/sidebars.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Creating a sidebar enables you to: - - create an ordered group of docs - - render a sidebar for each doc of that group - - provide next/previous navigation - - The sidebars can be generated from the filesystem, or explicitly defined here. - - Create as many sidebars as you want. - */ - -module.exports = { - // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], - - // But you can create a sidebar manually - /* - tutorialSidebar: [ - { - type: 'category', - label: 'Tutorial', - items: ['hello'], - }, - ], - */ -}; diff --git a/docs/src/css/code.scss b/docs/src/css/code.scss deleted file mode 100644 index c066c41cc..000000000 --- a/docs/src/css/code.scss +++ /dev/null @@ -1,12 +0,0 @@ -.docusaurus-highlight-code-line { - background-color: rgba(0, 0, 0, 0.1); - display: block; - margin: 0 calc(-1 * var(--ifm-pre-padding)); - padding: 0 var(--ifm-pre-padding); -} - -html { - [class^='codeBlockContainer'] { - border-radius: 0; - } -} diff --git a/docs/src/css/custom.scss b/docs/src/css/custom.scss deleted file mode 100644 index 63bcdfd82..000000000 --- a/docs/src/css/custom.scss +++ /dev/null @@ -1,100 +0,0 @@ -@import 'fonts'; -@import 'icons'; -@import 'layout'; -@import 'code'; -@import 'navbar'; -@import 'themes'; - -:root { - --ifm-code-border-radius: 0; - --ifm-color-primary: #8c9640; - --ifm-color-primary-dark: #b4be68; - --ifm-color-primary-darker: #8c9640; - --ifm-color-primary-darkest: #5a5f34; - --ifm-color-primary-light: #c3cb86; - --ifm-color-primary-lighter: #d2d8a4; - --ifm-color-primary-lightest: #dadfb4; - --ifm-code-font-size: 85%; - --ifm-spacing-horizontal: 25px; - --ifm-font-family-monospace: 'Sometype Mono'; - --ifm-font-family-base: 'Sometype Mono'; - --ifm-navbar-height: 60px; - --ifm-menu-link-padding-horizontal: 20px; - --ifm-font-size-base: 95%; - --ifm-navbar-shadow: none; -} - -html[data-theme='light'] { - --ifm-font-color-base: #333; -} - -html[data-theme='dark'] { - --ifm-background-color: #121516; - --ifm-toc-border-color: #43494d; - --ifm-navbar-background-color: #121516; - --ifm-footer-background-color: #121516; - --ifm-color-primary: #d2d8a4; - --ifm-link-color: #d2d8a4; - --ifm-menu-color-active: #dadfb4; - --ifm-navbar-link-hover-color: #d2d8a4; -} - -html[data-theme='light'] { - --ifm-color-primary: var(--ifm-color-primary-darker); - --ifm-footer-background-color: #fafafa; -} - -.table-of-contents li { - position: relative; -} - -.table-of-contents__link--active { - font-weight: 500; -} - -.table-of-contents__left-border { - border-left-width: 2px; -} - -.pagination-nav__link { - border-radius: 0; - border-width: 2px; -} - -[class^='docItemContainer'] { - padding-top: 2rem; -} - -.menu.thin-scrollbar { - padding-bottom: 20px; -} - -a.menu__link { - position: relative -} - -a.menu__link.menu__link--active:before { - content: ''; - display: block; - background: var(--ifm-color-primary); - width: 6px; - height: 6px; - border-radius: 50%; - position: absolute; - transform: translateY(-50%); - top: 50%; - left: 6px; -} - -.table-of-contents { - font-size: 0.85rem; - position: relative; -} - -html button { - font-family: inherit; -} - -[class*='backToTopButton'] { - display: none !important; -} diff --git a/docs/src/css/fonts.scss b/docs/src/css/fonts.scss deleted file mode 100644 index 5884a163c..000000000 --- a/docs/src/css/fonts.scss +++ /dev/null @@ -1,47 +0,0 @@ -@font-face { - font-family: 'Sometype Mono'; - src: url('/static/fonts/sometype-mono.regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Sometype Mono'; - src: url('/static/fonts/sometype-mono.regular-italic.ttf') format('truetype'); - font-weight: 400; - font-style: italic; - font-display: swap; -} - -@font-face { - font-family: 'Sometype Mono'; - src: url('/static/fonts/sometype-mono.medium.ttf') format('truetype'); - font-weight: 500; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Sometype Mono'; - src: url('/static/fonts/sometype-mono.medium-italic.ttf') format('truetype'); - font-weight: 500; - font-style: italic; - font-display: swap; -} - -@font-face { - font-family: 'Sometype Mono'; - src: url('/static/fonts/sometype-mono.bold.ttf') format('truetype'); - font-weight: bold; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Sometype Mono'; - src: url('/static/fonts/sometype-mono.bold-italic.ttf') format('truetype'); - font-weight: bold; - font-style: italic; - font-display: swap; -} \ No newline at end of file diff --git a/docs/src/css/icons.scss b/docs/src/css/icons.scss deleted file mode 100644 index 486d7040d..000000000 --- a/docs/src/css/icons.scss +++ /dev/null @@ -1,43 +0,0 @@ -.header-github-link { - padding-right: 24px; - - &:before { - background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='none' stroke='%231C1E21' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M15 22V18.13C15.0375 17.6532 14.9731 17.1738 14.811 16.7238C14.6489 16.2738 14.3929 15.8634 14.06 15.52C17.2 15.17 20.5 13.98 20.5 8.52C20.4997 7.12383 19.9627 5.7812 19 4.77C19.4559 3.54851 19.4236 2.19835 18.91 0.999999C18.91 0.999999 17.73 0.649999 15 2.48C12.708 1.85882 10.292 1.85882 8 2.48C5.27 0.649999 4.09 0.999999 4.09 0.999999C3.57638 2.19835 3.54414 3.54851 4 4.77C3.03013 5.7887 2.49252 7.14346 2.5 8.55C2.5 13.97 5.8 15.16 8.94 15.55C8.611 15.89 8.35726 16.2954 8.19531 16.7399C8.03335 17.1844 7.96681 17.6581 8 18.13V22M8 19C3 20.5 3 16.5 1 16L8 19Z'/%3E%3C/svg%3E") - no-repeat; - content: ''; - display: flex; - height: 24px; - width: 24px; - - html[data-theme='dark'] & { - background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M15 22V18.13C15.0375 17.6532 14.9731 17.1738 14.811 16.7238C14.6489 16.2738 14.3929 15.8634 14.06 15.52C17.2 15.17 20.5 13.98 20.5 8.52C20.4997 7.12383 19.9627 5.7812 19 4.77C19.4559 3.54851 19.4236 2.19835 18.91 0.999999C18.91 0.999999 17.73 0.649999 15 2.48C12.708 1.85882 10.292 1.85882 8 2.48C5.27 0.649999 4.09 0.999999 4.09 0.999999C3.57638 2.19835 3.54414 3.54851 4 4.77C3.03013 5.7887 2.49252 7.14346 2.5 8.55C2.5 13.97 5.8 15.16 8.94 15.55C8.611 15.89 8.35726 16.2954 8.19531 16.7399C8.03335 17.1844 7.96681 17.6581 8 18.13V22M8 19C3 20.5 3 16.5 1 16L8 19Z'/%3E%3C/svg%3E") - no-repeat; - } - } - - &:hover { - opacity: 0.6; - } -} - -.header-twitter-link { - padding-top: 5px; - - &:before { - background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='none' stroke='%231C1E21' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M23 0.999985C22.0424 1.67546 20.9821 2.19209 19.86 2.52999C19.2577 1.8375 18.4573 1.34668 17.567 1.12391C16.6767 0.901145 15.7395 0.957181 14.8821 1.28444C14.0247 1.6117 13.2884 2.19439 12.773 2.9537C12.2575 3.71302 11.9877 4.61232 12 5.52998V6.52998C10.2426 6.57555 8.50127 6.1858 6.93101 5.39543C5.36074 4.60506 4.01032 3.43862 3 1.99998C3 1.99998 -1 11 8 15C5.94053 16.398 3.48716 17.0989 1 17C10 22 21 17 21 5.49998C20.9991 5.22144 20.9723 4.94358 20.92 4.66999C21.9406 3.66348 22.6608 2.3927 23 0.999985V0.999985Z'/%3E%3C/svg%3E") - no-repeat; - content: ''; - display: flex; - height: 24px; - width: 24px; - - html[data-theme='dark'] & { - background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M23 0.999985C22.0424 1.67546 20.9821 2.19209 19.86 2.52999C19.2577 1.8375 18.4573 1.34668 17.567 1.12391C16.6767 0.901145 15.7395 0.957181 14.8821 1.28444C14.0247 1.6117 13.2884 2.19439 12.773 2.9537C12.2575 3.71302 11.9877 4.61232 12 5.52998V6.52998C10.2426 6.57555 8.50127 6.1858 6.93101 5.39543C5.36074 4.60506 4.01032 3.43862 3 1.99998C3 1.99998 -1 11 8 15C5.94053 16.398 3.48716 17.0989 1 17C10 22 21 17 21 5.49998C20.9991 5.22144 20.9723 4.94358 20.92 4.66999C21.9406 3.66348 22.6608 2.3927 23 0.999985V0.999985Z'/%3E%3C/svg%3E") - no-repeat; - } - } - - &:hover { - opacity: 0.6; - } -} diff --git a/docs/src/css/layout.scss b/docs/src/css/layout.scss deleted file mode 100644 index 680ad0059..000000000 --- a/docs/src/css/layout.scss +++ /dev/null @@ -1,63 +0,0 @@ -html { - font-weight: 500; -} - -body { - padding: 0; -} - -.main-wrapper { - border-left: 2px solid var(--ifm-toc-border-color); - border-right: 2px solid var(--ifm-toc-border-color); -} - -main > .container { - padding-top: 0 !important; -} - -.markdown { - h1:first-child { - --ifm-h1-font-size: 2.6rem; - } -} - -.markdown > h2 { - --ifm-h2-font-size: 1.8rem; -} - -@media (max-width: 767px) { - .markdown { - h1, - h2, - h3, - h4, - h5, - h6 { - --ifm-h1-font-size: 2rem !important; - --ifm-h2-font-size: 1.5rem; - --ifm-h3-font-size: 1.25rem; - --ifm-h4-font-size: 1rem; - --ifm-h5-font-size: 0.875rem; - --ifm-h6-font-size: 0.85rem; - } - } -} - -@media (min-width: 768px) { - html[data-theme='dark'], - html[data-theme='light'] { - --ifm-navbar-height: 85px; - } - - body { - padding: 0 25px; - } - - .footer { - margin-bottom: 25px; - } - - .navbar__inner { - padding-top: 25px; - } -} diff --git a/docs/src/css/navbar.scss b/docs/src/css/navbar.scss deleted file mode 100644 index 62216bb19..000000000 --- a/docs/src/css/navbar.scss +++ /dev/null @@ -1,57 +0,0 @@ -.navbar__brand { - height: 2.9rem; - margin-left: 15px; -} - -.navbar__toggle { - margin-left: 20px; - margin-right: 0; -} - -.navbar--fixed-top { - border-bottom: 2px solid var(--ifm-toc-border-color); - padding: 0; -} - -.navbar__items { - flex: initial; -} - -.navbar__items:first-child:after { - content: ''; - position: absolute; - width: 97px; - height: 2px; - right: -97px; - background: var(--ifm-toc-border-color); - -webkit-transform: rotate(37.28deg) translateX(-1px); - -moz-transform: rotate(37.28deg) translateX(-1px); - transform: rotate(37.28deg) translateX(-1px); - -webkit-transform-origin: left bottom; - -moz-transform-origin: left bottom; - transform-origin: left bottom; - top: -2px; -} - -.navbar__items:first-child { - border-left: 2px solid var(--ifm-toc-border-color); - border-top: 2px solid var(--ifm-toc-border-color); - width: calc(var(--doc-sidebar-width) - 75px); - position: relative; -} - -@media (max-width: 997px) { - .navbar__inner .navbar__items:first-child { - width: calc(100% - 77px); - } -} - -@media (min-width: 997px) { - aside[class^='docSidebarContainer'] { - border-right-width: 2px; - } - - html [class^='tableOfContents'] { - top: var(--ifm-navbar-height); - } -} diff --git a/docs/src/css/themes.scss b/docs/src/css/themes.scss deleted file mode 100644 index 6628b9d5d..000000000 --- a/docs/src/css/themes.scss +++ /dev/null @@ -1,47 +0,0 @@ -html[data-theme='light'] { - .pagination-nav__link { - background: white; - } - - .main-wrapper { - background: #fafafa; - } - [class^='codeBlockContainer'] { - box-shadow: none; - border: 1px solid #ebeef1; - } - - .footer { - border: 2px solid var(--ifm-toc-border-color); - } - - .navbar__items { - &:first-child { - background: #fafafa; - - &:before { - content: ''; - width: 0; - position: absolute; - right: -76px; - height: 0; - border-bottom: 56px solid #fafafa; - border-right: 76px solid transparent; - } - } - } -} - -html[data-theme='dark'] { - .docusaurus-highlight-code-line { - background-color: rgba(0, 0, 0, 0.3); - } - - [class^='codeBlockContainer'] { - box-shadow: none; - } - - .footer { - border: 2px solid var(--ifm-toc-border-color); - } -} diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/static/CNAME b/docs/static/CNAME deleted file mode 100644 index 7b4781890..000000000 --- a/docs/static/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.openbrush.io \ No newline at end of file diff --git a/docs/static/fonts/sometype-mono.bold-italic.ttf b/docs/static/fonts/sometype-mono.bold-italic.ttf deleted file mode 100644 index 23ac240ce..000000000 Binary files a/docs/static/fonts/sometype-mono.bold-italic.ttf and /dev/null differ diff --git a/docs/static/fonts/sometype-mono.bold.ttf b/docs/static/fonts/sometype-mono.bold.ttf deleted file mode 100644 index 27938ab95..000000000 Binary files a/docs/static/fonts/sometype-mono.bold.ttf and /dev/null differ diff --git a/docs/static/fonts/sometype-mono.medium-italic.ttf b/docs/static/fonts/sometype-mono.medium-italic.ttf deleted file mode 100644 index 74b2e59f4..000000000 Binary files a/docs/static/fonts/sometype-mono.medium-italic.ttf and /dev/null differ diff --git a/docs/static/fonts/sometype-mono.medium.ttf b/docs/static/fonts/sometype-mono.medium.ttf deleted file mode 100644 index bb12467c9..000000000 Binary files a/docs/static/fonts/sometype-mono.medium.ttf and /dev/null differ diff --git a/docs/static/fonts/sometype-mono.regular-italic.ttf b/docs/static/fonts/sometype-mono.regular-italic.ttf deleted file mode 100644 index 413f24599..000000000 Binary files a/docs/static/fonts/sometype-mono.regular-italic.ttf and /dev/null differ diff --git a/docs/static/fonts/sometype-mono.regular.ttf b/docs/static/fonts/sometype-mono.regular.ttf deleted file mode 100644 index 2d690d433..000000000 Binary files a/docs/static/fonts/sometype-mono.regular.ttf and /dev/null differ diff --git a/docs/static/img/favicon.svg b/docs/static/img/favicon.svg deleted file mode 100644 index 0f4e0e4cc..000000000 --- a/docs/static/img/favicon.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/docs/static/img/logo-dark.svg b/docs/static/img/logo-dark.svg deleted file mode 100644 index 1ca3bb3fe..000000000 --- a/docs/static/img/logo-dark.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/docs/static/img/logo.svg b/docs/static/img/logo.svg deleted file mode 100644 index 7a70de404..000000000 --- a/docs/static/img/logo.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/docs/tsconfig.json b/docs/tsconfig.json deleted file mode 100644 index 2070d53f0..000000000 --- a/docs/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // This file is not used in compilation. It is here just for a nice editor experience. - "extends": "@tsconfig/docusaurus/tsconfig.json", - "compilerOptions": { - "paths": { - "@site/*": ["./*"] - } - }, - "include": ["src/"] -} diff --git a/examples/README.md b/examples/README.md index 85a053a31..3d4032784 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,4 @@ ## Overview This folder contains examples of how the OpenBrush library can be reused & how to customize the base implementation. -For more information please check the [documentation](https://docs.openbrush.io/smart-contracts/overview). \ No newline at end of file +For more information please check the [documentation](https://learn.brushfam.io/docs/OpenBrush/smart-contracts/overview). \ No newline at end of file diff --git a/lang/Cargo.toml b/lang/Cargo.toml index 8031b95a2..ef482d3c1 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" license = "MIT" repository = "https://github.com/Brushfam/openbrush-contracts" -documentation = "https://docs.openbrush.io" -homepage = "https://727.ventures" +documentation = "https://learn.brushfam.io/docs/openbrush" +homepage = "https://brushfam.io/" description = "Aggregator of all openbrush functionality and utils." keywords = ["wasm", "brushfam", "smart-contracts", "blockchain", "ink"] categories = ["no-std", "embedded"] diff --git a/lang/codegen/Cargo.toml b/lang/codegen/Cargo.toml index 7b384a31f..1f8307d8b 100644 --- a/lang/codegen/Cargo.toml +++ b/lang/codegen/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" license = "MIT" repository = "https://github.com/Brushfam/openbrush-contracts" -documentation = "https://docs.openbrush.io" -homepage = "https://727.ventures" +documentation = "https://learn.brushfam.io/docs/openbrush" +homepage = "https://brushfam.io/" description = "OpenBrush codegeneration for macros in `openbrush_lang_macro`." keywords = ["wasm", "brushfam", "smart-contracts", "blockchain", "ink"] categories = ["no-std", "embedded"] diff --git a/lang/macro/Cargo.toml b/lang/macro/Cargo.toml index 7de062d6b..85b25c3d0 100644 --- a/lang/macro/Cargo.toml +++ b/lang/macro/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" license = "MIT" repository = "https://github.com/Brushfam/openbrush-contracts" -documentation = "https://docs.openbrush.io" -homepage = "https://727.ventures" +documentation = "https://learn.brushfam.io/docs/openbrush" +homepage = "https://brushfam.io/" description = "OpenBrush macros to extend functionality of the ink!." keywords = ["wasm", "brushfam", "smart-contracts", "blockchain", "ink"] categories = ["no-std", "embedded"]