Skip to main content

How to create an autofill proposal link

Proposals and the actions they contain can be generated programmatically and passed in via URLβ€”this enables a more user-friendly experience for users of a specific app or service that rely on DAO DAO for their governance needs.

Similar to the bulk import actions feature, you must find the key and the shape of the data object for each action. However, unlike bulk import, you must also find the proposal module adapter ID, and the action key in the object must be actionKey instead of key.

The link is simply the proposal creation page with a prefill parameter added, like this:

https://daodao.zone/dao/<DAO_ADDRESS>/proposals/create?prefill=<PREFILL_DATA>

The prefill parameter can either be:

  • a stringified JSON object
  • a base64-encoded JSON object

⚠️ Remember to URL-escape the prefill parameter, whichever format it's in. For example, base64 strings sometimes contain + and =, which must be escaped as %2B and %3D respectively, or else they'll be decoded incorrectly and fail to autofill the proposal.

The format of the prefill object is always:

{
"id": "<PROPOSAL MODULE ADAPTER ID>",
"data": {
// PROPOSAL MODULE FORM DATA
}
}

You can use base64encode.org to encode the JSON object to a base64 string and urlencoder.org to URL-escape the base64 string.

IPFS​

The prefill object can get quite large which becomes impractical to use in a URL, so instead you can upload a JSON blob to IPFS and replace the prefill parameter with pi, pointing to the IPFS hash containing the object:

https://daodao.zone/dao/<DAO_ADDRESS>/proposals/create?pi=<IPFS_HASH>

Proposal module adapter ID​

The proposal module adapter IDs can be found in @dao-dao/utils/constants/adapters.ts.

For dao-proposal-single (the most commonly used proposal module that supports proposals with Yes/No/Abstain votes), the ID is DaoProposalSingle. For dao-proposal-multiple (which supports multiple choice proposals), the ID is DaoProposalMultiple.

Proposal module form data​

The form data structure can be found in @dao-dao/types/proposal-module-adapter.ts.

The single choice proposal form data is:

{
"title": "<PROPOSAL TITLE>",
"description": "<PROPOSAL DESCRIPTION>",
"actionData": [
{
"actionKey": "<ACTION KEY>",
"data": {
// ACTION DATA
}
},
...
]
}

The multiple choice proposal form data is:

{
"title": "<PROPOSAL TITLE>",
"description": "<PROPOSAL DESCRIPTION>",
"choices": [
{
"title": "<OPTION TITLE>",
"description": "<OPTION DESCRIPTION>",
"actionData": [
{
"actionKey": "<ACTION KEY>",
"data": {
// ACTION DATA
}
},
...
]
},
...
]
}

See the bottom of this document for a complete example.

Action keys​

The action keys can be found in @dao-dao/types/actions.ts in the ActionKey enum. For example:

  • spend
  • execute
  • mintNft
  • mint

The key and data format for an action are defined in its README.md, and the actions can be found in the following places:

Here are some common ones:

spend​

For sending money from the treasury.

{
"fromChainId": "<CHAIN ID>",
"toChainId": "<CHAIN ID>",
"from": "<FROM ADDRESS>",
"to": "<RECIPIENT ADDRESS>",
"amount": "<AMOUNT>",
"denom": "<DENOM>"
}

execute​

For executing a smart contract.

{
"chainId": "<CHAIN ID>",
"sender": "<EXECUTOR ADDRESS>",
"address": "<SMART CONTRACT ADDRESS>",
"message": "<SMART CONTRACT MESSAGE>",
"funds": [
{
"denom": "<DENOM>",
"amount": "<AMOUNT>",
"decimals": "<DECIMALS>"
}
]
}

mintNft​

For minting an NFT in a collection the DAO controls.

{
"contractChosen": true,
"collectionAddress": "<NFT COLLECTION ADDRESS>",
"mintMsg": {
"owner": "<RECIPIENT ADDRESS>",
"token_id": "<UNIQUE TOKEN ID>",
"token_uri": "<JSON METADATA URL>"
}
}

mint​

For minting governance tokens in a token-based DAO.

{
"recipient": "<RECIPIENT ADDRESS>",
"amount": "<TOKEN AMOUNT>"
}

Example​

Here's an example of a proposal link that creates a single choice proposal with a spend action and an execute action in the DAO DAO DAO.

