浏览代码

Squash merge of branch docs-v2.x into master

Francisco Giordano 6 年之前
父节点
当前提交
90831c8970

+ 3 - 2
contracts/introspection/README.adoc

@@ -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.
 
 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.
 

+ 2 - 0
contracts/ownership/README.adoc

@@ -4,6 +4,8 @@ Contract modules for simple authorization and access control mechanisms.
 
 TIP: For more complex needs see xref:access.adoc.
 
+== Contracts
+
 {{Ownable}}
 
 {{Secondary}}

+ 11 - 8
contracts/token/ERC20/README.adoc

@@ -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].
 
 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
- - {ERC20Detailed}: includes the <<ERC20Detailed-name,`name`>>,
+
+* {IERC20}: the interface all ERC20 implementations should conform to
+* {ERC20}: the base implementation of the ERC20 interface
+* {ERC20Detailed}: includes the <<ERC20Detailed-name,`name`>>,
    <<ERC20Detailed-symbol,`symbol`>> and <<ERC20Detailed-decimals,`decimals`>>
    optional standard extension to the base interface
 
 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 pause token operations for all users ({ERC20Pausable}).
+
+* 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 pause token operations for all users ({ERC20Pausable}).
 
 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!
 

+ 2 - 2
contracts/token/ERC721/IERC721.sol

