Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a3ae817
treasury payouts post migration test
dhirajs0 Oct 27, 2025
8364248
treasury payputs script added
dhirajs0 Oct 27, 2025
08d7caf
just command added for treasury payouts
dhirajs0 Oct 29, 2025
984edf8
first letter of chains capitalized
dhirajs0 Oct 29, 2025
e9d5876
fix
dhirajs0 Oct 29, 2025
1a65855
Merge branch 'main' into treasury-payouts-post-migration
x3c41a Oct 30, 2025
7b2b362
Update justfiles/ahm.justfile
dhirajs0 Nov 3, 2025
274ace6
Update justfiles/ahm.justfile
dhirajs0 Nov 3, 2025
750a6f3
Update justfiles/ahm.justfile
dhirajs0 Nov 3, 2025
06f6920
Update package.json
dhirajs0 Nov 3, 2025
a0db8e2
Merge branch 'main' into treasury-payouts-post-migration
x3c41a Nov 3, 2025
42a6719
Revert "Update package.json"
dhirajs0 Nov 3, 2025
52662db
alice addres fetched from the alicePair
dhirajs0 Nov 3, 2025
b4c6ef2
Revert "Update justfiles/ahm.justfile"
dhirajs0 Nov 3, 2025
43409cf
removed the early payout filter from the spends
dhirajs0 Nov 3, 2025
430669a
helper function to extract beneficiary address from the spend data
dhirajs0 Nov 3, 2025
3b8bf96
Added utility functions to extract asset type information from spendData
dhirajs0 Nov 3, 2025
c3923a4
Created a helper function to retrieve beneficiary balances based on a…
dhirajs0 Nov 3, 2025
984166f
updated the validFrom block to a past block to make the spend eligibl…
dhirajs0 Nov 3, 2025
86d7b65
Added validation to ensure beneficiary balance after payout increased…
dhirajs0 Nov 3, 2025
7be7830
Merge branch 'main' into treasury-payouts-post-migration
dhirajs0 Nov 3, 2025
0b31328
Merge branch 'main' into treasury-payouts-post-migration
x3c41a Nov 4, 2025
fbac063
Merge branch 'main' into treasury-payouts-post-migration
dhirajs0 Nov 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions justfiles/ahm.justfile

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, when ahm is over, we either need to merge it or close.

Leaving this one up to you

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let me resolve the merge conflict. Then we are good to go.

Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,16 @@ rust-test runtime base_path:
--features {{ runtime }}-ahm \
--features try-runtime \
post_migration_checks_only -- --include-ignored --nocapture --test-threads 1

# Run treasury payout tests for a given network
treasury-payouts network="polkadot":
#!/usr/bin/env bash
set -ex
Comment thread
dhirajs0 marked this conversation as resolved.
Outdated

if [[ "{{ network }}" != "kusama" && "{{ network }}" != "polkadot" ]]; then
echo "Error: network must be one of: kusama, polkadot"
exit 1
fi

just ahm _npm-build
node dist/migration-tests/treasury_payout/run_treasury_payouts.js {{ network }}
Comment thread
dhirajs0 marked this conversation as resolved.
24 changes: 24 additions & 0 deletions migration-tests/treasury_payout/run_treasury_payouts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { runTreasuryPayoutTests } from './treasury_payouts.js';
import { logger } from '../../shared/logger.js';

const main = async () => {
const network = process.argv[2] || 'polkadot';

if (network !== 'kusama' && network !== 'polkadot') {
logger.error('Invalid network. Please specify "kusama" or "polkadot"');
process.exit(1);
}

try {
await runTreasuryPayoutTests(network as 'kusama' | 'polkadot');
process.exit(0);
} catch (error) {
logger.error('❌ Tests failed:', error);
process.exit(1);
}
};

