Skip to content
Merged
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Current adapter implementations include:

- `LegacyVRFAdapter.sol`
- `ChainlinkV25DirectFundingAdapter.sol`
- `SomniaNativeVRFAdapter.sol` — Somnia Reactivity-native, drand-mixed VRF
- `DevRandomnessAdapter.sol` — deterministic and **DEV/TEST ONLY**

See `docs/CHAIN_AGNOSTIC_ARCHITECTURE.md` for the architecture rules.
Expand All @@ -92,6 +93,7 @@ The frontend uses `frontendSnapshotV3()`, `claimRelic(bool)` and `equipOwnedReli
| Environment | Status | Scope |
| --- | --- | --- |
| RISE Testnet | Public beta | Current wallet-connected deployment and frontend integration. |
| Somnia Shannon Testnet | Adapter and deployment path test-covered | Native verifiable VRF requests use Somnia's coordinator-funded Reactivity/drand flow. No public Delveworn deployment is advertised until its address and live fulfillment are verified. |
| Local Anvil | Development only | Deterministic contract, relic, balance and request/callback testing through `DevRandomnessAdapter`. |
| Chainlink VRF v2.5 adapter | Implemented and test-covered | Adapter support exists, but no public deployment is presented as production-ready. |
| Other EVM networks | Architecture target | The core is designed for adapter-based deployments; these networks are not yet advertised as supported public deployments. |
Expand Down Expand Up @@ -189,6 +191,22 @@ bash scripts/dev-autofulfill.sh

The development adapter preserves the two-transaction request/callback lifecycle. It is operator-controlled and must never be used as a production or competitive randomness source.

## Somnia Shannon deployment

`SomniaNativeVRFAdapter` translates Delveworn's provider-neutral request and callback interface to Somnia's native `requestRandomWords` / `rawFulfillRandomWords` ABI. Requests always set `useVerifiableEntropy: true`. The default Shannon coordinator is `0x0834459256bbb8d2efee23dc6c3f1722266182dd`.

Deploy the adapter and a fresh Delveworn core with the native coordinator:

```bash
forge script script/DeploySomniaShannon.s.sol:DeploySomniaShannon \
--rpc-url https://dream-rpc.somnia.network \
--gas-limit 150000000 \
--broadcast \
--private-key "$SOMNIA_DEPLOYER_PRIVATE_KEY"
```

The high transaction gas limit accommodates Shannon's deployment gas accounting; unused gas is not charged. The script deliberately defaults to Somnia's maximum `2_500_000` callback gas and minimum `16`-block commit delay. Override them only within Somnia's documented bounds with `SOMNIA_VRF_CALLBACK_GAS_LIMIT` and `SOMNIA_VRF_COMMIT_DELAY_BLOCKS`. The native coordinator pays entropy-delivery costs; the adapter requires no subscription or prefunding.

## Deployment

Vercel is connected to this repository with `frontend` as its Root Directory. Changes merged to `main` trigger the production deployment. Configure deployment variables in Vercel rather than committing `.env.local`.
Expand Down
1 change: 1 addition & 0 deletions docs/CHAIN_AGNOSTIC_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Current adapters include:

- `LegacyVRFAdapter` for compatibility with the original VRF-style request/callback interface.
- `ChainlinkV25DirectFundingAdapter` for Chainlink VRF v2.5 wrapper direct funding with the chain's native token.
- `SomniaNativeVRFAdapter` for Somnia's Reactivity-native, drand-mixed request and callback interface.

## Deployment strategy

Expand Down
44 changes: 30 additions & 14 deletions script/DeploySomniaShannon.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,46 @@
pragma solidity ^0.8.24;

import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {Delveworn} from "../src/Delveworn.sol";
import {ChainlinkV25DirectFundingAdapter} from "../src/adapters/ChainlinkV25DirectFundingAdapter.sol";
import {SomniaNativeVRFAdapter} from "../src/adapters/SomniaNativeVRFAdapter.sol";

contract DeploySomniaShannon is Script {
address internal constant DEFAULT_VRF_WRAPPER = 0x763cC914d5CA79B04dC4787aC14CcAd780a16BD2;

function run() external returns (ChainlinkV25DirectFundingAdapter adapter, Delveworn dungeon) {
address wrapper = vm.envOr("SOMNIA_SHANNON_VRF_WRAPPER", DEFAULT_VRF_WRAPPER);
uint32 callbackGasLimit = uint32(vm.envOr("SOMNIA_VRF_CALLBACK_GAS_LIMIT", uint256(500_000)));
uint16 requestConfirmations = uint16(vm.envOr("SOMNIA_VRF_REQUEST_CONFIRMATIONS", uint256(3)));
uint256 prefundWei = vm.envOr("SOMNIA_VRF_PREFUND_WEI", uint256(0));
address internal constant DEFAULT_VRF_COORDINATOR = 0x0834459256bBB8D2EFEe23dc6C3F1722266182dD;
uint256 internal constant MAX_CALLBACK_GAS_LIMIT = 2_500_000;
uint256 internal constant MIN_COMMIT_DELAY_BLOCKS = 16;
uint256 internal constant MAX_COMMIT_DELAY_BLOCKS = 200;

function run() external returns (SomniaNativeVRFAdapter adapter, Delveworn dungeon) {
address coordinator = vm.envOr("SOMNIA_SHANNON_VRF_COORDINATOR", DEFAULT_VRF_COORDINATOR);
uint256 callbackGasLimitValue = vm.envOr("SOMNIA_VRF_CALLBACK_GAS_LIMIT", uint256(2_500_000));
uint256 commitDelayBlocksValue = vm.envOr("SOMNIA_VRF_COMMIT_DELAY_BLOCKS", uint256(16));

require(coordinator != address(0), "Invalid Somnia VRF coordinator");
require(
callbackGasLimitValue > 0 && callbackGasLimitValue <= MAX_CALLBACK_GAS_LIMIT, "Invalid callback gas limit"
);
require(
commitDelayBlocksValue >= MIN_COMMIT_DELAY_BLOCKS && commitDelayBlocksValue <= MAX_COMMIT_DELAY_BLOCKS,
"Invalid commit delay"
);

uint32 callbackGasLimit = uint32(callbackGasLimitValue);
uint16 commitDelayBlocks = uint16(commitDelayBlocksValue);

vm.startBroadcast();

adapter = new ChainlinkV25DirectFundingAdapter(wrapper, callbackGasLimit, requestConfirmations);
adapter = new SomniaNativeVRFAdapter(coordinator, callbackGasLimit, commitDelayBlocks);

dungeon = new Delveworn(address(adapter));
adapter.setConsumer(address(dungeon));

if (prefundWei > 0) {
(bool success,) = address(adapter).call{value: prefundWei}("");
require(success, "Adapter prefund failed");
}

vm.stopBroadcast();

console2.log("Somnia native VRF coordinator:", coordinator);
console2.log("Somnia native VRF adapter:", address(adapter));
console2.log("Delveworn:", address(dungeon));
console2.log("Callback gas limit:", callbackGasLimit);
console2.log("Commit delay blocks:", commitDelayBlocks);
}
}
90 changes: 90 additions & 0 deletions src/adapters/SomniaNativeVRFAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IRandomnessAdapter, IRandomnessConsumer} from "../interfaces/IRandomnessAdapter.sol";
import {ISomniaVRFCoordinator} from "../interfaces/ISomniaVRFCoordinator.sol";

/// @notice Provider adapter for Somnia's Reactivity-native, drand-mixed VRF.
/// @dev Requests always use verifiable entropy so the same adapter configuration is safe on
/// Shannon testnet and Somnia mainnet. The provider-neutral seed is intentionally ignored.
contract SomniaNativeVRFAdapter is IRandomnessAdapter {
uint32 public constant MAX_CALLBACK_GAS_LIMIT = 2_500_000;
uint16 public constant MIN_COMMIT_DELAY_BLOCKS = 16;
uint16 public constant MAX_COMMIT_DELAY_BLOCKS = 200;
uint32 public constant MAX_NUM_WORDS = 500;

address public immutable owner;
ISomniaVRFCoordinator public immutable coordinator;
uint32 public immutable callbackGasLimit;
uint16 public immutable commitDelayBlocks;

address public consumer;

mapping(uint256 => bool) public pendingRequests;

event ConsumerSet(address indexed consumer);
event AdapterRandomnessRequested(uint256 indexed requestId, address indexed consumer, uint32 numberCount);
event AdapterRandomnessFulfilled(uint256 indexed requestId, address indexed consumer);

error OnlyOwner();
error OnlyConsumer();
error OnlyCoordinator();
error ConsumerAlreadySet();
error InvalidAddress();
error InvalidConfig();
error InvalidNumberCount();
error UnknownRequest();

constructor(address coordinatorAddress, uint32 callbackGasLimit_, uint16 commitDelayBlocks_) {
if (coordinatorAddress == address(0) || coordinatorAddress.code.length == 0) revert InvalidAddress();
if (callbackGasLimit_ == 0 || callbackGasLimit_ > MAX_CALLBACK_GAS_LIMIT) revert InvalidConfig();
if (commitDelayBlocks_ < MIN_COMMIT_DELAY_BLOCKS || commitDelayBlocks_ > MAX_COMMIT_DELAY_BLOCKS) {
revert InvalidConfig();
}

owner = msg.sender;
coordinator = ISomniaVRFCoordinator(coordinatorAddress);
callbackGasLimit = callbackGasLimit_;
commitDelayBlocks = commitDelayBlocks_;
}

function setConsumer(address consumerAddress) external {
if (msg.sender != owner) revert OnlyOwner();
if (consumer != address(0)) revert ConsumerAlreadySet();
if (consumerAddress == address(0) || consumerAddress.code.length == 0) revert InvalidAddress();

consumer = consumerAddress;
emit ConsumerSet(consumerAddress);
}

function requestRandomNumbers(uint32 numNumbers, uint256) external override returns (uint256 requestId) {
if (msg.sender != consumer || consumer == address(0)) revert OnlyConsumer();
if (numNumbers == 0 || numNumbers > MAX_NUM_WORDS) revert InvalidNumberCount();

requestId = coordinator.requestRandomWords(
ISomniaVRFCoordinator.RandomWordsRequest({
callbackGasLimit: callbackGasLimit,
commitDelayBlocks: commitDelayBlocks,
numWords: numNumbers,
useVerifiableEntropy: true
})
);

if (requestId == 0 || pendingRequests[requestId]) revert UnknownRequest();
pendingRequests[requestId] = true;

emit AdapterRandomnessRequested(requestId, msg.sender, numNumbers);
}

/// @notice Somnia native VRF callback entrypoint.
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(coordinator)) revert OnlyCoordinator();
if (!pendingRequests[requestId]) revert UnknownRequest();

delete pendingRequests[requestId];

IRandomnessConsumer(consumer).rawFulfillRandomNumbers(requestId, randomWords);

emit AdapterRandomnessFulfilled(requestId, consumer);
}
}
14 changes: 14 additions & 0 deletions src/interfaces/ISomniaVRFCoordinator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Minimal interface for Somnia's Reactivity-native VRF coordinator.
interface ISomniaVRFCoordinator {
struct RandomWordsRequest {
uint32 callbackGasLimit;
uint16 commitDelayBlocks;
uint32 numWords;
bool useVerifiableEntropy;
}

function requestRandomWords(RandomWordsRequest calldata request) external returns (uint256 requestId);
}
Loading