# ResearchEscrow — testnet study, one milestone

This is an **unaudited prototype**, not audited mainnet code and not a legally sufficient contract or scientific validation mechanism. It permits deployment only on chain IDs **31337** and **11155111**. No live wallet, public RPC, Sepolia deployment, transaction with real funds, or connection to the Site was used. The tests deploy only into an ephemeral in-process EVM with simulated balances.

## What was built and checked

- `ResearchEscrow.sol`: native-ETH escrow for exactly one milestone; no dependencies, upgrade path, proxy, delegatecall or loops.
- `ResearchEscrow.json`: compiler metadata, ABI, creation bytecode and runtime bytecode template. The runtime template contains immutable placeholders; it is not the final bytecode of any deployed instance.
- `ResearchEscrow.abi.json`: standalone ABI.
- `compile-report.json`: compiler, source/bytecode hashes and compilation diagnostics.
- `compiler-input.json`: full Standard JSON compilation input.
- `test/escrow.test.mjs`: 13 local EVM tests; adversarial recipients live in `test/Receivers.sol`.
- `test-report.json`: results of the executed local tests, including environment and scope.
- `integration.json`: constructor, methods and states for an integration consumer. It contains no deployment address.

Compiler: **0.8.37+commit.f401782d.Emscripten.clang**, fixed by the exact Solidity pragma and npm dependency. Settings: optimizer enabled, 200 runs, legacy pipeline (`viaIR: false`), EVM target `paris`, metadata hash `ipfs`. Creation bytecode: 4,933 bytes; runtime template: 4,033 bytes. Solidity reported no warnings for `ResearchEscrow.sol`; the single warning is deliberate use of deprecated `selfdestruct` in the adversarial forced-ETH test fixture.

Reproduce locally with Node.js and npm:

```sh
npm ci --ignore-scripts --no-audit --no-fund
npm run compile
npm test
```

Pinned development dependencies: solc 0.8.37, ethers 6.16.0, Ganache 7.9.2; the lockfile fixes the resolved dependency tree. These are local test tools, not production services. On the tested Node build Ganache could not load its optional µWS native binding and used its JavaScript fallback. No server or listening RPC port is started by the tests.

## Constructor

```solidity
constructor(
    address client_,
    address researcher_,
    address arbiter_,
    bytes32 termsHash_,
    uint256 amountWei_,
    uint64 submissionDeadline_
)
```

The three addresses must be nonzero and pairwise distinct. The caller must equal `client_`. The amount and terms commitment must be nonzero, and the deadline must be in the future. Constructor calls are nonpayable; funding is a separate transaction.

All six values are immutable. A different role, price, deadline or term set requires a new escrow. Amounts are integer **wei**, and deadlines are Unix **seconds**, interpreted using the included block's timestamp. The chain-ID check prevents ordinary deployment on chain 1; it cannot certify a network's identity or stop somebody from editing/copying this prototype.

A deployment only records configured addresses; it does not establish researcher/arbiter identity, their independent consent, institutional affiliation or agreement to enforceable legal terms. Those steps must be handled separately before any future funding.

## State and caller contract

| State | Number | Allowed action |
|---|---:|---|
| `AwaitingDeposit` | 0 | Client calls `deposit()` before the deadline with exactly `amountWei`. |
| `Funded` | 1 | Researcher submits one evidence commitment by the deadline. Either party can open a dispute by that deadline. With neither submission nor dispute, the client can refund after the deadline. |
| `Submitted` | 2 | Client accepts the exact commitment, or either party opens a dispute. Deadline expiry cannot refund this state. |
| `Disputed` | 3 | Only the immutable arbiter can award the whole principal to the client or researcher. Passing time does not override the dispute. |
| `Resolved` | 4 | Only the recorded beneficiary can withdraw the credited principal. Settlement cannot be reopened. |

`resolution()` is `None=0`, `ClientAcceptance=1`, `MissedDeadline=2`, or `Arbitration=3`. A resolved escrow is **credited**, not necessarily paid: only a successful `withdraw` and its `Withdrawn` event establish an on-chain transfer. After withdrawal the state remains `Resolved` and `withdrawable()` is zero.

