Solidity API
Overview
Witness's system is comprised of a few contracts, each playing different roles in producing and consuming provenance. This document aims to give a high level overview of the contracts and the functionalities they provide.
Witness
Witness.sol
is the contract responsible for checkpointing merkle roots and verifying their consistency. It also includes a few helper functions for things like verifying proofs. Notably, clients can use Witness.sol
to get the current merkle root or verify proofs they have stored locally without needing to rely on the Witness server.
The remaining contracts illustrate patterns for consuming provenance proofs from Witness onchain.
WitnessConsumer
WitnessConsumer.sol
establishes the core pattern for verifying the provenance of data onchain; if you're writing a contract that has a general need to verify provenance without falling into one of the more specific patterns below, this is the contract you should use.
Implementing a contract on top of WitnessConsumer.sol
requires the following steps:
- Overriding
getProvenanceHash
: this function is used to map the data you're verifying the provenance of to its hash representation which was actually checkpointed on Witness. As an example, if my data is astring
, the correspondinggetProvenanceHash
implementation may look like this:
function getProvenanceHash(bytes calldata bridgeData) public view virtual override returns (bytes32) {
// We can recover the underlying string value like this if we want to manipulate it to produce the leaf hash.
(string memory str) = abi.decode(bridgeData, (string));
// Different usecases may map the data to a leaf differently; in this case we simply append a suffix to the string.
bytes memory preimage = abi.encode(string.concat(str, "my suffix"));
return keccak256(preimage);
}
-
Initialize your contract by passing in the correct
Witness.sol
address to the constructor. This will be the address of the Witness instance you want to verify the provenance of your data against. -
Consume the provenance!
WitnessConsumer.sol
providesverifyProvenance
andsafeVerifyProof
methods which your contract can call into to verify proofs.
IERC721P
IERC721P.sol
is an interface for a contract which implements the ERC721P standard. This standard is an extension of ERC721 which adds methods related to verifying and minting NFTs based off of provenance proofs. This interface inherits from WitnessConsumer.sol
and adds the following additional methods to be overwritten if you chose to inherit from this:
-
getBridgedOwner
: this function is used to map the data you're verifying the provenance of to the address of the owner of the NFT, without needing to pay gas to submit the transaction onchain. Based on the logic your contract uses to mint NFTs, this may be as simple as returning the address of the user in the committed data payload, or conducting some more complex logic to determine the owner. -
bridgedTokenURI
: this function is used to map the data you're verifying the provenance of to the URI of the NFT, without needing to pay gas to submit the transaction onchain. -
bridge
: this function is the ERC721P equivalent of amint
function. The main difference from a simplermint
method is that this method should verify the provenance of the data its being provided before minting the NFT.
While IERC721P.sol
is abstract and unopinionated in defining these methods, we provide a simple default flavor of this with ERC721P.sol
. Overall, the goal of these methods is to create a dynamic where NFTs can be counterfactually minted by way of verifying the existance of their initial provenance with a contract onchain which would support full-fledged stateful tokenization of the data at any future point in time. Supporting reading things like the URI or owner of an NFT before it has to be fully minted is a key part of this, as it allows for things like marketplaces to display NFTs without users needing to pay the gas to mint them.
ERC721P
ERC721P.sol
is a sample implementation of IERC721P.sol
which provides simple implementations of the methods defined in IERC721P.sol
. This contract can be used as a starting point for applications that want to build on the ERC721P standard.
Specifically, ERC721P.sol
is built around a simple data schema involving the committed data payload entailing a string
which acts as the metadata URI, as well as an address
which represents the owner of the NFT. The getBridgedOwner
and bridgedTokenURI
methods are implemented to simply return these values, getProvenanceHash
does a simple hash over the abi encoding of these values, and bridge
simply mints an NFT to the owner with the given URI.
While this aims to be a simple implementation, it's important to keep in mind that these methods can include much more complex logic. For example, bridge
could enforce some fee payment in order to promote the provenant data into a stateful NFT, or it could support handling signed messages from the original owner of the data to endorse another party bridging it.