main().catch((error) => {
logger.error('Unexpected error:', error);
process.exit(1);
});
187 changes: 187 additions & 0 deletions migration-tests/treasury_payout/treasury_payouts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import '@polkadot/api-augment';
import '@polkadot/types-augment';
import { sendTransaction, setupNetworks } from '@acala-network/chopsticks-testing';
import { Keyring } from '@polkadot/api';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { logger } from '../../shared/logger.js';
import assert from 'assert';

// Wait for crypto to be ready before creating Keyring
await cryptoWaitReady();
const keyring = new Keyring({ type: 'sr25519' });
const alicePair = keyring.addFromUri('//Alice');

export interface NetworkConfig {
relayEndpoint: string;
relayPort: number;
assetHubEndpoint: string;
assetHubPort: number;
}


/**
* @param networkName - 'kusama' or 'polkadot'
Comment thread
dhirajs0 marked this conversation as resolved.
Outdated
* @param config - Network configuration with endpoints and ports
*/
async function testTreasuryPayouts(networkName: 'kusama' | 'polkadot', config: NetworkConfig): Promise<void> {
logger.debug(`Starting treasury payouts test on forked ${networkName} network`);

// Setup networks based on configuration
const networks = await setupNetworks({
[networkName]: {
endpoint: config.relayEndpoint,
port: config.relayPort,
},
[networkName === 'kusama' ? 'assetHubKusama' : 'assetHubPolkadot']: {
Comment thread
dhirajs0 marked this conversation as resolved.
Outdated
endpoint: config.assetHubEndpoint,
port: config.assetHubPort,
},
});

const relayChain = networkName === 'kusama'
? (networks as any).kusama
: (networks as any).polkadot;
const assetHub = networkName === 'kusama'
? (networks as any).assetHubKusama
: (networks as any).assetHubPolkadot;

try {
// Fund Alice account for transaction fees
const aliceAddress = '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5';
Comment thread
dhirajs0 marked this conversation as resolved.
Outdated
const fundingAmount = 1000e10;

await assetHub.dev.setStorage({
System: {
account: [
[
[aliceAddress],
{
providers: 1,
data: {
free: fundingAmount
}
}
]
]
}
});

logger.debug('✅ Alice account funded successfully');

// Get all spends from Asset Hub
const spends = await assetHub.api.query.treasury.spends.entries();
logger.debug(`Found ${spends.length} total spends`);

// Get current relay chain block number
const currentRelayChainBlockNumber = (await relayChain.api.query.system.number()).toNumber();
logger.debug(`Current relay chain block number: ${currentRelayChainBlockNumber}`);

// Filter spends which are pending or failed and are neither expired nor early payout
const pendingOrFailedSpends = spends.filter((spend: any) => {
const spendData = spend[1]?.unwrap();
return (
(spendData?.status.isPending || spendData?.status.isFailed) && // pending or failed
spendData?.validFrom.toNumber() < currentRelayChainBlockNumber && // not early payout
spendData?.expireAt.toNumber() > currentRelayChainBlockNumber // not expired

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat 👏

);
});

logger.debug(`Found ${pendingOrFailedSpends.length} eligible spends for payout testing`);

if (pendingOrFailedSpends.length === 0) {
logger.info('No eligible spends found for payout testing');
return;
}

// Test payout for each eligible spend
for (const spend of pendingOrFailedSpends) {
const spendIndex = spend[0].toHuman?.() as number;

try {
// Create and sign the payout transaction
const payoutTx = assetHub.api.tx.treasury.payout(spendIndex);
await sendTransaction(payoutTx.signAsync(alicePair));

await assetHub.api.rpc('dev_newBlock', { count: 1 });

// Check for Paid event
const events = await assetHub.api.query.system.events();
const paidEvent = events.find((record: any) => {
const { event } = record;
return event.section === 'treasury' && event.method === 'Paid';
});

assert(paidEvent, `Paid event is not found for spend ${spendIndex} Event: ${events.map((record: any) => record.event.toHuman()).join(', ')}`);
assert(assetHub.api.events.treasury.Paid.is(paidEvent.event), `Paid event is not found for spend ${spendIndex} Event: ${paidEvent.event.toHuman()}`);

// Verify the spend status changed to attempted
const spendAfter = await assetHub.api.query.treasury.spends(spendIndex);
const spendDataAfter = spendAfter?.unwrap();
assert(spendDataAfter?.status.isAttempted, `Spend ${spendIndex} status is not attempted ${spendDataAfter?.status.toHuman()}`);

// check status of the spend in the Asset Hub
const checkStatusTx = assetHub.api.tx.treasury.checkStatus(spendIndex);
await sendTransaction(checkStatusTx.signAsync(alicePair));

await assetHub.api.rpc('dev_newBlock', { count: 1 });

// check for SpendProcessed event
const eventsAfterCheckStatus = await assetHub.api.query.system.events();
const spendProcessedEvent = eventsAfterCheckStatus.find((record: any) => {
const { event } = record;
return event.section === 'treasury' && event.method === 'SpendProcessed';
});
assert(spendProcessedEvent, `SpendProcessed event is not found for spend ${spendIndex} Event: ${eventsAfterCheckStatus.map((record: any) => record.event.toHuman()).join(', ')}`);
assert(assetHub.api.events.treasury.SpendProcessed.is(spendProcessedEvent.event), `SpendProcessed event is not found for spend ${spendIndex} Event: ${spendProcessedEvent.event.toHuman()}`);
} catch (error) {
logger.error(`❌ Failed to execute payout for spend ${spendIndex}:`, error);
continue; // continue with next spend
}
}

logger.debug('✅ Treasury payouts test completed successfully');

} catch (error) {
logger.error('❌ Treasury payouts test failed:', error);
throw error;
} finally {
// Cleanup
await relayChain.teardown();
await assetHub.teardown();
}
}


