Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "contracts/lib/forge-std"]
path = contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "examples/shitpost/contracts/lib/forge-std"]
path = examples/shitpost/contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std
3 changes: 3 additions & 0 deletions examples/shitpost/contracts/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DEPLOYER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000
DEPLOYER_ADDRESS=0x0000000000000000000000000000000000000000
RPC_URL=https://rpc.00000000000000000
14 changes: 14 additions & 0 deletions examples/shitpost/contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
11 changes: 11 additions & 0 deletions examples/shitpost/contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Shitpost utility contracts


## Deploy

```
source .env && forge script DeployContract \
--rpc-url $RPC_URL
--broadcast \
--private-key $DEPLOYER_PRIVATE_KEY
```
19 changes: 19 additions & 0 deletions examples/shitpost/contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
remappings = [
"@forge/=lib/forge-std/src/"
]

fs_permissions = [{ access = "read-write", path = "./deployments/"}]

solc = "0.8.28"
evm_version = "berlin"
optimizer = true
optimizer_runs = 1
bytecode_hash = "none"
cbor_metadata = false


# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions examples/shitpost/contracts/lib/forge-std
Submodule forge-std added at 77041d
20 changes: 20 additions & 0 deletions examples/shitpost/contracts/script/DeployContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity ^0.8.25;

import "forge-std/Script.sol";
import "../src/CDNToWalrusRegistry.sol";
import {console} from "@forge/console.sol";

contract DeployContract is Script {
function run() external {
uint256 deployerPK = vm.envUint("DEPLOYER_PRIVATE_KEY");

vm.startBroadcast(deployerPK);

CDNToWalrusRegistry registry = new CDNToWalrusRegistry();

vm.stopBroadcast();

console.log("CDNToWalrusRegistry deployed at:", address(registry));
console.log("Owner set to:", registry.owner());
}
}
27 changes: 27 additions & 0 deletions examples/shitpost/contracts/src/CDNToWalrusRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pragma solidity ^0.8.25;

import {Ownable} from "./base/Ownable.sol";

contract CDNToWalrusRegistry is Ownable {
mapping(bytes32 => bytes32) private cdnIdToWalrusId;
mapping(bytes32 => bytes32) private walrusIdToCdnId;

event CDNWalrusRecordCreated(bytes32 indexed cdnId, bytes32 indexed walrusId);

constructor() Ownable(msg.sender) {}

function setRecord(bytes32 cdnId, bytes32 walrusId) public onlyOwner {
cdnIdToWalrusId[cdnId] = walrusId;
walrusIdToCdnId[walrusId] = cdnId;

emit CDNWalrusRecordCreated(cdnId, walrusId);
}

function getWalrusIdByCdnId(bytes32 cdnId) public view returns (bytes32) {
return cdnIdToWalrusId[cdnId];
}

function getCdnIdByWalrusId(bytes32 walrusId) public view returns (bytes32) {
return walrusIdToCdnId[walrusId];
}
}
28 changes: 28 additions & 0 deletions examples/shitpost/contracts/src/base/Context.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}

function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}

function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
100 changes: 100 additions & 0 deletions examples/shitpost/contracts/src/base/Ownable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "./Context.sol";

/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;

/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);

/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}

/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}

/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}

/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
130 changes: 130 additions & 0 deletions examples/shitpost/contracts/test/CDNToWalrusRegistry.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
pragma solidity ^0.8.25;

import {Test} from "forge-std/Test.sol";
import "../src/CDNToWalrusRegistry.sol";
import {console} from "@forge/console.sol";

