Skip to content

Commit

Permalink
feat: Add osnap plugin (snapshot-labs#4312)
Browse files Browse the repository at this point in the history
* add activate osnap button

Signed-off-by: ryanwolhuter <[email protected]>

* bubble emits up

Signed-off-by: ryanwolhuter <[email protected]>

* add is osnap enabled prop

Signed-off-by: ryanwolhuter <[email protected]>

* add safe app link

Signed-off-by: ryanwolhuter <[email protected]>

* add disclaimer

Signed-off-by: ryanwolhuter <[email protected]>

* reduce text size

Signed-off-by: ryanwolhuter <[email protected]>

* update safe app url

Signed-off-by: ryanwolhuter <[email protected]>

* fix tailwind mistake

Signed-off-by: ryanwolhuter <[email protected]>

* use params object

Signed-off-by: ryanwolhuter <[email protected]>

* add is osnap enabled check

Signed-off-by: ryanwolhuter <[email protected]>

* add new osnap plugin

Signed-off-by: ryanwolhuter <[email protected]>

* use presence of plugin to determine if is enabled

Signed-off-by: ryanwolhuter <[email protected]>

* move enable osnap logic out of settings

Signed-off-by: ryanwolhuter <[email protected]>

* switch to composition api

Signed-off-by: ryanwolhuter <[email protected]>

* update types

Signed-off-by: ryanwolhuter <[email protected]>

* remove batch logic from transactions

Signed-off-by: ryanwolhuter <[email protected]>

* remove multisend from contract interaction

Signed-off-by: ryanwolhuter <[email protected]>

* remove multisend from transfer funds

Signed-off-by: ryanwolhuter <[email protected]>

* add correct token type

Signed-off-by: ryanwolhuter <[email protected]>

* refactor transfer nft

Signed-off-by: ryanwolhuter <[email protected]>

* refactor raw transaction input

Signed-off-by: ryanwolhuter <[email protected]>

* add model value and config types

Signed-off-by: ryanwolhuter <[email protected]>

* add model value types

Signed-off-by: ryanwolhuter <[email protected]>

* add transactions by treasury address type

Signed-off-by: ryanwolhuter <[email protected]>

* add high level interface types

Signed-off-by: ryanwolhuter <[email protected]>

* remove batches logic

Signed-off-by: ryanwolhuter <[email protected]>

* wire up module address query

Signed-off-by: ryanwolhuter <[email protected]>

* refactor raw transaction

Signed-off-by: ryanwolhuter <[email protected]>

* refactor contract interaction

Signed-off-by: ryanwolhuter <[email protected]>

* only allow one safe

Signed-off-by: ryanwolhuter <[email protected]>

* refactor proposal component

Signed-off-by: ryanwolhuter <[email protected]>

* rename prop for clarity

Signed-off-by: ryanwolhuter <[email protected]>

* auto select safe on mount

Signed-off-by: ryanwolhuter <[email protected]>

* fix reference error

Signed-off-by: ryanwolhuter <[email protected]>

* fix collectable fetch bug

Signed-off-by: ryanwolhuter <[email protected]>

* fix short name in type

Signed-off-by: ryanwolhuter <[email protected]>

* remove redundant labels

Signed-off-by: ryanwolhuter <[email protected]>

* add types for token fetch logic

Signed-off-by: ryanwolhuter <[email protected]>

* fix typo

Signed-off-by: ryanwolhuter <[email protected]>

* use update for event name

Signed-off-by: ryanwolhuter <[email protected]>

* add update transaction event to raw

Signed-off-by: ryanwolhuter <[email protected]>

* add placeholder data display

Signed-off-by: ryanwolhuter <[email protected]>

* add types for transfer nft

Signed-off-by: ryanwolhuter <[email protected]>

* rename is proposal to is read only

Signed-off-by: ryanwolhuter <[email protected]>

* add contract interaction validation

Signed-off-by: ryanwolhuter <[email protected]>

* clear parameter on method change

Signed-off-by: ryanwolhuter <[email protected]>

* add individual parameter examples

Signed-off-by: ryanwolhuter <[email protected]>

* use disabled inputs for read only

Signed-off-by: ryanwolhuter <[email protected]>

* remove unused files

Signed-off-by: ryanwolhuter <[email protected]>

* remove legacy function code

Signed-off-by: ryanwolhuter <[email protected]>

* organize exports

Signed-off-by: ryanwolhuter <[email protected]>

* rename for clarity

Signed-off-by: ryanwolhuter <[email protected]>

* use method name instead of index

Signed-off-by: ryanwolhuter <[email protected]>

* use existing data if it exists

Signed-off-by: ryanwolhuter <[email protected]>

* use list of one collectable when is read only

Signed-off-by: ryanwolhuter <[email protected]>

* re-add activate osnap button

Signed-off-by: ryanwolhuter <[email protected]>

* only show osnap button when plugin is present

Signed-off-by: ryanwolhuter <[email protected]>

* handle undefined assets

Signed-off-by: ryanwolhuter <[email protected]>

* make transaction fields possibly undefined

Signed-off-by: ryanwolhuter <[email protected]>

* disable dropdown when read only

Signed-off-by: ryanwolhuter <[email protected]>

* add loading safes state

Signed-off-by: ryanwolhuter <[email protected]>

* use modal select for transaction type

Signed-off-by: ryanwolhuter <[email protected]>

* add transaction builder styles

Signed-off-by: ryanwolhuter <[email protected]>

* add style variant for proposal

Signed-off-by: ryanwolhuter <[email protected]>

* re-add osnap modal

Signed-off-by: ryanwolhuter <[email protected]>

* use separate page for read only

Signed-off-by: ryanwolhuter <[email protected]>

* fix import error

Signed-off-by: ryanwolhuter <[email protected]>

* make address required

Signed-off-by: ryanwolhuter <[email protected]>

* use transfer funds as default

Signed-off-by: ryanwolhuter <[email protected]>

* add legacy plugin warning

Signed-off-by: ryanwolhuter <[email protected]>

* update is osnap enabled on window focus

Signed-off-by: ryanwolhuter <[email protected]>

* extract safe link logic

Signed-off-by: ryanwolhuter <[email protected]>

* use modal for safe select

Signed-off-by: ryanwolhuter <[email protected]>

* add comments for types

Signed-off-by: ryanwolhuter <[email protected]>

* remove redundant functions

Signed-off-by: ryanwolhuter <[email protected]>

* rename for clarity

Signed-off-by: ryanwolhuter <[email protected]>

* remove weird thing

Signed-off-by: ryanwolhuter <[email protected]>

* apply formatting

Signed-off-by: ryanwolhuter <[email protected]>

* revert original changes

Signed-off-by: ryanwolhuter <[email protected]>

* undo old changes

Signed-off-by: ryanwolhuter <[email protected]>

* add end newlines to match old

Signed-off-by: ryanwolhuter <[email protected]>

* update import

Signed-off-by: ryanwolhuter <[email protected]>

* undo accidental rename

Signed-off-by: ryanwolhuter <[email protected]>

* handle legacy plugin

Signed-off-by: ryanwolhuter <[email protected]>

* add no safes warning

Signed-off-by: ryanwolhuter <[email protected]>

* undo wrong change

Signed-off-by: ryanwolhuter <[email protected]>

* remove old redundant styles

Signed-off-by: ryanwolhuter <[email protected]>

* add doc comments for functions

Signed-off-by: ryanwolhuter <[email protected]>

* add proposal details types

Signed-off-by: ryanwolhuter <[email protected]>

* add comments

Signed-off-by: ryanwolhuter <[email protected]>

* add address input types

Signed-off-by: ryanwolhuter <[email protected]>

* add amount input types

Signed-off-by: ryanwolhuter <[email protected]>

* rename component for clarity

Signed-off-by: ryanwolhuter <[email protected]>

* add tokens modal types

Signed-off-by: ryanwolhuter <[email protected]>

* add tokens modal item types

Signed-off-by: ryanwolhuter <[email protected]>

* rename for clarity

Signed-off-by: ryanwolhuter <[email protected]>

* extract submit proposal modal to component

Signed-off-by: ryanwolhuter <[email protected]>

* add error notifications

Signed-off-by: ryanwolhuter <[email protected]>

* simplify proposal state

Signed-off-by: ryanwolhuter <[email protected]>

* split steps into components

Signed-off-by: ryanwolhuter <[email protected]>

* remove redundant action button state

Signed-off-by: ryanwolhuter <[email protected]>

* use shared header

Signed-off-by: ryanwolhuter <[email protected]>

* don't format amount on change

Signed-off-by: ryanwolhuter <[email protected]>

* add assertion failed in oo state

Signed-off-by: ryanwolhuter <[email protected]>

* remove balance logic from details query

Signed-off-by: ryanwolhuter <[email protected]>

* separate proposal details chain call

Signed-off-by: ryanwolhuter <[email protected]>

* refactor proposal state fetching

Signed-off-by: ryanwolhuter <[email protected]>

* warn about disputed proposals

Signed-off-by: ryanwolhuter <[email protected]>

* run prettier

Signed-off-by: ryanwolhuter <[email protected]>

* remove unused types

Signed-off-by: ryanwolhuter <[email protected]>

* move get paged events to events

Signed-off-by: ryanwolhuter <[email protected]>

* add doc comments

Signed-off-by: ryanwolhuter <[email protected]>

* rename for clarity

Signed-off-by: ryanwolhuter <[email protected]>

* do not show edit plugin modal when is osnap

Signed-off-by: ryanwolhuter <[email protected]>

* add readme

Signed-off-by: ryanwolhuter <[email protected]>

* apply formatting

Signed-off-by: ryanwolhuter <[email protected]>

* fix wrong function interface

Signed-off-by: ryanwolhuter <[email protected]>

* format

Signed-off-by: ryanwolhuter <[email protected]>

* merge latest from origin

Signed-off-by: david <[email protected]>

* fix network id interpretation, and crash when in space.members

* update changes from master

* fix: spacing changes on outcomes page for osnap plugin (snapshot-labs#103)

* feat: improve look and feel of transaction builder in osnap (snapshot-labs#104)

* fix: formatting

Signed-off-by: david <[email protected]>

* fix: reset to snapshot's standard 42px height (snapshot-labs#105)

* feat: uma 1936 add dark mode osnap buttons (snapshot-labs#106)

* fix: cosmetic issues (snapshot-labs#107)

* fix: remove extra spacing (snapshot-labs#108)

* fix: change osnap activated to use snapshot's primary blue color (snapshot-labs#109)

* fix:  font colors in configure osnap modal (snapshot-labs#111)

* fix: simplify osnap transactions (snapshot-labs#112)

* fix: shorten module address in snapshot proposal transaction (snapshot-labs#110)

* fix: change osnap opt in instructions container and checkbox (snapshot-labs#113)

* fix: remove View on IPFS link (snapshot-labs#114)

* fix: show pending notification on signed tx (snapshot-labs#115)

* fix: remove custom inactive osnap button styles (snapshot-labs#116)

* fix: remove request transaction execution warning message (snapshot-labs#117)

---------

Signed-off-by: ryanwolhuter <[email protected]>
Signed-off-by: david <[email protected]>
Co-authored-by: ryanwolhuter <[email protected]>
Co-authored-by: Gerhard Steenkamp <[email protected]>
  • Loading branch information
3 people authored Nov 29, 2023
1 parent a68e86a commit 6c46fb3
Show file tree
Hide file tree
Showing 55 changed files with 6,190 additions and 177 deletions.
2 changes: 1 addition & 1 deletion snapshot-spaces
2 changes: 1 addition & 1 deletion src/components/BaseModalSelectItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defineProps<{
/>
<BasePill>{{ tag }}</BasePill>
</div>
<span class="text-skin-text" v-text="description" />
<span class="break-all text-skin-text" v-text="description" />
</div>
<i-ho-check v-if="selected" class="absolute right-0 text-md" />
</div>
Expand Down
62 changes: 62 additions & 0 deletions src/components/ModalOsnap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script setup lang="ts">
import { TreasuryWallet } from '@/helpers/interfaces';
import { Network } from '@/plugins/oSnap/types';
import { makeConfigureOsnapUrl } from '@/plugins/oSnap/utils/getters';
const props = defineProps<{
open: boolean;
isOsnapEnabled: boolean;
treasury: TreasuryWallet;
spaceName: string;
}>();
defineEmits<{
(e: 'close'): void;
}>();
const spaceUrl = window.location.href.replace('/settings', '');
const href = computed(() =>
makeConfigureOsnapUrl({
spaceUrl,
spaceName: props.spaceName,
safeAddress: props.treasury.address,
network: props.treasury.network as Network
})
);
</script>

<template>
<BaseModal :open="open" @close="$emit('close')">
<template #header>
<h3 v-text="'Configure oSnap'" />
</template>
<div class="m-4 grid grid-cols-[auto,auto] gap-2">
<i-ho-information-circle class="mt-1 text-sm" />
<p class="text-sm">
oSnap seamlessly integrates with Snapshot and your treasury,
automatically executing governance votes on-chain. Bypass the need for
privileged signers to create a DAO that's more efficient and truly
decentralized.
<BaseLink class="mt-1 block text-skin-link" link="https://uma.xyz/osnap"
>Learn more</BaseLink
>
</p>
</div>

<template #footer>
<a
:href="href"
target="_blank"
class="block w-full rounded-full py-[12px] text-white"
:class="
isOsnapEnabled ? 'bg-[hsl(349,65%,52%)]' : 'bg-[hsl(240,83%,58%)]'
"
@click.stop="$emit('close')"
>
{{ isOsnapEnabled ? 'Deactivate' : 'Activate' }} oSnap
<i-ho-external-link class="mb-[2px] ml-1 inline-block text-xs" />
</a>
<p class="mt-2 text-xs text-skin-text">
Note that the deactivation process takes place in the Safe app
</p>
</template>
</BaseModal>
</template>
3 changes: 3 additions & 0 deletions src/components/SettingsPluginsBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const modalPluginsOpen = ref(false);
function handleEditPlugins(name: string) {
if (props.isViewOnly) return;
// the oSnap plugin does not require any configuration
// so we don't need to open the modal
if (name === 'oSnap') return;
currentPlugin.value = {};
currentPlugin.value[name] = clone(form.value.plugins[name]);
modalPluginsOpen.value = true;
Expand Down
50 changes: 46 additions & 4 deletions src/components/SettingsTreasuriesBlock.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { ExtendedSpace, TreasuryWallet } from '@/helpers/interfaces';
import { clone } from '@snapshot-labs/snapshot.js/src/utils';
import { TreasuryWallet } from '@/helpers/interfaces';
const props = defineProps<{
context: 'setup' | 'settings';
space: ExtendedSpace;
error: string | Record<string, any>;
isViewOnly?: boolean;
}>();
Expand All @@ -17,16 +18,21 @@ const treasuryObj = {
};
const modalTreasuryOpen = ref(false);
const modalOsnapOpen = ref(false);
const currentTreasuryIndex = ref<number | null>(null);
const currentTreasury = ref<TreasuryWallet>(clone(treasuryObj));
const hasOsnapPlugin = computed(() => {
return Object.keys(props.space.plugins).includes('oSnap');
});
const isOsnapEnabledOnCurrentTreasury = ref(false);
function handleRemoveTreasury(i) {
function handleRemoveTreasury(i: number) {
form.value.treasuries = form.value.treasuries.filter(
(treasury, index) => index !== i
);
}
function handleEditTreasury(i) {
function handleEditTreasury(i: number) {
if (props.isViewOnly) return;
currentTreasuryIndex.value = i;
currentTreasury.value = clone(form.value.treasuries[i]);
Expand All @@ -39,7 +45,7 @@ function handleAddTreasury() {
modalTreasuryOpen.value = true;
}
function handleSubmitTreasury(treasury) {
function handleSubmitTreasury(treasury: TreasuryWallet) {
if (currentTreasuryIndex.value !== null) {
const treasuriesClone = clone(form.value.treasuries);
treasuriesClone[currentTreasuryIndex.value] = treasury;
Expand All @@ -48,16 +54,44 @@ function handleSubmitTreasury(treasury) {
form.value.treasuries = form.value.treasuries.concat(treasury);
}
}
function handleOpenConfigureOsnapModal(
treasuryIndex: number,
isEnabled: boolean
) {
if (props.isViewOnly || !hasOsnapPlugin.value) return;
currentTreasuryIndex.value = treasuryIndex;
currentTreasury.value = clone(form.value.treasuries[treasuryIndex]);
isOsnapEnabledOnCurrentTreasury.value = isEnabled;
modalOsnapOpen.value = true;
}
function handleCloseConfigureOsnapModal() {
modalOsnapOpen.value = false;
isOsnapEnabledOnCurrentTreasury.value = false;
}
</script>

<template>
<BaseBlock :title="$t('settings.treasuries.label')">
<div v-if="hasOsnapPlugin && form.treasuries.length === 0" class="mb-3">
<h2>Warning: no treasuries</h2>
<p>
You have installed the oSnap plugin, but you don't have any treasuries.
</p>
<p>
Please add a Safe as a treasury and enable oSnap on it to use the oSnap
plugin.
</p>
</div>
<div v-if="form.treasuries.length" class="mb-3 grid gap-3">
<SettingsTreasuriesBlockItem
:treasuries="form.treasuries"
:is-view-only="isViewOnly"
:has-osnap-plugin="hasOsnapPlugin"
@edit-treasury="i => handleEditTreasury(i)"
@remove-treasury="i => handleRemoveTreasury(i)"
@configure-osnap="handleOpenConfigureOsnapModal"
/>
</div>

Expand All @@ -78,6 +112,14 @@ function handleSubmitTreasury(treasury) {
@close="modalTreasuryOpen = false"
@add="handleSubmitTreasury"
/>
<ModalOsnap
v-if="hasOsnapPlugin"
:open="modalOsnapOpen"
:treasury="currentTreasury"
:space-name="form.name"
:is-osnap-enabled="isOsnapEnabledOnCurrentTreasury"
@close="handleCloseConfigureOsnapModal"
/>
</teleport>
</BaseBlock>
</template>
36 changes: 15 additions & 21 deletions src/components/SettingsTreasuriesBlockItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,26 @@ import { TreasuryWallet } from '@/helpers/interfaces';
defineProps<{
treasuries: TreasuryWallet[];
isViewOnly?: boolean;
hasOsnapPlugin: boolean;
}>();
const emit = defineEmits(['removeTreasury', 'editTreasury']);
const emit = defineEmits<{
removeTreasury: [index: number];
editTreasury: [index: number];
configureOsnap: [index: number, isEnabled: boolean];
}>();
</script>

<template>
<div
<SettingsTreasuriesBlockItemButton
v-for="(treasury, i) in treasuries"
:key="i"
class="flex h-full truncate"
@click="emit('editTreasury', i)"
>
<button
class="flex w-full items-center justify-between rounded-md border p-4"
:class="{ 'cursor-default': isViewOnly }"
>
<div class="flex items-center gap-2 truncate pr-[20px] text-left">
<h4 class="truncate">{{ treasury.name }}</h4>
</div>
<BaseButtonIcon
v-show="!isViewOnly"
class="-mr-2"
@click.stop="emit('removeTreasury', i)"
>
<BaseIcon name="close" size="14" />
</BaseButtonIcon>
</button>
</div>
:treasury="treasury"
:treasury-index="i"
:is-view-only="!!isViewOnly"
:has-osnap-plugin="hasOsnapPlugin"
@edit-treasury="i => emit('editTreasury', i)"
@remove-treasury="i => emit('removeTreasury', i)"
@configure-osnap="(i, isEnabled) => emit('configureOsnap', i, isEnabled)"
/>
</template>
65 changes: 65 additions & 0 deletions src/components/SettingsTreasuriesBlockItemButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { TreasuryWallet } from '@/helpers/interfaces';
import { Network } from '@/plugins/oSnap/types';
import { getIsOsnapEnabled } from '@/plugins/oSnap/utils/getters';
const props = defineProps<{
treasury: TreasuryWallet;
treasuryIndex: number;
isViewOnly?: boolean;
hasOsnapPlugin: boolean;
}>();
const emit = defineEmits<{
removeTreasury: [index: number];
editTreasury: [index: number];
configureOsnap: [index: number, isEnabled: boolean];
}>();
const isOsnapEnabled = ref(false);
async function updateIsOsnapEnabled() {
if (!props.hasOsnapPlugin) return;
isOsnapEnabled.value = await getIsOsnapEnabled(
props.treasury.network as Network,
props.treasury.address
);
}
onMounted(async () => {
await updateIsOsnapEnabled();
window.addEventListener('focus', updateIsOsnapEnabled);
});
onUnmounted(() => {
window.removeEventListener('focus', updateIsOsnapEnabled);
});
</script>

<template>
<button
class="flex h-full w-full items-center justify-between truncate rounded-md border p-4"
:class="{ 'cursor-default': isViewOnly }"
@click="emit('editTreasury', treasuryIndex)"
>
<div class="flex items-center gap-2 truncate pr-[20px] text-left">
<h4 class="truncate">{{ treasury.name }}</h4>
</div>
<div class="ml-auto mr-3">
<SettingsTreasuryActivateOsnapButton
v-show="hasOsnapPlugin"
:is-osnap-enabled="isOsnapEnabled"
@click.stop="
!isViewOnly && emit('configureOsnap', treasuryIndex, isOsnapEnabled)
"
/>
</div>
<BaseButtonIcon
v-show="!isViewOnly"
class="-mr-2"
@click.stop="emit('removeTreasury', treasuryIndex)"
>
<BaseIcon name="close" size="14" />
</BaseButtonIcon>
</button>
</template>
21 changes: 21 additions & 0 deletions src/components/SettingsTreasuryActivateOsnapButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
defineProps<{
isOsnapEnabled: boolean;
}>();
</script>

<template>
<button
v-if="isOsnapEnabled"
class="flex items-center gap-2 rounded-full px-3 py-2 bg-skin-primary text-white"
>
<span class="block h-[6px] w-[6px] rounded-full bg-green" />oSnap activated
</button>
<button
v-else
class="bg-skin-link text-skin-bg flex items-center gap-2 rounded-full px-3 py-2"
>
<span class="block h-[6px] w-[6px] rounded-full bg-skin-bg opacity-30" />
Activate oSnap
</button>
</template>
50 changes: 50 additions & 0 deletions src/components/SpaceCreateLegacyOsnap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { ExtendedSpace } from '@/helpers/interfaces';
defineProps<{
space: ExtendedSpace | undefined;
legacyOsnap: { selection: boolean };
}>();
defineEmits<{
(event: 'legacyOsnapToggle'): void;
}>();
</script>

<template>
<div class="mb-4">
<div v-if="space?.voting?.type && space.voting.type !== 'basic'">
<h6>Where is oSnap?</h6>
<p class="mb-3">
oSnap is currently disabled because your space's voting settings
disallow the basic voting type which is a requirement for oSnap to work
properly.
</p>
<p>
Have your admin visit your
<a :href="`#/${space.id}/settings`">settings page</a> under Voting ->
Type, and make sure either "Any" or "Basic Voting" is selected. This
will allow you to create oSnap proposals.
</p>
</div>
<div v-else>
<h6>oSnap Proposal</h6>
<p>
Are you planning for this proposal to initiate a transaction that your
organizations Safe will execute if approved? (Remember, oSnap enables
trustless and permissionless execution)
</p>
<br />
<input
id="toggleOsnap"
type="checkbox"
:checked="legacyOsnap.selection"
@change="$emit('legacyOsnapToggle')"
/>
<label for="toggleOsnap">
Yes, use oSnap for transactions (this will restrict voting type to
Basic).
</label>
</div>
</div>
</template>
Loading

0 comments on commit 6c46fb3

Please sign in to comment.