@polkadot/util-crypto#blake2AsHex TypeScript Examples
The following examples show how to use
@polkadot/util-crypto#blake2AsHex.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: test-authorize-upgrade-hash.ts From moonbeam with GNU General Public License v3.0 | 6 votes |
describe("Compute AuthorizeUpgrade hash", () => {
it("Srtool should compute with prefix 0x0103", function () {
let authorizeUpgradeHash1 = blake2AsHex("0x0103" + MOONBASE_RUNTIME_701_BLAKE2_256);
expect(authorizeUpgradeHash1).to.equal(MOONBASE_RUNTIME_701_AUTHORIZE_UPGRADE_SRTOOL);
});
it("Real hash should be computed with prefix 0x0603", function () {
let authorizeUpgradeHash6 = blake2AsHex("0x0603" + MOONBASE_RUNTIME_701_BLAKE2_256);
expect(authorizeUpgradeHash6).to.equal(MOONBASE_RUNTIME_701_AUTHORIZE_UPGRADE_REAL);
});
});
Example #2
Source File: test-precompile-democracy.ts From moonbeam with GNU General Public License v3.0 | 6 votes |
notePreimagePrecompile = async <
Call extends SubmittableExtrinsic<ApiType>,
ApiType extends ApiTypes
>(
context: DevTestContext,
iFace: Interface,
proposal: Call
): Promise<`0x${string}`> => {
const encodedProposal = proposal.method.toHex();
const data = iFace.encodeFunctionData(
// action
"note_preimage",
[encodedProposal]
);
const tx = await createTransaction(context, {
from: GENESIS_ACCOUNT,
privateKey: GENESIS_ACCOUNT_PRIVATE_KEY,
value: "0x0",
gas: "0x200000",
gasPrice: GAS_PRICE,
to: ADDRESS_DEMO_PRECOMPILE,
data,
});
await context.createBlock({
transactions: [tx],
});
// return encodedHash
return blake2AsHex(encodedProposal);
}
Example #3
Source File: governance.ts From moonbeam with GNU General Public License v3.0 | 6 votes |
notePreimage = async <
Call extends SubmittableExtrinsic<ApiType>,
ApiType extends ApiTypes
>(
context: DevTestContext,
proposal: Call,
account: KeyringPair
): Promise<string> => {
const encodedProposal = proposal.method.toHex() || "";
await context.polkadotApi.tx.democracy.notePreimage(encodedProposal).signAndSend(account);
await context.createBlock();
return blake2AsHex(encodedProposal);
}
Example #4
Source File: generate-runtimes-body.ts From moonbeam with GNU General Public License v3.0 | 6 votes |
// Srtool expects the pallet parachain_system to be at index 1. However, in the case of moonbase,
// the pallet parachain_system is at index 6, so we have to recalculate the hash of the
// authorizeUpgrade call in the case of moonbase by hand.
function authorizeUpgradeHash(runtimeName: string, srtool: any): string {
if (runtimeName == "moonbase") {
return blake2AsHex(
MOONBASE_PREFIX_PARACHAINSYSTEM_AUTHORIZE_UPGRADE +
srtool.runtimes.compressed.blake2_256.substr(2) // remove "0x" prefix
);
} else if (runtimeName == "moonriver") {
return blake2AsHex(
MOONRIVER_PREFIX_PARACHAINSYSTEM_AUTHORIZE_UPGRADE +
srtool.runtimes.compressed.blake2_256.substr(2) // remove "0x" prefix
);
} else {
return blake2AsHex(
MOONBEAM_PREFIX_PARACHAINSYSTEM_AUTHORIZE_UPGRADE +
srtool.runtimes.compressed.blake2_256.substr(2) // remove "0x" prefix
);
}
}
Example #5
Source File: selector.ts From ask-old with MIT License | 6 votes |
constructor(key: string) {
this.key = key;
// remove 0x
let hexStr = blake2AsHex(key, 256).substring(2);
let selectorArr = [];
for (let index = 0; index < 32; index++) {
selectorArr.push("0x" + hexStr.substring(index * 2, index * 2 + 2));
}
this.hex = `0x${hexStr}`;
this.short = `0x${hexStr.substr(0, 8)}`;
this.hexArr = `[${selectorArr.join(",")}]`;
this.shortArr = `[${selectorArr.splice(0, 4).join(",")}]`;
}
Example #6
Source File: getDeriveAccount.ts From parity-bridges-ui with GNU General Public License v3.0 | 5 votes |
export default function getDeriveAccount({ ss58Format = 42, bridgeId, address }: Data): string {
if (!address) {
return address;
}
const input = [...compactAddLength(stringToU8a(accountDerivation)), ...bridgeId, ...decodeAddress(address)];
return encodeAddress(blake2AsHex(Uint8Array.from(input)), ss58Format);
}
Example #7
Source File: generate.ts From gear-js with GNU General Public License v3.0 | 5 votes |
export function generateProgramId(code: Buffer | Uint8Array, salt: string | Hex | Uint8Array): Hex {
const codeHashU8a = blake2AsU8a(code, 256);
const saltU8a = CreateType.create('Vec<u8>', salt).toU8a().slice(1);
const id = new Uint8Array(codeHashU8a.byteLength + saltU8a.byteLength);
id.set(codeHashU8a);
id.set(saltU8a, codeHashU8a.byteLength);
return blake2AsHex(id, 256);
}
Example #8
Source File: generate.ts From gear-js with GNU General Public License v3.0 | 5 votes |
export function generateCodeHash(code: Buffer | Uint8Array): Hex {
return blake2AsHex(code, 256);
}
Example #9
Source File: Hash.tsx From crust-apps with Apache License 2.0 | 5 votes |
function Hash (): React.ReactElement {
const { t } = useTranslation();
const [{ data, hash, isHexData }, setState] = useState<State>({
data: '',
hash: blake2AsHex(stringToU8a(''), 256),
isHexData: false
});
const _onChangeData = useCallback(
(data: string): void => {
const isHexData = isHex(data);
setState({
data,
hash: blake2AsHex(
isHexData
? hexToU8a(data)
: stringToU8a(data),
256
),
isHexData
});
},
[]
);
return (
<div className='toolbox--Hash'>
<div className='ui--row'>
<Input
autoFocus
className='full'
help={t<string>('The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.')}
label={t<string>('from the following data')}
onChange={_onChangeData}
value={data}
/>
</div>
<div className='ui--row'>
<Static
className='medium'
help={t<string>('Detection on the input string to determine if it is hex or non-hex.')}
label={t<string>('hex input data')}
value={
isHexData
? t<string>('Yes')
: t<string>('No')
}
/>
</div>
<div className='ui--row'>
<Output
className='full'
help={t<string>('The blake2b 256-bit hash of the actual input data.')}
isHidden={hash.length === 0}
isMonospace
label={t<string>('the resulting hash is')}
value={hash}
withCopy
/>
</div>
</div>
);
}
Example #10
Source File: PreImage.tsx From crust-apps with Apache License 2.0 | 5 votes |
ZERO_HASH = blake2AsHex('')
Example #11
Source File: test-balance-reducible.ts From moonbeam with GNU General Public License v3.0 | 5 votes |
describeDevMoonbeam("Reducible Balance", (context) => {
it("should show the reducible balanced when some amount is locked", async function () {
const keyring = new Keyring({ type: "ethereum" });
const genesisAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
// Balance should be untouched
expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT)).to.equal(
GENESIS_ACCOUNT_BALANCE.toString()
);
// Grab existential deposit
let existentialDeposit = (await context.polkadotApi.consts.balances.existentialDeposit) as any;
// Let's lock some funds by doing a public referendum proposal
let lock_amount = (await context.polkadotApi.consts.democracy.minimumDeposit) as any;
const proposal = context.polkadotApi.tx.balances.setBalance(TEST_ACCOUNT, 100, 100);
// We encode the proposal
let encodedProposal = (proposal as SubmittableExtrinsic)?.method.toHex() || "";
let encodedHash = blake2AsHex(encodedProposal);
// Submit the pre-image
await context.polkadotApi.tx.democracy
.notePreimage(encodedProposal)
.signAndSend(genesisAccount);
await context.createBlock();
// Record balance
let beforeBalance = await context.web3.eth.getBalance(GENESIS_ACCOUNT);
// Fees
const fee = (
await context.polkadotApi.tx.democracy
.propose(encodedHash, lock_amount)
.paymentInfo(genesisAccount)
).partialFee as any;
// Propose
await context.polkadotApi.tx.democracy
.propose(encodedHash, lock_amount)
.signAndSend(genesisAccount);
await context.createBlock();
expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT)).to.equal(
(
BigInt(beforeBalance) -
BigInt(lock_amount) +
BigInt(existentialDeposit) -
BigInt(fee)
).toString()
);
});
});
Example #12
Source File: PreImage.tsx From crust-apps with Apache License 2.0 | 4 votes |
function PreImage ({ className = '', imageHash, isImminent = false, onClose }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api, apiDefaultTxSudo } = useApi();
const [accountId, setAccountId] = useState<string | null>(null);
const [{ encodedHash, encodedProposal, storageFee }, setHash] = useState<HashState>({ encodedHash: ZERO_HASH, encodedProposal: '', storageFee: BN_ZERO });
const [proposal, setProposal] = useState<SubmittableExtrinsic>();
useEffect((): void => {
const encodedProposal = (proposal as SubmittableExtrinsic)?.method.toHex() || '';
const storageFee = api.consts.democracy.preimageByteDeposit.mul(
encodedProposal
? new BN((encodedProposal.length - 2) / 2)
: BN_ZERO
);
setHash({ encodedHash: blake2AsHex(encodedProposal), encodedProposal, storageFee });
}, [api, proposal]);
const isMatched = imageHash
? imageHash.eq(encodedHash)
: true;
return (
<Modal
className={className}
header={t<string>('Submit preimage')}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t<string>('This account will pay the fees for the preimage, based on the size thereof.')}>
<InputAddress
help={t<string>('The account you want to register the preimage from')}
label={t<string>('send from account')}
labelExtra={
<Available
label={<span className='label'>{t<string>('transferrable')}</span>}
params={accountId}
/>
}
onChange={setAccountId}
type='account'
/>
</Modal.Columns>
<Modal.Columns hint={
<>
<p>{t<string>('The image (proposal) will be stored on-chain against the hash of the contents.')}</p>
<p>{t<string>('When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.')}</p>
</>
}>
<Extrinsic
defaultValue={apiDefaultTxSudo}
label={t<string>('propose')}
onChange={setProposal}
/>
<Input
className='disabledLook'
help={t<string>('The hash of the selected proposal, use it for submitting the proposal')}
isDisabledError={!isMatched}
label={t<string>('preimage hash')}
value={encodedHash}
/>
</Modal.Columns>
{!isImminent && (
<Modal.Columns hint={t<string>('The calculated storage costs based on the size and the per-bytes fee.')}>
<InputBalance
defaultValue={storageFee}
help={t<string>('The amount reserved to store this image')}
isDisabled
label={t<string>('calculated storage fee')}
/>
</Modal.Columns>
)}
</Modal.Content>
<Modal.Actions onCancel={onClose}>
<TxButton
accountId={accountId}
icon='plus'
isDisabled={!proposal || !accountId || !isMatched || !encodedProposal}
label={t<string>('Submit preimage')}
onStart={onClose}
params={[encodedProposal]}
tx={
isImminent
? api.tx.democracy.noteImminentPreimage
: api.tx.democracy.notePreimage
}
/>
</Modal.Actions>
</Modal>
);
}
Example #13
Source File: test-precompile-democracy.ts From moonbeam with GNU General Public License v3.0 | 4 votes |
// When forgetting to call notePreimage, all following steps should work as intended
// until the end where the proposal is never enacted
describeDevMoonbeam("Democracy - forget notePreimage", (context) => {
let genesisAccount: KeyringPair, alith: KeyringPair;
let encodedHash: string;
let enactmentPeriod, votingPeriod;
let iFace: Interface;
before("Setup genesis account for substrate", async () => {
const keyring = new Keyring({ type: "ethereum" });
genesisAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
iFace = await deployAndInterfaceContract(context, "Democracy");
// notePreimage
// compute proposal hash but don't submit it
const encodedProposal =
context.polkadotApi.tx.parachainStaking
.setParachainBondAccount(GENESIS_ACCOUNT)
.method.toHex() || "";
encodedHash = blake2AsHex(encodedProposal);
});
it("vote", async function () {
this.timeout(200000);
// propose
const { events: eventsPropose } = await createBlockWithExtrinsic(
context,
genesisAccount,
context.polkadotApi.tx.democracy.propose(encodedHash, PROPOSAL_AMOUNT)
);
expect(eventsPropose[2].toHuman().method).to.eq("Proposed");
expect(eventsPropose[5].toHuman().method).to.eq("ExtrinsicSuccess");
await context.createBlock();
// second
const { events: eventsSecond } = await createBlockWithExtrinsic(
context,
alith,
context.polkadotApi.tx.democracy.second(0, 1000)
);
expect(eventsSecond[2].toHuman().method).to.eq("Seconded");
expect(eventsSecond[5].toHuman().method).to.eq("ExtrinsicSuccess");
// let Launchperiod elapse to turn the proposal into a referendum
// launchPeriod minus the 3 blocks that already elapsed
for (let i = 0; i < 7200; i++) {
await context.createBlock();
}
// referendumCount
let referendumCount = await context.polkadotApi.query.democracy.referendumCount();
expect(referendumCount.toHuman()).to.equal("1");
// vote
await context.createBlock();
const { events: eventsVote } = await createBlockWithExtrinsic(
context,
alith,
context.polkadotApi.tx.democracy.vote(0, {
Standard: { balance: VOTE_AMOUNT, vote: { aye: true, conviction: 1 } },
})
);
expect(eventsVote[1].toHuman().method).to.eq("Voted");
expect(eventsVote[4].toHuman().method).to.eq("ExtrinsicSuccess");
// referendumInfoOf
const referendumInfoOf = await context.polkadotApi.query.democracy.referendumInfoOf(0);
expect((referendumInfoOf.toHuman() as any).Ongoing.proposalHash).to.equal(encodedHash);
expect((referendumInfoOf.toHuman() as any).Ongoing.tally.ayes).to.equal(
"10,000,000,000,000,000,000"
);
expect((referendumInfoOf.toHuman() as any).Ongoing.tally.turnout).to.equal(
"10,000,000,000,000,000,000"
);
// let votePeriod + enactmentPeriod elapse to turn the proposal into a referendum
for (let i = 0; i < Number(votingPeriod) + Number(enactmentPeriod); i++) {
await context.createBlock();
}
// the enactement should fail
let parachainBondInfo = await context.polkadotApi.query.parachainStaking.parachainBondInfo();
expect(parachainBondInfo.toHuman()["account"]).to.equal(ZERO_ADDRESS);
});
});
Example #14
Source File: test-democracy.ts From moonbeam with GNU General Public License v3.0 | 4 votes |
// When forgetting to call notePreimage, all following steps should work as intended
// until the end where the proposal is never enacted
describeDevMoonbeam("Democracy - forget notePreimage", (context) => {
let genesisAccount: KeyringPair, alith: KeyringPair;
let encodedHash: string;
let enactmentPeriod, votingPeriod;
before("Setup genesis account for substrate", async () => {
const keyring = new Keyring({ type: "ethereum" });
genesisAccount = await keyring.addFromUri(GENESIS_ACCOUNT_PRIVATE_KEY, null, "ethereum");
alith = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
// notePreimage
// compute proposal hash but don't submit it
const encodedProposal =
context.polkadotApi.tx.parachainStaking
.setParachainBondAccount(GENESIS_ACCOUNT)
.method.toHex() || "";
encodedHash = blake2AsHex(encodedProposal);
});
it("vote", async function () {
this.timeout(200000);
// propose
const { events: eventsPropose } = await createBlockWithExtrinsic(
context,
genesisAccount,
context.polkadotApi.tx.democracy.propose(encodedHash, PROPOSAL_AMOUNT)
);
expect(eventsPropose[7].toHuman().method).to.eq("ExtrinsicSuccess");
await context.createBlock();
// second
const { events: eventsSecond } = await createBlockWithExtrinsic(
context,
alith,
context.polkadotApi.tx.democracy.second(0, 1000)
);
expect(eventsSecond[2].toHuman().method).to.eq("Seconded");
expect(eventsSecond[5].toHuman().method).to.eq("ExtrinsicSuccess");
// let Launchperiod elapse to turn the proposal into a referendum
// launchPeriod minus the 3 blocks that already elapsed
for (let i = 0; i < 7200; i++) {
await context.createBlock();
}
// referendumCount
let referendumCount = (await context.polkadotApi.query.democracy.referendumCount()) as any;
expect(referendumCount.toBigInt()).to.equal(1n);
// vote
await context.createBlock();
const { events: eventsVote } = await createBlockWithExtrinsic(
context,
alith,
context.polkadotApi.tx.democracy.vote(0, {
Standard: { balance: VOTE_AMOUNT, vote: { aye: true, conviction: 1 } },
})
);
expect(eventsVote[1].toHuman().method).to.eq("Voted");
expect(eventsVote[4].toHuman().method).to.eq("ExtrinsicSuccess");
// referendumInfoOf
const referendumInfoOf = (
(await context.polkadotApi.query.democracy.referendumInfoOf(0)) as any
).unwrap() as any;
const onGoing = referendumInfoOf.asOngoing;
expect(onGoing.proposalHash.toHex()).to.equal(encodedHash);
expect(onGoing.tally.ayes.toBigInt()).to.equal(10n * GLMR);
expect(onGoing.tally.turnout.toBigInt()).to.equal(10n * GLMR);
// let votePeriod + enactmentPeriod elapse to turn the proposal into a referendum
for (let i = 0; i < Number(votingPeriod) + Number(enactmentPeriod); i++) {
await context.createBlock();
}
// the enactement should fail
let parachainBondInfo = await context.polkadotApi.query.parachainStaking.parachainBondInfo();
expect(parachainBondInfo.toJSON()["account"]).to.equal(ZERO_ADDRESS);
});
});
Example #15
Source File: test-crowdloan.ts From moonbeam with GNU General Public License v3.0 | 4 votes |
describeDevMoonbeam("Crowdloan", (context) => {
let sudoAccount: KeyringPair;
before("Setup genesis account for substrate", async () => {
const keyring = new Keyring({ type: "ethereum" });
sudoAccount = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
});
it("should be able to initialize through democracy", async function () {
let calls = [];
// We are gonna put the initialization and completion in a batch_all utility call
calls.push(
context.polkadotApi.tx.crowdloanRewards.initializeRewardVec([
[relayChainAddress, GENESIS_ACCOUNT, 1_500_000n * GLMR],
[relayChainAddress_2, null, 1_500_000n * GLMR],
])
);
let initBlock = (await context.polkadotApi.query.crowdloanRewards.initRelayBlock()) as any;
calls.push(
context.polkadotApi.tx.crowdloanRewards.completeInitialization(
initBlock.toBigInt() + VESTING_PERIOD
)
);
// Here we build the utility call
const proposal = context.polkadotApi.tx.utility.batchAll(calls);
// We encode the proposal
let encodedProposal = (proposal as SubmittableExtrinsic)?.method.toHex() || "";
let encodedHash = blake2AsHex(encodedProposal);
// Submit the pre-image
await context.polkadotApi.tx.democracy.notePreimage(encodedProposal).signAndSend(sudoAccount);
await context.createBlock();
// Propose
await context.polkadotApi.tx.democracy
.propose(encodedHash, 1000n * GLMR)
.signAndSend(sudoAccount);
await context.createBlock();
const publicPropCount = await context.polkadotApi.query.democracy.publicPropCount();
// we only use sudo to enact the proposal
await context.polkadotApi.tx.sudo
.sudoUncheckedWeight(
context.polkadotApi.tx.democracy.enactProposal(encodedHash, publicPropCount),
1
)
.signAndSend(sudoAccount);
await context.createBlock();
let isInitialized = await context.polkadotApi.query.crowdloanRewards.initialized();
expect(isInitialized.toHuman()).to.be.true;
// Get reward info of associated
let reward_info_associated = await getAccountPayable(context, GENESIS_ACCOUNT);
// Get reward info of unassociated
let reward_info_unassociated = (
(await context.polkadotApi.query.crowdloanRewards.unassociatedContributions(
relayChainAddress_2
)) as any
).unwrap();
// Check payments
expect(reward_info_associated.totalReward.toBigInt()).to.equal(1_500_000n * GLMR);
expect(reward_info_associated.claimedReward.toBigInt()).to.equal(450_000n * GLMR);
expect(reward_info_unassociated.totalReward.toBigInt()).to.equal(1_500_000n * GLMR);
expect(reward_info_unassociated.claimedReward.toBigInt()).to.equal(0n);
// check balances
const account = (await context.polkadotApi.query.system.account(GENESIS_ACCOUNT)) as any;
expect(account.data.free.toBigInt() - GENESIS_ACCOUNT_BALANCE).to.equal(
reward_info_associated.claimedReward.toBigInt()
);
});
});
Example #16
Source File: edgeware_function_picker.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
EdgewareFunctionPicker = {
form: { module: '', function: '', args: [] },
getMethod() {
const mod = this.form.module;
const func = this.form.function;
const args = this.form.args;
try {
return (app.chain as Substrate).chain.getTxMethod(mod, func, args);
} catch (error) {
// eslint-disable-next-line
return;
}
},
view: (vnode) => {
vnode.state.form = vnode.state.form || {};
vnode.state.form.module = vnode.state.form.module || (app.chain as Substrate).chain.listApiModules()[0];
vnode.state.form.function = vnode.state.form.function
|| (app.chain as Substrate).chain.listModuleFunctions(vnode.state.form.module)[0];
vnode.state.form.args = vnode.state.form.args || [];
let argumentInputs;
try {
argumentInputs = (app.chain as Substrate).chain.generateArgumentInputs(
vnode.state.form.module, vnode.state.form.function
);
} catch (e) {
return m('.FunctionPicker', 'Invalid function!');
}
return m('.FunctionPicker', {
style: 'margin-bottom: 19.5px',
}, [
m('div', [
m(DropdownFormField, {
title: 'Module',
name: 'module',
choices: (app.chain as Substrate).chain.listApiModules().map((mod) => {
return { label: mod, value: mod };
}),
value: vnode.state.form.module,
defaultValue: (app.chain as Substrate).chain.listApiModules()[0],
callback: (result) => {
vnode.state.form.module = result;
vnode.state.form.function = (app.chain as Substrate).chain.listModuleFunctions(result)[0];
vnode.state.form.args = [];
m.redraw();
setTimeout(() => { m.redraw(); }, 0);
},
}),
m(DropdownFormField, {
title: 'Function',
name: 'function',
choices: (app.chain as Substrate).chain.listModuleFunctions(vnode.state.form.module).map((func) => {
return { label: func, value: func };
}),
defaultValue: (app.chain as Substrate).chain.listModuleFunctions(vnode.state.form.module)[0],
value: vnode.state.form.function,
callback: (result) => {
vnode.state.form.function = result;
vnode.state.form.args = [];
setTimeout(() => { m.redraw(); }, 0);
},
}),
]),
m('div', argumentInputs.map(({ name, type }, index) => {
if (`${type}` === 'Compact<BalanceOf>') {
return m(FormGroup, [
m(FormLabel, `${name} (${app.chain.currency})`),
m(Input, {
placeholder: `${name} (${app.chain.currency})`,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.args[index] = app.chain.chain.coins(parseFloat(result), true);
m.redraw(); // TODO: why is this needed?
}
})
]);
}
if ((`${type}`).match(/Vec<[A-Za-z]+>/)) {
return m(FormGroup, [
m(FormLabel, `${name} (${type})`),
m(Input, {
placeholder: `${name} (${type})`,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.args[index] = result.split(',').map((str) => str.trim());
m.redraw(); // TODO: why is this needed?
},
}),
]);
}
return m(FormGroup, [
m(FormLabel, `${name}`),
m(Input, {
placeholder: `${name} (${type})`,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.args[index] = result;
m.redraw(); // TODO: why is this needed?
}
}),
]);
})),
m(FormGroup, { style: 'margin-top: 20px' }, [
m(FormLabel, 'Proposal Hash'),
m(Input, {
disabled: true,
value: (EdgewareFunctionPicker.getMethod())
? blake2AsHex(EdgewareFunctionPicker.getMethod().toHex())
: '',
}),
]),
]);
}
}
Example #17
Source File: new_proposal_form.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
NewProposalForm = {
form: {},
oncreate: (vnode) => {
vnode.state.toggleValue = 'proposal';
},
oninit: (vnode) => {
vnode.state.aaveTabCount = 1;
vnode.state.activeAaveTabIndex = 0;
vnode.state.aaveProposalState = [
{
target: null,
value: null,
calldata: null,
signature: null,
withDelegateCall: false,
},
];
vnode.state.cosmosProposalType = SupportedCosmosProposalTypes.Text;
vnode.state.sputnikProposalType =
SupportedSputnikProposalTypes.AddMemberToRole;
},
view: (vnode) => {
const callback = vnode.attrs.callback;
const author = app.user.activeAccount;
const proposalTypeEnum = vnode.attrs.typeEnum;
if (!author) return m('div', 'Must be logged in');
if (!callback) return m('div', 'Must have callback');
if (app.chain?.network === ChainNetwork.Plasm)
return m('div', 'Unsupported network');
let hasCouncilMotionChooser: boolean;
let hasAction: boolean;
let hasToggle: boolean;
let hasPreimageInput: boolean;
let hasTitleAndDescription: boolean;
let hasBountyTitle: boolean;
let hasTopics: boolean;
let hasBeneficiary: boolean;
let hasAmount: boolean;
let hasPhragmenInfo: boolean;
let hasDepositChooser: boolean;
// bounty proposal
let hasBountyValue: boolean;
// tip
let hasTipsFields: boolean;
// council motion
let hasVotingPeriodAndDelaySelector: boolean;
let hasReferendumSelector: boolean;
let hasExternalProposalSelector: boolean;
let hasTreasuryProposalSelector: boolean;
let hasThreshold: boolean;
// moloch proposal
let hasMolochFields: boolean;
// compound proposal
let hasCompoundFields: boolean;
// aave proposal
let hasAaveFields: boolean;
// sputnik proposal
let hasSputnikFields: boolean;
let hasCosmosFields: boolean;
// data loaded
let dataLoaded = true;
if (proposalTypeEnum === ProposalType.SubstrateDemocracyProposal) {
hasAction = true;
hasToggle = true;
hasDepositChooser = vnode.state.toggleValue === 'proposal';
if (hasDepositChooser) {
dataLoaded = !!(app.chain as Substrate).democracyProposals?.initialized;
}
} else if (proposalTypeEnum === ProposalType.SubstrateCollectiveProposal) {
hasCouncilMotionChooser = true;
hasAction =
vnode.state.councilMotionType === 'createExternalProposal' ||
vnode.state.councilMotionType === 'createExternalProposalMajority';
hasVotingPeriodAndDelaySelector =
vnode.state.councilMotionType === 'createFastTrack' ||
vnode.state.councilMotionType === 'createExternalProposalDefault';
hasReferendumSelector =
vnode.state.councilMotionType === 'createEmergencyCancellation';
hasExternalProposalSelector =
vnode.state.councilMotionType === 'vetoNextExternal' ||
vnode.state.councilMotionType === 'createFastTrack' ||
vnode.state.councilMotionType === 'createExternalProposalDefault';
hasTreasuryProposalSelector =
vnode.state.councilMotionType === 'createTreasuryApprovalMotion' ||
vnode.state.councilMotionType === 'createTreasuryRejectionMotion';
hasThreshold = vnode.state.councilMotionType !== 'vetoNextExternal';
if (hasExternalProposalSelector)
dataLoaded = !!(app.chain as Substrate).democracyProposals?.initialized;
} else if (proposalTypeEnum === ProposalType.OffchainThread) {
hasTitleAndDescription = true;
hasTopics = true;
} else if (proposalTypeEnum === ProposalType.SubstrateTreasuryProposal) {
hasBeneficiary = true;
hasAmount = true;
const treasury = (app.chain as Substrate).treasury;
dataLoaded = !!treasury.initialized;
} else if (proposalTypeEnum === ProposalType.SubstrateBountyProposal) {
hasBountyTitle = true;
hasBountyValue = true;
const bountyTreasury = (app.chain as Substrate).bounties;
dataLoaded = !!bountyTreasury.initialized;
} else if (proposalTypeEnum === ProposalType.SubstrateTreasuryTip) {
hasTipsFields = true;
// TODO: this is only true if the proposer is doing reportAwesome()
// we need special code for newTip().
const tips = (app.chain as Substrate).tips;
dataLoaded = !!tips.initialized;
} else if (proposalTypeEnum === ProposalType.PhragmenCandidacy) {
hasPhragmenInfo = true;
const elections = (app.chain as Substrate).phragmenElections;
dataLoaded = !!elections.initialized;
} else if (proposalTypeEnum === ProposalType.CosmosProposal) {
hasCosmosFields = true;
dataLoaded = !!(app.chain as Cosmos).governance.initialized;
} else if (proposalTypeEnum === ProposalType.MolochProposal) {
hasMolochFields = true;
} else if (proposalTypeEnum === ProposalType.CompoundProposal) {
hasCompoundFields = true;
} else if (proposalTypeEnum === ProposalType.AaveProposal) {
hasAaveFields = true;
} else if (proposalTypeEnum === ProposalType.SputnikProposal) {
hasSputnikFields = true;
} else {
return m('.NewProposalForm', 'Invalid proposal type');
}
if (
hasAction &&
!(app.user.activeAccount as SubstrateAccount).isCouncillor
) {
dataLoaded = false;
}
const createNewProposal = () => {
const done = (result) => {
vnode.state.error = '';
callback(result);
return result;
};
let createFunc: (...args) => ITXModalData | Promise<ITXModalData> = (
a
) => {
return (
proposalSlugToClass().get(proposalTypeEnum) as ProposalModule<
any,
any,
any
>
).createTx(...a);
};
let args = [];
if (proposalTypeEnum === ProposalType.OffchainThread) {
app.threads
.create(
author.address,
OffchainThreadKind.Forum,
OffchainThreadStage.Discussion,
app.activeChainId(),
vnode.state.form.title,
vnode.state.form.topicName,
vnode.state.form.topicId,
vnode.state.form.description
)
.then(done)
.then(() => {
m.redraw();
})
.catch((err) => {
console.error(err);
});
return;
} else if (proposalTypeEnum === ProposalType.SubstrateDemocracyProposal) {
const deposit = vnode.state.deposit
? app.chain.chain.coins(vnode.state.deposit, true)
: (app.chain as Substrate).democracyProposals.minimumDeposit;
if (!EdgewareFunctionPicker.getMethod()) {
notifyError('Missing arguments');
return;
} else if (vnode.state.toggleValue === 'proposal') {
const proposalHash = blake2AsHex(
EdgewareFunctionPicker.getMethod().toHex()
);
args = [
author,
EdgewareFunctionPicker.getMethod(),
proposalHash,
deposit,
];
createFunc = ([au, mt, pr, dep]) =>
(app.chain as Substrate).democracyProposals.createTx(
au,
mt,
pr,
dep
);
} else if (vnode.state.toggleValue === 'preimage') {
vnode.attrs.onChangeSlugEnum('democracypreimage');
const encodedProposal = EdgewareFunctionPicker.getMethod().toHex();
args = [author, EdgewareFunctionPicker.getMethod(), encodedProposal];
createFunc = ([au, mt, pr]) =>
(app.chain as Substrate).democracyProposals.notePreimage(
au,
mt,
pr
);
} else if (vnode.state.toggleValue === 'imminent') {
vnode.attrs.onChangeSlugEnum('democracyimminent');
const encodedProposal = EdgewareFunctionPicker.getMethod().toHex();
args = [author, EdgewareFunctionPicker.getMethod(), encodedProposal];
createFunc = ([au, mt, pr]) =>
(app.chain as Substrate).democracyProposals.noteImminentPreimage(
au,
mt,
pr
);
} else {
throw new Error('Invalid toggle state');
}
} else if (
proposalTypeEnum === ProposalType.SubstrateCollectiveProposal &&
vnode.state.councilMotionType === 'vetoNextExternal'
) {
args = [author, vnode.state.nextExternalProposalHash];
createFunc = ([a, h]) =>
(app.chain as Substrate).council.vetoNextExternal(a, h);
} else if (
proposalTypeEnum === ProposalType.SubstrateCollectiveProposal
) {
if (!vnode.state.threshold) throw new Error('Invalid threshold');
const threshold = vnode.state.threshold;
if (vnode.state.councilMotionType === 'createExternalProposal') {
args = [
author,
threshold,
EdgewareFunctionPicker.getMethod(),
EdgewareFunctionPicker.getMethod().encodedLength,
];
createFunc = ([a, t, mt, l]) =>
(app.chain as Substrate).council.createExternalProposal(
a,
t,
mt,
l
);
} else if (
vnode.state.councilMotionType === 'createExternalProposalMajority'
) {
args = [
author,
threshold,
EdgewareFunctionPicker.getMethod(),
EdgewareFunctionPicker.getMethod().encodedLength,
];
createFunc = ([a, t, mt, l]) =>
(app.chain as Substrate).council.createExternalProposalMajority(
a,
t,
mt,
l
);
} else if (
vnode.state.councilMotionType === 'createExternalProposalDefault'
) {
args = [
author,
threshold,
EdgewareFunctionPicker.getMethod(),
EdgewareFunctionPicker.getMethod().encodedLength,
];
createFunc = ([a, t, mt, l]) =>
(app.chain as Substrate).council.createExternalProposalDefault(
a,
t,
mt,
l
);
} else if (vnode.state.councilMotionType === 'createFastTrack') {
args = [
author,
threshold,
vnode.state.nextExternalProposalHash,
vnode.state.votingPeriod,
vnode.state.enactmentDelay,
];
createFunc = ([a, b, c, d, e]) =>
(app.chain as Substrate).council.createFastTrack(a, b, c, d, e);
} else if (
vnode.state.councilMotionType === 'createEmergencyCancellation'
) {
args = [author, threshold, vnode.state.referendumId];
createFunc = ([a, t, h]) =>
(app.chain as Substrate).council.createEmergencyCancellation(
a,
t,
h
);
} else if (
vnode.state.councilMotionType === 'createTreasuryApprovalMotion'
) {
args = [author, threshold, vnode.state.treasuryProposalIndex];
createFunc = ([a, t, i]) =>
(app.chain as Substrate).council.createTreasuryApprovalMotion(
a,
t,
i
);
} else if (
vnode.state.councilMotionType === 'createTreasuryRejectionMotion'
) {
args = [author, threshold, vnode.state.treasuryProposalIndex];
createFunc = ([a, t, i]) =>
(app.chain as Substrate).council.createTreasuryRejectionMotion(
a,
t,
i
);
} else {
throw new Error('Invalid council motion type');
}
return createTXModal(createFunc(args)).then(done);
} else if (proposalTypeEnum === ProposalType.SubstrateTreasuryProposal) {
if (!vnode.state.form.beneficiary)
throw new Error('Invalid beneficiary address');
const beneficiary = app.chain.accounts.get(
vnode.state.form.beneficiary
);
args = [author, vnode.state.form.amount, beneficiary];
} else if (proposalTypeEnum === ProposalType.SubstrateBountyProposal) {
if (!vnode.state.form.title) throw new Error('Invalid title');
if (!vnode.state.form.value) throw new Error('Invalid value');
args = [author, vnode.state.form.value, vnode.state.form.title];
createFunc = ([a, v, t]) =>
(app.chain as Substrate).bounties.createTx(a, v, t);
return createTXModal(createFunc(args)).then(done);
} else if (proposalTypeEnum === ProposalType.SubstrateBountyProposal) {
if (!vnode.state.form.reason) throw new Error('Invalid reason');
if (!vnode.state.form.beneficiary)
throw new Error('Invalid beneficiary address');
const beneficiary = app.chain.accounts.get(
vnode.state.form.beneficiary
);
args = [vnode.state.form.reason, beneficiary];
} else if (proposalTypeEnum === ProposalType.PhragmenCandidacy) {
args = [author];
createFunc = ([a]) =>
(
app.chain as Substrate
).phragmenElections.activeElection.submitCandidacyTx(a);
} else if (proposalTypeEnum === ProposalType.CosmosProposal) {
let prop: ProtobufAny;
const { title, description } = vnode.state.form;
const deposit = vnode.state.deposit
? new CosmosToken(
(app.chain as Cosmos).governance.minDeposit.denom,
vnode.state.deposit,
false
)
: (app.chain as Cosmos).governance.minDeposit;
if (
vnode.state.cosmosProposalType === SupportedCosmosProposalTypes.Text
) {
prop = (app.chain as Cosmos).governance.encodeTextProposal(
title,
description
);
} else if (
vnode.state.cosmosProposalType ===
SupportedCosmosProposalTypes.CommunitySpend
) {
prop = (app.chain as Cosmos).governance.encodeCommunitySpend(
title,
description,
vnode.state.recipient,
vnode.state.payoutAmount
);
} else {
throw new Error('Unknown Cosmos proposal type.');
}
// TODO: add disabled / loading
(app.chain as Cosmos).governance
.submitProposalTx(author as CosmosAccount, deposit, prop)
.then((result) => {
done(result);
navigateToSubpage(`/proposal/${result}`);
})
.catch((err) => notifyError(err.message));
return;
} else if (proposalTypeEnum === ProposalType.MolochProposal) {
// TODO: check that applicant is valid ETH address in hex
if (!vnode.state.applicantAddress)
throw new Error('Invalid applicant address');
if (typeof vnode.state.tokenTribute !== 'number')
throw new Error('Invalid token tribute');
if (typeof vnode.state.sharesRequested !== 'number')
throw new Error('Invalid shares requested');
if (!vnode.state.title) throw new Error('Invalid title');
const details = JSON.stringify({
title: vnode.state.title,
description: vnode.state.description || '',
});
(app.chain as Moloch).governance
.createPropWebTx(
author as MolochMember,
vnode.state.applicantAddress,
new BN(vnode.state.tokenTribute),
new BN(vnode.state.sharesRequested),
details
)
// TODO: handling errors?
.then((result) => done(result))
.then(() => m.redraw())
.catch((err) => notifyError(err.data?.message || err.message));
return;
} else if (proposalTypeEnum === ProposalType.CompoundProposal) {
vnode.state.proposer = app.user?.activeAccount?.address;
if (!vnode.state.proposer)
throw new Error('Invalid address / not logged in');
if (!vnode.state.description) throw new Error('Invalid description');
const targets = [];
const values = [];
const calldatas = [];
const signatures = [];
for (let i = 0; i < vnode.state.aaveTabCount; i++) {
const aaveProposal = vnode.state.aaveProposalState[i];
if (aaveProposal.target) {
targets.push(aaveProposal.target);
} else {
throw new Error(`No target for Call ${i + 1}`);
}
values.push(aaveProposal.value || '0');
calldatas.push(aaveProposal.calldata || '');
signatures.push(aaveProposal.signature || '');
}
// if they passed a title, use the JSON format for description.
// otherwise, keep description raw
let description = vnode.state.description;
if (vnode.state.title) {
description = JSON.stringify({
description: vnode.state.description,
title: vnode.state.title,
});
}
const details: CompoundProposalArgs = {
description,
targets,
values,
calldatas,
signatures,
};
(app.chain as Compound).governance
.propose(details)
.then((result: string) => {
done(result);
return result;
})
.then((result: string) => {
notifySuccess(`Proposal ${result} created successfully!`);
m.redraw();
})
.catch((err) => notifyError(err.data?.message || err.message));
return;
} else if (proposalTypeEnum === ProposalType.AaveProposal) {
vnode.state.proposer = app.user?.activeAccount?.address;
if (!vnode.state.proposer)
throw new Error('Invalid address / not logged in');
if (!vnode.state.executor) throw new Error('Invalid executor');
if (!vnode.state.ipfsHash) throw new Error('No ipfs hash');
const targets = [];
const values = [];
const calldatas = [];
const signatures = [];
const withDelegateCalls = [];
for (let i = 0; i < vnode.state.aaveTabCount; i++) {
const aaveProposal = vnode.state.aaveProposalState[i];
if (aaveProposal.target) {
targets.push(aaveProposal.target);
} else {
throw new Error(`No target for Call ${i + 1}`);
}
values.push(aaveProposal.value || '0');
calldatas.push(aaveProposal.calldata || '');
withDelegateCalls.push(aaveProposal.withDelegateCall || false);
signatures.push(aaveProposal.signature || '');
}
// TODO: preload this ipfs value to ensure it's correct
const ipfsHash = utils.formatBytes32String(vnode.state.ipfsHash);
const details: AaveProposalArgs = {
executor: vnode.state.executor as string,
targets,
values,
calldatas,
signatures,
withDelegateCalls,
ipfsHash,
};
(app.chain as Aave).governance
.propose(details)
.then((result) => done(result))
.then(() => m.redraw())
.catch((err) => notifyError(err.data?.message || err.message));
return;
// @TODO: Create Proposal via WebTx
} else if (proposalTypeEnum === ProposalType.SputnikProposal) {
// TODO: make type of proposal switchable
const member = vnode.state.member;
const description = vnode.state.description;
let propArgs: NearSputnikProposalKind;
if (
vnode.state.sputnikProposalType ===
SupportedSputnikProposalTypes.AddMemberToRole
) {
propArgs = {
AddMemberToRole: { role: 'council', member_id: member },
};
} else if (
vnode.state.sputnikProposalType ===
SupportedSputnikProposalTypes.RemoveMemberFromRole
) {
propArgs = {
RemoveMemberFromRole: { role: 'council', member_id: member },
};
} else if (
vnode.state.sputnikProposalType ===
SupportedSputnikProposalTypes.Transfer
) {
// TODO: validate amount / token id
const token_id = vnode.state.tokenId || '';
let amount: string;
// treat NEAR as in dollars but tokens as whole #s
if (!token_id) {
amount = app.chain.chain
.coins(+vnode.state.payoutAmount, true)
.asBN.toString();
} else {
amount = `${+vnode.state.payoutAmount}`;
}
propArgs = { Transfer: { receiver_id: member, token_id, amount } };
} else if (
vnode.state.sputnikProposalType === SupportedSputnikProposalTypes.Vote
) {
propArgs = 'Vote';
} else {
throw new Error('unsupported sputnik proposal type');
}
(app.chain as NearSputnik).dao
.proposeTx(description, propArgs)
.then((result) => done(result))
.then(() => m.redraw())
.catch((err) => notifyError(err.message));
return;
} else if (proposalTypeEnum === ProposalType.SubstrateTreasuryTip) {
if (!vnode.state.form.beneficiary)
throw new Error('Invalid beneficiary address');
const beneficiary = app.chain.accounts.get(
vnode.state.form.beneficiary
);
args = [author, vnode.state.form.description, beneficiary];
} else {
throw new Error('Invalid proposal type');
}
Promise.resolve(createFunc(args))
.then((modalData) => createTXModal(modalData))
.then(done);
};
// default state options
const motions = SubstrateCollectiveProposal.motions;
if (!vnode.state.councilMotionType) {
vnode.state.councilMotionType = motions[0].name;
vnode.state.councilMotionDescription = motions[0].description;
}
// shorthands
const isSubstrate = app.chain.base === ChainBase.Substrate;
const asSubstrate = app.chain as Substrate;
const isCosmos = app.chain.base === ChainBase.CosmosSDK;
const asCosmos = app.chain as Cosmos;
if (!dataLoaded) {
if (
app.chain?.base === ChainBase.Substrate &&
(app.chain as Substrate).chain?.timedOut
) {
return m(ErrorPage, {
message: 'Could not connect to chain',
title: 'Proposals',
});
}
return m(Spinner, {
fill: true,
message: 'Connecting to chain...',
size: 'xl',
style: 'visibility: visible; opacity: 1;',
});
}
const activeEntityInfo = app.chain.meta;
const { activeAaveTabIndex, aaveProposalState } = vnode.state;
return m(Form, { class: 'NewProposalForm' }, [
m(Grid, [
m(Col, [
vnode.state.error && m('.error', vnode.state.error.message),
hasCouncilMotionChooser && [
m(DropdownFormField, {
title: 'Motion',
choices: motions.map((m_) => ({
name: 'councilMotionType',
value: m_.name,
label: m_.label,
})),
callback: (result) => {
vnode.state.councilMotionType = result;
vnode.state.councilMotionDescription = motions.find(
(m_) => m_.name === result
).description;
m.redraw();
},
}),
vnode.state.councilMotionDescription &&
m(
'.council-motion-description',
vnode.state.councilMotionDescription
),
],
// actions
hasAction && m(EdgewareFunctionPicker),
hasTopics &&
m(TopicSelector, {
topics: app.chain.meta.topics,
updateFormData: (topicName: string, topicId?: number) => {
vnode.state.form.topicName = topicName;
vnode.state.form.topicId = topicId;
},
tabindex: 3,
}),
hasBountyTitle && [
m(FormGroup, [
m(FormLabel, 'Title'),
m(Input, {
placeholder: 'Bounty title (stored on chain)',
name: 'title',
autofocus: true,
autocomplete: 'off',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.title = result;
m.redraw();
},
}),
]),
],
hasTitleAndDescription && [
m(FormGroup, [
m(FormLabel, 'Title'),
m(Input, {
placeholder: 'Enter a title',
name: 'title',
autofocus: true,
autocomplete: 'off',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.title = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Description'),
m(TextArea, {
name: 'description',
placeholder: 'Enter a description',
oninput: (e) => {
const result = (e.target as any).value;
if (vnode.state.form.description === result) return;
vnode.state.form.description = result;
m.redraw();
},
}),
]),
],
hasBeneficiary && [
m(FormGroup, [
m(FormLabel, 'Beneficiary'),
m(Input, {
name: 'beneficiary',
placeholder: 'Beneficiary of proposal',
defaultValue: author.address,
oncreate: (vvnode) => {
vnode.state.form.beneficiary = author.address;
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.beneficiary = result;
m.redraw();
},
}),
]),
],
hasAmount && [
m(FormGroup, [
m(FormLabel, `Amount (${app.chain.chain.denom})`),
m(Input, {
name: 'amount',
autofocus: true,
placeholder: 'Amount of proposal',
autocomplete: 'off',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.amount = app.chain.chain.coins(
parseFloat(result),
true
);
m.redraw();
},
}),
]),
m('p', [
'Bond: ',
app.chain.chain
.coins(
Math.max(
(vnode.state.form.amount?.inDollars || 0) *
(app.chain as Substrate).treasury.bondPct,
(app.chain as Substrate).treasury.bondMinimum.inDollars
),
true
)
.format(),
` (${
(app.chain as Substrate).treasury.bondPct * 100
}% of requested amount, `,
`minimum ${(
app.chain as Substrate
).treasury.bondMinimum.format()})`,
]),
],
hasPhragmenInfo &&
m('.council-slot-info', [
m('p', [
'Becoming a candidate requires a deposit of ',
formatCoin(
(app.chain as Substrate).phragmenElections.candidacyBond
),
'. It will be returned if you are elected, or carried over to the next election if you are in the top ',
`${
(app.chain as Substrate).phragmenElections.desiredRunnersUp
} runners-up.`,
]),
]),
hasToggle && [
m(RadioSelectorFormField, {
callback: async (value) => {
vnode.state.toggleValue = value;
vnode.attrs.onChangeSlugEnum(value);
m.redraw();
},
choices: [
{ label: 'Create Proposal', value: 'proposal', checked: true },
{ label: 'Upload Preimage', value: 'preimage', checked: false },
{
label: 'Upload Imminent Preimage',
value: 'imminent',
checked: false,
},
],
name: 'democracy-tx-switcher',
}),
],
hasBountyValue && [
m(FormGroup, [
m(FormLabel, `Value (${app.chain.chain.denom})`),
m(Input, {
name: 'value',
placeholder: 'Amount allocated to bounty',
autocomplete: 'off',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.value = app.chain.chain.coins(
parseFloat(result),
true
);
m.redraw();
},
}),
]),
],
hasDepositChooser && [
m(FormGroup, [
m(
FormLabel,
`Deposit (${
app.chain.base === ChainBase.Substrate
? app.chain.currency
: (app.chain as Cosmos).governance.minDeposit.denom
})`
),
m(Input, {
name: 'deposit',
placeholder: `Min: ${
app.chain.base === ChainBase.Substrate
? (app.chain as Substrate).democracyProposals.minimumDeposit
.inDollars
: +(app.chain as Cosmos).governance.minDeposit
}`,
oncreate: (vvnode) =>
$(vvnode.dom).val(
app.chain.base === ChainBase.Substrate
? (app.chain as Substrate).democracyProposals
.minimumDeposit.inDollars
: +(app.chain as Cosmos).governance.minDeposit
),
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.deposit = parseFloat(result);
m.redraw();
},
}),
]),
],
hasVotingPeriodAndDelaySelector && [
m(FormGroup, [
m(FormLabel, 'Voting Period'),
m(Input, {
name: 'voting_period',
placeholder: 'Blocks (minimum enforced)',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.votingPeriod = +result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Enactment Delay'),
m(Input, {
name: 'enactment_delay',
placeholder: 'Blocks (minimum enforced)',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.enactmentDelay = +result;
m.redraw();
},
}),
]),
],
hasReferendumSelector &&
m(DropdownFormField, {
title: 'Referendum',
choices: (app.chain as Substrate).democracy.store
.getAll()
.map((r) => ({
name: 'referendum',
value: r.identifier,
label: `${r.shortIdentifier}: ${r.title}`,
})),
callback: (result) => {
vnode.state.referendumId = result;
m.redraw();
},
}),
hasExternalProposalSelector &&
m(DropdownFormField, {
title: 'Proposal',
choices: (app.chain as Substrate).democracyProposals.nextExternal
? [
{
name: 'external_proposal',
value: (
app.chain as Substrate
).democracyProposals.nextExternal[0].hash.toString(),
label: `${(
app.chain as Substrate
).democracyProposals.nextExternal[0].hash
.toString()
.slice(0, 8)}...`,
},
]
: [],
callback: (result) => {
vnode.state.nextExternalProposalHash = result;
m.redraw();
},
}),
hasTreasuryProposalSelector &&
m(DropdownFormField, {
title: 'Treasury Proposal',
choices: (app.chain as Substrate).treasury.store
.getAll()
.map((r) => ({
name: 'external_proposal',
value: r.identifier,
label: r.shortIdentifier,
})),
callback: (result) => {
vnode.state.treasuryProposalIndex = result;
m.redraw();
},
}),
hasThreshold && [
m(FormGroup, [
m(FormLabel, 'Threshold'),
m(Input, {
name: 'threshold',
placeholder: 'How many members must vote yes to execute?',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.threshold = +result;
m.redraw();
},
}),
]),
],
hasMolochFields && [
m(FormGroup, [
m(FormLabel, 'Applicant Address (will receive Moloch shares)'),
m(Input, {
name: 'applicant_address',
placeholder: 'Applicant Address',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.applicantAddress = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(
FormLabel,
'Token Tribute (offered to Moloch, must be pre-approved for transfer)'
),
m(Input, {
name: 'token_tribute',
placeholder: 'Tribute in tokens',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.tokenTribute = +result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Shares Requested'),
m(Input, {
name: 'shares_requested',
placeholder: 'Moloch shares requested',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.sharesRequested = +result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Proposal Title'),
m(Input, {
name: 'title',
placeholder: 'Proposal Title',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.title = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Proposal Description'),
m(Input, {
name: 'description',
placeholder: 'Proposal Description',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.description = result;
m.redraw();
},
}),
]),
],
hasCompoundFields &&
m('.AaveGovernance', [
m(FormGroup, [
m(FormLabel, 'Proposer (you)'),
m('', [
m(User, {
user: author,
linkify: true,
popover: true,
showAddressWithDisplayName: true,
}),
]),
]),
m(FormGroup, [
m(FormLabel, 'Proposal Title (leave blank for no title)'),
m(Input, {
name: 'title',
placeholder: 'Proposal Title',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.title = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Proposal Description'),
m(TextArea, {
name: 'description',
placeholder: 'Proposal Description',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.description = result;
m.redraw();
},
}),
]),
m('.tab-selector', [
m(
Tabs,
{
align: 'left',
class: 'tabs',
},
[
aaveProposalState.map((_, index) =>
m(TabItem, {
key: index,
label: `Call ${index + 1}`,
active: activeAaveTabIndex === index,
onclick: () => {
vnode.state.activeAaveTabIndex = index;
},
})
),
]
),
m(PopoverMenu, {
closeOnContentClick: true,
content: [
m(MenuItem, {
iconLeft: Icons.EDIT_2,
label: 'Add',
onclick: () => {
vnode.state.aaveTabCount++;
vnode.state.activeAaveTabIndex =
vnode.state.aaveTabCount - 1;
vnode.state.aaveProposalState.push({
target: null,
value: null,
calldata: null,
signature: null,
withDelegateCall: false,
});
},
}),
m(MenuItem, {
iconLeft: Icons.TRASH_2,
label: 'Delete',
disabled: vnode.state.activeAaveTabIndex === 0,
onclick: () => {
vnode.state.aaveTabCount--;
vnode.state.activeAaveTabIndex =
vnode.state.aaveTabCount - 1;
vnode.state.aaveProposalState.pop();
},
}),
],
trigger: m(Button, {
iconLeft: Icons.MORE_HORIZONTAL,
basic: true,
}),
}),
]),
m(FormGroup, [
m(FormLabel, 'Target Address'),
m(Input, {
name: 'targets',
placeholder: 'Add Target',
value: aaveProposalState[activeAaveTabIndex].target,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[activeAaveTabIndex].target =
result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Value'),
m(Input, {
name: 'values',
placeholder: 'Enter amount in wei',
value: aaveProposalState[activeAaveTabIndex].value,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[activeAaveTabIndex].value =
result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Calldata'),
m(Input, {
name: 'calldatas',
placeholder: 'Add Calldata',
value: aaveProposalState[activeAaveTabIndex].calldata,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[activeAaveTabIndex].calldata =
result;
m.redraw();
},
}),
]),
m(FormGroup, [
m('.flex-label', [
m(FormLabel, 'Function Signature'),
m('.helper-text', 'Optional'),
]),
m(Input, {
name: 'signatures',
placeholder: 'Add a signature',
value: aaveProposalState[activeAaveTabIndex].signature,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[
activeAaveTabIndex
].signature = result;
m.redraw();
},
}),
]),
]),
hasAaveFields &&
m('.AaveGovernance', [
m(FormGroup, [
m(FormLabel, 'Proposer (you)'),
m('', [
m(User, {
user: author,
linkify: true,
popover: true,
showAddressWithDisplayName: true,
}),
]),
]),
// TODO: validate this is the correct length, or else hash it ourselves
m(FormGroup, [
m(FormLabel, 'IPFS Hash'),
m(Input, {
name: 'ipfsHash',
placeholder: 'Proposal IPFS Hash',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.ipfsHash = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Executor'),
(app.chain as Aave).governance.api.Executors.map((r) =>
m(
`.executor ${
vnode.state.executor === r.address && '.selected-executor'
}`,
{
onclick: () => {
vnode.state.executor = r.address;
},
},
[
m('.label', 'Address'),
m('', r.address),
m('.label .mt-16', 'Time Delay'),
m('', `${r.delay / (60 * 60 * 24)} Day(s)`),
]
)
),
]),
// TODO: display offchain copy re AIPs and ARCs from https://docs.aave.com/governance/
m('.tab-selector', [
m(
Tabs,
{
align: 'left',
class: 'tabs',
},
[
aaveProposalState.map((_, index) =>
m(TabItem, {
key: index,
label: `Call ${index + 1}`,
active: activeAaveTabIndex === index,
onclick: () => {
vnode.state.activeAaveTabIndex = index;
},
})
),
]
),
m(PopoverMenu, {
closeOnContentClick: true,
content: [
m(MenuItem, {
iconLeft: Icons.EDIT_2,
label: 'Add',
onclick: () => {
vnode.state.aaveTabCount++;
vnode.state.activeAaveTabIndex =
vnode.state.aaveTabCount - 1;
vnode.state.aaveProposalState.push({
target: null,
value: null,
calldata: null,
signature: null,
withDelegateCall: false,
});
},
}),
m(MenuItem, {
iconLeft: Icons.TRASH_2,
label: 'Delete',
disabled: vnode.state.activeAaveTabIndex === 0,
onclick: () => {
vnode.state.aaveTabCount--;
vnode.state.activeAaveTabIndex =
vnode.state.aaveTabCount - 1;
vnode.state.aaveProposalState.pop();
},
}),
],
trigger: m(Button, {
iconLeft: Icons.MORE_HORIZONTAL,
basic: true,
}),
}),
]),
m(FormGroup, [
m(FormLabel, 'Target Address'),
m(Input, {
name: 'targets',
placeholder: 'Add Target',
value: aaveProposalState[activeAaveTabIndex].target,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[activeAaveTabIndex].target =
result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Value'),
m(Input, {
name: 'values',
placeholder: 'Enter amount in wei',
value: aaveProposalState[activeAaveTabIndex].value,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[activeAaveTabIndex].value =
result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Calldata'),
m(Input, {
name: 'calldatas',
placeholder: 'Add Calldata',
value: aaveProposalState[activeAaveTabIndex].calldata,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[activeAaveTabIndex].calldata =
result;
m.redraw();
},
}),
]),
m(FormGroup, [
m('.flex-label', [
m(FormLabel, 'Function Signature'),
m('.helper-text', 'Optional'),
]),
m(Input, {
name: 'signatures',
placeholder: 'Add a signature',
value: aaveProposalState[activeAaveTabIndex].signature,
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.aaveProposalState[
activeAaveTabIndex
].signature = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Delegate Call'),
m('', [
m(Button, {
label: 'TRUE',
class: `button ${
aaveProposalState[activeAaveTabIndex].withDelegateCall ===
true && 'active'
}`,
onclick: () => {
vnode.state.aaveProposalState[
activeAaveTabIndex
].withDelegateCall = true;
},
}),
m(Button, {
label: 'FALSE',
class: `ml-12 button ${
aaveProposalState[activeAaveTabIndex].withDelegateCall ===
false && 'active'
}`,
onclick: () => {
vnode.state.aaveProposalState[
activeAaveTabIndex
].withDelegateCall = false;
},
}),
]),
]),
]),
hasSputnikFields && [
// TODO: add deposit copy
m(DropdownFormField, {
title: 'Proposal Type',
value: vnode.state.sputnikProposalType,
defaultValue: SupportedSputnikProposalTypes.AddMemberToRole,
choices: Object.values(SupportedSputnikProposalTypes).map(
(v) => ({
name: 'proposalType',
label: v,
value: v,
})
),
callback: (result) => {
vnode.state.sputnikProposalType = result;
m.redraw();
},
}),
m(FormGroup, [
vnode.state.sputnikProposalType !==
SupportedSputnikProposalTypes.Vote && m(FormLabel, 'Member'),
vnode.state.sputnikProposalType !==
SupportedSputnikProposalTypes.Vote &&
m(Input, {
name: 'member',
defaultValue: 'tokenfactory.testnet',
oncreate: (vvnode) => {
vnode.state.member = 'tokenfactory.testnet';
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.member = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Description'),
m(Input, {
name: 'description',
defaultValue: '',
oncreate: (vvnode) => {
vnode.state.description = '';
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.description = result;
m.redraw();
},
}),
]),
vnode.state.sputnikProposalType ===
SupportedSputnikProposalTypes.Transfer &&
m(FormGroup, [
m(FormLabel, 'Token ID (leave blank for Ⓝ)'),
m(Input, {
name: 'token_id',
defaultValue: '',
oncreate: (vvnode) => {
vnode.state.tokenId = '';
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.tokenId = result;
m.redraw();
},
}),
]),
vnode.state.sputnikProposalType ===
SupportedSputnikProposalTypes.Transfer &&
m(FormGroup, [
m(FormLabel, 'Amount'),
m(Input, {
name: 'amount',
defaultValue: '',
oncreate: (vvnode) => {
vnode.state.payoutAmount = '';
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.payoutAmount = result;
m.redraw();
},
}),
]),
],
hasCosmosFields && [
m(DropdownFormField, {
title: 'Proposal Type',
value: vnode.state.cosmosProposalType,
defaultValue: SupportedCosmosProposalTypes.Text,
choices: Object.values(SupportedCosmosProposalTypes).map((v) => ({
name: 'proposalType',
label: v,
value: v,
})),
callback: (result) => {
vnode.state.cosmosProposalType = result;
m.redraw();
},
}),
m(FormGroup, [
m(FormLabel, 'Title'),
m(Input, {
placeholder: 'Enter a title',
name: 'title',
autofocus: true,
autocomplete: 'off',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.title = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Description'),
m(TextArea, {
name: 'description',
placeholder: 'Enter a description',
oninput: (e) => {
const result = (e.target as any).value;
if (vnode.state.form.description === result) return;
vnode.state.form.description = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(
FormLabel,
`Deposit (${(app.chain as Cosmos).governance.minDeposit.denom})`
),
m(Input, {
name: 'deposit',
placeholder: `Min: ${+(app.chain as Cosmos).governance
.minDeposit}`,
oncreate: (vvnode) =>
$(vvnode.dom).val(
+(app.chain as Cosmos).governance.minDeposit
),
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.deposit = +result;
m.redraw();
},
}),
]),
vnode.state.cosmosProposalType !==
SupportedCosmosProposalTypes.Text &&
m(FormGroup, [
m(FormLabel, 'Recipient'),
m(Input, {
name: 'recipient',
placeholder: app.user.activeAccount.address,
defaultValue: '',
oncreate: (vvnode) => {
vnode.state.recipient = '';
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.recipient = result;
m.redraw();
},
}),
]),
vnode.state.cosmosProposalType !==
SupportedCosmosProposalTypes.Text &&
m(FormGroup, [
m(
FormLabel,
`Amount (${
(app.chain as Cosmos).governance.minDeposit.denom
})`
),
m(Input, {
name: 'amount',
placeholder: '12345',
defaultValue: '',
oncreate: (vvnode) => {
vnode.state.payoutAmount = '';
},
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.payoutAmount = result;
m.redraw();
},
}),
]),
],
hasTipsFields && [
m(FormGroup, [
m('.label', 'Finder'),
m(User, {
user: author,
linkify: true,
popover: true,
showAddressWithDisplayName: true,
}),
]),
m(FormGroup, [
m(FormLabel, 'Beneficiary'),
m(Input, {
name: 'beneficiary',
placeholder: 'Beneficiary of treasury proposal',
oninput: (e) => {
const result = (e.target as any).value;
vnode.state.form.beneficiary = result;
m.redraw();
},
}),
]),
m(FormGroup, [
m(FormLabel, 'Reason'),
m(TextArea, {
name: 'reason',
placeholder:
'What’s the reason you want to tip the beneficiary?',
oninput: (e) => {
const result = (e.target as any).value;
if (vnode.state.form.description === result) return;
vnode.state.form.description = result;
m.redraw();
},
}),
]),
],
m(FormGroup, [
m(Button, {
disabled:
proposalTypeEnum === ProposalType.SubstrateCollectiveProposal &&
!(author as SubstrateAccount).isCouncillor,
intent: 'primary',
rounded: true,
label:
proposalTypeEnum === ProposalType.OffchainThread
? 'Create thread'
: 'Send transaction',
onclick: (e) => {
e.preventDefault();
createNewProposal();
},
tabindex: 4,
type: 'submit',
}),
]),
]),
]),
]);
},
}