Skip to content

gitbondhq/escrow

Repository files navigation

GitBond Escrow Contract

GitBond Escrow is an upgradeable ERC-20 escrow contract for refundable contribution deposits. It supports both the standard allowance-based deposit flow and a permit-backed single-transaction flow for self-submitted deposits.

Core Contract Surface

  • createEscrow(key, counterparty, beneficiary, token, amount)
    • Pulls amount from msg.sender.
    • Keeps the approval-based path for tokens that do not use permit().
  • createEscrowWithPermit(key, payer, counterparty, beneficiary, token, amount, permit_)
    • Requires msg.sender == payer.
    • Validates GitBond-specific inputs before calling the token.
    • Calls IERC20Permit(token).permit(payer, address(this), amount, ...).
    • Pulls amount from payer in the same transaction.
    • Preferred path whenever the token supports permit().
  • refundEscrow(key), slashEscrow(key), and claim(token)
    • refundEscrow(key) can be called by the owner, the escrow counterparty, or a refund delegate elected by that counterparty.
    • slashEscrow(key) can be called by the owner, the escrow counterparty, or a slash delegate elected by that counterparty.
    • Refunds credit the beneficiary claim balance for that token; slashes credit the counterparty and treasury claim balances for that token.
    • claim(token) lets the caller withdraw their full claim balance for that token.
  • addRefundDelegate(delegate), removeRefundDelegate(delegate), addSlashDelegate(delegate), removeSlashDelegate(delegate)
    • Lets each counterparty manage its own settlement delegates.
  • setCounterparty(key, newCounterparty)
    • Owner-only update to the slash destination for an active escrow.
  • claimableAmount(claimant, token)
    • Returns the current aggregate claim balance for that claimant and token.
  • treasuryClaimableAmount(token)
    • Returns the current aggregate claim balance for the treasury and token.
  • isRefundDelegate(counterparty, delegate) / isSlashDelegate(counterparty, delegate)
    • Returns whether a delegate is currently authorized by that counterparty.
  • pause() / unpause()
    • Owner-only circuit breaker for user-facing flows.

Both creation entrypoints reuse the same internal _createEscrowFrom(...) path, so validation, accounting, token transfer, and event emission stay identical. All token movements use standard ERC-20 transfers:

  • escrow creation uses transferFrom(...)
  • claim payouts use transfer(...)

Permit Integration

createEscrowWithPermit(...) uses the token's own audited permit() implementation. GitBond does not implement custom EIP-712 domain logic or signature recovery. If the token does not support permit() or the provided permit parameters are wrong, createEscrowWithPermit(...) reverts with the token's native error behavior.

PermitParams is:

struct PermitParams {
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
}

Recommended client behavior:

  • Prefer createEscrowWithPermit(...) when the token and network support permit().
  • Use createEscrow(...) as the compatibility path when permit() is unavailable.
  • Call createEscrowWithPermit(...) directly from the payer account; relayed permit submissions are intentionally rejected.

Example permit-backed call:

GitBondEscrow.PermitParams memory permit_ = GitBondEscrow.PermitParams({
    deadline: deadline,
    v: v,
    r: r,
    s: s
});

escrow.createEscrowWithPermit(
    key,
    payer,
    counterparty,
    beneficiary,
    token,
    amount,
    permit_
);

Fallback approval-based call:

IERC20(token).approve(address(escrow), amount);
escrow.createEscrow(key, counterparty, beneficiary, token, amount);

Configuration / Admin Controls

  • setTreasury(address) updates the treasury address.
  • setTokenWhitelist(address, bool) whitelists or un-whitelists six-decimal tokens.
  • removeTokenFromWhitelist(address) removes a token from the whitelist.
  • setPlatformCut(uint256) sets the slash treasury cut in basis points, capped at 3000.
  • setMinEscrowAmount(uint256) sets the deposit floor and must remain non-zero.

All admin methods are onlyOwner.

Deployment note:

  • The contract does not whitelist tokens automatically at initialization unless you pass them in initialize (currently via deploy scripts).
  • After deployment in standard mode, the owner must explicitly whitelist each supported ERC-20.

Deployment mode:

  • The deploy script uses OpenZeppelin Foundry Upgrades Upgrades.deployUUPSProxy(...).
  • The library validates the implementation, deploys the implementation contract, then deploys the UUPS proxy.

Whitelist trust assumption:

  • The whitelist is a trusted-token allowlist, not a complete defense against arbitrary ERC-20 implementations.
  • GitBond assumes whitelisted tokens implement transfer, transferFrom, balance accounting, and permit semantics honestly when those paths are used.
  • The on-chain whitelist check enforces six decimals, but safe operation still depends on the owner only whitelisting known-good stablecoins.

Storage / Upgrade Safety

  • Storage is isolated in GitBondEscrowStorage using an ERC-7201-style namespaced storage slot.
  • This branch removes an unused pre-launch escrow field, so treat it as the version to deploy rather than an in-place storage-compatible upgrade from earlier drafts.

Key Constants

  • USD_TOKEN_DECIMALS = 6
  • DEFAULT_MIN_ESCROW_AMOUNT = 20_000

Deployment & Upgrade

Initial deployment uses script/GitBondEscrow.s.sol:GitBondEscrowScript.

Required environment variables:

  • ESCROW_TREASURY
  • ESCROW_OWNER
  • Optional: ESCROW_IMPLEMENTATION with default GitBondEscrow.sol

Upgrade uses script/GitBondEscrow.s.sol:UpgradeGitBondEscrowScript.

Required environment variables:

  • ESCROW_PROXY
  • Optional: ESCROW_IMPLEMENTATION with default GitBondEscrow.sol

The upgrade script runs OpenZeppelin upgrade validation before upgrading the proxy.

Local Development

Build:

forge build

Test:

forge test

Format:

forge fmt

Project Files

  • Contract: src/GitBondEscrow.sol
  • Unit tests: test/GitBondEscrow.t.sol
  • Invariant tests: test/GitBondEscrow.invariant.t.sol
  • Deployment and upgrade scripts: script/GitBondEscrow.s.sol
  • Operational scripts: script/CreateEscrow.s.sol, script/RefundEscrow.s.sol, script/SlashEscrow.s.sol, script/ClaimEscrow.s.sol
  • Product context: PLAN.md

About

Solidity escrow contract for GitBond on Tempo. UUPS upgradeable, ERC-20 permit support, refund and slash settlement.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors