Smart Contracts
ConfidentialDealRoom
The core contract that stores encrypted deal handles, manages deal state, and coordinates Nox ACL viewer access.Arbitrum Sepolia
ConfidentialDealRoom
0x809Ad39e12360C3073B2AB2913D238287DB540c5Function
createDeal
createDeal(bytes32,bytes,bytes32,bytes) returns (uint256)| Param | Type | Description |
|---|---|---|
| caller | Founder | Expected caller role: Founder |
Function
submitScore
submitScore(uint256,bytes32,bytes)| Param | Type | Description |
|---|---|---|
| caller | Founder | Expected caller role: Founder |
Function
counterDeal
counterDeal(uint256,bytes32,bytes)| Param | Type | Description |
|---|---|---|
| caller | Investor | Expected caller role: Investor |
Function
acceptDeal
acceptDeal(uint256)| Param | Type | Description |
|---|---|---|
| caller | Founder | Expected caller role: Founder |
Function
closeDeal
closeDeal(uint256)| Param | Type | Description |
|---|---|---|
| caller | Investor | Expected caller role: Investor |
Function
rejectDeal
rejectDeal(uint256)| Param | Type | Description |
|---|---|---|
| caller | Founder | Expected caller role: Founder |
Solidity source
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {INoxCompute} from "@iexec-nox/nox-protocol-contracts/contracts/interfaces/INoxCompute.sol";
import {TEEType} from "@iexec-nox/nox-protocol-contracts/contracts/shared/TypeUtils.sol";
import {euint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol";
import {IERC7984} from "@iexec-nox/nox-confidential-contracts/contracts/interfaces/IERC7984.sol";
contract ConfidentialDealRoom {
enum DealStatus { Open, Countered, Accepted, Closed, Rejected }
struct Deal {
address founder;
address investor;
bytes32 encryptedValuation;
bytes32 encryptedEquityPct;
bytes32 encryptedRiskScore;
bytes32 encryptedFundAmount;
DealStatus status;
uint256 createdAt;
}
error DealNotFound(uint256 dealId);
error Unauthorized();
error InvalidDealState(DealStatus current);
error FounderCannotCounter();
error MissingFundAmount();
error NoxValidateInputProofFailed(bytes data);
error NoxAddViewerFailed(string step, bytes data);
error NoxAllowFailed(string step, bytes data);
event DealCreated(uint256 indexed dealId, address indexed founder, bytes32 valuationHandle, bytes32 equityHandle);
event ScoreSubmitted(uint256 indexed dealId, bytes32 scoreHandle);
event DealCountered(uint256 indexed dealId, address indexed investor, bytes32 fundAmountHandle);
event DealAccepted(uint256 indexed dealId);
event DealClosed(uint256 indexed dealId, bytes32 transferredAmountHandle);
event DealRejected(uint256 indexed dealId);
INoxCompute public immutable nox;
IERC7984 public immutable confidentialToken;
mapping(uint256 dealId => Deal) public deals;
uint256 public dealCount;
constructor(address noxAddress, address confidentialTokenAddress) {
nox = INoxCompute(noxAddress);
confidentialToken = IERC7984(confidentialTokenAddress);
}
function createDeal(bytes32 valuationHandle, bytes calldata valuationProof, bytes32 equityHandle, bytes calldata equityProof) external returns (uint256 dealId) {
nox.validateInputProof(valuationHandle, msg.sender, valuationProof, TEEType.Uint256);
nox.validateInputProof(equityHandle, msg.sender, equityProof, TEEType.Uint256);
dealId = dealCount;
dealCount = dealCount + 1;
deals[dealId] = Deal(msg.sender, address(0), valuationHandle, equityHandle, bytes32(0), bytes32(0), DealStatus.Open, block.timestamp);
try nox.allow(valuationHandle, address(this)) {} catch (bytes memory reason) { revert NoxAllowFailed("valuation->dealRoom", reason); }
try nox.allow(equityHandle, address(this)) {} catch (bytes memory reason) { revert NoxAllowFailed("equity->dealRoom", reason); }
nox.addViewer(valuationHandle, msg.sender);
nox.addViewer(equityHandle, msg.sender);
emit DealCreated(dealId, msg.sender, valuationHandle, equityHandle);
}
function submitScore(uint256 dealId, bytes32 scoreHandle, bytes calldata scoreProof) external {
Deal storage deal = _requireDeal(dealId);
if (msg.sender != deal.founder) revert Unauthorized();
if (deal.status == DealStatus.Closed || deal.status == DealStatus.Rejected) revert InvalidDealState(deal.status);
nox.validateInputProof(scoreHandle, msg.sender, scoreProof, TEEType.Uint256);
deal.encryptedRiskScore = scoreHandle;
try nox.allow(scoreHandle, address(this)) {} catch (bytes memory reason) { revert NoxAllowFailed("score->dealRoom", reason); }
nox.addViewer(scoreHandle, deal.founder);
if (deal.investor != address(0)) nox.addViewer(scoreHandle, deal.investor);
emit ScoreSubmitted(dealId, scoreHandle);
}
function counterDeal(uint256 dealId, bytes32 fundAmountHandle, bytes calldata fundAmountProof) external {
Deal storage deal = _requireDeal(dealId);
if (deal.status != DealStatus.Open) revert InvalidDealState(deal.status);
if (msg.sender == deal.founder) revert FounderCannotCounter();
try nox.validateInputProof(fundAmountHandle, msg.sender, fundAmountProof, TEEType.Uint256) {} catch (bytes memory reason) { revert NoxValidateInputProofFailed(reason); }
deal.investor = msg.sender;
deal.encryptedFundAmount = fundAmountHandle;
deal.status = DealStatus.Countered;
try nox.allow(fundAmountHandle, address(this)) {} catch (bytes memory reason) { revert NoxAllowFailed("fundAmount->dealRoom", reason); }
try nox.addViewer(fundAmountHandle, msg.sender) {} catch (bytes memory reason) { revert NoxAddViewerFailed("fundAmount->investor", reason); }
try nox.addViewer(fundAmountHandle, deal.founder) {} catch (bytes memory reason) { revert NoxAddViewerFailed("fundAmount->founder", reason); }
try nox.addViewer(deal.encryptedValuation, msg.sender) {} catch (bytes memory reason) { revert NoxAddViewerFailed("valuation->investor", reason); }
try nox.addViewer(deal.encryptedEquityPct, msg.sender) {} catch (bytes memory reason) { revert NoxAddViewerFailed("equity->investor", reason); }
if (deal.encryptedRiskScore != bytes32(0)) {
try nox.addViewer(deal.encryptedRiskScore, msg.sender) {} catch (bytes memory reason) { revert NoxAddViewerFailed("score->investor", reason); }
}
emit DealCountered(dealId, msg.sender, fundAmountHandle);
}
function acceptDeal(uint256 dealId) external {
Deal storage deal = _requireDeal(dealId);
if (msg.sender != deal.founder) revert Unauthorized();
if (deal.status != DealStatus.Countered) revert InvalidDealState(deal.status);
deal.status = DealStatus.Accepted;
emit DealAccepted(dealId);
}
function closeDeal(uint256 dealId) external {
Deal storage deal = _requireDeal(dealId);
if (msg.sender != deal.investor) revert Unauthorized();
if (deal.status != DealStatus.Accepted) revert InvalidDealState(deal.status);
if (deal.encryptedFundAmount == bytes32(0)) revert MissingFundAmount();
nox.allow(deal.encryptedFundAmount, address(confidentialToken));
euint256 transferred = confidentialToken.confidentialTransferFrom(deal.investor, deal.founder, euint256.wrap(deal.encryptedFundAmount));
deal.status = DealStatus.Closed;
emit DealClosed(dealId, euint256.unwrap(transferred));
}
function rejectDeal(uint256 dealId) external {
Deal storage deal = _requireDeal(dealId);
if (msg.sender != deal.founder) revert Unauthorized();
if (deal.status != DealStatus.Open && deal.status != DealStatus.Countered) revert InvalidDealState(deal.status);
deal.status = DealStatus.Rejected;
emit DealRejected(dealId);
}
function _requireDeal(uint256 dealId) internal view returns (Deal storage deal) {
if (dealId >= dealCount) revert DealNotFound(dealId);
deal = deals[dealId];
}
}