There are some circumstances where you will want a smart contract function to be only callable by NFTgate. This is specifically useful when considering off-chain allowlists, or to centralize an NFT drop for more organized queueing of users and prevent gas wars.
We created NFTgateKeyManager to help simplify this process. Here’s a link to the Github Repo for more details.
How it works
Under the hood, NFTgateKeyManager uses the signature pattern similar to EIP-1271 to help ensure that your data has not been spoofed.
Features
- Makes implementing signature minting for your function trivial.
- NFTgate automatically rotates and updates the key for you from time to time to following good security hygiene. Set once and forget!
Let’s see how we can use it!
The NFTgateKeyManager Interface
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @title NFTgate Key Manager
/// @author Winston Yeo
/// @notice NFTgateKeyManager makes it easy for developers to restrict certain functions to NFTgate.
/// @dev Developers are in charge of registering the contract with the initial NFTgate key.
/// NFTgate will then help you automatically rotate and update your key in line with good security hygiene
interface NFTgateKeyManager {
/// @notice Registers a NFTgate Key to a contract
/// @dev Registers the @param _paperKey with the caller of the function (your contract)
/// @param _NFTgateKey The NFTgate key that is associated with the checkout.
/// You should be able to find this in the response of the checkout API or on the checkout dashbaord.
/// @return bool indicating if the @param _NFTgateKey was successfully registered with the calling address
function register(address _NFTgateKey) external returns (bool);
/// @notice Verifies if the given @param _data is from NFTgate and have not been used before
/// @dev Called as the first line in your function or extracted in a modifier. Refer to the Documentation for more usage details.
/// @param _hash The bytes32 encoding of the data passed into your function.
/// This is done by calling keccak256(abi.encode(...your params in order))
/// @param _nonce a random set of bytes NFTgate passes your function which you forward. This helps ensure that the @param _hash has not been used before.
/// @param _signature used to verify that NFTgate was the one who sent the @param _hash
/// @return bool indicating if the @param _hash was successfully verified
function verify(
bytes32 _hash,
bytes32 _nonce,
bytes calldata _signature
) external returns (bool);
}
Usage in Your Contract
From above we can see that NFTgateKeyManager is used in two stages, the registration and verification.
Let’s start with the register function:
import "@nftgate/contracts/keyManager/NFTgateKeyManager.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
const YourContract {
NFTgateKeyManager NFTgateKeyManager;
// to set the initial NFTgateKey for the contract
constructor(..., address _NFTgateKeyManagerAddress) {
NFTgateKeyManager = NFTgateKeyManager(_NFTgateKeyManagerAddress);
}
// onlyNFTgate modifier to easily restrict multiple different function
modifier onlyNFTgate(bytes32 _hash, bytes32 _nonce, bytes calldata _signature) {
bool success = NFTgateKeyManager.verify(_hash, _nonce, _signature);
require(success, "Failed to verify signature");
_;
}
function registerNFTgateKey(address _NFTgateKey) external onlyOwner {
require(paperKeyManager.register(_NFTgateKey), "Error registering key");
}
// using the modifier
function yourFunction(... your params, bytes32 _nonce, bytes calldata _signature)
onlyNFTgate(keccak256(abi.encode(...your params)), _nonce, _signature)
...
{
// your function
}
}
- You’d want to first store the NFTgateKeyManager contract address as a state variable in your contract. (contract addresses here
- Deploy your contract to the chain of interest
- Register your contact on the dashboard as aÂ
CUSTOM_CONTRACT
 or through the API - You should now be able to grab theÂ
NFTgateKeyManager Token
 from the dashboard. - Call your register function (Â
NFTgateKeyManager.register(_NFTgateKey);
 whereÂ_NFTgateKey
 is theÂNFTgateKeyManager Token
 you got from step 4. Note that you can only callÂregister
 once. - For functions that you want to restrict to only NFTgate, simply make sure that it:
- Accepts all your parameters andÂ
bytes32 _nonce
 andÂbytes calldata _signature
 at the end. - CallsÂ
NFTgateKeyManager.verify(keccak256(abi.encode(...you params in the order that you received it)), _nonce, _signature);
. You can extract this out as aÂmodifier
 if you plan to use it multiple times.
- Accepts all your parameters andÂ
That’s it! Below is a sample of what this could look like.
- Using With Checkout Intents
As mentioned, using NFTgateKeyManager
 is only supported by CUSTOM_CONTRACT
.
To use it, we introduce two more magic variables $NONCE
 and $SIGNATURE
 which you can pass in as params for your mintMethod
Below is an example of creating an SDK intent. Creating a link Intent is the same with additional link-related parameters
/**
* We have two magic variables:
* * `$WALLET` corresponds to the user's wallet
* * `$QUANTITY`corresponds to the quantity the user wants to purchase.
* * `$NONCE` is a unique string to prevent replay attack from attackers trying
* to use the same signature more than once
* * `$SIGNATURE`corresponds to the signature that the user wants to purchase.
* It will resolve from {@param recipientWalletAddress} or {@param quantity} as expected.
* Note that you can pass in the actual values themselves if you so choose.
*
*/
let headersList = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
let bodyContent = JSON.stringify({
"contractId": "YOUR_CONTRACT_ID",
"mintMethod": {
"args": {
// this is just an exmaple call. corresponds to the following stub in solidity
// function NFTgateMint(uint256 _tokenId, uint256 _quantity, address _to, bytes32 _nonce, bytes _signature)
"_tokenId": 1,
"_quantity": "$QUANTITY",
"_to": "$WALLET"
"_nonce": "$NONCE",
"_signature": "$SIGNATURE"
},
"name": "NFTgateMint",
"callOptions": {
"gasPriority": "medium"
},
"payment": {
"currency": "DERC20",
"value": "0.001 * $QUANTITY"
}
},
"walletAddress": "RECIPIENT_EMAIL_ADDRESS",
"email": "RECIPIENT_EMAIL",
"useNFTgateKey": true,
});
let response = await fetch("https://nftgate.net/api/2022-08-12/checkout-sdk-intent", {
method: "POST",
body: bodyContent,
headers: headersList
});
const { sdkClientSecret } = await response.json();
With the client secret, you can now use it with the Checkout Elements!
NFTgateKeyManager contract addresses
Reach out to us if the NFTgateKeyManager is not on the chain that you’re on!
Chain | Contract Address |
---|---|
Mumbai | 0x0c93b186B730985628C04DaDA34B681f38143615 |
Goerli | 0x7924D04fd976B2A3dE32D9CEDCBC90Be4400353e |
Polygon | 0x43Ca8B5b235A0f607259C8fEACd15f9f06f91878 |
Mainnet | 0x678a3F64A1bF33Ba0746fFD88Ba749B40B565Da5 |