Documentation Index
Fetch the complete documentation index at: https://docs.shieldhq.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Shield Contract
The Shield contract manages access policies on the Base blockchain.
Contract Address
| Network | Address |
|---|
| Base Mainnet | 0x4b8F46e5E3d95D78f30F80F1280fE7e5F92c8ce8 |
Overview
The Shield contract stores access policies that define:
- Who can access content (recipient)
- When access expires (timestamp)
- How many attempts are allowed (maxAttempts)
- How many attempts have been made (attempts)
Data Structures
AccessPolicy
struct AccessPolicy {
address sender; // Creator of the policy
address recipient; // Authorized accessor
uint64 expiry; // Expiration timestamp
uint32 maxAttempts; // Maximum access attempts
uint32 attempts; // Current attempt count
bool valid; // Policy validity flag
}
Storage
mapping(bytes32 => AccessPolicy) public policies;
Policies are indexed by policyId (keccak256 hash).
Functions
createPolicy
Create a new access policy.
function createPolicy(
bytes32 policyId,
address recipient,
uint256 expiry,
uint256 maxAttempts
) external
Parameters:
| Parameter | Type | Description |
|---|
policyId | bytes32 | Unique identifier (hash) |
recipient | address | Wallet authorized to access |
expiry | uint256 | Unix timestamp of expiration |
maxAttempts | uint256 | Maximum access attempts allowed |
Requirements:
- Policy doesn’t already exist
- Recipient is not zero address
- Expiry is in the future
- maxAttempts > 0
Emits: PolicyCreated
Example:
const policyId = keccak256(toUtf8Bytes(uniqueString));
const recipient = '0x742d...';
const expiry = Math.floor(Date.now() / 1000) + 86400; // 24 hours
const maxAttempts = 3;
await contract.createPolicy(policyId, recipient, expiry, maxAttempts);
logAttempt
Log an access attempt. Must be called by recipient.
function logAttempt(bytes32 policyId, bool success) external
Parameters:
| Parameter | Type | Description |
|---|
policyId | bytes32 | Policy to log attempt for |
success | bool | Whether access was granted |
Requirements:
- Policy exists
- Policy is valid
- Caller is the recipient
- Not expired
- Attempts remaining
Emits: VerificationAttempt
Example:
await contract.logAttempt(policyId, true);
isPolicyValid
Check if a policy is currently valid.
function isPolicyValid(bytes32 policyId) external view returns (bool)
Returns true if:
- Policy exists
valid flag is true
- Not expired
- Attempts remaining
Example:
const isValid = await contract.isPolicyValid(policyId);
Events
PolicyCreated
event PolicyCreated(
bytes32 indexed policyId,
address indexed sender,
address indexed recipient,
uint256 expiry,
uint256 maxAttempts
);
Emitted when a new policy is created.
VerificationAttempt
event VerificationAttempt(
bytes32 indexed policyId,
bool success
);
Emitted when an access attempt is logged.
Access Control
| Function | Who Can Call |
|---|
createPolicy | Anyone |
logAttempt | Recipient only |
isPolicyValid | Anyone (view) |
Error Messages
| Error | Meaning |
|---|
| ”Policy already exists” | policyId is taken |
| ”Policy does not exist” | policyId not found |
| ”Policy is not valid” | valid flag is false |
| ”Only the recipient can log an attempt” | Wrong caller |
| ”Policy has expired” | expiry < block.timestamp |
| ”Max attempts reached” | attempts >= maxAttempts |
Flow Example
Creating a Link
// 1. Generate unique policyId
const uniqueString = `${address}-${Date.now()}-${randomBytes(8)}`;
const policyId = keccak256(toUtf8Bytes(uniqueString));
// 2. Create policy
const tx = await contract.createPolicy(
policyId,
recipientAddress,
Math.floor(Date.now() / 1000) + 86400,
3
);
await tx.wait();
// 3. Store metadata (off-chain)
await fetch('/api/storeMetadata', {
method: 'POST',
body: JSON.stringify({
policyId,
cid: ipfsCid,
sender: address,
recipient: recipientAddress,
// ...
})
});
Accessing Content
// 1. Verify SIWE
const token = await authenticate();
// 2. Check policy
const isValid = await contract.isPolicyValid(policyId);
if (!isValid) throw new Error('Policy invalid');
// 3. Log attempt
const tx = await contract.logAttempt(policyId, true);
await tx.wait();
// 4. Fetch and decrypt content
const content = await fetchFromIPFS(cid);
const decrypted = await decrypt(content, secretKey);
Security Considerations
- Immutable policies: Once created, cannot be modified
- Self-enforcing: Rules are enforced by contract, not trust
- Transparent: All policies and attempts are public
- Auditable: Complete history on-chain
Gas Costs
| Function | Approximate Gas |
|---|
| createPolicy | ~50,000 |
| logAttempt | ~30,000 |
| isPolicyValid | ~5,000 (view) |
Source Code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Shield {
struct AccessPolicy {
address sender;
address recipient;
uint64 expiry;
uint32 maxAttempts;
uint32 attempts;
bool valid;
}
mapping(bytes32 => AccessPolicy) public policies;
event PolicyCreated(bytes32 indexed policyId, address indexed sender, address indexed recipient, uint256 expiry, uint256 maxAttempts);
event VerificationAttempt(bytes32 indexed policyId, bool success);
function createPolicy(bytes32 policyId, address recipient, uint256 expiry, uint256 maxAttempts) external {
require(policies[policyId].sender == address(0), "Policy already exists");
policies[policyId] = AccessPolicy({
sender: msg.sender,
recipient: recipient,
expiry: uint64(expiry),
maxAttempts: uint32(maxAttempts),
attempts: 0,
valid: true
});
emit PolicyCreated(policyId, msg.sender, recipient, expiry, maxAttempts);
}
function logAttempt(bytes32 policyId, bool success) external {
AccessPolicy storage policy = policies[policyId];
require(policy.sender != address(0), "Policy does not exist");
require(policy.valid, "Policy is not valid");
require(msg.sender == policy.recipient, "Only the recipient can log an attempt");
require(block.timestamp < policy.expiry, "Policy has expired");
require(policy.attempts < policy.maxAttempts, "Max attempts reached");
policy.attempts++;
if (policy.attempts >= policy.maxAttempts) {
policy.valid = false;
}
emit VerificationAttempt(policyId, success);
}
function isPolicyValid(bytes32 policyId) external view returns (bool) {
AccessPolicy storage policy = policies[policyId];
return policy.valid && block.timestamp < policy.expiry && policy.attempts < policy.maxAttempts;
}
}