A Reverie criou um guia técnico e abrangente para enviar uma proposta de governança a fim de transferir $ethDYDX do Tesouro da Comunidade por meio de uma solicitação pull (PR, na sigla em inglês) ao repositório governance-contracts da dYdX.
Para criar essa proposta, um membro da comunidade dYdX precisa ter pelo menos 5M de tokens de governança(0,5% do total do fornecimento) em poder proposicional (limite de proposta para um voto de timelock curto).
Requisitos preliminares
As etapas a seguir devem ser concluídas antes da solicitação de pull (PR):
Ciclo de vida da proposta: a DRC deve ser postada conforme o modelo de proposta e precisa haver uma votação de Snapshot bem-sucedida.
Endereço de destino: o endereço de destino deve ser gerado com antecedência. Se o endereço de destino for uma carteira multi-sig, ela deve ser criada.
Conta do GitHub: uma conta do GitHub para fazer o fork do repositório.
Valor da transferência (opcional): de preferência, o valor da transferência solicitada precisa ser estabelecido antes do PR. No entanto, se estiver usando um valor referencial, ele poderá ser definido como uma etapa final antes da aprovação.
Hash do IPFS (opcional): se a valor da transferência for conhecido, o DIP deve ser finalizado e enviado para o IPFS a fim de gerar o hash. No entanto, isso pode ser definido em uma etapa final antes da aprovação se o valor ainda não foi determinado.
Em src/config/index.ts, adicione duas novas variáveis à constante configSchema que será usada para fins de teste. Nos blocos de código a seguir, altere os campos 'PROPOSAL_NAME' e 'PROPOSAL' para o nome da proposta que será enviada.
Observação: o valor de financiamento precisará ser multiplicado por 10^18 conforme o padrão ERC20. Se valor ainda não for conhecido, um valor temporário pode ser usado (por exemplo, 10 → 10000000000000000000)
Em src/lib/constants.ts, adicione a variável hash IPFS que fará referência ao DIP aprovado no outro repositório:
src/lib/constants.ts...// Add a link to where the hash can be foundexportconstDIP_NUMBER_IPFS_HASH='0x0000000000000000000000000000000000000000000000000000000000000000';
Observação: se o DIP ainda não tiver sido publicado, um valor temporário pode ser usado para testes (por exemplo, ‘0x0000000000000000000000000000000000000000000000000000000000000000’)\
4. Código de proposta
Em src/migrations, crie um novo arquivo com o nome da proposta → proposal-name.ts e preencha o código a seguir:
b. Crie a tarefa hardhat e preencha-a com as informações da proposta na linha de abertura da tarefa.
Substitua com o nome da proposta em ‘deploy:proposal-name‘ e substitua por uma breve descrição em ‘Descrição da proposta’.
A última linha chama a função que você importou do código da proposta, portanto será preciso ajustá-la.
tasks/deployment/proposal-name.tshardhatTask('deploy:proposal-name','Proposal Description.') .addParam('proposalIpfsHashHex', 'IPFS hash for the uploaded DIP describing the proposal', DIP_NUMBER_IPFS_HASH, types.string)
.addParam('dydxTokenAddress', 'Address of the deployed DYDX token contract', mainnetAddresses.dydxToken, types.string)
.addParam('governorAddress', 'Address of the deployed DydxGovernor contract', mainnetAddresses.governor, types.string)
.addParam('shortTimelockAddress', 'Address of the deployed short timelock Executor contract', mainnetAddresses.shortTimelock, types.string)
.addParam('communityTreasuryAddress', 'Address of the deployed community treasury contract', mainnetAddresses.communityTreasury, types.string)
.setAction(async (args) => {awaitcreateProposalNameProposal(args); });
6. Testes de construção
Agora que o código está pronto para a implantação, é hora de fazer alguns testes na proposta. Os testes são feitos localmente usando um fork da mainnet para simular uma proposta que está sendo executada on-chain.
a. Adicionar testes de proposta
Em test/migrations, adicione novamente um novo arquivo com o nome da proposta → proposal-name.ts e inclua o código a seguir:
Adicione as importações necessárias, incluindo as funções da proposta:
createProposalNameProposal → esta é a função que criamos em /src/migrations/proposal-name \
MOCK_PROPOSAL_IPFS_HASH → usaremos um mock hash para fins de teste
Adicione testes para ambas as funções criando uma função de teste geral → executeProposalNameProposalForTest, substitua o nome para corresponder à proposta
Também chamamos a variável config TEST_PROPOSAL_NAME_TRUST_WITH_PROPOSAL criada anteriormente e a PROPOSAL_NAME_ADDRESS de deployConfig
...exportasyncfunctionexecuteProposalNameProposalForTest( deployedContracts:AllDeployedContracts,) {constdeployConfig=getDeployConfig();if (config.TEST_PROPOSAL_NAME_TRUST_WITH_PROPOSAL) {awaitfundProposalNameViaProposal({ dydxTokenAddress:deployedContracts.dydxToken.address, governorAddress:deployedContracts.governor.address, shortTimelockAddress:deployedContracts.shortTimelock.address, communityTreasuryAddress:deployedContracts.communityTreasury.address, destinationAddress:deployConfig.PROPOSAL_NAME_ADDRESS, }); } else {awaitfundProposalNameNoProposal({ dydxTokenAddress:deployedContracts.dydxToken.address, shortTimelockAddress:deployedContracts.shortTimelock.address, communityTreasuryAddress:deployedContracts.communityTreasury.address, destinationAddress:deployConfig.PROPOSAL_NAME_ADDRESS, }); }}...// put this above the configureForTest function
c. Adicione o contrato aos assistentes de testes
Em test/helpers/get-deployed-contracts-for-test.ts, adicione a função criada acima para que os testes sejam executados nos testes do fork da mainnet:
Importe a função executeProposalNameProposalForTest do arquivo de migrações:
Adicione a função à função getDeployedContractsForTest(), fora do último else loop:
test/helpers/get-deployed-contracts-for-test.tsasyncfunctiongetDeployedContractsForTest():Promise<AllDeployedContracts> {if (!config.isHardhat()) {returngetAllContracts(); }let deployedContracts:AllDeployedContracts;if (config.FORK_MAINNET) { deployedContracts =awaitgetAllContracts(); } else { deployedContracts =awaitdeployContractsForTest();// Execute the proposals which have already been executed on mainnet.//// The proposals will be executed when running on a local test network,// but will not be executed when running on a mainnet fork.awaitexecuteSafetyModuleRecoveryProposalsForTest(deployedContracts);awaitexecuteStarkProxyProposalForTest(deployedContracts);awaitexecuteGrantsProgramProposalForTest(deployedContracts);awaitexecuteGrantsProgramv15ProposalForTest(deployedContracts);awaitexecuteWindDownBorrowingPoolProposalForTest(deployedContracts);awaitexecuteUpdateMerkleDistributorRewardsParametersProposalForTest(deployedContracts);awaitexecuteWindDownSafetyModuleProposalForTest(deployedContracts); }awaitexecuteProposalNameProposalForTest(deployedContracts);// Execute the proposals which have not yet been executed on mainnet.awaitconfigureForTest(deployedContracts);return deployedContracts;}
d. Arquivo de teste final
Por fim, adicionamos um teste da hash de IPFS e o saldo da carteira multisig após a proposta mock para garantir que tudo se dê conforme o esperado.
Em test/misc, adicione um novo arquivo com o nome da proposta rotulado → proposal-name-proposal.spec.ts e preencha-o com estes dois testes:
Importamos o Hash IPFS da biblioteca por meio de DIP_NUMBER_IPFS_HASH
Colocamos direto no código o número de proposalId seguinte usando ProposalNameId
Conferimos a hash da proposta com a constante Hash
Verificamos PROPOSAL_NAME_ADDRESS para conferir se há um saldo esperado em PROPOSAL_FUNDING_AMOUNT
Observação: se este endereço já tiver DYDX, você precisará colocar o saldo direto no código para que o teste passe
test/misc/proposal-name-proposal.spec.tsimport { expect } from'chai';import { DIP_NUMBER_IPFS_HASH } from'../../src/lib/constants';import { describeContract, TestContext } from'../helpers/describe-contract';functioninit() {}describeContract('proposal-name', init, (ctx:TestContext) => {it('Proposal IPFS hash is correct',async () => {constProposalNameId= #;constproposal=awaitctx.governor.getProposalById(ProposalNameId);expect(proposal.ipfsHash).to.equal(DIP_NUMBER_IPFS_HASH); });it('Destination receives tokens from the community treasury',async () => {constbalance=awaitctx.dydxToken.balanceOf(ctx.config.PROPOSAL_NAME_ADDRESS);expect(balance).to.equal(ctx.config.PROPOSAL_FUNDING_AMOUNT); });});
7. Envio do PR
Quando todas essas alterações de código forem feitas e salvas localmente, podemos enviar o commit ao fork do repositório e abrir um PR para o repositório dYdX, que entrará para revisão:
a. Faça o commit das alterações pela linha de comando