> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Payroll by weight

> One transaction on Base or Ethereum that splits a USDC budget among a list of payees by weight, gives the rounding remainder to the last payee, and returns what each received.

This program divides a USDC budget among recipients according to their weights. It assigns any rounding remainder to the last recipient so that the payments total exactly the budget. A `budget` of 0 uses all USDC held by the Pot.

Before paying, it checks the recipients and weights. It rejects zero addresses, the Pot itself, duplicate recipients, weights that sum to zero and an unfunded budget. It also verifies the amount each recipient receives.

**Target chains:** Base and Ethereum. The same source compiles for both; the recorded simulations use a Base fork.

## Prerequisites

Install the [Sauce CLI](https://docs.eco.com/sauce-sdk/installation.md) to compile and simulate. To send the transaction, configure a wallet with funds for gas and the Kitchen fee. Provide the required tokens in your Pot once it is deployed, or through `--fund`; see [funding](https://docs.eco.com/sauce-sdk/cli/funding.md).

## Program

`pot` is the Pot the program runs on, available without declaring it; `USDC.balanceOf(pot)` reads its USDC balance ([Names](https://docs.eco.com/sauce/names.md#balances)).

```ts payroll-by-weight.sauce.ts theme={null}
// Pays a payroll in USDC from the Pot: `budget`, or all the USDC the Pot holds when `budget` is 0,
// split among `payees` by weight. Each payee gets budget × weight ÷ the sum of the weights,
// rounded down, and the last payee also gets the rounding remainder, so the payroll spends exactly
// the budget. Every payee is checked before anything moves: an empty list, a zero address, the
// Pot itself, a payee listed twice, weights that sum to 0 or a budget the Pot does not hold
// reverts the cook. Each payment is measured at its payee, and a payee that receives a different
// amount reverts the cook too. Returns what each payee received, in order.
//
//   sauce simulate payroll-by-weight.sauce.ts --chain <chain> --owner <address> \
//     --assume-balance USDC:<whole-tokens> --args <payees> <budget>
//   sauce run payroll-by-weight.sauce.ts --chain <chain> --wallet <name> --args <payees> <budget>
//
//   payees: { to: Address; weight: Uint256 }[]  who is paid and their weight, as a JSON array of
//                                               records: '[{"to":"<payee>","weight":3}]'
//   budget: Uint256                             the USDC paid in all, in its base units (6
//                                               decimals): 6000000000 is 6,000 USDC; 0 pays all
//                                               the USDC the Pot holds
//
// On Base, 100 USDC to three payees at 3:2:1:
//
//   sauce run payroll-by-weight.sauce.ts --chain base --wallet me --args \
//     '[{"to":"<payee1>","weight":3},
//       {"to":"<payee2>","weight":2},
//       {"to":"<payee3>","weight":1}]' 100000000
function main(payees: { to: Address; weight: Uint256 }[], budget: Uint256): Uint256[] {
  const count = payees.length;
  require(count > 0, "no payees");

  // Check every payee before any USDC moves.
  let totalWeight = 0;
  for (let i = 0; i < count; i++) {
    const to = payees[i].to;
    require(to != 0 && to != pot, "a payee is the zero address or the Pot");
    for (let j = 0; j < i; j++) {
      require(payees[j].to != to, "a payee is listed twice");
    }
    totalWeight += payees[i].weight;
  }
  require(totalWeight > 0, "the weights sum to 0");

  const held = USDC.balanceOf(pot);
  const spend = budget == 0 ? held : budget;
  require(spend > 0, "nothing to pay");
  require(held >= spend, "the budget is not in the Pot");

  const paid: Uint256[] = new Array(count);
  let left = spend;
  for (let i = 0; i < count; i++) {
    const to = payees[i].to;
    // Each share rounds down; the last payee takes what is left, the remainder included.
    const share = i == count - 1 ? left : Math.mulDiv(spend, payees[i].weight, totalWeight);
    left -= share;
    if (share > 0) {
      const before = USDC.balanceOf(to);
      USDC.transfer(to, share);
      paid[i] = USDC.balanceOf(to) - before;
      require(paid[i] == share, "a payee received a different amount");
    }
  }
  return paid;
}
```

## How it works

* `payees` is an array of records, `{ to: Address; weight: Uint256 }[]`, passed in `--args` as a JSON array of objects with the field names as keys.
* Validation is a separate first loop. The duplicate check compares each payee with every earlier one, so its cost grows with the square of the list length; it runs before the first transfer, so a bad list moves nothing.
* `Math.mulDiv(spend, weight, totalWeight)` computes each share as a full-precision product divided by the sum, rounded down. The last payee gets `left` instead, which absorbs every rounding loss: in the result below, 16,666,667 rather than 16,666,666 base units.
* `main` returns `Uint256[]`, one measured amount per payee in input order.

## Run the example

**1. Compile**

```bash theme={null}
sauce compile payroll-by-weight.sauce.ts --chain base
```

Replace `base` with `ethereum`. Each compile is 911 bytes, but the bytecode differs, because each carries its chain's USDC address.

**2. Simulate**

```bash theme={null}
sauce simulate payroll-by-weight.sauce.ts --chain base --owner <your-address> \
  --assume-balance USDC:150 \
  --args '[{"to":"<payee1>","weight":3},{"to":"<payee2>","weight":2},{"to":"<payee3>","weight":1}]' \
  100000000
```

`--assume-balance USDC:150` gives the Pot 150 USDC for this simulation only; the budget is 100 USDC at weights 3:2:1. `<payee1>`, `<payee2>` and `<payee3>` are three different addresses: one address listed twice reverts with "a payee is listed twice". With `0` as the budget, the program pays out all 150.

**3. Send**

```bash theme={null}
sauce run payroll-by-weight.sauce.ts --chain base --wallet <name> --fund USDC:100 \
  --args '[{"to":"<payee1>","weight":3},{"to":"<payee2>","weight":2},{"to":"<payee3>","weight":1}]' \
  100000000
```

`sauce tx` prints the unsigned transaction instead, for your own signer:

```bash theme={null}
sauce tx payroll-by-weight.sauce.ts --chain base --owner <your-address> --fund USDC:100 \
  --args '[{"to":"<payee1>","weight":3},{"to":"<payee2>","weight":2},{"to":"<payee3>","weight":1}]' \
  100000000
```

## Simulation result

Simulated on a local fork of Base at block 53159700 with the current contracts, with the command above, for `0x7099…79C8`, `0x3C44…93BC` and `0x90F7…b906`. Nothing was signed or sent.

```text theme={null}
The cook succeeds from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266: gas used 262952 for the cook, after the Pot's deploy (1. below), returns 0x0000000000000000000000000000000000000000000000000000000002faf0800000000000000000000000000000000000000000000000000000000001fca0550000000000000000000000000000000000000000000000000000000000fe502b
main returns Uint256[]: [50000000, 33333333, 16666667]
Left in the Pot, not in the owner's wallet:
  50 USDC (it held 150 USDC before)
...
  2. the cook, from the owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, gas used 262952
Assumed for this simulation only, by state override:
  the Pot holds 150 USDC: --assume-balance (its balance word 0xcb5320fe60abad81834e0e994e8a7a8eb33991917b05a5c0498fe1c0373a2087: Solidity mapping at slot 9)
What moved, in order:
...
  [3] Pot                -> 0x7099…79C8          50 USDC
  [4] Pot                -> 0x3C44…93BC          33.333333 USDC
  [5] Pot                -> 0x90F7…b906          16.666667 USDC
Net balance changes:
  0xDcFCc75A3C9a0EC798CAf81B6C84D4c5d5988c49 (the Pot): -100 USDC
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8: +50 USDC
  0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC: +33.333333 USDC
  0x90F79bf6EB2c4f870365E785982E1f101E93b906: +16.666667 USDC
  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (owner): -0.00000374478538635 ETH
```

100 USDC at 3:2:1 gives 50 and 33.333333 USDC by rounding down; the last payee received the remaining 16.666667 USDC, so the Pot spent exactly 100 USDC.

On the same fork, the guard, with one payee listed twice (`0x7099…79C8` in the run):

```bash theme={null}
sauce simulate payroll-by-weight.sauce.ts --chain base --owner <your-address> \
  --assume-balance USDC:150 \
  --args '[{"to":"<payee>","weight":3},{"to":"<payee>","weight":1}]' 100000000 \
  --expect-revert "a payee is listed twice"
```

```text theme={null}
As expected, the cook reverts: the program threw "a payee is listed twice"
The cook reverts: the program threw "a payee is listed twice"
```

The check in the first loop reverted the cook before any transfer.

## Next steps

* [Pay a list of recipients](https://docs.eco.com/quickstarts/sauce/pay-list.md): equal amounts with a cap
* [Swap, then pay an invoice](https://docs.eco.com/quickstarts/sauce/swap-then-pay.md): fund the payment from WETH in the same transaction
* [`sauce simulate`](https://docs.eco.com/sauce-sdk/cli/simulate), [Builtins](https://docs.eco.com/sauce/builtins.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.