{
"id": "DaoProposalSingle",
"data": {
"title": "Example Proposal",
"description": "This proposal will send 100 JUNO to our community fund and update our voting module's unstaking duration.",
"actionData": [
{
"actionKey": "spend",
"data": {
"fromChainId": "juno-1",
"toChainId": "juno-1",
"from": "juno10h0hc64jv006rr8qy0zhlu4jsxct8qwa0vtaleayh0ujz0zynf2s2r7v8q",
"to": "juno1community...fund456",
"amount": "100",
"denom": "ujuno"
}
},
{
"actionKey": "execute",
"data": {
"chainId": "juno-1",
"sender": "juno10h0hc64jv006rr8qy0zhlu4jsxct8qwa0vtaleayh0ujz0zynf2s2r7v8q",
"address": "juno1voting...module789",
"message": "{\"update_config\":{\"duration\":86400}}",
"funds": []
}
}
]
}
}

Base64-encoding the entire object:

ewogICJpZCI6ICJEYW9Qcm9wb3NhbFNpbmdsZSIsCiAgImRhdGEiOiB7CiAgICAidGl0bGUiOiAiRXhhbXBsZSBQcm9wb3NhbCIsCiAgICAiZGVzY3JpcHRpb24iOiAiVGhpcyBwcm9wb3NhbCB3aWxsIHNlbmQgMTAwIEpVTk8gdG8gb3VyIGNvbW11bml0eSBmdW5kIGFuZCB1cGRhdGUgb3VyIHZvdGluZyBtb2R1bGUncyB1bnN0YWtpbmcgZHVyYXRpb24uIiwKICAgICJhY3Rpb25EYXRhIjogWwogICAgICB7CiAgICAgICAgImFjdGlvbktleSI6ICJzcGVuZCIsCiAgICAgICAgImRhdGEiOiB7CiAgICAgICAgICAiZnJvbUNoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJ0b0NoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJmcm9tIjogImp1bm8xMGgwaGM2NGp2MDA2cnI4cXkwemhsdTRqc3hjdDhxd2EwdnRhbGVheWgwdWp6MHp5bmYyczJyN3Y4cSIsCiAgICAgICAgICAidG8iOiAianVubzFjb21tdW5pdHkuLi5mdW5kNDU2IiwKICAgICAgICAgICJhbW91bnQiOiAiMTAwIiwKICAgICAgICAgICJkZW5vbSI6ICJ1anVubyIKICAgICAgICB9CiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWN0aW9uS2V5IjogImV4ZWN1dGUiLAogICAgICAgICJkYXRhIjogewogICAgICAgICAgImNoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJzZW5kZXIiOiAianVubzEwaDBoYzY0anYwMDZycjhxeTB6aGx1NGpzeGN0OHF3YTB2dGFsZWF5aDB1anowenluZjJzMnI3djhxIiwKICAgICAgICAgICJhZGRyZXNzIjogImp1bm8xdm90aW5nLi4ubW9kdWxlNzg5IiwKICAgICAgICAgICJtZXNzYWdlIjogIntcInVwZGF0ZV9jb25maWdcIjp7XCJkdXJhdGlvblwiOjg2NDAwfX0iLAogICAgICAgICAgImZ1bmRzIjogW10KICAgICAgICB9CiAgICAgIH0KICAgIF0KICB9Cn0=

And then URL-escaping the base64 string simply replaces the = at the end with %3D:

