Skip to content

Nerwo/nerwo-contracts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

369 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nerwo Platform: Smart contracts

Overview

The NerwoEscrow contract facilitates secure transactions between a client and a freelancerr. The contract holds funds on behalf of the client until the transaction is completed or a dispute arises. In case of disputes, an external arbitrator resolves the issue and determines the outcome.

The main features of the contract include:

  • Creating escrow
  • Making payments
  • Reimbursements
  • Raising disputes
  • Ruling

Gas and deployment costs

See GAS_COSTS.md for Forge commands, deployment gas estimates, and gwei-to-ETH conversion examples.

Operational policy

Token caps

changeTokenCap is an admin-only function that controls which tokens can fund escrow transactions and the maximum amount accepted per transaction. Unknown tokens have a cap of 0 and are rejected by default. Setting a token cap to 0 disables it; setting it to CAP_UNLIMITED enables it without a per-transaction amount limit.

Only ERC20 implementations that satisfy all of the following may be enabled with a non-zero cap:

  • Standard transfer/transferFrom semantics — the amount received by the recipient must equal the amount specified by the sender.
  • No fee-on-transfer behavior. The escrow re-reads the balance after transferFrom and rejects transactions that fall below MIN_AMOUNT after fees, but the sanity check is not a substitute for a clean token.
  • No rebasing or supply-elastic mechanics that would cause the escrowed balance to drift between createTransaction and payout.
  • No transfer hooks (ERC777 tokensReceived/tokensToSend, ERC1363 callbacks, or any custom callback that re-enters the caller).
  • No blacklist that the contract owner does not control or is not aware of. If the token can blacklist arbitrary addresses, payouts to a blacklisted party will fall through to pendingWithdrawals rather than reverting — the funds are recoverable but only once the address is unblocked.

In short: token caps are for plain, stateless ERC20 tokens. Anything exotic (fee-on-transfer, rebasing, hookable, upgradeable proxies the team does not control) must not be enabled.

Fee recipient

setFeeRecipientAndBasisPoint may set the fee recipient to a contract address. If that address cannot receive funds (rejecting receive(), blacklist on the relevant token, etc.) the fee is credited to pendingWithdrawals and the recipient can claim it later via claimWithdrawal. The rest of the payout flow is unaffected, so a broken fee recipient cannot brick the contract.

Arbitrator

The arbitrator is set at construction. The contract assumes the arbitrator returns rulings in [0, 2]; any out-of-range ruling is treated as a 50/50 split so a misbehaving arbitrator cannot lock escrowed funds. Mid-dispute price changes are tolerated: the arbitration cost is frozen on the first payArbitrationFee call.

Functions

createTransaction

createTransaction(bytes16 offerID, IERC20 token, uint256 amount, address freelancer)

Allows the client to create a new transaction by providing the ERC20 token, freelancer's address, the transaction amount, and the backend UUIDv7 offer ID as bytes16. The client must have approved the amount the ERC20 token transfer. The token must have a non-zero cap and amount must not exceed it.

pay

pay(uint256 transactionID)

Allows the client to pay the freelancer for the provided goods or services. The function checks whether the caller is the transaction client, whether the transaction has a valid status, and whether the amount is not already paid.

reimburse

reimburse(uint256 transactionID, uint256 amountReimbursed)

Allows the freelancer to reimburse the client if the goods or services cannot be fully provided. The function checks whether the caller is the transaction freelancer, whether the transaction has a valid status, and whether the amount is not already reimbursed.

payArbitrationFee(uint256 transactionID)

Allows the client or the freelancer to pay the arbitration fee to raise a dispute. The function verifies whether the caller is the transaction client or freelancer, whether the transaction has a valid status, and whether the correct arbitration fee has been paid before proceeding.

timeOut

timeOut(uint256 transactionID)

Allows the client or the freelancer to request a ruling in their favor if the other party fails to pay the arbitration fee within the specified timeout. The function checks whether the transaction has a valid status and whether the timeout has been reached before proceeding.

rule

function rule(uint256 disputeID, uint256 ruling) external

Receives and executes the final arbitrator ruling for a dispute. This function must be called by the configured arbitrator.

_executeRuling

_executeRuling(uint256 transactionID, uint256 ruling)

Internal function to execute a ruling of a dispute. It reimburses the arbitration fee to the winning party and updates the transaction status accordingly.

getTransaction

getTransaction(uint256 transactionID)

External function helper for frontend calls, it returns the transaction or raises an error if the transaction does not exist.

getArbitrationCost

function getArbitrationCost() external view returns (uint256)

Ask arbitrator for abitration cost.

Events

Payment

event Payment(uint256 indexed transactionID, address indexed client, IERC20 indexed token, uint256 amount)

