Getting Started
To get started with the Solidity SDK, run the following command to create a new project:
npx thirdweb create contract
Or, install the contracts
package into your existing Solidity project:
- Hardhat
- Forge
npm install @thirdweb-dev/contracts
forge install thirdweb-dev/contracts
Using the Solidity SDK
The Solidity SDK can be used to build new smart contracts end-to-end, or to add functionality to your own, existing smart contract using extensions.
All functions in the base contracts and extensions can be modified by overriding them.
Using Base Contracts
The Solidity SDK includes base contracts that are fully complete smart contracts that can be customized by overriding functions OR by adding extensions.
1. To start, import and inherit the base contract. You can find the list of all available base contracts here.
2. Base contracts expect certain constructor arguments to function as intended. Implement a constructor for your smart contract and pass the appropriate values to a constructor for the base contract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
contract MyNFT is ERC721Base {
constructor(
address _defaultAdmin,
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721Base(_defaultAdmin, _name, _symbol, _royaltyRecipient, _royaltyBps) {}
}
3. Now you're all set up! 🎉 Your smart contract now has all the extensions provided by the base contract it inherits and is ready to be deployed to any EVM blockchain of your choice.
Using Extensions
Extensions are to be used via inheritance - your project's smart contract will inherit from them.
Additional extensions can be added to existing smart contracts or to the base contracts to add extra functionality and unlocking features in the SDKs and Dashboard.
1. To start, import and inherit the extension. You can find the list of all available extensions here.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";
contract MyNFT is ERC721Base, Permissions {
constructor(
address _defaultAdmin,
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721Base(_defaultAdmin, _name, _symbol, _royaltyRecipient, _royaltyBps) {}
}
Note:
- Some Extensions are Abstract and so require certain functions to be implemented*.
- Some Extensions are Interfaces and so require all the functions to be implemented*.
*implement = write the logic for the function with a matching function signature (matching name, parameters, visibility and return type)
2. Use the functions provided by the Extension to change the behavior of your smart contract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";
contract MyNFT is ERC721Base, Permissions {
bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor(
address _defaultAdmin,
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721Base(_defaultAdmin, _name, _symbol, _royaltyRecipient, _royaltyBps) {}
/**
* `_canMint` is a function available in `ERC721Base`.
*
* It is called every time a wallet tries to mint NFTs on this
* contract, and lets you define the condition in which an
* attempt to mint NFTs should be permitted, or rejected.
*
* By default, `ERC721Base` only lets the contract's owner mint
* NFTs. Here, we override that functionality.
*
* We use the `Permissions` extension to specify that anyone holding
* "MINTER_ROLE" should be able to mint NFTs.
*/
function _canMint() internal view override returns (bool) {
return hasRole(MINTER_ROLE, msg.sender);
}
}
Inheritance and Overriding Functions
Inheritance allows you to extend your smart contract's properties to include the parent contract's attributes and properties. The inherited functions from this parent contract can be modified in the child contract via a process known as overriding.
Parent contract: Contract that the inheriting contract is inheriting from.
Child contract: The inheriting contract.
To override a function, to add your own custom logic, simply use the keyword override
when declaring the function, making sure that the function signature matches.
To add the original logic from the parent contract, use the keyword super
.
For example, the ERC721Base
contract has an implementation of the function mintTo
, I could instead override this function to add custom logic
and restrict this function in the myNFT
contract to only allow 1 NFT per wallet:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";
contract MyNFT is ERC721Base, Permissions {
constructor(
address _defaultAdmin,
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721Base(_defaultAdmin, _name, _symbol, _royaltyRecipient, _royaltyBps) {}
function mintTo(address _to, string memory _tokenURI) public override {
require(balanceOf(_to) < 1, "only 1 NFT per wallet!");
super.mintTo(_to, _tokenURI);
}
}