const polkadot = () => {
return {
relayEndpoint: 'wss://polkadot-rpc.n.dwellir.com',
relayPort: 8008,
assetHubEndpoint: 'wss://asset-hub-polkadot-rpc.n.dwellir.com',
assetHubPort: 8009,
}
}

const kusama = () => {
return {
relayEndpoint: 'wss://rpc.ibp.network/kusama',
relayPort: 8008,
assetHubEndpoint: 'wss://sys.ibp.network/asset-hub-kusama',
assetHubPort: 8009,
}
}
// Note: The test on polkadot will work only after treasury is migrated to asset hub on polkadot.
/**
* Main function to run treasury payout tests
* @param network - 'kusama' or 'polkadot'
*/
export async function runTreasuryPayoutTests(network: 'kusama' | 'polkadot'): Promise<void> {
try {
const config = network === 'kusama' ? kusama() : polkadot();
await testTreasuryPayouts(network, config);
logger.info(`✅ Treasury payout tests completed successfully for ${network}`);
} catch (error) {
logger.error(`❌ Treasury payout tests failed for ${network}:`, error);
throw error;
}
}

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"authorize-upgrade": "node dist/zombie-bite-scripts/authorize_upgrade_ah.js",
"report-account-migration-status": "node dist/zombie-bite-scripts/report_account_migration_status.js",
"compare-state": "node dist/migration-tests/index.js",
"treasury-payouts": "node dist/migration-tests/treasury_payout/run_treasury_payouts.js",

@dhirajs0 dhirajs0 Oct 27, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a script for this test.

Comment thread
dhirajs0 marked this conversation as resolved.
Comment thread
dhirajs0 marked this conversation as resolved.
"find-rc-block-bite": "node dist/zombie-bite-scripts/find_rc_block_bite.js",
"make-new-snapshot": "node dist/zombie-bite-scripts/make_new_snapshot.js",
"create-migration-done-file": "node dist/zombie-bite-scripts/create_migration_done_file.js",
Expand Down