Payment Splitter
Automatically split payments equally among multiple recipients
The Payment Splitter contract distributes funds equally among a predefined list of payees. Send lovelace to the contract, and any payee can trigger a payout that splits the balance evenly among all recipients.
Use Cases
- Shared project donation addresses
- Revenue splitting for collaborators
- Team payment distribution
- Royalty sharing agreements
- Automated expense splitting
Quick Start
Install the Package
npm install @meshsdk/contract @meshsdk/coreInitialize the Contract
import { MeshPaymentSplitterContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
const provider = new BlockfrostProvider("<Your-API-Key>");
const meshTxBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
const payees = [
"addr_test1qp...alice_address",
"addr_test1qp...bob_address",
"addr_test1qp...charlie_address",
"addr_test1qp...diana_address",
];
const contract = new MeshPaymentSplitterContract(
{
mesh: meshTxBuilder,
fetcher: provider,
wallet: wallet,
networkId: 0,
},
payees
);Configuration Parameters
| Parameter | Type | Description |
|---|---|---|
payees | string[] | Array of Bech32 addresses that receive payments |
Contract Logic
The Payment Splitter enforces equal distribution:
| Rule | Description |
|---|---|
| Fixed Payees | Recipients are defined at contract initialization |
| Equal Split | All payees receive exactly the same amount |
| Payee Trigger | Only a payee can trigger the payout |
| Full Distribution | The entire contract balance is distributed |
Split Calculation
amount_per_payee = total_balance / number_of_payeesFor example, with 4 payees and 100 ADA in the contract:
- Each payee receives: 100 ADA / 4 = 25 ADA
Validation Rules
- Transaction must be signed by one of the payees
- All outputs must go to payee addresses
- All payees must receive equal amounts
- The sum must equal the contract balance (minus fees)
Available Actions
Send Lovelace to Splitter
Deposit lovelace into the payment splitter contract.
Method Signature
contract.sendLovelaceToSplitter(lovelaceAmount: number): Promise<string>Parameters
| Parameter | Type | Description |
|---|---|---|
lovelaceAmount | number | Amount of lovelace to send to the contract |
Code Example
import { MeshPaymentSplitterContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
// Initialize provider and contract
const provider = new BlockfrostProvider("<Your-API-Key>");
const meshTxBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
const payees = [
"addr_test1qp...alice",
"addr_test1qp...bob",
"addr_test1qp...charlie",
"addr_test1qp...diana",
];
const contract = new MeshPaymentSplitterContract(
{
mesh: meshTxBuilder,
fetcher: provider,
wallet: wallet,
networkId: 0,
},
payees
);
// Send 20 ADA to the splitter (will be split 4 ways = 5 ADA each)
const lovelaceAmount = 20000000; // 20 ADA
const tx = await contract.sendLovelaceToSplitter(lovelaceAmount);
const signedTx = await wallet.signTx(tx);
const txHash = await wallet.submitTx(signedTx);
console.log("Funds deposited. Transaction hash:", txHash);What Happens on Success
- Lovelace transfers from your wallet to the contract address
- Funds accumulate until a payee triggers payout
- Anyone can send funds to the contract (not just payees)
Trigger Payout
Distribute the contract balance equally among all payees.
Method Signature
contract.triggerPayout(): Promise<string>Parameters
None. The contract distributes its entire balance.
Code Example
import { MeshPaymentSplitterContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
// Initialize provider and contract (must be a payee's wallet)
const provider = new BlockfrostProvider("<Your-API-Key>");
const meshTxBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
const payees = [
"addr_test1qp...alice",
"addr_test1qp...bob",
"addr_test1qp...charlie",
"addr_test1qp...diana",
];
const contract = new MeshPaymentSplitterContract(
{
mesh: meshTxBuilder,
fetcher: provider,
wallet: payeeWallet, // Must be one of the payees
networkId: 0,
},
payees
);
// Trigger the payout
const tx = await contract.triggerPayout();
const signedTx = await payeeWallet.signTx(tx, true);
const txHash = await payeeWallet.submitTx(signedTx);
console.log("Payout triggered. Transaction hash:", txHash);What Happens on Success
- Contract balance is divided equally among all payees
- Each payee receives their share
- Contract balance becomes zero
- New deposits can be made and split again
Full Working Example
import { MeshPaymentSplitterContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
async function paymentSplitterDemo() {
// Connect wallet
const wallet = await MeshCardanoBrowserWallet.enable("eternl");
// Initialize provider
const provider = new BlockfrostProvider("<Your-API-Key>");
const meshTxBuilder = new MeshTxBuilder({
fetcher: provider,
submitter: provider,
});
// Define payees (4 team members)
const payees = [
"addr_test1qp...alice",
"addr_test1qp...bob",
"addr_test1qp...charlie",
"addr_test1qp...diana",
];
// Initialize contract
const contract = new MeshPaymentSplitterContract(
{
mesh: meshTxBuilder,
fetcher: provider,
wallet: wallet,
networkId: 0,
},
payees
);
// Step 1: Donor sends 16 ADA to the splitter
const depositTx = await contract.sendLovelaceToSplitter(16000000);
const signedDepositTx = await wallet.signTx(depositTx);
const depositTxHash = await wallet.submitTx(signedDepositTx);
console.log("Deposited:", depositTxHash);
// Wait for confirmation
await new Promise((resolve) => setTimeout(resolve, 60000));
// Step 2: A payee triggers the payout (each gets 4 ADA)
// In production, switch to a payee's wallet
const payoutTx = await contract.triggerPayout();
const signedPayoutTx = await wallet.signTx(payoutTx, true);
const payoutTxHash = await wallet.submitTx(signedPayoutTx);
console.log("Payout triggered:", payoutTxHash);
console.log("Each payee receives: 4 ADA");
}
paymentSplitterDemo().catch(console.error);Accumulating Donations
The contract can receive multiple deposits before triggering a payout:
// Donation 1: 10 ADA
await contract.sendLovelaceToSplitter(10000000);
// Donation 2: 5 ADA
await contract.sendLovelaceToSplitter(5000000);
// Donation 3: 5 ADA
await contract.sendLovelaceToSplitter(5000000);
// Total in contract: 20 ADA
// Trigger payout: each of 4 payees gets 5 ADA
await contract.triggerPayout();Minimum Payout Considerations
Ensure the contract balance is large enough to cover:
- Minimum UTxO value for each output (approximately 1 ADA per payee)
- Transaction fees
const minPerPayee = 1000000; // 1 ADA minimum
const numPayees = 4;
const minDeposit = minPerPayee * numPayees + 500000; // 4.5 ADA minimum
// Always deposit at least this amount
await contract.sendLovelaceToSplitter(minDeposit);Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
Script validation failed | Unequal distribution | Contract enforces equal splits; check calculations |
Missing required signer | Non-payee triggering | Only payees can trigger payouts |
Insufficient funds | Balance too low | Ensure enough funds for all payees + fees |
Output below minimum | Balance divided too small | Deposit more funds before payout |
Debugging Tips
- Verify payee list: Ensure all payee addresses are correct and match initialization
- Check wallet is a payee: The wallet triggering payout must be in the payee list
- Calculate minimum deposit:
(min_utxo * num_payees) + fees - Use partial signing: Always pass
truetosignTx()for payout transactions
Calculating Split Amounts
const contractBalance = 20000000; // 20 ADA
const numPayees = 4;
const estimatedFees = 200000; // ~0.2 ADA
const distributableAmount = contractBalance - estimatedFees;
const amountPerPayee = Math.floor(distributableAmount / numPayees);
console.log(`Each payee receives: ${amountPerPayee / 1000000} ADA`);
// Output: Each payee receives: 4.95 ADA