Skip to content
DocsDevelopersContracts & addresses

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.

PropertyMainnetOrchard Testnet
Appapp.daoships.orgtestnet.daoships.org
ZoneCyprus-1Cyprus-1
Chain ID915000
RPC URLhttps://rpc.quai.network/cyprus1https://orchard.rpc.quai.network/cyprus1
Block explorerhttps://quaiscan.iohttps://orchard.quaiscan.io
Native currencyQUAI (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

ContractMainnetOrchard TestnetRole
Poster0x004Db03AA2593B4885AFEFF688ca2634D1533fac0x0032eA61e8fF1b12A70C39696CfdA06198d2095eEIP-3722 metadata bus
DAOShipSingleton0x002956ba6223d17b67Af509bb057928299B116110x000F38Dc0B711a57086ca0bD6fa2041D8Cd9Fe03Logic for DAOShip clones
SharesERC20Singleton0x0019d1FcdB7Aa83aCD17a5484f3246d1959d38fF0x001a4f36ead605149A0144C771B7cbf4116753a9Logic for voting-token clones
LootERC20Singleton0x0059458879E8f1FCA65f3068d9BC587b0Fd812860x002abA2592A1111b8EcfFcA98A7f30b8de30cA58Logic for loot-token clones
DAOShipLauncher0x001aa208480e8495067217c5238913dF1eC683d70x005D0D996cB3f25bEC37E1827FeAfCe5AC9f7856Clone factory (DAO + tokens)
DAOShipAndVaultLauncher0x0067b50Dac689d8688eF8575B82Bc663802f3AF50x0054Cb24fA412B2b276D5F73f4A7adC70f0f0CbfAtomic factory (DAO + vault)

The app also references this Quai Vault infrastructure, pre-deployed by the Quai Vault project:

ContractMainnetOrchard TestnetRole
QuaiVaultFactory0x003613aC5FFd45bFF7B2F0210DA2fF660908c4880x002d1305D597c157bB975967FA2e5337674b0E5FCreates vault (avatar) proxies
VaultSingleton0x0038E6d84412A10CdcE41b0f62A05350023f1fb60x004E539Cf477A5Cb456A56023f083cD91Bc4934eQuai Vault implementation
MultiSendCallOnly0x003f62e6a7f2EB6b94345a9A41671888eC4A3ebA0x002ae8A47C2da497fe569AfCF0486410aA1093E0Batched-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 singleton

Only 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;                          // onlyGovernor

Permission 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.