Reverie, Topluluk Hazinesinden $ethDYDX aktarmak amacıyla, dYdX yönetişim sözleşmeleri repository'sine bir Çekme İsteği (PR) yaparak bir yönetişim teklifi gönderilmesi hakkında kapsamlı, teknik bir kılavuz hazırladı.
Bu teklifi oluşturabilmesi için, bir dYdX topluluğu üyesinin, teklif gücünün en az (short timelock oylama için teklif eşiği olan) 5 milyon Yönetişim Token'ına(toplam arzın %0,5'i) sahip olması gerekir.
Ön Koşullar
Çekme Talebi (PR) tamamlanmadan önce aşağıdaki adımlar tamamlanmalıdır:
Teklif Yaşam Döngüsü: Teklif şablonunun ardından DRC gönderilmeli ve başarılı bir Snapshot oylaması gerçekleştirilmelidir.
Hedef Adresi: Hedef adresi, zamanı gelmeden oluşturulmalıdır. Hedef adresi bir multi-sig (çok imzalı adres) ise, multi-sig cüzdanı oluşturulmalıdır.
GitHub hesabı: Repository'yi fork etmek için bir GitHub hesabı.
Transfer Miktarı (İsteğe Bağlı): Tercihen PR öncesinde istenen transfer miktarı belirlenmelidir. Bununla birlikte, bir itibari (notional) miktar kullanılıyorsa, onay öncesinde son adım olarak belirlenebilir.
DIP IPFS Karması (İsteğe Bağlı): Transfer miktarı biliniyorsa, DIP kesinleştirilip IPFS'ye push edilerek karması (hash) oluşturulmalıdır. Bununla birlikte, miktar henüz belirlenmemişse, onay öncesinde son adımda belirlenebilir.
src/config/index.ts dosyasında test amaçları için kullanılacak configSchema sabitine iki yeni değişken ekleyin. Aşağıdaki kod bloklarında 'PROPOSAL_NAME' ve 'PROPOSAL' alanlarını gönderilecek teklifin adı olarak değiştirin.
Not: ERC20 standardı gereğince fonlama miktarının 10^18 ile çarpılması gerekecektir. Miktar henüz bilinmiyorsa, geçici bir miktar kullanılabilir (ör. 10 → 10000000000000000000)
src/lib/constants.ts...// Add a link to where the hash can be foundexportconstDIP_NUMBER_IPFS_HASH='0x0000000000000000000000000000000000000000000000000000000000000000';
Not: DIP henüz yayınlanmadıysa, test için geçici bir değer kullanılabilir (ör. ‘0x0000000000000000000000000000000000000000000000000000000000000000’)\
4. Teklif kodu
src/migrations dizininde teklifin adını taşıyan yeni bir dosya oluşturun → teklifin-adı.ts ve bu dosyaya aşağıdaki kodu ekleyin:
b. Hardhat görevini oluşturun ve içine görev açılış satırındaki teklif bilgilerini ekleyin.
‘deploy:proposal-name:‘in yerine teklifin adını girin ve ‘Proposal Description’ın yerine kısa bir açıklama girin.
Son satırda, teklif kodundan import ettiğiniz fonksiyon çağrılır; bu nedenle bunun da değiştirilmesi gerekir.
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. Testlerin hazırlanması
Kod devreye alıma (deployment) hazır olduğuna göre, artık teklif üzerinde bazı testler oluşturma zamanı geldi. Testler hem yerel olarak yapılır, hem de zincir üzerinde yürütülen bir teklifi simüle etmek için bir mainnet forku kullanılarak yapılır.
a. Teklif Testleri Ekleyin
test/migrations dizininde teklifin adını taşıyan yeni bir dosya oluşturun → teklifin-adı.ts ve içine aşağıdaki kodu ekleyin:
Teklif fonksiyonları dahil olmak üzere gereken importları ekleyin:
createProposalNameProposal → bu, /src/migrations/teklifin-adı içinde oluşturduğumuz fonksiyondur. \
MOCK_PROPOSAL_IPFS_HASH → test için bir sahte (mock) karma (hash) kullanacağız
Genel bir test fonksiyonu oluşturarak her iki fonksiyon için de testler ekleyin → executeProposalNameProposalForTest; bu fonksiyonun adını teklif adı ile uyacak şekilde değiştirin
Ayrıca daha önce oluşturulan config değişkeni TEST_PROPOSAL_NAME_TRUST_WITH_PROPOSAL'ı ve deployConfig'den PROPOSAL_NAME_ADDRESS'i çağıracağız
...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
Bu fonksiyonu son else döngüsünün dışında, getDeployedContractsForTest() fonksiyonuna ekleyin:
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. En Son Test dosyası
Son olarak, her şeyin beklendiği gibi sonuçlanmasını sağlamak için sahte (mock) tekliften sonra hem IPFS karması hem de multisig bakiyesi (balance) için bir test ekliyoruz.
test/misc dizinine teklifin adı ile etiketlenmiş yeni bir dosya ekleyin → teklifin-adı-proposal.spec.ts ve dosya içine aşağıdaki iki testi ekleyin:
DIP_NUMBER_IPFS_HASH üzerinden lib'deki IPFS Hash'i import ediyoruz
ProposalNameId'yi kullanarak bir sonraki proposalId numarasını doğrudan kodun içine ekliyoruz
teklif karmasının sabit Karma'ya eşit olup olmadığını kontrol ediyoruz
PROPOSAL_NAME_ADDRESS içindeki bakiyenin (balance) beklendiği gibi PROPOSAL_FUNDING_AMOUNT bakiyesine (balance) eşit olup olmadığını kontrol ediyoruz
Not: Bu adreste zaten DYDX varsa, testten geçmek için bakiyeye (balance) doğrudan kodun içine eklemeniz gerekecektir
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. PR (Pull Request) gönderilmesi
Tüm bu kod değişiklikleri yapılıp yerel olarak kaydedildikten sonra, fork edilen repository'ye commit yapabilir ve gözden geçirilmesi için dYdX repository'sine bir PR gönderebiliriz: