Commit 90831c89 by Francisco Giordano

Squash merge of branch docs-v2.x into master

parent 2187fae1
...@@ -5,8 +5,9 @@ This set of interfaces and contracts deal with [type introspection](https://en.w ...@@ -5,8 +5,9 @@ This set of interfaces and contracts deal with [type introspection](https://en.w
Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors. Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors.
There are two main ways to approach this. There are two main ways to approach this.
- Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`.
- Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts. * Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`.
* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts.
Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security. Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security.
......
...@@ -4,6 +4,8 @@ Contract modules for simple authorization and access control mechanisms. ...@@ -4,6 +4,8 @@ Contract modules for simple authorization and access control mechanisms.
TIP: For more complex needs see xref:access.adoc. TIP: For more complex needs see xref:access.adoc.
== Contracts
{{Ownable}} {{Ownable}}
{{Secondary}} {{Secondary}}
...@@ -5,20 +5,23 @@ This set of interfaces, contracts, and utilities are all related to the https:// ...@@ -5,20 +5,23 @@ This set of interfaces, contracts, and utilities are all related to the https://
TIP: For an overview of ERC20 tokens and a walkthrough on how to create a token contract read our xref:ROOT:tokens.adoc#erc20[ERC20 guide]. TIP: For an overview of ERC20 tokens and a walkthrough on how to create a token contract read our xref:ROOT:tokens.adoc#erc20[ERC20 guide].
There a few core contracts that implement the behavior specified in the EIP: There a few core contracts that implement the behavior specified in the EIP:
- {IERC20}: the interface all ERC20 implementations should conform to
- {ERC20}: the base implementation of the ERC20 interface * {IERC20}: the interface all ERC20 implementations should conform to
- {ERC20Detailed}: includes the <<ERC20Detailed-name,`name`>>, * {ERC20}: the base implementation of the ERC20 interface
* {ERC20Detailed}: includes the <<ERC20Detailed-name,`name`>>,
<<ERC20Detailed-symbol,`symbol`>> and <<ERC20Detailed-decimals,`decimals`>> <<ERC20Detailed-symbol,`symbol`>> and <<ERC20Detailed-decimals,`decimals`>>
optional standard extension to the base interface optional standard extension to the base interface
Additionally there are multiple custom extensions, including: Additionally there are multiple custom extensions, including:
- designation of addresses that can create token supply ({ERC20Mintable}), with an optional maximum cap ({ERC20Capped})
- destruction of own tokens ({ERC20Burnable}) * designation of addresses that can create token supply ({ERC20Mintable}), with an optional maximum cap ({ERC20Capped})
- designation of addresses that can pause token operations for all users ({ERC20Pausable}). * destruction of own tokens ({ERC20Burnable})
* designation of addresses that can pause token operations for all users ({ERC20Pausable}).
Finally, there are some utilities to interact with ERC20 contracts in various ways. Finally, there are some utilities to interact with ERC20 contracts in various ways.
- {SafeERC20} is a wrapper around the interface that eliminates the need to handle boolean return values.
- {TokenTimelock} can hold tokens for a beneficiary until a specified time. * {SafeERC20} is a wrapper around the interface that eliminates the need to handle boolean return values.
* {TokenTimelock} can hold tokens for a beneficiary until a specified time.
NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned! NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned!
......
...@@ -30,7 +30,7 @@ contract IERC721 is IERC165 { ...@@ -30,7 +30,7 @@ contract IERC721 is IERC165 {
* - `from`, `to` cannot be zero. * - `from`, `to` cannot be zero.
* - `tokenId` must be owned by `from`. * - `tokenId` must be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this * - If the caller is not `from`, it must be have been allowed to move this
* NFT by either {approve} or {setApproveForAll}. * NFT by either {approve} or {setApprovalForAll}.
*/ */
function safeTransferFrom(address from, address to, uint256 tokenId) public; function safeTransferFrom(address from, address to, uint256 tokenId) public;
/** /**
...@@ -39,7 +39,7 @@ contract IERC721 is IERC165 { ...@@ -39,7 +39,7 @@ contract IERC721 is IERC165 {
* *
* Requirements: * Requirements:
* - If the caller is not `from`, it must be approved to move this NFT by * - If the caller is not `from`, it must be approved to move this NFT by
* either {approve} or {setApproveForAll}. * either {approve} or {setApprovalForAll}.
*/ */
function transferFrom(address from, address to, uint256 tokenId) public; function transferFrom(address from, address to, uint256 tokenId) public;
function approve(address to, uint256 tokenId) public; function approve(address to, uint256 tokenId) public;
......
...@@ -9,7 +9,7 @@ contract IERC721Receiver { ...@@ -9,7 +9,7 @@ contract IERC721Receiver {
/** /**
* @notice Handle the receipt of an NFT * @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient * @dev The ERC721 smart contract calls this function on the recipient
* after a {IERC721-safeTransfer}. This function MUST return the function selector, * after a {IERC721-safeTransferFrom}. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be * otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This * returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer. * function MAY throw to revert and reject the transfer.
......
...@@ -14,8 +14,9 @@ The fully featured token implementing all three interfaces is prepackaged as {ER ...@@ -14,8 +14,9 @@ The fully featured token implementing all three interfaces is prepackaged as {ER
Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <<IERC721-safeTransferFrom,`safeTransferFrom`>>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface. Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <<IERC721-safeTransferFrom,`safeTransferFrom`>>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface.
Finally, some custom extensions are also included: Finally, some custom extensions are also included:
- {ERC721Mintable} — like the ERC20 version, this allows certain addresses to mint new tokens
- {ERC721Pausable} — like the ERC20 version, this allows addresses to freeze transfers of tokens * {ERC721Mintable} — like the ERC20 version, this allows certain addresses to mint new tokens
* {ERC721Pausable} — like the ERC20 version, this allows addresses to freeze transfers of tokens
NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned! NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned!
......
...@@ -2,46 +2,64 @@ ...@@ -2,46 +2,64 @@
:{{name}}: pass:normal[xref:#{{anchor}}[`{{name}}`]] :{{name}}: pass:normal[xref:#{{anchor}}[`{{name}}`]]
{{/linkable}} {{/linkable}}
[.contract]
[[{{anchor}}]] [[{{anchor}}]]
== `{{name}}` === `{{name}}`
{{natspec.devdoc}} {{natspec.devdoc}}
{{#if modifiers}}
[.contract-index]
.Modifiers
{{#inheritance}} {{#inheritance}}
{{#ownModifiers}} {{#ownModifiers}}
- xref:#{{anchor}}[{{signature}}] * xref:#{{anchor}}[`{{signature}}`]
{{/ownModifiers}} {{/ownModifiers}}
{{/inheritance}} {{/inheritance}}
{{/if}}
{{#if functions}}
[.contract-index]
.Functions
{{#inheritance}} {{#inheritance}}
{{#ownFunctions}} {{#ownFunctions}}
- xref:#{{anchor}}[{{signature}}] * xref:#{{anchor}}[`{{signature}}`]
{{/ownFunctions}} {{/ownFunctions}}
{{/inheritance}} {{/inheritance}}
{{/if}}
{{#if events}}
[.contract-index]
.Events
{{#inheritance}} {{#inheritance}}
{{#ownEvents}} {{#ownEvents}}
- xref:#{{anchor}}[{{signature}}] * xref:#{{anchor}}[`{{signature}}`]
{{/ownEvents}} {{/ownEvents}}
{{/inheritance}} {{/inheritance}}
{{/if}}
{{#ownModifiers}} {{#ownModifiers}}
[.contract-item]
[[{{anchor}}]] [[{{anchor}}]]
=== {{name}}({{args}}) ==== `{{name}}({{args}})`
{{natspec.devdoc}} {{natspec.devdoc}}
{{/ownModifiers}} {{/ownModifiers}}
{{#ownFunctions}} {{#ownFunctions}}
[.contract-item]
[[{{anchor}}]] [[{{anchor}}]]
=== {{name}}({{args}}){{#if outputs}}{{outputs}}{{/if}} ==== `{{name}}({{args}}){{#if outputs}}{{outputs}}{{/if}}`
{{natspec.devdoc}} {{natspec.devdoc}}
{{/ownFunctions}} {{/ownFunctions}}
{{#ownEvents}} {{#ownEvents}}
[.contract-item]
[[{{anchor}}]] [[{{anchor}}]]
=== {{name}}({{args}}) ==== `{{name}}({{args}})`
{{natspec.devdoc}} {{natspec.devdoc}}
......
...@@ -7,7 +7,7 @@ Access control—that is, "who is allowed to do this thing"—is incredibly impo ...@@ -7,7 +7,7 @@ Access control—that is, "who is allowed to do this thing"—is incredibly impo
The most common and basic form of access control is the concept of _ownership_: there's an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. The most common and basic form of access control is the concept of _ownership_: there's an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user.
OpenZeppelin provides link:api/ownership#ownable[`Ownable`] for implementing ownership in your contracts. OpenZeppelin provides xref:api:ownership.adoc#Ownable[`Ownable`] for implementing ownership in your contracts.
[source,solidity] [source,solidity]
---- ----
...@@ -26,9 +26,13 @@ contract MyContract is Ownable { ...@@ -26,9 +26,13 @@ contract MyContract is Ownable {
} }
---- ----
By default, the link:api/ownership#Ownable.owner()[`owner`] of an `Ownable` contract is the account that deployed it, which is usually exactly what you want. By default, the xref:api:ownership.adoc#Ownable-owner--[`owner`] of an `Ownable` contract is the account that deployed it, which is usually exactly what you want.
Ownable also lets you: - link:api/ownership#Ownable.transferOwnership(address)[`transferOwnership`] from the owner account to a new one - link:api/ownership#Ownable.renounceOwnership()[`renounceOwnership`] for the owner to lose this administrative privilege, a common pattern after an initial stage with centralized administration is over - *⚠ Warning! ⚠* Removing the owner altogether will mean that administrative tasks that are protected by `onlyOwner` will no longer be callable! Ownable also lets you:
* xref:api:ownership.adoc#Ownable-transferOwnership-address-[`transferOwnership`] from the owner account to a new one, and
* xref:api:ownership.adoc#Ownable-renounceOwnership--[`renounceOwnership`] for the owner to lose this administrative privilege, a common pattern after an initial stage with centralized administration is over.
WARNING: Removing the owner altogether will mean that administrative tasks that are protected by `onlyOwner` will no longer be callable!
Note that *a contract can also be the owner of another one*! This opens the door to using, for example, a https://github.com/gnosis/MultiSigWallet[Gnosis Multisig] or https://safe.gnosis.io[Gnosis Safe], an https://aragon.org[Aragon DAO], an https://www.uport.me[ERC725/uPort] identity contract, or a totally custom contract that _you_ create. Note that *a contract can also be the owner of another one*! This opens the door to using, for example, a https://github.com/gnosis/MultiSigWallet[Gnosis Multisig] or https://safe.gnosis.io[Gnosis Safe], an https://aragon.org[Aragon DAO], an https://www.uport.me[ERC725/uPort] identity contract, or a totally custom contract that _you_ create.
...@@ -46,9 +50,9 @@ Most of software development uses access control systems that are role-based: so ...@@ -46,9 +50,9 @@ Most of software development uses access control systems that are role-based: so
[[using-roles]] [[using-roles]]
=== Using `Roles` === Using `Roles`
OpenZeppelin provides link:api/access#roles[`Roles`] for implementing role-based access control. Its usage is straightforward: for each role that you want to define, you'll store a variable of type `Role`, which will hold the list of accounts with that role. OpenZeppelin provides xref:api:access.adoc#Roles[`Roles`] for implementing role-based access control. Its usage is straightforward: for each role that you want to define, you'll store a variable of type `Role`, which will hold the list of accounts with that role.
Here's an simple example of using `Roles` in an link:tokens#erc20[`ERC20` token]: we'll define two roles, `namers` and `minters`, that will be able to change the name of the token contract, and mint new tokens, respectively. Here's an simple example of using `Roles` in an xref:tokens.adoc#ERC20[`ERC20` token]: we'll define two roles, `namers` and `minters`, that will be able to change the name of the token contract, and mint new tokens, respectively.
[source,solidity] [source,solidity]
---- ----
...@@ -96,13 +100,13 @@ contract MyToken is ERC20, ERC20Detailed { ...@@ -96,13 +100,13 @@ contract MyToken is ERC20, ERC20Detailed {
So clean! By splitting concerns this way, we can define more granular levels of permission, which was lacking in the _ownership_ approach to access control. Note that an account may have more than one role, if desired. So clean! By splitting concerns this way, we can define more granular levels of permission, which was lacking in the _ownership_ approach to access control. Note that an account may have more than one role, if desired.
OpenZeppelin uses `Roles` extensively with predefined contracts that encode rules for each specific role. A few examples are: link:api/token/ERC20#erc20mintable[`ERC20Mintable`] which uses the link:api/access#minterrole[`MinterRole`] to determine who can mint tokens, and link:api/crowdsale#whitelistcrowdsale[`WhitelistCrowdsale`] which uses both link:api/access#whitelistadminrole[`WhitelistAdminRole`] and link:api/access#whitelistedrole[`WhitelistedRole`] to create a set of accounts that can purchase tokens. OpenZeppelin uses `Roles` extensively with predefined contracts that encode rules for each specific role. A few examples are: xref:api:token/ERC20.adoc#ERC20Mintable[`ERC20Mintable`] which uses the xref:api:access.adoc#MinterRole[`MinterRole`] to determine who can mint tokens, and xref:api:crowdsale.adoc#WhitelistCrowdsale[`WhitelistCrowdsale`] which uses both xref:api:access.adoc#WhitelistAdminRole[`WhitelistAdminRole`] and xref:api:access.adoc#WhitelistedRole[`WhitelistedRole`] to create a set of accounts that can purchase tokens.
This flexibility allows for interesting setups: for example, a link:api/crowdsale#mintedcrowdsale[`MintedCrowdsale`] expects to be given the `MinterRole` of an `ERC20Mintable` in order to work, but the token contract could also extend link:api/token/ERC20#erc20pausable[`ERC20Pausable`] and assign the link:api/access#pauserrole[`PauserRole`] to a DAO that serves as a contingency mechanism in case a vulnerability is discovered in the contract code. Limiting what each component of a system is able to do is known as the https://en.wikipedia.org/wiki/Principle_of_least_privilege[principle of least privilege], and is a good security practice. This flexibility allows for interesting setups: for example, a xref:api:crowdsale.adoc#MintedCrowdsale[`MintedCrowdsale`] expects to be given the `MinterRole` of an `ERC20Mintable` in order to work, but the token contract could also extend xref:api:token/ERC20.adoc#ERC20Pausable[`ERC20Pausable`] and assign the xref:api:access.adoc#PauserRole[`PauserRole`] to a DAO that serves as a contingency mechanism in case a vulnerability is discovered in the contract code. Limiting what each component of a system is able to do is known as the https://en.wikipedia.org/wiki/Principle_of_least_privilege[principle of least privilege], and is a good security practice.
[[usage-in-openzeppelin]] [[usage-in-openzeppelin]]
== Usage in OpenZeppelin == Usage in OpenZeppelin
You'll notice that none of the OpenZeppelin contracts use `Ownable`. `Roles` is a prefferred solution, because it provides the user of the library with enough flexibility to adapt the provided contracts to their needs. You'll notice that none of the OpenZeppelin contracts use `Ownable`. `Roles` is a prefferred solution, because it provides the user of the library with enough flexibility to adapt the provided contracts to their needs.
There are some cases, though, where there's a direct relationship between contracts. For example, link:api/crowdsale#refundablecrowdsale[`RefundableCrowdsale`] deploys a link:api/payment#refundescrow[`RefundEscrow`] on construction, to hold its funds. For those cases, we'll use link:api/ownership#secondary[`Secondary`] to create a _secondary_ contract that allows a _primary_ contract to manage it. You could also think of these as _auxiliary_ contracts. There are some cases, though, where there's a direct relationship between contracts. For example, xref:api:crowdsale.adoc#RefundableCrowdsale[`RefundableCrowdsale`] deploys a xref:api:payment.adoc#RefundEscrow[`RefundEscrow`] on construction, to hold its funds. For those cases, we'll use xref:api:ownership.adoc#Secondary[`Secondary`] to create a _secondary_ contract that allows a _primary_ contract to manage it. You could also think of these as _auxiliary_ contracts.
...@@ -7,7 +7,7 @@ In a nutshell, the API being stable means _if your project is working today, it ...@@ -7,7 +7,7 @@ In a nutshell, the API being stable means _if your project is working today, it
[[versioning-scheme]] [[versioning-scheme]]
== Versioning scheme == Versioning scheme
We follow https://semver.org/[SemVer], which means API breakage may occur between major releases. Read more about the link:release-schedule[release schedule] to know how often this happens (not very). We follow https://semver.org/[SemVer], which means API breakage may occur between major releases. Read more about the xref:release-schedule.adoc[release schedule] to know how often this happens (not very).
[[solidity-functions]] [[solidity-functions]]
== Solidity functions == Solidity functions
......
...@@ -2,9 +2,23 @@ ...@@ -2,9 +2,23 @@
Crowdsales are a popular use for Ethereum; they let you allocate tokens to network participants in various ways, mostly in exchange for Ether. They come in a variety of shapes and flavors, so let's go over the various types available in OpenZeppelin and how to use them. Crowdsales are a popular use for Ethereum; they let you allocate tokens to network participants in various ways, mostly in exchange for Ether. They come in a variety of shapes and flavors, so let's go over the various types available in OpenZeppelin and how to use them.
Crowdsales have a bunch of different properties, but here are some important ones: - Price & Rate Configuration - Does your crowdsale sell tokens at a fixed price? - Does the price change over time or as a function of demand? - Emission - How is this token actually sent to participants? - Validation — Who is allowed to purchase tokens? - Are there KYC / AML checks? - Is there a max cap on tokens? - What if that cap is per-participant? - Is there a starting and ending time frame? - Distribution - Does distribution of funds happen in real-time or after the crowdsale? - Can participants receive a refund if the goal is not met? Crowdsales have a bunch of different properties, but here are some important ones:
To manage all of the different combinations and flavors of crowdsales, OpenZeppelin provides a highly configurable link:api/crowdsale#crowdsale[`Crowdsale`] base contract that can be combined with various other functionalities to construct a bespoke crowdsale. * Price & Rate Configuration
* Does your crowdsale sell tokens at a fixed price?
* Does the price change over time or as a function of demand?
* Emission
* How is this token actually sent to participants?
* Validation — Who is allowed to purchase tokens?
* Are there KYC / AML checks?
* Is there a max cap on tokens?
* What if that cap is per-participant?
* Is there a starting and ending time frame?
* Distribution
* Does distribution of funds happen in real-time or after the crowdsale?
* Can participants receive a refund if the goal is not met?
To manage all of the different combinations and flavors of crowdsales, OpenZeppelin provides a highly configurable xref:api:crowdsale.adoc#Crowdsale[`Crowdsale`] base contract that can be combined with various other functionalities to construct a bespoke crowdsale.
[[crowdsale-rate]] [[crowdsale-rate]]
== Crowdsale Rate == Crowdsale Rate
...@@ -46,8 +60,8 @@ One more for practice: if I want to issue "1 TKN for every dollar (USD) in Ether ...@@ -46,8 +60,8 @@ One more for practice: if I want to issue "1 TKN for every dollar (USD) in Ether
One of the first decisions you have to make is "how do I get these tokens to users?". This is usually done in one of three ways: One of the first decisions you have to make is "how do I get these tokens to users?". This is usually done in one of three ways:
* (default) — The `Crowdsale` contract owns tokens and simply transfers tokens from its own ownership to users that purchase them. * (default) — The `Crowdsale` contract owns tokens and simply transfers tokens from its own ownership to users that purchase them.
* link:api/crowdsale#mintedcrowdsale[`MintedCrowdsale`] — The `Crowdsale` mints tokens when a purchase is made. * xref:api:crowdsale.adoc#MintedCrowdsale[`MintedCrowdsale`] — The `Crowdsale` mints tokens when a purchase is made.
* link:api/crowdsale#allowancecrowdsale[`AllowanceCrowdsale`] — The `Crowdsale` is granted an allowance to another wallet (like a Multisig) that already owns the tokens to be sold in the crowdsale. * xref:api:crowdsale.adoc#AllowanceCrowdsale[`AllowanceCrowdsale`] — The `Crowdsale` is granted an allowance to another wallet (like a Multisig) that already owns the tokens to be sold in the crowdsale.
[[default-emission]] [[default-emission]]
=== Default Emission === Default Emission
...@@ -73,7 +87,7 @@ new Crowdsale( ...@@ -73,7 +87,7 @@ new Crowdsale(
[[minted-crowdsale]] [[minted-crowdsale]]
=== Minted Crowdsale === Minted Crowdsale
To use a link:api/crowdsale#mintedcrowdsale[`MintedCrowdsale`], your token must also be a link:api/token/ERC20#erc20mintable[`ERC20Mintable`] token that the crowdsale has permission to mint from. This can look like: To use a xref:api:crowdsale.adoc#MintedCrowdsale[`MintedCrowdsale`], your token must also be a xref:api:token/ERC20.adoc#ERC20Mintable[`ERC20Mintable`] token that the crowdsale has permission to mint from. This can look like:
[source,solidity] [source,solidity]
---- ----
...@@ -119,7 +133,7 @@ contract MyCrowdsaleDeployer { ...@@ -119,7 +133,7 @@ contract MyCrowdsaleDeployer {
[[allowancecrowdsale]] [[allowancecrowdsale]]
=== AllowanceCrowdsale === AllowanceCrowdsale
Use an link:api/crowdsale#allowancecrowdsale[`AllowanceCrowdsale`] to send tokens from another wallet to the participants of the crowdsale. In order for this to work, the source wallet must give the crowdsale an allowance via the ERC20 link:api/token/ERC20#IERC20.approve(address,uint256)[`approve`] method. Use an xref:api:crowdsale.adoc#AllowanceCrowdsale[`AllowanceCrowdsale`] to send tokens from another wallet to the participants of the crowdsale. In order for this to work, the source wallet must give the crowdsale an allowance via the ERC20 xref:api:token/ERC20.adoc#IERC20-approve-address-uint256-[`approve`] method.
[source,solidity] [source,solidity]
---- ----
...@@ -151,10 +165,10 @@ IERC20(tokenAddress).approve(CROWDSALE_ADDRESS, SOME_TOKEN_AMOUNT); ...@@ -151,10 +165,10 @@ IERC20(tokenAddress).approve(CROWDSALE_ADDRESS, SOME_TOKEN_AMOUNT);
There are a bunch of different validation requirements that your crowdsale might be a part of: There are a bunch of different validation requirements that your crowdsale might be a part of:
* link:api/crowdsale#cappedcrowdsale[`CappedCrowdsale`] — adds a cap to your crowdsale, invalidating any purchases that would exceed that cap * xref:api:crowdsale.adoc#CappedCrowdsale[`CappedCrowdsale`] — adds a cap to your crowdsale, invalidating any purchases that would exceed that cap
* link:api/crowdsale#individuallycappedcrowdsale[`IndividuallyCappedCrowdsale`] — caps an individual's contributions. * xref:api:crowdsale.adoc#IndividuallyCappedCrowdsale[`IndividuallyCappedCrowdsale`] — caps an individual's contributions.
* link:api/crowdsale#whitelistcrowdsale[`WhitelistCrowdsale`] — only allow whitelisted participants to purchase tokens. this is useful for putting your KYC / AML whitelist on-chain! * xref:api:crowdsale.adoc#WhitelistCrowdsale[`WhitelistCrowdsale`] — only allow whitelisted participants to purchase tokens. this is useful for putting your KYC / AML whitelist on-chain!
* link:api/crowdsale#timedcrowdsale[`TimedCrowdsale`] — adds an link:api/crowdsale#TimedCrowdsale.openingTime()[`openingTime`] and link:api/crowdsale#TimedCrowdsale.closingTime()[`closingTime`] to your crowdsale * xref:api:crowdsale.adoc#TimedCrowdsale[`TimedCrowdsale`] — adds an xref:api:crowdsale.adoc#TimedCrowdsale-openingTime--[`openingTime`] and xref:api:Crowdsale.adoc#TimedCrowdsale-closingTime--[`closingTime`] to your crowdsale
Simply mix and match these crowdsale flavors to your heart's content: Simply mix and match these crowdsale flavors to your heart's content:
...@@ -194,7 +208,7 @@ OpenZeppelin is here to make that easy! ...@@ -194,7 +208,7 @@ OpenZeppelin is here to make that easy!
[[postdeliverycrowdsale]] [[postdeliverycrowdsale]]
=== PostDeliveryCrowdsale === PostDeliveryCrowdsale
The link:api/crowdsale#postdeliverycrowdsale[`PostDeliveryCrowdsale`], as its name implies, distributes tokens after the crowdsale has finished, letting users call link:api/crowdsale#PostDeliveryCrowdsale.withdrawTokens(address)[`withdrawTokens`] in order to claim the tokens they've purchased. The xref:api:crowdsale.adoc#PostDeliveryCrowdsale[`PostDeliveryCrowdsale`], as its name implies, distributes tokens after the crowdsale has finished, letting users call xref:api:crowdsale.adoc#PostDeliveryCrowdsale-withdrawTokens_address-[`withdrawTokens`] in order to claim the tokens they've purchased.
[source,solidity] [source,solidity]
---- ----
...@@ -221,7 +235,7 @@ contract MyCrowdsale is Crowdsale, TimedCrowdsale, PostDeliveryCrowdsale { ...@@ -221,7 +235,7 @@ contract MyCrowdsale is Crowdsale, TimedCrowdsale, PostDeliveryCrowdsale {
[[refundablecrowdsale]] [[refundablecrowdsale]]
=== RefundableCrowdsale === RefundableCrowdsale
The link:api/crowdsale#refundablecrowdsale[`RefundableCrowdsale`] offers to refund users if a minimum goal is not reached. If the goal is not reached, the users can link:api/crowdsale#RefundableCrowdsale.claimRefund(address%20payable)[`claimRefund`] to get their Ether back. The xref:api:crowdsale.adoc#RefundableCrowdsale[`RefundableCrowdsale`] offers to refund users if a minimum goal is not reached. If the goal is not reached, the users can xref:api:crowdsale.adoc#RefundableCrowdsale-claimRefund-address-payable-[`claimRefund`] to get their Ether back.
[source,solidity] [source,solidity]
---- ----
......
...@@ -25,7 +25,7 @@ To install the OpenZeppelin library, run the following in your Solidity project ...@@ -25,7 +25,7 @@ To install the OpenZeppelin library, run the following in your Solidity project
$ npm install openzeppelin-solidity $ npm install openzeppelin-solidity
---- ----
_OpenZeppelin features a stable API, which means your contracts won't break unexpectedly when upgrading to a newer minor version. You can read ṫhe details in our link:api-stability[API Stability] document._ NOTE: OpenZeppelin features a stable API, which means your contracts won't break unexpectedly when upgrading to a newer minor version. You can read ṫhe details in our xref:api-stability.adoc[API Stability] document.
[[usage]] [[usage]]
== Usage == Usage
...@@ -45,21 +45,19 @@ contract MyContract is Ownable { ...@@ -45,21 +45,19 @@ contract MyContract is Ownable {
Truffle and other Ethereum development toolkits will automatically detect the installed library, and compile the imported contracts. Truffle and other Ethereum development toolkits will automatically detect the installed library, and compile the imported contracts.
______________________________________________________________________________________________________________________ IMPORTANT: You should always use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself.
You should always use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself.
______________________________________________________________________________________________________________________
[[next-steps]] [[next-steps]]
== Next Steps == Next Steps
Check out the the guides in the sidebar to learn about different concepts, and how to use the contracts that OpenZeppelin provides. Check out the the guides in the sidebar to learn about different concepts, and how to use the contracts that OpenZeppelin provides.
* link:access-control[Learn about Access Control] * xref:access-control.adoc[Learn about Access Control]
* link:crowdsales[Learn about Crowdsales] * xref:crowdsales.adoc[Learn about Crowdsales]
* link:tokens[Learn about Tokens] * xref:tokens.adoc[Learn about Tokens]
* link:utilities[Learn about our Utilities] * xref:utilities.adoc[Learn about our Utilities]
OpenZeppelin's link:api/token/ERC20[full API] is also thoroughly documented, and serves as a great reference when developing your smart contract application. OpenZeppelin's xref:api:token/ERC20.adoc[full API] is also thoroughly documented, and serves as a great reference when developing your smart contract application.
Additionally, you can also ask for help or follow OpenZeppelin's development in the https://forum.openzeppelin.com[community forum]. Additionally, you can also ask for help or follow OpenZeppelin's development in the https://forum.openzeppelin.com[community forum].
......
= Release Schedule = Release Schedule
OpenZeppelin follows a link:api-stability[semantic versioning scheme]. OpenZeppelin follows a xref:api-stability.adoc[semantic versioning scheme].
[[minor-releases]] [[minor-releases]]
== Minor releases == Minor releases
......
...@@ -5,12 +5,12 @@ OpenZeppelin provides a ton of useful utilities that you can use in your project ...@@ -5,12 +5,12 @@ OpenZeppelin provides a ton of useful utilities that you can use in your project
[[cryptography]] [[cryptography]]
== Cryptography == Cryptography
* link:api/cryptography#ecdsa[`ECDSA`] — provides functions for recovering and managing Ethereum account ECDSA signatures: * xref:api:cryptography.adoc#ECDSA[`ECDSA`] — provides functions for recovering and managing Ethereum account ECDSA signatures:
* to use it, declare: `using ECDSA for bytes32;` * to use it, declare: `using ECDSA for bytes32;`
* signatures are tightly packed, 65 byte `bytes` that look like `{v (1)} {r (32)} {s (32)}` * signatures are tightly packed, 65 byte `bytes` that look like `{v (1)} {r (32)} {s (32)}`
** this is the default from `web3.eth.sign` so you probably don't need to worry about this format ** this is the default from `web3.eth.sign` so you probably don't need to worry about this format
* recover the signer using link:api/cryptography#ECDSA.recover(bytes32,bytes)[`myDataHash.recover(signature)`] * recover the signer using xref:api:cryptography.adoc#ECDSA-recover-bytes32-bytes-[`myDataHash.recover(signature)`]
* if you are using `eth_personalSign`, the signer will hash your data and then add the prefix `\x19Ethereum Signed Message:\n`, so if you're attempting to recover the signer of an Ethereum signed message hash, you'll want to use link:api/cryptography#ECDSA.toEthSignedMessageHash(bytes32)[`toEthSignedMessageHash`] * if you are using `eth_personalSign`, the signer will hash your data and then add the prefix `\x19Ethereum Signed Message:\n`, so if you're attempting to recover the signer of an Ethereum signed message hash, you'll want to use xref:api:cryptography.adoc#ECDSA-toEthSignedMessageHash-bytes32-[`toEthSignedMessageHash`]
Use these functions in combination to verify that a user has signed some information on-chain: Use these functions in combination to verify that a user has signed some information on-chain:
...@@ -26,19 +26,19 @@ keccack256( ...@@ -26,19 +26,19 @@ keccack256(
.recover(signature) .recover(signature)
---- ----
* link:api/cryptography#merkleproof[`MerkleProof`] — provides link:api/cryptography#MerkleProof.verify(bytes32%5B%5D,bytes32,bytes32)[`verify`] for verifying merkle proofs. * xref:api:cryptography.adoc#MerkleProof[`MerkleProof`] — provides xref:api:cryptography.adoc#MerkleProof-verify-bytes32---bytes32-bytes32-[`verify`] for verifying merkle proofs.
[[introspection]] [[introspection]]
== Introspection == Introspection
In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC165 is a standard that helps do runtime interface detection. OpenZeppelin provides some helpers, both for implementing ERC165 in your contracts and querying other contracts: In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC165 is a standard that helps do runtime interface detection. OpenZeppelin provides some helpers, both for implementing ERC165 in your contracts and querying other contracts:
* link:api/introspection#ierc165[`IERC165`] — this is the ERC165 interface that defines link:api/introspection#IERC165.supportsInterface(bytes4)[`supportsInterface`]. When implementing ERC165, you'll conform to this interface. * xref:api:introspection.adoc#IERC165[`IERC165`] — this is the ERC165 interface that defines xref:api:introspection.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC165, you'll conform to this interface.
* link:api/introspection#erc165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using link:api/introspection#ERC165._registerInterface(bytes4)[`_registerInterface(bytes4)`]: check out example usage as part of the ERC721 implementation. * xref:api:introspection.adoc#ERC165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using xref:api:introspection.adoc#ERC165-_registerInterface-bytes4-[`_registerInterface(bytes4)`]: check out example usage as part of the ERC721 implementation.
* link:api/introspection#erc165checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about. * xref:api:introspection.adoc#ERC165Checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about.
* include with `using ERC165Checker for address;` * include with `using ERC165Checker for address;`
* link:api/introspection#ERC165Checker._supportsInterface(address,bytes4)[`myAddress._supportsInterface(bytes4)`] * xref:api:introspection.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`]
* link:api/introspection#ERC165Checker._supportsAllInterfaces(address,bytes4%5B%5D)[`myAddress._supportsAllInterfaces(bytes4[])`] * xref:api:introspection.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[])`]
[source,solidity] [source,solidity]
---- ----
...@@ -66,7 +66,7 @@ contract MyContract { ...@@ -66,7 +66,7 @@ contract MyContract {
[[math]] [[math]]
== Math == Math
The most popular math related library OpenZeppelin provides is link:api/math#safemath[`SafeMath`], which provides mathematical functions that protect your contract from overflows and underflows. The most popular math related library OpenZeppelin provides is xref:api:math.adoc#SafeMath[`SafeMath`], which provides mathematical functions that protect your contract from overflows and underflows.
Include the contract with `using SafeMath for uint256;` and then call the functions: Include the contract with `using SafeMath for uint256;` and then call the functions:
...@@ -81,15 +81,15 @@ Easy! ...@@ -81,15 +81,15 @@ Easy!
[[payment]] [[payment]]
== Payment == Payment
Want to split some payments between multiple people? Maybe you have an app that sends 30% of art purchases to the original creator and 70% of the profits to the current owner; you can build that with link:api/payment#paymentsplitter[`PaymentSplitter`]! Want to split some payments between multiple people? Maybe you have an app that sends 30% of art purchases to the original creator and 70% of the profits to the current owner; you can build that with xref:api:payment.adoc#PaymentSplitter[`PaymentSplitter`]!
In solidity, there are some security concerns with blindly sending money to accounts, since it allows them to execute arbitrary code. You can read up on these security concerns in the https://consensys.github.io/smart-contract-best-practices/[Ethereum Smart Contract Best Practices] website. One of the ways to fix reentrancy and stalling problems is, instead of immediately sending Ether to accounts that need it, you can use link:api/payment#pullpayment[`PullPayment`], which offers an link:api/payment#PullPayment._asyncTransfer(address,uint256)[`_asyncTransfer`] function for sending money to something and requesting that they link:api/payment#PullPayment.withdrawPayments(address%20payable)[`withdrawPayments()`] it later. In solidity, there are some security concerns with blindly sending money to accounts, since it allows them to execute arbitrary code. You can read up on these security concerns in the https://consensys.github.io/smart-contract-best-practices/[Ethereum Smart Contract Best Practices] website. One of the ways to fix reentrancy and stalling problems is, instead of immediately sending Ether to accounts that need it, you can use xref:api:payment.adoc#PullPayment[`PullPayment`], which offers an xref:api:payment.adoc#PullPayment-_asyncTransfer-address-uint256-[`_asyncTransfer`] function for sending money to something and requesting that they xref:api:payment.adoc#PullPayment-withdrawPayments-address-payable-[`withdrawPayments()`] it later.
If you want to Escrow some funds, check out link:api/payment#escrow[`Escrow`] and link:api/payment#conditionalescrow[`ConditionalEscrow`] for governing the release of some escrowed Ether. If you want to Escrow some funds, check out xref:api:payment.adoc#Escrow[`Escrow`] and xref:api:payment.adoc#ConditionalEscrow[`ConditionalEscrow`] for governing the release of some escrowed Ether.
[[misc]] [[misc]]
=== Misc === Misc
Want to check if an address is a contract? Use link:api/utils#address[`Address`] and link:api/utils#Address.isContract(address)[`Address.isContract()`]. Want to check if an address is a contract? Use xref:api:utils.adoc#Address[`Address`] and xref:api:utils.adoc#Address-isContract-address-[`Address.isContract()`].
Want to keep track of some numbers that increment by 1 every time you want another one? Check out link:api/drafts#counter[`Counter`]. This is especially useful for creating incremental ERC721 `tokenId`s like we did in the last section. Want to keep track of some numbers that increment by 1 every time you want another one? Check out xref:api:drafts.adoc#Counter[`Counter`]. This is especially useful for creating incremental ERC721 `tokenId`s like we did in the last section.
...@@ -187,12 +187,6 @@ ...@@ -187,12 +187,6 @@
"universalify": "^0.1.0" "universalify": "^0.1.0"
} }
}, },
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"dev": true
},
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
...@@ -364,12 +358,6 @@ ...@@ -364,12 +358,6 @@
"supports-color": "^5.3.0" "supports-color": "^5.3.0"
} }
}, },
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"dev": true
},
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
...@@ -469,9 +457,9 @@ ...@@ -469,9 +457,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "12.0.2", "version": "12.6.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz",
"integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==", "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==",
"dev": true "dev": true
}, },
"abbrev": { "abbrev": {
...@@ -2606,6 +2594,26 @@ ...@@ -2606,6 +2594,26 @@
"randombytes": "^2.0.0" "randombytes": "^2.0.0"
} }
}, },
"dir-glob": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
"integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
"dev": true,
"requires": {
"path-type": "^3.0.0"
},
"dependencies": {
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"pify": "^3.0.0"
}
}
}
},
"doctrine": { "doctrine": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
...@@ -5684,6 +5692,12 @@ ...@@ -5684,6 +5692,12 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true "dev": true
}, },
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"dev": true
},
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
...@@ -6431,6 +6445,12 @@ ...@@ -6431,6 +6445,12 @@
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
"dev": true "dev": true
}, },
"lodash.startcase": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
"integrity": "sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=",
"dev": true
},
"lodash.template": { "lodash.template": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
...@@ -8248,6 +8268,12 @@ ...@@ -8248,6 +8268,12 @@
"simple-concat": "^1.0.0" "simple-concat": "^1.0.0"
} }
}, },
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
},
"slice-ansi": { "slice-ansi": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
...@@ -8905,12 +8931,12 @@ ...@@ -8905,12 +8931,12 @@
} }
}, },
"solidity-docgen": { "solidity-docgen": {
"version": "0.3.0-beta.2", "version": "0.3.0-beta.4",
"resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.3.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.3.0-beta.4.tgz",
"integrity": "sha512-q4X2GuJQVglHHLnvLVHYdJUAGbr5VKHmcRWel+QE8PUwi9m78xAYXg+80p4fDqEfXeI4SPz37wJWhYA51rqpLw==", "integrity": "sha512-VTjyJvNjURg6AVhofc84NjNwjyw2PZ1SVxYpdHDynb+xJkgWmK2LL+hIlIxfuhHji+dkMdIhhP80eQxwhPK5kA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@oclif/command": "^1.5.16", "@oclif/command": "^1.5.17",
"@oclif/config": "^1.13.2", "@oclif/config": "^1.13.2",
"@oclif/errors": "^1.2.2", "@oclif/errors": "^1.2.2",
"@oclif/plugin-help": "^2.2.0", "@oclif/plugin-help": "^2.2.0",
...@@ -8925,15 +8951,6 @@ ...@@ -8925,15 +8951,6 @@
"typescript": "^3.5.3" "typescript": "^3.5.3"
}, },
"dependencies": { "dependencies": {
"dir-glob": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
"integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
"dev": true,
"requires": {
"path-type": "^3.0.0"
}
},
"fs-extra": { "fs-extra": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
...@@ -8996,23 +9013,6 @@ ...@@ -8996,23 +9013,6 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"pify": "^3.0.0"
},
"dependencies": {
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
}
}
},
"pify": { "pify": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
...@@ -9025,12 +9025,6 @@ ...@@ -9025,12 +9025,6 @@
"integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==",
"dev": true "dev": true
}, },
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
},
"solc": { "solc": {
"version": "0.5.10", "version": "0.5.10",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.5.10.tgz", "resolved": "https://registry.npmjs.org/solc/-/solc-0.5.10.tgz",
......
#!/usr/bin/env node
const path = require('path');
const proc = require('child_process');
const startCase = require('lodash.startcase');
const baseDir = process.argv[2];
const files = proc.execFileSync(
'find', [baseDir, '-type', 'f'], { encoding: 'utf8' }
).split('\n').filter(s => s !== '');
console.log('.API');
for (const file of files) {
const doc = file.replace(baseDir, '');
const title = path.parse(file).name;
console.log(`* xref:${doc}[${startCase(title)}]`);
}
...@@ -4,10 +4,4 @@ OUTDIR=docs/modules/api/pages/ ...@@ -4,10 +4,4 @@ OUTDIR=docs/modules/api/pages/
rm -rf "$OUTDIR" rm -rf "$OUTDIR"
solidity-docgen -t docs -o "$OUTDIR" -e contracts/mocks,contracts/examples solidity-docgen -t docs -o "$OUTDIR" -e contracts/mocks,contracts/examples
node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"
gen-nav() {
echo '.API'
find "$OUTDIR" -type f | sed -Ee "s:$OUTDIR(.+):* xref\:\1[]:"
}
gen-nav > "$OUTDIR/../nav.adoc"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment