Skip to content

Commit cabd925

Browse files
committed
test script and env fix
1 parent 74b6182 commit cabd925

5 files changed

Lines changed: 100 additions & 23 deletions

File tree

scripts/dispenser-tests/dispenser.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('ERC20 Vault Integration', () => {
6767
ENV.SUBSTRATE_CHAIN_ID,
6868
)
6969

70-
const derived = deriveEthAddress()
70+
const derived = deriveEthAddress(DISPENSER_SIGNING_PATH, palletSS58Prefix0)
7171
derivedEthAddress = derived.derivedEthAddress
7272
derivedPubKey = derived.derivedPubKey
7373

scripts/dispenser-tests/env.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ const GAS_LIMIT = envBigInt('GAS_LIMIT', 100_000n)
9090
const DEFAULT_MAX_FEE_PER_GAS = envBigInt('DEFAULT_MAX_FEE_PER_GAS', 30_000_000_000n)
9191
const DEFAULT_MAX_PRIORITY_FEE_PER_GAS = envBigInt('DEFAULT_MAX_PRIORITY_FEE_PER_GAS', 2_000_000_000n)
9292

93+
const FAUCET_OWNER_KEY = env('FAUCET_OWNER_KEY')
94+
const EVM_FUNDER_KEY = env('EVM_FUNDER_KEY')
95+
9396
// Validate critical values
9497
if (!ethers.isAddress(FAUCET_ADDRESS)) throw new Error(`Invalid FAUCET_ADDRESS: ${FAUCET_ADDRESS}`)
9598
if (!ethers.isAddress(TARGET_ADDRESS)) throw new Error(`Invalid TARGET_ADDRESS: ${TARGET_ADDRESS}`)
@@ -109,6 +112,8 @@ export const ENV = {
109112
EVM_CHAIN_ID,
110113
ROOT_PUBLIC_KEY,
111114
FAUCET_ADDRESS,
115+
FAUCET_OWNER_KEY,
116+
EVM_FUNDER_KEY,
112117

113118
// Test params
114119
TARGET_ADDRESS,

scripts/dispenser-tests/key-derivation.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ export class KeyDerivation {
66

77
static derivePublicKey(
88
rootPublicKey: string,
9+
predecessorId: string,
10+
path: string,
911
chainId: string
1012
): string {
1113
const ec = new EC("secp256k1");
1214

1315
const uncompressedRoot = rootPublicKey.slice(4);
1416

15-
const derivationPath = `${this.EPSILON_PREFIX},${chainId}`;
17+
const derivationPath = `${this.EPSILON_PREFIX},${chainId},${predecessorId},${path}`;
1618
const hash = ethers.keccak256(ethers.toUtf8Bytes(derivationPath));
1719
const scalarHex = hash.slice(2);
1820

scripts/dispenser-tests/utils.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -682,12 +682,17 @@ async function ensureAliceHasFaucetAsset(
682682
console.log(`Alice faucet asset (${faucetAsset}) balance after mint: ${afterBal}`)
683683
}
684684

685-
export function deriveEthAddress(): {
685+
export function deriveEthAddress(
686+
path: string,
687+
palletSS58: string,
688+
): {
686689
derivedPubKey: string
687690
derivedEthAddress: string
688691
} {
689692
const derivedPubKey = KeyDerivation.derivePublicKey(
690693
ENV.ROOT_PUBLIC_KEY,
694+
palletSS58,
695+
path,
691696
ENV.SUBSTRATE_CHAIN_ID,
692697
)
693698

@@ -716,11 +721,12 @@ export async function ensureDerivedEthHasGas(
716721

717722
if (ethBalance >= estimatedGas) return
718723

719-
if (ENV.EVM_NETWORK === 'anvil') {
720-
const ANVIL_DEFAULT_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
721-
const funder = new ethers.Wallet(ANVIL_DEFAULT_KEY, provider)
724+
const ANVIL_DEFAULT_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
725+
const funderKey = ENV.EVM_NETWORK === 'anvil' ? ANVIL_DEFAULT_KEY : ENV.EVM_FUNDER_KEY
726+
if (funderKey) {
727+
const funder = new ethers.Wallet(funderKey, provider)
722728
const fundAmount = estimatedGas * 10n
723-
console.log(`Funding ${derivedEthAddress} with ${ethers.formatEther(fundAmount)} ETH from Anvil account...`)
729+
console.log(`Funding ${derivedEthAddress} with ${ethers.formatEther(fundAmount)} ETH...`)
724730
const tx = await funder.sendTransaction({ to: derivedEthAddress, value: fundAmount })
725731
await tx.wait()
726732
console.log(`Funded. Tx: ${tx.hash}`)
@@ -731,7 +737,7 @@ export async function ensureDerivedEthHasGas(
731737
`Insufficient ETH at ${derivedEthAddress}\n` +
732738
` Need: ${ethers.formatEther(estimatedGas)} ETH\n` +
733739
` Have: ${ethers.formatEther(ethBalance)} ETH\n` +
734-
` Please fund this address with ETH for gas`,
740+
` Fund this address with ETH for gas, or set EVM_FUNDER_KEY to auto-fund`,
735741
)
736742
}
737743

@@ -759,7 +765,15 @@ export async function ensureFaucetMpcAddress(
759765

760766
console.log(`Setting faucet MPC to derived address ${derivedEthAddress}...`)
761767
// Use Anvil account 0 (deployer/owner) to call setMPC
762-
const ownerKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
768+
const ANVIL_OWNER_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
769+
const ownerKey = ENV.EVM_NETWORK === 'anvil' ? ANVIL_OWNER_KEY : ENV.FAUCET_OWNER_KEY
770+
if (!ownerKey) {
771+
throw new Error(
772+
`Faucet MPC is ${currentMpc} but derived address is ${derivedEthAddress}.\n` +
773+
` Set FAUCET_OWNER_KEY (owner of ${ENV.FAUCET_ADDRESS}) to auto-setMPC, ` +
774+
`or call setMPC(${derivedEthAddress}) manually from the owner.`,
775+
)
776+
}
763777
const ownerWallet = new ethers.Wallet(ownerKey, provider)
764778
const contractWithSigner = faucetContract.connect(ownerWallet)
765779
const tx = await (contractWithSigner as any).setMPC(derivedEthAddress)
@@ -860,14 +874,15 @@ export async function executeAsRoot(
860874
return
861875
}
862876

863-
// Try TC path first — much faster than governance referendum
864877
const isTcMember = await isSignerTcMember(api, signer)
865878
if (isTcMember) {
866-
await executeViaTechCommittee(api, signer, call, label)
867-
return
879+
const applied = await executeViaTechCommittee(api, signer, call, label)
880+
if (applied) return
881+
console.log(`${label}: TC motion did not apply the call (needs Root); falling back to referendum`)
882+
} else {
883+
console.log(`${label}: signer is not a TC member, using referendum`)
868884
}
869885

870-
console.log(`${label}: signer is not a TC member, falling back to referendum`)
871886
await executeAsRootViaReferendum(api, signer, call, label)
872887
}
873888

@@ -953,7 +968,7 @@ async function executeViaTechCommittee(
953968
signer: any,
954969
call: SubmittableExtrinsic<'promise'>,
955970
label: string,
956-
) {
971+
): Promise<boolean> {
957972
const members = await (api.query as any).technicalCommittee.members()
958973
const memberList = members.toJSON() as string[]
959974
const memberCount = memberList.length
@@ -981,20 +996,26 @@ async function executeViaTechCommittee(
981996
}
982997
}
983998

984-
// Check if the call was executed (Executed event = threshold was 1 or auto-closed)
985-
const executed = result.events.some(
986-
(r: any) =>
987-
r.event.section === 'technicalCommittee' &&
988-
(r.event.method === 'Executed' || r.event.method === 'Closed'),
989-
)
999+
for (const { event } of result.events) {
1000+
if (event.section === 'technicalCommittee' && event.method === 'Executed') {
1001+
const res: any = (event.data as any).result ?? event.data[1]
1002+
if (res && res.isErr) {
1003+
console.warn(`${label}: TC motion executed but inner call failed: ${res.asErr.toString()}`)
1004+
return false
1005+
}
1006+
console.log(`${label}: TC proposal executed immediately`)
1007+
return true
1008+
}
1009+
}
9901010

991-
if (executed) {
992-
console.log(`${label}: TC proposal executed immediately`)
993-
} else if (threshold > 1) {
1011+
if (threshold > 1) {
9941012
console.log(`${label}: TC proposal needs ${threshold - 1} more Aye votes from other members`)
9951013
// Poll for execution (other TC members may vote via separate process)
9961014
await pollForTcExecution(api, result, label)
1015+
return true
9971016
}
1017+
1018+
return false
9981019
}
9991020

10001021
/**
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { waitReady } from '@polkadot/wasm-crypto'
2+
import {
3+
createApi,
4+
createKeyringAndAccounts,
5+
executeAsRoot,
6+
getTokenFree,
7+
} from './utils'
8+
9+
async function main() {
10+
await waitReady()
11+
const api = await createApi()
12+
13+
const faucetAsset = (api.consts.ethDispenser.faucetAsset as any).toNumber()
14+
const { alice } = createKeyringAndAccounts()
15+
16+
const before = await getTokenFree(api, alice.address, faucetAsset)
17+
console.log(`\n[validate] Alice = ${alice.address}`)
18+
console.log(`[validate] asset ${faucetAsset} BEFORE = ${before.toString()}`)
19+
20+
const mintAmount = 1_000_000_000_000_000n // 0.001 WETH (18 decimals)
21+
const mintCall = (api.tx as any).currencies.updateBalance(
22+
alice.address,
23+
faucetAsset,
24+
mintAmount.toString(),
25+
)
26+
27+
console.log(`[validate] minting ${mintAmount} via executeAsRoot (TC → referendum fallback)...`)
28+
await executeAsRoot(api, alice, mintCall, 'VALIDATE mint faucet asset to Alice')
29+
30+
const after = await getTokenFree(api, alice.address, faucetAsset)
31+
const delta = after - before
32+
console.log(`[validate] asset ${faucetAsset} AFTER = ${after.toString()}`)
33+
console.log(`[validate] delta = ${delta.toString()}`)
34+
console.log(
35+
`[validate] RESULT = ${
36+
delta > 0n
37+
? 'PASS ✅ balance increased — executeAsRoot applied the Root call'
38+
: 'NO-CHANGE ❌ Root call did not apply within the poll window (see log above)'
39+
}`,
40+
)
41+
42+
await api.disconnect()
43+
process.exit(delta > 0n ? 0 : 2)
44+
}
45+
46+
main().catch((e) => {
47+
console.error('[validate] FATAL', e)
48+
process.exit(1)
49+
})

0 commit comments

Comments
 (0)