A clean, production-grade API Skill for the Pharos AI Agent Carnival hackathon. It exposes a single deployable HTTP endpoint that sequentially splits USDC or native PROS payments from a funding wallet to multiple recipients on Pharos Mainnet without taking custody of any funds.
This Skill is deployed and running on Railway: https://splipha-production.up.railway.app/
Warning
The deployed instance uses a Skill Wallet funded with a small amount of PROS/USDC for demonstration purposes only. Please test using very small sandbox amounts (e.g. 0.01 PROS / 0.01 USDC) to verify execution and avoid draining the sandbox. For production use or to test with larger sums, clone this repo and run it with your own SKILL_PRIVATE_KEY in environment variables.
This module complies with the Pharos Standardized Skill Specification and includes a machine-readable OpenAPI 3.0 descriptor:
- OpenAPI Schema File: openapi.json
This schema allows frameworks, testing tools, and future Phase 2 AI Agents to automatically discover, validate, and orchestrate calls to this skill's endpoints.
graph TD
A[Caller Request] --> B[Pre-flight Validation validate.ts]
B -->|Check 1-6: Formats & Bounds| C{Passed?}
C -->|No| D[Return 400 Bad Request]
C -->|Yes| E[Check 7-8: Onchain Balances & Allowance]
E -->|Failed| D
E -->|Passed| F[Execution Engine splitter.ts]
F -->|Sequential Tx 1| G[Pharos Mainnet]
G -->|Receipt Success| H[Sequential Tx 2]
H -->|Receipt Success| I[Return 200 OK + explorerUrls]
F -->|Any Tx Reverts| J[Halt Immediately + Return partial success & failedAt details]
All validations run in src/validate.ts before any transaction is submitted onchain to protect callers from wasting gas fees:
- Local Input Validations (Checks 1-6): In-memory checks completed instantly without network calls.
- Token: Must be exactly
"PROS"or"USDC". - Caller Address: Must be a valid EVM address format (
/^0x[a-fA-F0-9]{40}$/). - Recipient Addresses: All recipient addresses must be valid EVM format.
- Duplicates: No duplicate recipient addresses allowed.
- Amounts: All amounts must be positive, non-zero integer strings.
- Recipient Count: Must have between 2 and 20 recipients.
- Token: Must be exactly
- Onchain Network Validations (Checks 7-8): Queries live blockchain state via Pharos RPC. 7. Balance: Verifies the caller has enough tokens (including gas reserves for PROS splits). 8. Allowance: Verifies the caller has approved the Skill Wallet (USDC only).
Splipha is engineered following strict security principles for financial skill modules:
- Zero-Custody Architecture: The API is completely stateless and does not store private keys, session data, or tokens. It acts strictly as an execution router.
- Onchain Checksum Protection: All inputs undergo lowercase normalization to prevent transaction failures caused by invalid EIP-55 checksum formats.
- Sequential Atomic-Halting: Transactions are executed one-by-one. If a transaction fails mid-execution, the loop terminates immediately, returning the exact transaction index that failed and listing all successful transfers to avoid silent partial executions.
- Network Name: Pharos Mainnet
- Chain ID:
1672 - RPC URL:
https://rpc.pharos.xyz - Block Explorer:
https://pharosscan.xyz
| Token | Decimals | Contract Address |
|---|---|---|
PROS |
18 | Native Token |
USDC |
6 | 0xc879c018db60520f4355c26ed1a6d572cdac1815 (ERC-20, verified on PharosScan) |
To prevent floating-point rounding errors in JavaScript, all arithmetic is executed using native BigInt operations. Amounts in API requests must be passed in the token's smallest atomic unit:
-
USDC (6 Decimals):
- Formula:
$\text{Units} = \text{Amount} \times 10^6$ - Example: To send
1.5USDC, write"1500000"
- Formula:
-
PROS (18 Decimals):
- Formula:
$\text{Units} = \text{Amount} \times 10^{18}$ - Example: To send
0.01PROS, write"10000000000000000"
- Formula:
- Endpoint:
GET /health - Response:
{
"status": "ok",
"network": "Pharos Mainnet",
"chainId": 1672,
"supportedTokens": ["PROS", "USDC"]
}- cURL Command:
curl http://localhost:3000/health- Endpoint:
GET /balance/:address - Response:
{
"address": "0x1234567890123456789012345678901234567890",
"PROS": "2300000000000000000",
"USDC": "450000000"
}- cURL Command:
curl http://localhost:3000/balance/0x1234567890123456789012345678901234567890- Endpoint:
POST /split - Request Format:
- Minimum 2 recipients, maximum 20 recipients.
- All amounts must be strings representing integers in the token's smallest unit (6 decimals for USDC, 18 decimals for PROS).
- For USDC:
callerAddressmust have previously approved the Skill Wallet as a spender. - For PROS:
callerAddressmust match the Skill Wallet address since native transfers are executed directly from the transaction signer.
curl -X 'POST' \
'http://localhost:3000/split' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"token": "USDC",
"callerAddress": "0xf9D38027Ace9abe0072d231F17d73850A96c3bBf",
"recipients": [
{
"address": "0xBE3BC24E08e7f1335f06C36C0D7aE52a374Adb3b",
"amount": "100000"
},
{
"address": "0x2BFD1D773DB1b5e9A72ad918b4ECAF58f6D91760",
"amount": "100000"
},
{
"address": "0x325d336F21abB3011ea65Ea67C34E02083c73Bad",
"amount": "100000"
},
{
"address": "0xa991beDe7A2995B76C6C97fcC226dFbac358EE5B",
"amount": "100000"
}
]
}'{
"success": true,
"token": "USDC",
"totalDistributed": "400000",
"transfers": [
{
"to": "0xBE3BC24E08e7f1335f06C36C0D7aE52a374Adb3b",
"amount": "100000",
"txHash": "0x0a7108d5a7556922f20be902cd4e04e45497b1ae90794e19d37d5cc699162065",
"explorerUrl": "https://pharosscan.xyz/tx/0x0a7108d5a7556922f20be902cd4e04e45497b1ae90794e19d37d5cc699162065"
},
{
"to": "0x2BFD1D773DB1b5e9A72ad918b4ECAF58f6D91760",
"amount": "100000",
"txHash": "0xa48369bef81f08b29bbd680f5c234da73e4b23d67f0efed2307910242749c330",
"explorerUrl": "https://pharosscan.xyz/tx/0xa48369bef81f08b29bbd680f5c234da73e4b23d67f0efed2307910242749c330"
},
{
"to": "0x325d336F21abB3011ea65Ea67C34E02083c73Bad",
"amount": "100000",
"txHash": "0xe407a71823ff6a4a87029053283a2540a6f6546b0438a4105659727f19531ec4",
"explorerUrl": "https://pharosscan.xyz/tx/0xe407a71823ff6a4a87029053283a2540a6f6546b0438a4105659727f19531ec4"
},
{
"to": "0xa991beDe7A2995B76C6C97fcC226dFbac358EE5B",
"amount": "100000",
"txHash": "0x6c0433be5b45be97a456781c1e9ed54040a4121f10cd44ed747600f480ba8fd4",
"explorerUrl": "https://pharosscan.xyz/tx/0x6c0433be5b45be97a456781c1e9ed54040a4121f10cd44ed747600f480ba8fd4"
}
]
}curl -X 'POST' \
'http://localhost:3000/split' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"token": "PROS",
"callerAddress": "0xf9D38027Ace9abe0072d231F17d73850A96c3bBf",
"recipients": [
{
"address": "0xBE3BC24E08e7f1335f06C36C0D7aE52a374Adb3b",
"amount": "10000000000000000"
},
{
"address": "0x2BFD1D773DB1b5e9A72ad918b4ECAF58f6D91760",
"amount": "10000000000000000"
},
{
"address": "0x325d336F21abB3011ea65Ea67C34E02083c73Bad",
"amount": "10000000000000000"
},
{
"address": "0xa991beDe7A2995B76C6C97fcC226dFbac358EE5B",
"amount": "10000000000000000"
}
]
}'{
"success": true,
"token": "PROS",
"totalDistributed": "40000000000000000",
"transfers": [
{
"to": "0xBE3BC24E08e7f1335f06C36C0D7aE52a374Adb3b",
"amount": "10000000000000000",
"txHash": "0xd33be01a4d36255da5e079468dbea038192e36e71bbc71806e54e8faede90f8d",
"explorerUrl": "https://pharosscan.xyz/tx/0xd33be01a4d36255da5e079468dbea038192e36e71bbc71806e54e8faede90f8d"
},
{
"to": "0x2BFD1D773DB1b5e9A72ad918b4ECAF58f6D91760",
"amount": "10000000000000000",
"txHash": "0x3f0d34bde7fbe0bae0c5786544484ab2928350f628165abe89f8e49f5c8e91b9",
"explorerUrl": "https://pharosscan.xyz/tx/0x3f0d34bde7fbe0bae0c5786544484ab2928350f628165abe89f8e49f5c8e91b9"
},
{
"to": "0x325d336F21abB3011ea65Ea67C34E02083c73Bad",
"amount": "10000000000000000",
"txHash": "0xd55415c4c4e406983690a0d6512b57dc23a2fb85b051d6c536038a448e7d8cda",
"explorerUrl": "https://pharosscan.xyz/tx/0xd55415c4c4e406983690a0d6512b57dc23a2fb85b051d6c536038a448e7d8cda"
},
{
"to": "0xa991beDe7A2995B76C6C97fcC226dFbac358EE5B",
"amount": "10000000000000000",
"txHash": "0x6610f7269f0493011f572f1d69a932f3fed0e54d1c3b55935d9af002a5e538a2",
"explorerUrl": "https://pharosscan.xyz/tx/0x6610f7269f0493011f572f1d69a932f3fed0e54d1c3b55935d9af002a5e538a2"
}
]
}Before executing a USDC split, the USDC owner wallet (callerAddress) must grant a spending allowance to the Skill Wallet address (your deployed server address, e.g. 0xf9D38027Ace9abe0072d231F17d73850A96c3bBf on the live sandbox demo).
You can easily execute this approval on the block explorer:
- Go to the USDC Contract on PharosScan: USDC Contract Write Tab.
- Click "Connect to Web3" and connect your wallet containing the USDC.
- Locate the
approvefunction and fill in:spender: The public address of your Skill Wallet (the address executing transactions).value: The total amount of USDC you want to authorize in atomic units (6 decimals. E.g.1000000for 1.0 USDC, or10000for 0.01 USDC).
- Click "Write" and confirm the transaction in your wallet.
Once that transaction is confirmed, you are fully authorized to call /split for USDC!
-
Clone the workspace / folder:
cd payment-splitter-skill -
Install dependencies:
npm install
-
Configure Environment Variables: Create a
.envfile based on.env.example:cp .env.example .env
Open
.envand set yourSKILL_PRIVATE_KEY. -
Start the API server:
npm start
-
Build the production bundle:
npm run build