contract CDNToWalrusRegistryTest is Test {
CDNToWalrusRegistry registry;
address owner = address(0x1);
address nonOwner = address(0x2);

// The specific IDs provided
bytes32 constant WALRUS_ID = 0x503d77961eb14a308a9b87d92c7898c19937cd267b3d9ded18df194a6d7f56fc; // base64url decode of "Pz3wmmi6F1KMmrN9QcGR_fnqyg4cupFI-nS-WSW2d1c"
bytes32 constant CDN_ID = 0xf5c2099e69ee4bdc4e9b87d92c7898c19937cd267b3d9ded18df194a6d7f56fc;

// Define the event signature for testing
event CDNWalrusRecordCreated(bytes32 indexed cdnId, bytes32 indexed walrusId);

function setUp() public {
vm.startPrank(owner);
registry = new CDNToWalrusRegistry();
vm.stopPrank();
}

function testConstructor() public {
assertEq(registry.owner(), owner, "Owner should be set to the deployer");
}

function testSetRecord() public {
vm.startPrank(owner);
registry.setRecord(CDN_ID, WALRUS_ID);
vm.stopPrank();

assertEq(registry.getWalrusIdByCdnId(CDN_ID), WALRUS_ID, "CDN ID should map to Walrus ID");
assertEq(registry.getCdnIdByWalrusId(WALRUS_ID), CDN_ID, "Walrus ID should map to CDN ID");
}

function testSetRecordEmitsEvent() public {
vm.startPrank(owner);

vm.expectEmit(true, true, false, true);
emit CDNWalrusRecordCreated(CDN_ID, WALRUS_ID);
registry.setRecord(CDN_ID, WALRUS_ID);

vm.stopPrank();
}

function testSetRecordFailsForNonOwner() public {
vm.startPrank(nonOwner);

vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, nonOwner));
registry.setRecord(CDN_ID, WALRUS_ID);

console.logBytes32(CDN_ID);
console.logBytes32(WALRUS_ID);

vm.stopPrank();
}

function testGetters() public {
vm.startPrank(owner);
registry.setRecord(CDN_ID, WALRUS_ID);
vm.stopPrank();

assertEq(registry.getWalrusIdByCdnId(CDN_ID), WALRUS_ID, "getWalrusIdByCdnId should return correct Walrus ID");
assertEq(registry.getCdnIdByWalrusId(WALRUS_ID), CDN_ID, "getCdnIdByWalrusId should return correct CDN ID");
}

function testTransferOwnership() public {
vm.startPrank(owner);
registry.transferOwnership(nonOwner);
vm.stopPrank();

assertEq(registry.owner(), nonOwner, "Ownership should be transferred to nonOwner");

// Now nonOwner should be able to add records
vm.startPrank(nonOwner);
registry.setRecord(CDN_ID, WALRUS_ID);
vm.stopPrank();

assertEq(registry.getWalrusIdByCdnId(CDN_ID), WALRUS_ID, "New owner should be able to add records");
}

function testTransferOwnershipFailsForNonOwner() public {
vm.startPrank(nonOwner);

vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, nonOwner));
registry.transferOwnership(nonOwner);

vm.stopPrank();
}

function testMultipleRecords() public {
bytes32 cdnId1 = keccak256("cdn1");
bytes32 walrusId1 = keccak256("walrus1");
bytes32 cdnId2 = keccak256("cdn2");
bytes32 walrusId2 = keccak256("walrus2");

vm.startPrank(owner);

registry.setRecord(cdnId1, walrusId1);
registry.setRecord(cdnId2, walrusId2);

vm.stopPrank();

assertEq(registry.getWalrusIdByCdnId(cdnId1), walrusId1, "First record should be correct");
assertEq(registry.getCdnIdByWalrusId(walrusId1), cdnId1, "First record inverse should be correct");

assertEq(registry.getWalrusIdByCdnId(cdnId2), walrusId2, "Second record should be correct");
assertEq(registry.getCdnIdByWalrusId(walrusId2), cdnId2, "Second record inverse should be correct");
}

function testOverwriteRecord() public {
bytes32 cdnId = keccak256("cdn");
bytes32 walrusId1 = keccak256("walrus1");
bytes32 walrusId2 = keccak256("walrus2");

vm.startPrank(owner);

// Add initial mapping
registry.setRecord(cdnId, walrusId1);
assertEq(registry.getWalrusIdByCdnId(cdnId), walrusId1, "Initial record should be set");

// Overwrite with new mapping
registry.setRecord(cdnId, walrusId2);
assertEq(registry.getWalrusIdByCdnId(cdnId), walrusId2, "Record should be overwritten");

vm.stopPrank();
}
}