@@ -30,7 +30,7 @@ contract IERC721 is IERC165 {
      * - `from`, `to` cannot be zero.
      * - `tokenId` must be owned by `from`.
      * - 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;
     /**
@@ -39,7 +39,7 @@ contract IERC721 is IERC165 {
      *
      * Requirements:
      * - 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 approve(address to, uint256 tokenId) public;

+ 1 - 1
contracts/token/ERC721/IERC721Receiver.sol

@@ -9,7 +9,7 @@ contract IERC721Receiver {
     /**
      * @notice Handle the receipt of an NFT
      * @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
      * returned can be obtained as `this.onERC721Received.selector`. This
      * function MAY throw to revert and reject the transfer.

+ 3 - 2
contracts/token/ERC721/README.adoc

@@ -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.
 
 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!
 

+ 25 - 7
docs/contract.hbs

@@ -2,46 +2,64 @@
 :{{name}}: pass:normal[xref:#{{anchor}}[`{{name}}`]]
 {{/linkable}}
 
+[.contract]
 [[{{anchor}}]]
-== `{{name}}`
+=== `{{name}}`
 
 {{natspec.devdoc}}
 
+{{#if modifiers}}
+[.contract-index]
+.Modifiers
 {{#inheritance}}
 {{#ownModifiers}}
-- xref:#{{anchor}}[{{signature}}]
+* xref:#{{anchor}}[`{{signature}}`]
 {{/ownModifiers}}
 {{/inheritance}}
+{{/if}}
 
+{{#if functions}}
+[.contract-index]
+.Functions
 {{#inheritance}}
 {{#ownFunctions}}
-- xref:#{{anchor}}[{{signature}}]
+* xref:#{{anchor}}[`{{signature}}`]
 {{/ownFunctions}}
 {{/inheritance}}
+{{/if}}
 
+{{#if events}}
+[.contract-index]
+.Events
 {{#inheritance}}
 {{#ownEvents}}
-- xref:#{{anchor}}[{{signature}}]
+* xref:#{{anchor}}[`{{signature}}`]
 {{/ownEvents}}
 {{/inheritance}}
+{{/if}}
 
 {{#ownModifiers}}
+[.contract-item]
 [[{{anchor}}]]
-=== {{name}}({{args}})
+==== `{{name}}({{args}})`
 
 {{natspec.devdoc}}
 
 {{/ownModifiers}}
+
 {{#ownFunctions}}
+[.contract-item]
 [[{{anchor}}]]
-=== {{name}}({{args}}){{#if outputs}} → {{outputs}}{{/if}}
+==== `{{name}}({{args}}){{#if outputs}} → {{outputs}}{{/if}}`
 
 {{natspec.devdoc}}
 
 {{/ownFunctions}}
+
 {{#ownEvents}}
+[.contract-item]
 [[{{anchor}}]]
-=== {{name}}({{args}})
+==== `{{name}}({{args}})`
 
 {{natspec.devdoc}}
 

+ 12 - 8
docs/modules/ROOT/pages/access-control.adoc

@@ -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.
 
-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]
 ----
@@ -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.
 
@@ -46,9 +50,9 @@ Most of software development uses access control systems that are role-based: so
 [[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]
 ----
@@ -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.
 
-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
 
 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.

+ 1 - 1
docs/modules/ROOT/pages/api-stability.adoc

@@ -7,7 +7,7 @@ In a nutshell, the API being stable means _if your project is working today, it
 [[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

+ 27 - 13
docs/modules/ROOT/pages/crowdsales.adoc

@@ -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 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?
-
-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.
+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?
+
+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
@@ -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:
 
 * (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.
-* 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#MintedCrowdsale[`MintedCrowdsale`] — The `Crowdsale` mints tokens when a purchase is made.
+* 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
@@ -73,7 +87,7 @@ new 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]
 ----
@@ -119,7 +133,7 @@ contract MyCrowdsaleDeployer {
 [[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]
 ----
@@ -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:
 
-* link:api/crowdsale#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.
-* link:api/crowdsale#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#CappedCrowdsale[`CappedCrowdsale`] — adds a cap to your crowdsale, invalidating any purchases that would exceed that cap
+* xref:api:crowdsale.adoc#IndividuallyCappedCrowdsale[`IndividuallyCappedCrowdsale`] — caps an individual's contributions.
+* xref:api:crowdsale.adoc#WhitelistCrowdsale[`WhitelistCrowdsale`] — only allow whitelisted participants to purchase tokens. this is useful for putting your KYC / AML whitelist on-chain!
+* 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:
 
@@ -194,7 +208,7 @@ OpenZeppelin is here to make that easy!
 [[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]
 ----
@@ -221,7 +235,7 @@ contract MyCrowdsale is Crowdsale, TimedCrowdsale, PostDeliveryCrowdsale {
 [[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]
 ----

+ 7 - 9
docs/modules/ROOT/pages/index.adoc

@@ -25,7 +25,7 @@ To install the OpenZeppelin library, run the following in your Solidity project
 $ 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
@@ -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.
 
-______________________________________________________________________________________________________________________
-You should always use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself.
-______________________________________________________________________________________________________________________
+IMPORTANT: 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
 
 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]
-* link:crowdsales[Learn about Crowdsales]
-* link:tokens[Learn about Tokens]
-* link:utilities[Learn about our Utilities]
+* xref:access-control.adoc[Learn about Access Control]
+* xref:crowdsales.adoc[Learn about Crowdsales]
+* xref:tokens.adoc[Learn about Tokens]
+* 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].
 

+ 1 - 1
docs/modules/ROOT/pages/release-schedule.adoc

@@ -1,6 +1,6 @@
 = 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

+ 18 - 17
docs/modules/ROOT/pages/tokens.adoc

@@ -27,7 +27,7 @@ You've probably heard of the <<ERC20>> or <<ERC721>> token standards, and that's
 [[ERC20]]
 == ERC20
 
-An ERC20 token contract keeps track of link:#different-kinds-of-tokens[_fungible_ tokens]: any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a *medium of exchange currency*, *voting rights*, *staking*, and more.
+An ERC20 token contract keeps track of <<different-kinds-of-tokens,_fungible_ tokens>>: any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a *medium of exchange currency*, *voting rights*, *staking*, and more.
 
 OpenZeppelin provides many ERC20-related contracts. On the xref:api:token/ERC20.adoc[`API reference`] you'll find detailed information on their properties and usage.
 
@@ -51,7 +51,7 @@ contract GLDToken is ERC20, ERC20Detailed {
 }
 ----
 
-OpenZeppelin contracts are often used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance], and here we're reusing xref:api:token/ERC20.adoc#erc20[`ERC20`] for the basic standard implementation and xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`] to get the xref:api:token/ERC20.adoc#ERC20Detailed-name[`name`], xref:api:token/ERC20.adoc#ERC20Detailed-symbol[`symbol`], and xref:api:token/ERC20.adoc#ERC20Detailed-decimals[`decimals`] properties. Additionally, we're creating an `initialSupply` of tokens, which will be assigned to the address that deploys the contract.
+OpenZeppelin contracts are often used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance], and here we're reusing xref:api:token/ERC20.adoc#erc20[`ERC20`] for the basic standard implementation and xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`] to get the xref:api:token/ERC20.adoc#ERC20Detailed-name--[`name`], xref:api:token/ERC20.adoc#ERC20Detailed-symbol--[`symbol`], and xref:api:token/ERC20.adoc#ERC20Detailed-decimals--[`decimals`] properties. Additionally, we're creating an `initialSupply` of tokens, which will be assigned to the address that deploys the contract.
 
 TIP: For a more complete discussion of ERC20 supply mechanisms, see xref:erc20-supply.adoc[our advanced guide].
 
@@ -63,7 +63,7 @@ That's it! Once deployed, we will be able to query the deployer's balance:
 1000
 ----
 
-We can also xref:api:token/ERC20.adoc#IERC20-transfer-address-uint256[transfer] these tokens to other accounts:
+We can also xref:api:token/ERC20.adoc#IERC20-transfer-address-uint256-[transfer] these tokens to other accounts:
 
 [source,javascript]
 ----
@@ -74,11 +74,12 @@ We can also xref:api:token/ERC20.adoc#IERC20-transfer-address-uint256[transfer]
 700
 ----
 
+[[a-note-on-decimals]]
 === A note on `decimals`
 
 Often, you'll want to be able to divide your tokens into arbitrary amounts: say, if you own `5 GLD`, you may want to send `1.5 GLD` to a friend, and keep `3.5 GLD` to yourself. Unfortunately, Solidity and the EVM do not support this behavior: only integer (whole) numbers can be used, which poses an issue. You may send `1` or `2` tokens, but not `1.5`.
 
-To work around this, xref:api:token/ERC20.adoc#erc20detailed[`ERC20Detailed`] provides a xref:api:token/ERC20.adoc#ERC20Detailed.decimals()[`decimals`] field, which is used to specify how many decimal places a token has. To be able to transfer `1.5 GLD`, `decimals` must be at least `1`, since that number has a single decimal place.
+To work around this, xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`] provides a xref:api:token/ERC20.adoc#ERC20Detailed-decimals--[`decimals`] field, which is used to specify how many decimal places a token has. To be able to transfer `1.5 GLD`, `decimals` must be at least `1`, since that number has a single decimal place.
 
 How can this be achieved? It's actually very simple: a token contract can use larger integer values, so that a balance of `50` will represent `5 GLD`, a transfer of `15` will correspond to `1.5 GLD` being sent, and so on.
 
@@ -89,7 +90,7 @@ You'll probably want to use a `decimals` value of `18`, just like Ether and most
 [[ERC721]]
 == ERC721
 
-We've discussed how you can make a _fungible_ token using link:#erc20[ERC20], but what if not all tokens are alike? This comes up in situations like *real estate* or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of link:#different-kinds-of-tokens[_non-fungible_ tokens], that is, where each token is unique.
+We've discussed how you can make a _fungible_ token using <<ERC20>>, but what if not all tokens are alike? This comes up in situations like *real estate* or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of <<different-kinds-of-tokens,_non-fungible_ tokens>>, that is, where each token is unique.
 
 ERC721 is a more complex standard than ERC20, with multiple optional extensions, and is split accross a number of contracts. OpenZeppelin provides flexibility regarding how these are combined, along with custom useful extensions. Check out the xref:api:token/ERC721.adoc[`API reference`] to learn more about these.
 
@@ -125,7 +126,7 @@ contract GameItem is ERC721Full {
 }
 ----
 
-The xref:api:token/ERC721.adoc#erc721full[`ERC721Full`] contract includes all standard extensions, and is probably the one you want to use. In particular, it includes xref:api:token/ERC721.adoc#erc721metadata[`ERC721Metadata`], which provides the xref:api:token/ERC721.adoc#ERC721Metadata._setTokenURI(uint256,string)[`_setTokenURI`] method we use to store an item's metadata.
+The xref:api:token/ERC721.adoc#ERC721Full[`ERC721Full`] contract includes all standard extensions, and is probably the one you want to use. In particular, it includes xref:api:token/ERC721.adoc#ERC721Metadata[`ERC721Metadata`], which provides the xref:api:token/ERC721.adoc#ERC721Metadata-_setTokenURI-uint256-string-[`_setTokenURI`] method we use to store an item's metadata.
 
 Also note that, unlike ERC20, ERC721 lacks a `decimals` field, since each token is distinct and cannot be partitioned.
 
@@ -161,22 +162,22 @@ This `tokenURI` should resolve to a JSON document that might look something like
 
 For more information about the `tokenURI` metadata JSON Schema, check out the https://eips.ethereum.org/EIPS/eip-721[ERC721 specification].
 
-_Note: you'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game! If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly). You could also leverage IPFS to store the tokenURI information, but these techniques are out of the scope of this overview guide._
+NOTE: you'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game! If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly). You could also leverage IPFS to store the tokenURI information, but these techniques are out of the scope of this overview guide.
 
 == Advanced standards
 
-link:#erc20[ERC20] and link:#erc721[ERC721] (fungible and non-fungible assets, respectively) are the first two token contract standards to enjoy widespread use and adoption, but over time, multiple weak points of these standards were identified, as more advanced use cases came up.
+<<ERC20>> and <<ERC721>> (fungible and non-fungible assets, respectively) are the first two token contract standards to enjoy widespread use and adoption, but over time, multiple weak points of these standards were identified, as more advanced use cases came up.
 
 As a result, a multitude of new token standards were and are still being developed, with different tradeoffs between complexity, compatibility and ease of use. We'll explore some of those here.
 
 [[ERC777]]
 == ERC777
 
-Like ERC20, ERC777 is a standard for link:#different-kinds-of-tokens[_fungible_ tokens], and is focused around allowing more complex interactions when trading tokens. More generally, it brings tokens and Ether closer together by providing the equivalent of a `msg.value` field, but for tokens.
+Like ERC20, ERC777 is a standard for <<different-kinds-of-tokens,_fungible_ tokens>>, and is focused around allowing more complex interactions when trading tokens. More generally, it brings tokens and Ether closer together by providing the equivalent of a `msg.value` field, but for tokens.
 
 The standard also bring multiple quality-of-life improvements, such as getting rid of the confusion around `decimals`, minting and burning with proper events, among others, but its killer feature are *receive hooks*. A hook is simply a function in a contract that is called when tokens are sent to it, meaning *accounts and contracts can react to receiving tokens*.
 
-This enables a lot of interesting use cases, including atomic purchases using tokens (no need to do `approve` and `transferFrom` in two separate transactions), rejecting reception of tokens (by reverting on the hook call), redirecting the received tokens to other addresses (similarly to how xref:api:payment#paymentsplitter[`PaymentSplitter`] does it), among many others.
+This enables a lot of interesting use cases, including atomic purchases using tokens (no need to do `approve` and `transferFrom` in two separate transactions), rejecting reception of tokens (by reverting on the hook call), redirecting the received tokens to other addresses (similarly to how xref:api:payment#PaymentSplitter[`PaymentSplitter`] does it), among many others.
 
 Furthermore, since contracts are required to implement these hooks in order to receive tokens, _no tokens can get stuck in a contract that is unaware of the ERC777 protocol_, as has happened countless times when using ERC20s.
 
@@ -186,7 +187,7 @@ The standard has you covered! The ERC777 standard is *backwards compatible with
 
 === Constructing an ERC777 Token Contract
 
-We will replicate the `GLD` example of the link:#constructing-an-erc20-token-contract[ERC20 guide], this time using ERC777. As always, check out the xref:api:token/ERC777.adoc[`API reference`] to learn more about the details of each function.
+We will replicate the `GLD` example of the <<constructing-an-erc20-token-contract,ERC20 guide>>, this time using ERC777. As always, check out the xref:api:token/ERC777.adoc[`API reference`] to learn more about the details of each function.
 
 [source,solidity]
 ----
@@ -207,13 +208,13 @@ contract GLDToken is ERC777 {
 }
 ----
 
-In this case, we'll be extending from the xref:api:token/ERC777.adoc#erc777[`ERC777`] contract, which provides an implementation with compatibility support for ERC20. The API is quite similar to that of xref:api:token/ERC777.adoc#erc777[`ERC777`], and we'll once again make use of xref:api:token/ERC777.adoc#ERC777._mint(address,address,uint256,bytes,bytes)[`_mint`] to assign the `initialSupply` to the deployer account. Unlike xref:api:token/ERC20.adoc#ERC20._mint(address,uint256)[ERC20's `_mint`], this one includes some extra parameters, but you can safely ignore those for now.
+In this case, we'll be extending from the xref:api:token/ERC777.adoc#ERC777[`ERC777`] contract, which provides an implementation with compatibility support for ERC20. The API is quite similar to that of xref:api:token/ERC777.adoc#ERC777[`ERC777`], and we'll once again make use of xref:api:token/ERC777.adoc#ERC777-_mint-address-address-uint256-bytes-bytes-[`_mint`] to assign the `initialSupply` to the deployer account. Unlike xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[ERC20's `_mint`], this one includes some extra parameters, but you can safely ignore those for now.
 
-You'll notice both xref:api:token/ERC777.adoc#IERC777.name()[`name`] and xref:api:token/ERC777.adoc#IERC777.symbol()[`symbol`] are assigned, but not xref:api:token/ERC777.adoc#ERC777.decimals()[`decimals`]. The ERC777 specification makes it mandatory to include support for these functions (unlike ERC20, where it is optional and we had to include xref:api:token/ERC20.adoc#erc20detailed[`ERC20Detailed`]), but also mandates that `decimals` always returns a fixed value of `18`, so there's no need to set it ourselves. For a review of `decimals`'s role and importance, refer back to our link:tokens#a-note-on-decimals[ERC20 guide].
+You'll notice both xref:api:token/ERC777.adoc#IERC777-name--[`name`] and xref:api:token/ERC777.adoc#IERC777-symbol--[`symbol`] are assigned, but not xref:api:token/ERC777.adoc#ERC777-decimals--[`decimals`]. The ERC777 specification makes it mandatory to include support for these functions (unlike ERC20, where it is optional and we had to include xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`]), but also mandates that `decimals` always returns a fixed value of `18`, so there's no need to set it ourselves. For a review of `decimals`'s role and importance, refer back to our <<a-note-on-decimals,ERC20 guide>>.
 
-Finally, we'll need to set the xref:api:token/ERC777.adoc#IERC777.defaultOperators()[`defaultOperators`]: special accounts (usually other smart contracts) that will be able to transfer tokens on behalf of their holders. If you're not planning on using operators in your token, you can simply pass an empty array. _Stay tuned for an upcoming in-depth guide on ERC777 operators!_
+Finally, we'll need to set the xref:api:token/ERC777.adoc#IERC777-defaultOperators--[`defaultOperators`]: special accounts (usually other smart contracts) that will be able to transfer tokens on behalf of their holders. If you're not planning on using operators in your token, you can simply pass an empty array. _Stay tuned for an upcoming in-depth guide on ERC777 operators!_
 
-That's it for a basic token contract! We can now deploy it, and use the same xref:api:token/ERC777.adoc#IERC777.balanceOf(address)[`balanceOf`] method to query the deployer's balance:
+That's it for a basic token contract! We can now deploy it, and use the same xref:api:token/ERC777.adoc#IERC777-balanceOf-address-[`balanceOf`] method to query the deployer's balance:
 
 [source,javascript]
 ----
@@ -221,7 +222,7 @@ That's it for a basic token contract! We can now deploy it, and use the same xre
 1000
 ----
 
-To move tokens from one account to another, we can use both xref:api:token/ERC777.adoc#ERC777.transfer(address,uint256)[`ERC20`'s `transfer`] method, or the new xref:api:token/ERC777.adoc#ERC777.send(address,uint256,bytes)[`ERC777`'s `send`], which fulfills a very similar role, but adds an optional `data` field:
+To move tokens from one account to another, we can use both xref:api:token/ERC777.adoc#ERC777-transfer-address-uint256-[`ERC20`'s `transfer`] method, or the new xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[`ERC777`'s `send`], which fulfills a very similar role, but adds an optional `data` field:
 
 [source,javascript]
 ----
@@ -235,7 +236,7 @@ To move tokens from one account to another, we can use both xref:api:token/ERC77
 
 === Contract recipients
 
-A key difference when using xref:api:token/ERC777.adoc#ERC777.send(address,uint256,bytes)[`send`] is that token transfers to other contracts may revert with the following message:
+A key difference when using xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[`send`] is that token transfers to other contracts may revert with the following message:
 
 [source,text]
 ----

+ 15 - 15
docs/modules/ROOT/pages/utilities.adoc

@@ -5,12 +5,12 @@ OpenZeppelin provides a ton of useful utilities that you can use in your project
 [[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;`
 * 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
-* recover the signer using link:api/cryptography#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`]
+* 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 xref:api:cryptography.adoc#ECDSA-toEthSignedMessageHash-bytes32-[`toEthSignedMessageHash`]
 
 Use these functions in combination to verify that a user has signed some information on-chain:
 
@@ -26,19 +26,19 @@ keccack256(
 .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
 
 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.
-* 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.
-* 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#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.
+* 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.
+* 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;`
-* link:api/introspection#ERC165Checker._supportsInterface(address,bytes4)[`myAddress._supportsInterface(bytes4)`]
-* link:api/introspection#ERC165Checker._supportsAllInterfaces(address,bytes4%5B%5D)[`myAddress._supportsAllInterfaces(bytes4[])`]
+* xref:api:introspection.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`]
+* xref:api:introspection.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[])`]
 
 [source,solidity]
 ----
@@ -66,7 +66,7 @@ contract MyContract {
 [[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:
 
@@ -81,15 +81,15 @@ Easy!
 [[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
 
-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.

+ 45 - 51
package-lock.json

@@ -187,12 +187,6 @@
             "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": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
@@ -364,12 +358,6 @@
             "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": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
@@ -469,9 +457,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "12.0.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz",
-      "integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==",
+      "version": "12.6.8",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz",
+      "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==",
       "dev": true
     },
     "abbrev": {
@@ -2606,6 +2594,26 @@
         "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": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -5684,6 +5692,12 @@
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
       "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": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -6431,6 +6445,12 @@
       "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
       "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": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
@@ -8248,6 +8268,12 @@
         "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": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
@@ -8905,12 +8931,12 @@
       }
     },
     "solidity-docgen": {
-      "version": "0.3.0-beta.2",
-      "resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.3.0-beta.2.tgz",
-      "integrity": "sha512-q4X2GuJQVglHHLnvLVHYdJUAGbr5VKHmcRWel+QE8PUwi9m78xAYXg+80p4fDqEfXeI4SPz37wJWhYA51rqpLw==",
+      "version": "0.3.0-beta.4",
+      "resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.3.0-beta.4.tgz",
+      "integrity": "sha512-VTjyJvNjURg6AVhofc84NjNwjyw2PZ1SVxYpdHDynb+xJkgWmK2LL+hIlIxfuhHji+dkMdIhhP80eQxwhPK5kA==",
       "dev": true,
       "requires": {
-        "@oclif/command": "^1.5.16",
+        "@oclif/command": "^1.5.17",
         "@oclif/config": "^1.13.2",
         "@oclif/errors": "^1.2.2",
         "@oclif/plugin-help": "^2.2.0",
@@ -8925,15 +8951,6 @@
         "typescript": "^3.5.3"
       },
       "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": {
           "version": "8.1.0",
           "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -8996,23 +9013,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": {
           "version": "4.0.1",
           "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
@@ -9025,12 +9025,6 @@
           "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==",
           "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": {
           "version": "0.5.10",
           "resolved": "https://registry.npmjs.org/solc/-/solc-0.5.10.tgz",

+ 2 - 1
package.json

@@ -57,12 +57,13 @@
     "ethereumjs-util": "^6.0.0",
     "ganache-cli": "^6.4.1",
     "ganache-cli-coverage": "https://github.com/frangio/ganache-cli/releases/download/v6.4.1-coverage/ganache-cli-coverage-6.4.1.tgz",
+    "lodash.startcase": "^4.4.0",
     "micromatch": "^4.0.2",
     "nodemon": "^1.19.0",
     "openzeppelin-test-helpers": "^0.4",
     "solhint": "2.1.0",
     "solidity-coverage": "github:rotcivegaf/solidity-coverage#5875f5b7bc74d447f3312c9c0e9fc7814b482477",
-    "solidity-docgen": "^0.3.0-beta.2",
+    "solidity-docgen": "^0.3.0-beta.4",
     "truffle": "^5.0.18"
   }
 }

+ 19 - 0
scripts/gen-nav.js

@@ -0,0 +1,19 @@
+#!/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)}]`);
+}

+ 1 - 7
scripts/prepare-docs.sh

@@ -4,10 +4,4 @@ OUTDIR=docs/modules/api/pages/
 
 rm -rf "$OUTDIR"
 solidity-docgen -t docs -o "$OUTDIR" -e contracts/mocks,contracts/examples
-
-gen-nav() {
-  echo '.API'
-  find "$OUTDIR" -type f | sed -Ee "s:$OUTDIR(.+):* xref\:\1[]:"
-}
-
-gen-nav > "$OUTDIR/../nav.adoc"
+node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"