| Method | Caller | Value and semantics |
|---|---|---|
| `deposit()` | Client | Payable; exact principal, one time; `block.timestamp < submissionDeadline`. |
| `submitEvidence(bytes32)` | Researcher | Nonzero commitment, once; `block.timestamp <= submissionDeadline`. No file or private text is uploaded by this method. |
| `accept(bytes32 expectedEvidenceHash)` | Client | Expected hash must match; credits the researcher without an external payment call. |
| `openDispute(bytes32 disputeHash)` | Either party | Nonzero reason commitment; freezes settlement. Submitted work can be disputed after its deadline. |
| `resolve(address beneficiary, bytes32 decisionHash)` | Arbiter | Nonzero decision commitment; beneficiary must be client or researcher; no splitting or arbiter fee. |
| `refundAfterDeadline()` | Client | Only `Funded`, strictly after deadline. Impossible after evidence submission or while disputed. |
| `withdraw(address payable recipient)` | Recorded beneficiary | Pull payment, once. A recipient may be a different address chosen by the beneficiary; zero and this escrow are refused. Failed recipient calls restore the credit. |

The only external call is native-ETH delivery in `withdraw`. It follows checks-effects-interactions and uses a withdrawal guard. Regular direct ETH transfers and unknown method calls revert. Forced ETH can still arrive; it never changes the entitlement and any surplus remains stuck because this prototype has no sweep authority. Do not transfer ERC-20 tokens: no token handling or rescue exists.

## Binding a scientific milestone

Suggested off-chain convention: hash the **exact bytes** of a separately retained UTF-8 terms manifest with `keccak256`. Record the hash algorithm and byte format, and retain the original file; do not silently reserialize, normalize or substitute SHA3-256 for Ethereum Keccak-256. The contract stores an opaque `bytes32` and cannot enforce the manifest convention.

The manifest should identify the intended chain, the three addresses, amount as an integer wei string, deadline in seconds, one milestone, objective deliverables, acceptance procedure, arbitration scope and references to the applicable off-chain agreement. Include a fresh random salt if low-entropy/private text should not be easy to guess from its hash. A salt reduces guessing risk; hashes are not encryption. Keep secrets and personal data off-chain.

Evidence, dispute and decision commitments can use separate domain-tagged manifests. Evidence should bind this escrow's address and chain, the immutable terms hash, the intended milestone, exact public/private artifact digests and the method/version needed for review. Only the hash is submitted. Store and share the actual evidence through a separately authorized channel. A nonzero hash by itself proves neither delivery, availability, authorship nor scientific correctness.

A future UI must read and verify the selected chain, actual escrow address, immutable getters, state and precise amount before requesting a wallet signature. It must distinguish `Submitted`, `Resolved/credited` and `Withdrawn`, and reconcile receipts with canonical chain state and a stated confirmation policy. Do not have an LLM, Kaska/n8n job, or a numeric solver automatically trigger acceptance or sign a transaction. This artifact contains no wallet integration, signing service, deploy script, RPC credential or live contract address.

## Material limitations

One milestone, one currency, one submission and an all-or-nothing arbitration outcome. No revision/appeal, partial payment, fee, automatic acceptance, automatic payment on timeout, emergency override, key rotation, cancellation by mutual signatures, or arbitration replacement is implemented. A missing or malicious arbiter can leave a disputed deposit locked indefinitely or award it to either authorized party. A researcher can submit an unhelpful hash and prevent deadline refund; this is handled only by the agreed arbitration process. Losing a party's key can prevent its withdrawal.

The submission deadline is not a promised wall-clock processing time or review deadline: transaction inclusion and order determine which valid transition occurs first. Acceptance is terminal, even if the credited principal has not yet been withdrawn. Arbitrator resolution is also terminal. Tests do not replace an audit, formal verification, economic review, privacy assessment, legal agreement, or deployment-specific review. No claim of mainnet readiness is made.

## Primary sources checked

- [Solidity 0.8.37 release, 10 September 2026](https://www.soliditylang.org/blog/2026/09/10/solidity-0.8.37-release-announcement/): compiler release selection; full build version is checked during compilation.
- [Solidity installation documentation](https://docs.soliditylang.org/en/v0.8.37/installing-solidity.html): official compiler installation reference.
- [Solidity security considerations](https://docs.soliditylang.org/en/v0.8.37/security-considerations.html): checks-effects-interactions, reentrancy, public state, gas/loops and forced Ether.
- [Solidity withdrawal pattern](https://docs.soliditylang.org/en/v0.8.37/common-patterns.html#withdrawal-from-contracts): credit first, withdraw separately; a recipient failure should not block settlement.
- [Solidity block/transaction variables](https://docs.soliditylang.org/en/v0.8.37/units-and-global-variables.html#block-and-transaction-properties): chain ID, sender, wei value and block timestamp semantics.
