A server-based allocator for The Compact that leverages protocol signatures and transactions for authentication. Autocator provides an API for requesting resource lock allocations across multiple blockchains by providing the details of associated compacts with accompanying sponsor signatures or onchain registrations. It also includes a frontend application for interacting directly with the server that also facilitates making deposits into resource locks it oversees.
Autocator is a fork of Smallocator with key differences:
- No "Sign in with Ethereum" authentication component
- Note that Autocator does not provide the same privacy guarantees as Smallocator — allocated token balances and suggested nonces are public. The primary intended mechainism for Autocator is the "deposit and register" flow, where information on the input tokens is already public, with support for retries and early withdrawals when necessary.
- Simplified flow: request nonce => sponsor signs or registers and informs Autocator => Autocator cosigns
- Requires either:
- a valid sponsor signature with the payload when submitting a compact, or
- an onchain registration of the compact, in which case there's no attached signature
A hosted version is available at Autocator.org — Note that it's likely not ready for real production use (do reach out if it goes down).
⚠️ Autocator is under development and is intended to serve as a reference for understanding server-based allocator functionality and for testing purposes. Use caution when using Autocator in a production environment.
- ✍️ EIP-712 Compact message validation and signing with sponsor signature verification
- 🔄 Support for onchain registration verification as an alternative to sponsor signatures
- 🤫 No witness data or signature provided, keeping sponsor intents secret (only the typestring and witness hash is supplied)
- 📊 GraphQL integration with The Compact Indexer for multi-chain indexing
- 💾 Persistent storage using PGLite to track attested compacts and used nonces
- 🔎 Comprehensive validation pipeline to ensure resource locks never end up in an overallocated state
- ☝️ Single-resource-lock, single-chain compacts only: No
BatchCompact
orMultichainCompact
attestations - ❄️ Strict nonce usage: Ensures every attested nonce is unique; no reuse on expirations and no direct onchain nonce consumption
- 🧭 No
attest()
callbacks for ERC6909 transfers: focused solely on attesting compacts - 🪞 No compact qualification: Attests to the exact compact provided to it without adding qualifiers or extra metadata
- 📡 Limited on-chain awareness: Uses indexer to verify onchain registrations
- ⏳ Straightforward finalization: Uses a simple, time-based approach per chain for determining transaction finality
A basic frontend is available at the root path (GET /
), or at localhost:3001/
when running locally in dev mode. While the primary intended mechanism of interaction with Autocator is via the API, the UI serves as a convenient and direct secondary access point.
It supports:
- viewing the health status of the server
- depositing Native tokens and ERC20 tokens
- viewing allocatable and allocated balances
- performing allocated transfers and withdrawals
- initiating, executing, and disabling forced withdrawals
Note that the frontend is still relatively unstable (any contributions here are welcome).
GET /health
Example response:
{
"status": "healthy",
"allocatorAddress": "0x1234567890123456789012345678901234567890",
"signingAddress": "0x9876543210987654321098765432109876543210",
"timestamp": "2024-03-07T12:00:00.000Z",
"supportedChains": [
{
"chainId": "1",
"allocatorId": "0x12345678901234567890abcd",
"finalizationThresholdSeconds": 25
},
{
"chainId": "10",
"allocatorId": "0x12345678901234567890abcd",
"finalizationThresholdSeconds": 10
},
{
"chainId": "130",
"allocatorId": "0x2345678901234567890abcde",
"finalizationThresholdSeconds": 2
},
{
"chainId": "8453",
"allocatorId": "0x2345678901234567890abcde",
"finalizationThresholdSeconds": 2
}
]
}
-
Get Suggested Nonce
GET /suggested-nonce/:chainId/:account
Returns the next available unused nonce for the specified account for creating new compacts on a specific chain.
Example response:
{ "nonce": "0x70997970C51812dc3A010C7d01b50e0d17dc79C800000000000000000000001" }
-
Submit Compact
POST /compact
Example request:
{ "chainId": "10", "compact": { "arbiter": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "sponsor": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "nonce": "0x70997970C51812dc3A010C7d01b50e0d17dc79C800000000000000000000001", "expires": "1732520000", "id": "0x300000000000000000000000000000000000000000000000000000000000001c", "amount": "1000000000000000000", "witnessTypeString": "ExampleWitness exampleWitness)ExampleWitness(uint256 foo, bytes32 bar)", "witnessHash": "0x0000000000000000000000000000000000000000000000000000000123" }, "sponsorSignature": "0x1234...7890" }
Example response:
{ "hash": "0x1234567890123456789012345678901234567890123456789012345678901234", "signature": "0x1234...7890", "nonce": "0x70997970C51812dc3A010C7d01b50e0d17dc79C800000000000000000000001" }
Note that nonce
is required and must be a valid nonce for the sponsor. Use the /suggested-nonce/:chainId/:account
endpoint to get a valid nonce. witnessTypeString
and witnessHash
can be null
in which case the attested compact will not incorporate witness data (values must be provided for both or omitted for both to be considered valid).
The sponsorSignature
is required unless the compact is registered onchain. If the signature is invalid or missing, Autocator will check if the compact is registered onchain, that the registration has not expired, and that the associated compact has not already been claimed. If it is, the compact will be cosigned by Autocator.
-
Get Compacts by Address
GET /compacts?sponsor=0x70997970C51812dc3A010C7d01b50e0d17dc79C8
Example response:
[ { "chainId": "10", "hash": "0x1234567890123456789012345678901234567890123456789012345678901234", "compact": { "arbiter": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "sponsor": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "nonce": "0x70997970C51812dc3A010C7d01b50e0d17dc79C800000000000000000000001", "expires": "1732520000", "id": "0x300000000000000000000000000000000000000000000000000000000000001c", "amount": "1000000000000000000", "witnessTypeString": "ExampleWitness exampleWitness)ExampleWitness(uint256 foo, bytes32 bar)", "witnessHash": "0x0000000000000000000000000000000000000000000000000000000000000123" }, "signature": "0x1234...7890", "createdAt": "2024-03-07T12:00:00Z" } ]
-
Get Specific Compact
GET /compact/:chainId/:claimHash
Example response:
{ "chainId": "10", "hash": "0x1234567890123456789012345678901234567890123456789012345678901234", "compact": { "arbiter": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "sponsor": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "nonce": "0x70997970C51812dc3A010C7d01b50e0d17dc79C800000000000000000000001", "expires": "1732520000", "id": "0x300000000000000000000000000000000000000000000000000000000000001c", "amount": "1000000000000000000", "witnessTypeString": "ExampleWitness exampleWitness)ExampleWitness(uint256 foo, bytes32 bar)", "witnessHash": "0x0000000000000000000000000000000000000000000000000000000123" }, "signature": "0x1234...7890", "createdAt": "2024-03-07T12:00:00Z" }
-
Get Resource Lock Balance
GET /balance/:chainId/:lockId
Returns balance information for a specific resource lock. Example response:
{ "allocatableBalance": "1000000000000000000", "allocatedBalance": "500000000000000000", "balanceAvailableToAllocate": "500000000000000000", "withdrawalStatus": 0 }
The
allocatableBalance
will be the current balance minus the sum of any unfinalized deposits or inbound transfers. The period after which these are considered finalized is configurable for each chain.The
allocatedBalance
will be the sum of any submitted compacts that:- have not been processed (as confirmed by a
Claim
event with the respective claim hash in a finalized block) - have not expired (as confirmed by a finalized block with a timestamp exceeding the
expires
value)
The
balanceAvailableToAllocate
will be:"0"
ifwithdrawalStatus
is non-zero"0"
ifallocatedBalance
>=allocatableBalance
allocatableBalance - allocatedBalance
otherwise
- have not been processed (as confirmed by a
-
Get All Resource Lock Balances
GET /balances
Returns balance information for all resource locks managed by this allocator. Example response:
{ "balances": [ { "chainId": "1", "lockId": "0x1234567890123456789012345678901234567890123456789012345678901234", "allocatableBalance": "1000000000000000000", "allocatedBalance": "500000000000000000", "balanceAvailableToAllocate": "500000000000000000", "withdrawalStatus": 0 } ] }
Each balance entry follows the same rules as the single balance endpoint.
- Node.js >= 18
- pnpm >= 9.14.1
- TypeScript >= 5.2
### Configuration & Installation ###
# 1. Clone this repo and enter cloned directory
git clone [email protected]:Uniswap/autocator.git && cd autocator
# 2. Copy example environment file (modify as needed)
cp .env.example .env
# 3. Install frontend and backend dependencies
pnpm install:all
# 4. Run tests
pnpm test
### Usage ###
# Run both frontend and backend in development mode with hot reload
pnpm dev:all
# Run tests
pnpm test
# Type checking
pnpm type-check
# Linting
pnpm lint
# Format code
pnpm format
# Build frontend and backend for production
pnpm build:all
# Start production server
pnpm start
The project utilizes Jest to implement various test suites:
- Unit tests for core functionality
- Integration tests for API endpoints
- Validation tests for compact messages
Run all tests with:
pnpm test
The project uses:
- ESLint for code linting
- Prettier for code formatting
- Husky for git hooks
- lint-staged for pre-commit checks
Pre-commit hooks ensure:
- Code is properly formatted
- Tests pass
- No TypeScript errors
- No ESLint warnings
The project includes a setup script for deploying to a cloud server with automatic HTTPS configuration using Let's Encrypt.
- A domain name pointing to your server (A record)
- unix cloud server (e.g., Ubuntu 22.04 LTS)
- SSH access to the server
Since the server might have limited resources, it's recommended to build the project locally first:
- Clone and set up the repository locally:
# On your local machine
git clone https://github.com/Uniswap/autocator.git
cd autocator
pnpm install:all
- Create a production .env file:
cp .env.example .env
# Edit .env with your production configuration
- Build the project:
pnpm build:all
This will create the dist
directory with both the backend and frontend builds, and will include your .env file in the dist directory.
- SSH into your server:
ssh user@your-server
- Clone the repository on the server:
git clone https://github.com/Uniswap/autocator.git
cd autocator
- Run the setup script with your domain and IP:
./scripts/setup-server.sh your-domain.com your-server-ip
For example:
./scripts/setup-server.sh autocator.org 157.230.64.12
- Transfer the built files from your local machine to the server:
# From your local repository
scp -r dist/* user@your-server:/opt/autocator/dist/
The setup script will:
- Install required dependencies (Node.js, pnpm, nginx, certbot)
- Set up the project in /opt/autocator
- Configure nginx with proper routing and CORS support
- Set up SSL certificates with Let's Encrypt
- Create and enable a systemd service
- Start the server
The deployment is configured to fully support CORS (Cross-Origin Resource Sharing), allowing API endpoints to be accessed from any domain. This makes it easy to integrate with frontend applications hosted on different domains.
Make sure your local .env file includes these key environment variables before building:
PRIVATE_KEY
: Your private key for signing compactsALLOCATOR_ADDRESS
: The address of your allocatorSIGNING_ADDRESS
: The address derived from your private keyINDEXER_URL
: The URL of The Compact Indexer
Monitor the server status:
sudo systemctl status autocator
View server logs:
sudo journalctl -u autocator -f
Test the health endpoint:
curl https://your-domain.com/health
MIT