ewogICJpZCI6ICJEYW9Qcm9wb3NhbFNpbmdsZSIsCiAgImRhdGEiOiB7CiAgICAidGl0bGUiOiAiRXhhbXBsZSBQcm9wb3NhbCIsCiAgICAiZGVzY3JpcHRpb24iOiAiVGhpcyBwcm9wb3NhbCB3aWxsIHNlbmQgMTAwIEpVTk8gdG8gb3VyIGNvbW11bml0eSBmdW5kIGFuZCB1cGRhdGUgb3VyIHZvdGluZyBtb2R1bGUncyB1bnN0YWtpbmcgZHVyYXRpb24uIiwKICAgICJhY3Rpb25EYXRhIjogWwogICAgICB7CiAgICAgICAgImFjdGlvbktleSI6ICJzcGVuZCIsCiAgICAgICAgImRhdGEiOiB7CiAgICAgICAgICAiZnJvbUNoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJ0b0NoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJmcm9tIjogImp1bm8xMGgwaGM2NGp2MDA2cnI4cXkwemhsdTRqc3hjdDhxd2EwdnRhbGVheWgwdWp6MHp5bmYyczJyN3Y4cSIsCiAgICAgICAgICAidG8iOiAianVubzFjb21tdW5pdHkuLi5mdW5kNDU2IiwKICAgICAgICAgICJhbW91bnQiOiAiMTAwIiwKICAgICAgICAgICJkZW5vbSI6ICJ1anVubyIKICAgICAgICB9CiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWN0aW9uS2V5IjogImV4ZWN1dGUiLAogICAgICAgICJkYXRhIjogewogICAgICAgICAgImNoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJzZW5kZXIiOiAianVubzEwaDBoYzY0anYwMDZycjhxeTB6aGx1NGpzeGN0OHF3YTB2dGFsZWF5aDB1anowenluZjJzMnI3djhxIiwKICAgICAgICAgICJhZGRyZXNzIjogImp1bm8xdm90aW5nLi4ubW9kdWxlNzg5IiwKICAgICAgICAgICJtZXNzYWdlIjogIntcInVwZGF0ZV9jb25maWdcIjp7XCJkdXJhdGlvblwiOjg2NDAwfX0iLAogICAgICAgICAgImZ1bmRzIjogW10KICAgICAgICB9CiAgICAgIH0KICAgIF0KICB9Cn0%3D

Using the DAO DAO DAO address and then adding the prefill parameter to the URL gives us the proposal creation page with the proposal prefilled:

https://daodao.zone/dao/juno10h0hc64jv006rr8qy0zhlu4jsxct8qwa0vtaleayh0ujz0zynf2s2r7v8q/proposals/create?prefill=ewogICJpZCI6ICJEYW9Qcm9wb3NhbFNpbmdsZSIsCiAgImRhdGEiOiB7CiAgICAidGl0bGUiOiAiRXhhbXBsZSBQcm9wb3NhbCIsCiAgICAiZGVzY3JpcHRpb24iOiAiVGhpcyBwcm9wb3NhbCB3aWxsIHNlbmQgMTAwIEpVTk8gdG8gb3VyIGNvbW11bml0eSBmdW5kIGFuZCB1cGRhdGUgb3VyIHZvdGluZyBtb2R1bGUncyB1bnN0YWtpbmcgZHVyYXRpb24uIiwKICAgICJhY3Rpb25EYXRhIjogWwogICAgICB7CiAgICAgICAgImFjdGlvbktleSI6ICJzcGVuZCIsCiAgICAgICAgImRhdGEiOiB7CiAgICAgICAgICAiZnJvbUNoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJ0b0NoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJmcm9tIjogImp1bm8xMGgwaGM2NGp2MDA2cnI4cXkwemhsdTRqc3hjdDhxd2EwdnRhbGVheWgwdWp6MHp5bmYyczJyN3Y4cSIsCiAgICAgICAgICAidG8iOiAianVubzFjb21tdW5pdHkuLi5mdW5kNDU2IiwKICAgICAgICAgICJhbW91bnQiOiAiMTAwIiwKICAgICAgICAgICJkZW5vbSI6ICJ1anVubyIKICAgICAgICB9CiAgICAgIH0sCiAgICAgIHsKICAgICAgICAiYWN0aW9uS2V5IjogImV4ZWN1dGUiLAogICAgICAgICJkYXRhIjogewogICAgICAgICAgImNoYWluSWQiOiAianVuby0xIiwKICAgICAgICAgICJzZW5kZXIiOiAianVubzEwaDBoYzY0anYwMDZycjhxeTB6aGx1NGpzeGN0OHF3YTB2dGFsZWF5aDB1anowenluZjJzMnI3djhxIiwKICAgICAgICAgICJhZGRyZXNzIjogImp1bm8xdm90aW5nLi4ubW9kdWxlNzg5IiwKICAgICAgICAgICJtZXNzYWdlIjogIntcInVwZGF0ZV9jb25maWdcIjp7XCJkdXJhdGlvblwiOjg2NDAwfX0iLAogICAgICAgICAgImZ1bmRzIjogW10KICAgICAgICB9CiAgICAgIH0KICAgIF0KICB9Cn0%3D