Contract Reference
This page lists the canonical deployed addresses for the DAO Ships protocol on both networks
it runs on, and what each contract does. The addresses below come from
deployment-addresses.json in the contracts repository and match the addresses the live app
uses in src/config/contracts.ts.
Orchard has had several deployments — derive, don't copy
DAO Ships has been deployed to Orchard more than once, and each deployment is complete and internally consistent. Repositories and config files still contain addresses from retired ones, so a set assembled by hand from mixed sources can look entirely correct and still be a deployment nobody uses.
The Orchard column below is the deployment that
testnet.daoships.org runs against and that the testnet indexer
schema records — verified against the running application, not against a config file. Every
address holds bytecode, and the whole set reproduces from its launcher via the walk
below.
If a value here disagrees with something you found elsewhere, derive the set from
DAOShipAndVaultLauncher and trust that. A derived set is consistent by construction. That is
the only check that distinguishes the live deployment from a retired one.
Networks
DAO Ships is live on Quai mainnet and remains deployed on the Orchard testnet for experimentation. Both run on the Cyprus-1 zone.
| Property | Mainnet | Orchard Testnet |
|---|---|---|
| App | app.daoships.org | testnet.daoships.org |
| Zone | Cyprus-1 | Cyprus-1 |
| Chain ID | 9 | 15000 |
| RPC URL | https://rpc.quai.network/cyprus1 | https://orchard.rpc.quai.network/cyprus1 |
| Block explorer | https://quaiscan.io | https://orchard.quaiscan.io |
| Native currency | QUAI (18 decimals) | Testnet QUAI (18 decimals) |
The shard path is part of the URL
https://rpc.quai.network — without /cyprus1 — returns 404. Confirm an endpoint before
building against it: quai_chainId should return 0x9 on mainnet and 0x3a98 (15000) on
Orchard.
Both deployments use Solidity 0.8.22 and OpenZeppelin v5.
Cyprus-1 uses a sharded address scheme — all addresses on this shard begin with the 0x00
prefix, which is why deployment relies on CREATE2 salt mining (see
Launch from TypeScript).
Deployed addresses
| Contract | Mainnet | Orchard Testnet | Role |
|---|---|---|---|
Poster | 0x004Db03AA2593B4885AFEFF688ca2634D1533fac | 0x0032eA61e8fF1b12A70C39696CfdA06198d2095e | EIP-3722 metadata bus |
DAOShipSingleton | 0x002956ba6223d17b67Af509bb057928299B11611 | 0x000F38Dc0B711a57086ca0bD6fa2041D8Cd9Fe03 | Logic for DAOShip clones |
SharesERC20Singleton | 0x0019d1FcdB7Aa83aCD17a5484f3246d1959d38fF | 0x001a4f36ead605149A0144C771B7cbf4116753a9 | Logic for voting-token clones |
LootERC20Singleton | 0x0059458879E8f1FCA65f3068d9BC587b0Fd81286 | 0x002abA2592A1111b8EcfFcA98A7f30b8de30cA58 | Logic for loot-token clones |
DAOShipLauncher | 0x001aa208480e8495067217c5238913dF1eC683d7 | 0x005D0D996cB3f25bEC37E1827FeAfCe5AC9f7856 | Clone factory (DAO + tokens) |
DAOShipAndVaultLauncher | 0x0067b50Dac689d8688eF8575B82Bc663802f3AF5 | 0x0054Cb24fA412B2b276D5F73f4A7adC70f0f0Cbf | Atomic factory (DAO + vault) |
The app also references this Quai Vault infrastructure, pre-deployed by the Quai Vault project:
| Contract | Mainnet | Orchard Testnet | Role |
|---|---|---|---|
QuaiVaultFactory | 0x003613aC5FFd45bFF7B2F0210DA2fF660908c488 | 0x002d1305D597c157bB975967FA2e5337674b0E5F | Creates vault (avatar) proxies |
VaultSingleton | 0x0038E6d84412A10CdcE41b0f62A05350023f1fb6 | 0x004E539Cf477A5Cb456A56023f083cD91Bc4934e | Quai Vault implementation |
MultiSendCallOnly | 0x003f62e6a7f2EB6b94345a9A41671888eC4A3ebA | 0x002ae8A47C2da497fe569AfCF0486410aA1093E0 | Batched-call DelegateCall target |
Addresses differ per network
The mainnet and testnet deployments are separate. Always select the column matching the chain ID your wallet or provider is connected to. (The three Quai Vault contracts above are the exception — that infrastructure is shared across DAO Ships deployments on the same network.)
Derive the addresses instead of copying them
A hardcoded table of nine addresses has nine chances to be stale. You only need two per
network — DAOShipAndVaultLauncher and Poster — because seven of the remaining eight are
reachable on-chain from the launcher:
DAOShipAndVaultLauncher (published)
├── .daoShipLauncher() → DAOShipLauncher
│ ├── .daoShipSingleton() → DAOShipSingleton
│ ├── .sharesSingleton() → SharesERC20Singleton
│ └── .lootSingleton() → LootERC20Singleton
├── .multisendCallOnly() → MultiSendCallOnly
└── .quaiVaultFactory() → QuaiVaultFactory
└── .implementation() → QuaiVault singletonOnly Poster is unreachable — it is standalone infrastructure with no back-reference from the
graph — so it is the second address you pin.
const launcher = new quais.Contract(LAUNCHER, DAOSHIP_AND_VAULT_LAUNCHER_ABI, provider);
const daoShipLauncher = await launcher.daoShipLauncher();
const vaultFactory = await launcher.quaiVaultFactory();
const multisend = await launcher.multisendCallOnly();
const dsl = new quais.Contract(daoShipLauncher, DAOSHIP_LAUNCHER_ABI, provider);
const [daoShipSingleton, sharesSingleton, lootSingleton] = await Promise.all([
dsl.daoShipSingleton(), dsl.sharesSingleton(), dsl.lootSingleton(),
]);A set derived this way is internally consistent by construction. A set assembled by hand from mixed sources is not — and the failure is quiet, because a coherent-but-wrong deployment behaves perfectly right up until you notice the indexer has never heard of your DAO.
`deployment-addresses.json` is not the whole set
daoships-contracts/deployment-addresses.json lists six of the nine: Poster, both token
singletons, DAOShipSingleton, DAOShipLauncher, and DAOShipAndVaultLauncher.
MultiSendCallOnly, QuaiVaultFactory, and the QuaiVault singleton are not in it — take
those from the walk above.
The QuaiVault singleton in particular is deployed by the Quai Vault
project, appears in no DAO Ships repository, and can change without a commit anywhere in this
org. Read it from QuaiVaultFactory.implementation() rather than caching it.
Navigators have no canonical address
There is no protocol-wide registry or singleton for Navigators. Each one is
deployed per DAO with its own immutable config, then granted permission (or enabled as a vault
module) by that DAO's governance. To find a DAO's navigators, query the indexer's ds_navigators
table — see Indexer & data layer.
Contract roles and key functions
DAOShip
The governance engine. Deployed as an EIP-1167 clone of DAOShipSingleton. Holds no funds —
all treasury actions execute against the vault via execTransactionFromModule.
Key functions:
function submitProposal(bytes calldata proposalData, uint32 expiration, string calldata details) external payable returns (uint32);
function sponsorProposal(uint32 id) external;
function submitVote(uint32 id, bool approved) external;
function processProposal(uint32 id, bytes calldata proposalData) external;
function ragequit(address to, uint256 sharesToBurn, uint256 lootToBurn, address[] calldata tokens) external;
function setNavigators(address[] calldata navigators, uint256[] calldata permissions) external; // governanceOnly
function setGuildTokens(address[] calldata tokens, bool[] calldata enabled) external; // governanceOnly
function setGovernanceConfig(bytes calldata governanceConfig) external; // onlyGovernorPermission constants: ADMIN = 1, MANAGER = 2, GOVERNOR = 4, MAX_PERMISSION = 7. The
governanceOnly modifier requires msg.sender == address(this) — i.e. the call must arrive
through a passed proposal.
DAOShipLauncher
EIP-1167 clone factory for DAOShip, SharesERC20, and LootERC20. Exposes
launchDAOShip(...) and calculateAddresses(sender, sharesSalt, lootSalt, daoShipSalt) for
deterministic address prediction.
DAOShipAndVaultLauncher
The atomic factory most callers use. One transaction predicts the DAOShip address, creates
the Quai Vault with DAOShip pre-enabled as a module, then launches DAOShip with the vault as
its avatar. Key functions:
function launchDAOShipAndVault(
bytes calldata initializationParamsTemplate,
string calldata shareTokenName, string calldata shareTokenSymbol,
string calldata lootTokenName, string calldata lootTokenSymbol,
address[] calldata vaultOwners, uint256 vaultThreshold,
uint256 vaultSalt, uint256 sharesSalt, uint256 lootSalt, uint256 daoShipSalt
) external returns (address payable daoShip, address vault);
function calculateAllAddresses(
address sender,
uint256 sharesSalt, uint256 lootSalt, uint256 daoShipSalt, uint256 vaultSalt,
address[] calldata vaultOwners, uint256 vaultThreshold, uint32 minExecutionDelay
) external view returns (address daoShip, address shares, address loot, address vault);It emits LaunchDAOShipAndVault(daoShip, vault, shares, loot, newVault, launcher).
SharesERC20 and LootERC20
SharesERC20 is the voting token with timestamp checkpoints and auto-delegation; LootERC20 is
the non-voting economic token. Both are minted and burned only by DAOShip (or a MANAGER
navigator) via mintShares / mintLoot and the corresponding burns. The protocol caps a DAO at
MAX_GUILD_TOKENS = 20 ragequit tokens.
Poster
An EIP-3722 content bus that emits NewPost and
stores no state, so a post costs around 25K gas regardless of content length:
function post(string content, string tag) external;
event NewPost(address indexed user, string content, string indexed tag);Indexers filter on tag (for example daoships.launcher.daoProfile) to attach metadata to DAOs,
proposals, and members. See the Indexer page for how these are stored.