EssentialContext
Inherit EssentialContext in your L2 contract
Last updated
Inherit EssentialContext in your L2 contract
Last updated
You must inherit EssentialContext
in your Layer 2 contract to enable NFT Global Entry. If you've used meta-transactions in the past you will be familiar with this process. Even if you are not offering Gasless Transactions, you must follow this guide for NFT Global Entry. Gasless Transactions can be added later without any contract updates.
There are a few important things to know when inheriting EssentialContext
for a Global Entry integration:
Transactions that depend on Global Entry must be restricted to calls from an EssentialForwarder
contract. Without this restriction, any caller could spoof NFT authorization and make unauthorized calls to your contract. EssentialContext
includes a modifier onlyForwarder
for this purpose. Global Entry uses a Forwarding contract for both gasless transactions and standard transactions.
When properly restricted, your functions that depend on Global Entry will only be called by an EssentialForwarder
when the submitter has proper NFT authorization. In other words, your contract can assume that _msgSender()
owns or is authorized to use _msgNFT()
.
When writing functions that depend on a specific NFT, do not use function parameters for NFT data. Your function will call _msgNFT()
to get a struct that represents the NFT being "used" in the transaction, including the chain ID, contract address and token ID of the NFT.
EssentialContext
and a version compatible with upgradeable proxy contracts EssentialContextUpgradeable
are available in our contracts SDK. Source code is available on .
You can install from NPM with
or
You can also install the package from Github in a foundry project:
Then, update your remappings to handle our monorepo structure:
EssentialContext
must be initialized in your constructor with the address of the EssentialForwarder
you're using.
If you're using 0xEssential's canonical EssentialForwarder
you can use this address across every network we support:
If you're using Global Entry in an upgradeable proxy we assume you already understand how to use the upgradeable flavor with an initialization call.
EssentialContext
provides a modifier and other convenience functions for restricting calls to your contract. You must restrict calls that depend on NFT authorization to only be called by an EssentialForwarder
otherwise anyone can spoof NFT ownership data and manipulate your contract.
You can add the onlyForwarder
modifier to any function that depends on NFT authorization:
EssentialContext
provides admin functions for managing your trustedForwarder
address should you need to change it.
An external function setTrustedForwarder(address newForwarder)
is restricted to the contract deployer. An internal function is also available if you have different access control needs for administrative functions. We don't anticipate frequent deploys of the canonical EssentialForwarder
contracts, but you may decide to deploy your own for customization purposes.
When writing your contract that integrates NFT Global Entry you will depend on the functions _msgSender()
and _msgNFT()
to get data about the caller (NFT owner) and NFT data.
You should not write functions with arguments like tokenId
or contractAddress
to represent the NFT being "used."
Calls made to your contract's Global Entry functions will include the EssentialForwarder
as the msg.sender
. You must replace msg.sender
with _msgSender()
in order to pull the address of the original submitter from calldata.
This means that _msgSender()
is not necessarily the address that signed or submitted the transaction, but is always an address that owns or has authorization to use that NFT, and has given the transaction submitter authorization as well.
This allows you to build games with great UX without sacrificing your game or users' security. A user can play a game, using an NFT from a hardware wallet, signing Global Entry transactions from a burner wallet, with game reward tokens going to a hot wallet. As long as the user has created a chain of delegations, transactions that pass a delegation tree walk will be valid.
Rather than writing external functions with arguments that represent the NFT being used, your L2 contract will instead use _msgNFT()
to get the verified data about an NFT _msgSender()
has authority to use.
_msgNFT()
returns a struct, IEssentialForwarder.GlobalEntryNFT
:
You can use this data however you need in your function logic to start creating crosschain token-gated applications. Maybe you're writing an onchain raffle with 1 NFT = 1 entry:
NFT Global Entry is only appropriate for applications outside of finance - do not use Global Entry for anything related to loans, staking or anything involving transferring ownership.
When 0xEssential's Ownership Oracle generates a proof of NFT ownership, the proof includes a timestamp. EssentialForwarder
verifies this proof, and requires that the timestamp is no more than 10 minutes old. It's important to understand how this can be abused, and to recognize that if you require a tighter grace period you may deploy your own EssentialForwarder
and customize that parameter. An NFT holder could in theory generate a proof of their ownership, sell the NFT, and still be able to submit a transaction that passes NFT Global Entry authorization checks for the next 10 minutes.
Ownership proofs also depend on an onchain nonce per proof requester, so ownership proofs become invalid as soon as they are used in an onchain transaction and the requester nonce is incremented.
0xEssential provide utilities to help you write tests in JS/TS with hardhat or in Solidity with foundry.
Once you've inherited EssentialContext
and written some logic that depends on a crosschain NFT, you're ready to start working on your client app. 0xEssential offers a client SDK for React apps, @xessential/react
and an ethers-based package @xessential/signer
for other JS/TS environments.
If you're not using React for your client app, you can use @xessential/signer
and the ethers compatible EssentialSigner
class to handle transaction preparation, proof fetching, and transaction sending, whether you're using gasless or standard transactions.
_msgSender()
is guaranteed to own or have authorization to use _msgNFT()
any time your function is called. Global Entry supports Account Delegation via , and allows your frontend to specify the address that your contract will receive as _msgSender()
.
If you're using React on your frontend, Global Entry is simple to integrate, particularly if you're already using wagmi
hooks. Head to our to get started.