Emitted when a payment is made. It provides the transaction ID, the ERC20 token address, the amount paid, and the address of the client.

Reimburse

event Reimburse(uint256 indexed transactionID, address indexed client, IERC20 indexed token, uint256 amount)

Emitted when a reimburse is made. It provides the transaction ID, the ERC20 token address, the amount paid, and the address of the client.

HasToPayFee

event HasToPayFee(uint256 indexed transactionID, address indexed party)

Emitted when a party has to pay an arbitration fee. It provides the transaction ID and the party that has to pay the fee.

DisputeCreated

event DisputeCreated(uint256 indexed transactionID, uint256 indexed disputeID, address indexed plaintiff)

Emitted when a dispute is created. It provides the transaction ID, the dispute ID and the first party opened the dispute.

TransactionCreated

event TransactionCreated(bytes16 indexed offerID, uint256 indexed transactionID, address indexed client, address freelancer, IERC20 token, uint256 amount)

Emitted when a new transaction is created (the Escrow). It provides all needed informations, including the backend UUIDv7 offer ID used to correlate the funded offer.

FeeRecipientPayment

event FeeRecipientPayment(uint256 indexed transactionID, IERC20 indexed token, uint256 feeAmount)

Emitted when a fee payment is made to the fee recipient. It provides the transaction ID, the ERC20 token address and the fee amount.

FeeRecipientChanged

event FeeRecipientChanged(address indexed newFeeRecipient, uint256 newBasisPoint)

Emitted when fee recipent is changed (admin function). It provides the old and new fee recipient.

TokenCapChanged

event TokenCapChanged(IERC20 indexed token, uint256 cap)

Emitted when a token transaction cap is changed (admin function). A cap of 0 disables the token; CAP_UNLIMITED removes the amount limit.

SendFailed (SafeTransfer)

event SendFailed(address indexed recipient, address indexed token, uint256 amount)

Emitted when sending funds fails. It the address of the ERC20 is 0 it refers to the native token (used for arbitration).

Custom Errors

NullAddress

error NullAddress()

Emitted when a required address is instead null.

NoTimeout

error NoTimeout()

Emitted when a timeOut operation is attempted before the feeTimeout period has elapsed.

InvalidCaller

error InvalidCaller()

Emitted when the function caller is not the expected one.

InvalidStatus

error InvalidStatus()

Emitted when the status of a transaction does not allow a certain operation to be performed.

InvalidAmount

error InvalidAmount()

Emitted when the function is called with an invalid amount.

InvalidTransaction

error InvalidTransaction()

Emitted when the requested transactionID does not refer to a valid transaction.

InvalidToken

error InvalidToken()

InvalidFeeBasisPoint

error InvalidFeeBasisPoint()

NotRuled

error NotRuled()

Emitted when a ruling is queried or attempted to be executed for a dispute that has not yet been ruled

Enumerations

Status

enum Status {NoDispute, Waitingclient, Waitingfreelancer, DisputeCreated, Resolved}

Represents the current status of a transaction:

  • NoDispute: The transaction has no dispute.
  • WaitingClient: The transaction is waiting for the client to pay the arbitration fee.
  • WaitingFreelancer: The transaction is waiting for the freelancer to pay the arbitration fee.
  • DisputeCreated: A dispute has been created for the transaction.
  • Resolved: The transaction has been resolved, either by a ruling or by the parties coming to an agreement.

Parties

The arties involved in a transaction are:

  • client: The party sending the payment.
  • freelancer: The party receiving the payment.

This contract allows two parties, a client and a freelancer, to engage in transactions with the possibility of raising disputes and having them resolved by an arbitrator. The contract handles payments, fee calculations, dispute creation, and evidence submission. In the case of a dispute, the arbitrator is responsible for providing a ruling, which will result in either the client being reimbursed or the freelancer being paid. The contract also enforces timeouts for various actions, such as paying arbitration fees and executing transactions.

Escrow flow

flowchart LR
    A(createTransaction) -->B{NoDispute}
    B -->|pay| C[Freelancer]
    B -->|reimburse| D[Client]
    B -->|payArbitrationFee| E{WaitingClient}
    B -->|payArbitrationFee| F{WaitingFreelancer}
    E -->|payArbitrationFee| G{DisputeCreated}
    F -->|payArbitrationFee| G{DisputeCreated}
    E -->|timeOut| C[Freelancer]
    F -->|timeOut| D[Client]
    G -->|rule = 1| D[Client]
    G -->|rule = 2| C[Freelancer]
    G -->|rule = 0| H[Split Payment]
Loading

About

Nerwo Platform: Smart contracts

Resources

License

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors