This repo is DEPRECATED and may contain incorrect information. Go here for the latest Fractal documentation.
Power your smart contracts with verified identities — without accessing or managing personal data.
Authorize transactions based on verified user uniqueness, reputation, and KYC/AML status:
- enable truly democratic governance with one-person-one vote;
- distribute airdrops fairly and avoid bot attacks;
- let artists autograph their NFTs to prove their authenticity;
- bring sybil-resistance to quadratic voting;
- unlock undercollateralized loans;
- ensure KYC/AML regulatory compliance.
Identity is how we get adoption. Early adopters take many risks, but most people are looking for a middle ground between the safe walled garden of Coinbase, and the wild west of liquidity farming. Making identity simple and secure is how we bring the next billion people into crypto and how we persuade institutions to deploy trillions of dollars of liquidity.
Authorize transactions by including a Fractal proof in their payload.
- no need to access or manage personal data
- minimal changes to user flow
GET https://credentials.fractal.id/
?message=<message user signed>
&signature=<user signature>
200 OK {
address: "<EVM address>",
approvedAt: <UNIX timestamp>,
fractalId: "<hex string>",
proof: "<hex string>",
validUntil: <UNIX timestamp>
}
400 BAD REQUEST { error: "<error code>" }
404 NOT FOUND { address: "<EVM address>", error: "<error code>" }
- Import our
CredentialVerifier.sol
contract to inherit itsrequiresCredential
modifier. - Change the first argument of
requiresCredential
(expectedCredential
) based on your KYC level and country requirements.- Format:
level:<kycLevel>;citizenship_not:<comma-separated country codes>;residency_not:<comma-separated country codes>
(ISO 3166-1 alpha-2 country codes).
- Format:
- Set the second to last argument of
requiresCredential
(maxAge
) to the maximum amount of time allowed to pass since KYC approval.
- In seconds (e.g. for
182
days, use15724800
:182*24*60*60
) - Use
0
to skip this check (i.e. if it's not important how long ago the KYC was approved)
👁️ See example (Solidity)
import "github.com/trustfractal/web3-identity/CredentialVerifier.sol";
contract Main is CredentialVerifier {
function main(
/* your transaction arguments go here */
bytes calldata proof,
uint validUntil,
uint approvedAt,
string memory fractalId
) external requiresCredential("level:plus;citizenship_not:de;residency_not:ca,us", proof, validUntil, approvedAt, 15724800, fractalId) {
/* your transaction logic goes here */
}
}
- Before a user interacts with your contract, ask them to sign a message authorizing Fractal to respond on their behalf.
- Send this message and signature to Fractal's API, which returns an expiry timestamp (24 hours in the future) and a proof (Fractal's signature of the user's credential).
- Use this timestamp and proof as arguments to your contract's method.
For more information on how to interact with Fractal's API, please read our docs.
👁️ See example (Javascript)
// using web3.js and MetaMask
const message = `I authorize Defistarter (dc3aa1910acbb7ff4d22c07e43a6926adc3a81305a9355a304410048c9a91afd) to get a proof from Fractal that:
- I passed KYC level plus+liveness
- I am not a citizen of the following countries: Germany (DE)
- I am not a resident of the following countries: Germany (DE)`;
const account = (await web3.eth.getAccounts())[0];
const signature = await ethereum.request({
method: "personal_sign",
params: [message, account],
});
const { address, approvedAt, fractalId, proof, validUntil } =
await FractalAPI.getProof(message, signature);
const mainContract = new web3.eth.Contract(contractABI, contractAddress);
mainContract.methods
.main(proof, validUntil, approvedAt, fractalId)
.send({ from: account });
Credential verification adds approximately 26k gas to the transaction cost.
Authorize transactions by looking up their sender on Fractal's DID Registry.
- no need to access or manage personal data
- no need to change the user flow
- no need for user interaction (e.g. airdrops)
A unique human has a unique Fractal ID, each with 1+ addresses and present in 0+ lists.
address [*]---[1] fractalId [*]---[*] listId
bytes32 fractalId = getFractalId(address walletAddress);
bool presence = isUserInList(bytes32 fractaId, string listId);
Every fractalId
in the DID Registry corresponds to a unique human. Use cases requiring additional guarantees, such as KYC/AML, can also make use of the following lists.
listId |
Meaning |
---|---|
basic |
Passed KYC level basic |
plus |
Passed KYC level plus |
residency_xy |
Resident in country xy (ISO 3166-1 alpha-2 country codes). E.g. residency_ca , residency_de , residency_us |
citizenship_xy |
Citizen of country xy (ISO 3166-1 alpha-2 country codes). E.g. citizenship_ca , citizenship_de , citizenship_us |
- Import our
FractalRegistry.sol
contract and set its address. - Adapt the
requiresRegistry
modifier
based on your KYC level and country requirements.
👁️ See example (Solidity)
import {FractalRegistry} from "github.com/trustfractal/web3-identity/FractalRegistry.sol";
contract Main {
FractalRegistry registry = FractalRegistry(0x5FD6eB55D12E759a21C09eF703fe0CBa1DC9d88D);
modifier requiresRegistry(
string memory allowedLevel,
string[3] memory blockedResidencyCountries,
string[2] memory blockedCitizenshipCountries
) {
bytes32 fractalId = registry.getFractalId(msg.sender);
require(fractalId != 0);
require(registry.isUserInList(fractalId, allowedLevel));
for (uint256 i = 0; i < blockedResidencyCountries.length; i++) {
require(!registry.isUserInList(fractalId, string.concat("residency_", blockedResidencyCountries[i])));
}
for (uint256 i = 0; i < blockedCitizenshipCountries.length; i++) {
require(!registry.isUserInList(fractalId, string.concat("citizenship_", blockedCitizenshipCountries[i])));
}
_;
}
function main(
/* your transaction arguments go here */
) external requiresRegistry("plus", ["ca", "de", "us"], ["de", "us"]) {
/* your transaction logic goes here */
}
}
No further steps are required. Fractal keeps the DID Registry up to date. Build your transactions as you normally would.
👁️ See example (Javascript)
// using web3.js
const mainContract = new web3.eth.Contract(contractABI, contractAddress);
mainContract.methods.main(validUntil, proof).send({ from: account });
The example above adds approximately 25k gas to the transaction cost. Gas usage increases with the number of lookups.