Technical Guide on building a dYdX Community Treasury Spending Proposal
A technical, step-by-step guide on how to create a proposal to transfer ethDYDX from the community treasury to a destination address.
Reverie has put together a comprehensive, technical guide for submitting a governance proposal to transfer $ethDYDX from the Community Treasury through a Pull Request (PR) to the dYdX governance-contracts repository.
To create this proposal, a dYdX community member must have at least 5M Governance Tokens (0.5% of total supply) of proposal power (proposal threshold for a short timelock vote).
Preliminary Requirements
The following steps must be completed ahead of completing the Pull Request (PR):
Proposal Lifecycle: The DRC must be posted following the proposal template and there must be a successful Snapshot vote.
Destination Address: The destination address must be generated ahead of time. If the destination address is a multi-sig, the multi-sig wallet must be created.
GitHub account: A GitHub account to fork the repository.
Transfer Amount (Optional): Preferably, the requested transfer amount has been established ahead of the PR. However, if using a notional amount, it can be set as a final step prior to approval.
DIP IPFS Hash (Optional): If the transfer amount is known, the DIP should be finalized and pushed to IPFS to generate it’s hash. However, this can be set a final step prior to approval if the amount is not yet determined.
Building the Proposal
Fork the dYdX governance-contracts repository to your GitHub account.

2. Clone the Repository, and change the [username] to your own.
git clone https://github.com/[username]/governance-contracts.git3. Configuration Variables
In src/config/index.ts, add two new variables to the configSchema constant that will be used for testing purposes. In the following code blocks, change the 'PROPOSAL_NAME' and 'PROPOSAL' fields to the name of the proposal being submitted.
In src/deploy-config/base-config.ts, add the destination address and transfer amount as new variables in the config constant:
Note: The funding amount will need to be multiplied by 10^18 per ERC20 standard. If the amount is not yet known, a temporary amount can be used (e.g. 10 → 10000000000000000000)
In src/lib/constants.ts, add the IPFS hash variable that will reference the DIP approved in the other repository:
Note: If the DIP hasn’t been published yet, a temporary value can be used for testing (e.g. ‘0x0000000000000000000000000000000000000000000000000000000000000000’)
4. Proposal code
In src/migrations, create a new file named after the proposal → proposal-name.ts and populate with the following code:
a. Add the imports needed at the top:
b. Create a new function using the proposal name below the imports and add the following code with two unique variables:
destinationAddress → this will be the address that receives the funding
deployConfig.PROPOSAL_FUNDING_AMOUNT → this is the variable we created earlier that will determine the amount to be transferred
5. Deployment Task
With the proposal created, we can write the deployment that will generate the transaction and calldata needed to submit the proposal.
In tasks/deployment, create a new file with the same name used for the proposal code → proposal-name.ts and populate with the following code:
a. Add the imports needed with the following variables:
DIP_NUMBER_IPFS_HASH → this is the variable we add in lib/constants
createProposalNameProposal → this is the function we created in /src/migrations/proposal-name
b. Create the hardhat task and populate it with the proposal information on the task opening line. Replace with the proposal-name in ‘deploy:proposal-name: and replace with a brief description in ‘Proposal Description’.
The last line calls the function you imported from the proposal code, so that will need to be adjusted.
6. Building tests
Now that the code is ready for deployment, it’s time to build some tests around the proposal. Testing is done both locally and using a mainnet fork to simulate a proposal being executed on-chain.
a. Add Proposal Tests
In test/migrations, add a new file again with the proposal name → proposal-name.ts and include the following code:
Add the imports needed including the proposal functions:
createProposalNameProposal → this is the function we created in /src/migrations/proposal-name.
MOCK_PROPOSAL_IPFS_HASH → we’ll use a mock hash for testing purposes
Add the testing functions with the following steps:
fundProposalNameViaProposal → create this function and rename it to match the proposal name.
destinationAddress → re-label this to match the destination name
deployConfig.PROPOSAL_FUNDING_AMOUNT → this will be replaced by the variable from the base-config file
FUND_PROPOSAL_NAME_PROPOSAL_ID → this is the variable we created in config/index.ts
createProposalNameProposal → imported this function above to be used
fundProposalNameViaNoProposal → create this function and rename it to match the proposal name
Run through the code below to replace all these variables with the proposal name and existing variables already created above:
b. Add Testing Functions to Test Script
In test/migrations/deploy-contracts-for-test.ts, we’ll add the functions created above so they are included in our tests:
Import the functions created
Add tests for both functions by creating a general testing function → executeProposalNameProposalForTest, replace the name to match the proposal
We also call the config variable TEST_PROPOSAL_NAME_TRUST_WITH_PROPOSAL previously created and the PROPOSAL_NAME_ADDRESS from deployConfig
c. Add contract to Testing helpers
In test/helpers/get-deployed-contracts-for-test.ts, add the function created above so that the tests are run in the mainnet fork testing:
Import the executeProposalNameProposalForTest function from the migrations file:
Add the function to the getDeployedContractsForTest() function, outside of the last else loop:
d. Final Test file
Finally, we add a test of both the IPFS hash and the balance of the multisig after the mock proposal to ensure everything ends up as expected.
In test/misc, add a new file with the proposal name labelled → proposal-name-proposal.spec.ts and populate with these two tests:
We import the IPFS Hash from lib through DIP_NUMBER_IPFS_HASH
we hardcode the next proposalId number using ProposalNameId
we check the proposal hash with the constant Hash
we check the PROPOSAL_NAME_ADDRESS to see if it has an expected balance of the PROPOSAL_FUNDING_AMOUNT
Note: if this address already has DYDX, you will need to hardcode into the balance for the test to pass
7. Submitting the PR
Once all these code changes are made and saved locally, we can commit to the forked repository and open a PR to the dYdX repository for review:
a. Commit the changes through the command line
b. Submit a PR to the dYdX repository

c. Wait for review and approval from the repository manager
Last updated
Was this helpful?