Skip to content

Commit

Permalink
feat: handle cross-chain dependencies in deployment (#218)
Browse files Browse the repository at this point in the history
* feat: allow retry on deployment steps

* feat: handle cross-chain dependencies
  • Loading branch information
Sekhmet authored Apr 11, 2024
1 parent 61d32c3 commit f72f4d7
Show file tree
Hide file tree
Showing 13 changed files with 967 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-jokes-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@snapshot-labs/sx": patch
---

add deployL1AvatarExecution function to StarknetTx
1 change: 1 addition & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@braintree/sanitize-url": "^6.0.4",
"@ensdomains/eth-ens-namehash": "^2.0.15",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0",
"@ethersproject/address": "^5.7.0",
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/constants": "^5.7.0",
Expand Down
179 changes: 125 additions & 54 deletions apps/ui/src/components/CreateDeploymentProgress.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
<script setup lang="ts">
import { shorten } from '@/helpers/utils';
import { getNetwork } from '@/networks';
import { StrategyConfig } from '@/networks/types';
import { StrategyConfig, Connector } from '@/networks/types';
import { NetworkID, SpaceMetadata, SpaceSettings } from '@/types';
type DeployingDependencyStep = {
id: `DEPLOYING_DEPS_${number}`;
title: string;
strategy: StrategyConfig;
};
type DeployingSpaceStep = {
id: 'DEPLOYING_SPACE';
title: string;
};
type IndexingSpaceStep = {
id: 'INDEXING_SPACE';
title: string;
};
type Step = DeployingDependencyStep | DeployingSpaceStep | IndexingSpaceStep;
const props = defineProps<{
networkId: NetworkID;
salt: string;
Expand All @@ -18,14 +36,19 @@ const props = defineProps<{
}>();
const { deployDependency, createSpace } = useActions();
const { web3, login } = useWeb3();
const currentStep = ref(0);
const completed = ref(false);
const failed = ref(false);
const connectorModalOpen = ref(false);
const connectorModalConnectors = ref([] as string[]);
const connectorCallbackFn: Ref<((value: string | false) => void) | null> = ref(null);
const txIds = ref({});
const deployedExecutionStrategies = ref([] as StrategyConfig[]);
const network = computed(() => getNetwork(props.networkId));
const steps = computed(() => {
const steps = computed<Step[]>(() => {
const dependenciesSteps = props.executionStrategies.filter(strategy => strategy.deploy);
return [
Expand All @@ -35,8 +58,9 @@ const steps = computed(() => {
: 'Deploying dependency';
return {
id: `DEPLOYING_DEPS_${i}`,
title
id: `DEPLOYING_DEPS_${i}` as const,
title,
strategy: config
};
}),
{
Expand All @@ -50,48 +74,46 @@ const steps = computed(() => {
];
});
async function deploy() {
const dependenciesSteps = props.executionStrategies.filter(strategy => strategy.deploy);
const executionStrategies = props.executionStrategies.filter(
strategy => !strategy.deploy
) as StrategyConfig[];
function getConnector(supportedConnectors: string[]) {
connectorModalOpen.value = true;
connectorModalConnectors.value = supportedConnectors;
return new Promise<string | false>(resolve => {
connectorCallbackFn.value = resolve;
});
}
for (let i = 0; i < dependenciesSteps.length; i++) {
const strategy = dependenciesSteps[i];
function handleConnectorPick(connector: string) {
connectorCallbackFn.value?.(connector);
connectorModalOpen.value = false;
}
try {
const result = await deployDependency(
props.networkId,
props.controller,
props.predictedSpaceAddress,
strategy
);
if (!result) {
failed.value = true;
return;
}
const { address, txId } = result;
txIds.value[`DEPLOYING_DEPS_${i}`] = txId;
const confirmedReceipt = await network.value.helpers.waitForTransaction(txId);
if (confirmedReceipt.status === 0) throw new Error('Transaction failed');
executionStrategies.push({
...strategy,
deploy: undefined,
address
});
currentStep.value = currentStep.value + 1;
} catch {
failed.value = true;
return;
}
function handleConnectorClose() {
connectorCallbackFn.value?.(false);
connectorModalOpen.value = false;
}
async function deployStep(step: DeployingDependencyStep | DeployingSpaceStep) {
const supportedConnectors =
'strategy' in step && step.strategy.deployConnectors
? step.strategy.deployConnectors
: network.value.managerConnectors;
if (!supportedConnectors.includes(web3.value.type as Connector)) {
const connector = await getConnector(supportedConnectors);
if (!connector) throw new Error('No connector selected');
await login(connector);
}
try {
const result = await createSpace(
let result;
if (step.id === 'DEPLOYING_SPACE') {
const executionStrategies = [
...props.executionStrategies.filter(strategy => strategy.address !== ''),
...deployedExecutionStrategies.value
];
result = await createSpace(
props.networkId,
props.salt,
props.metadata,
Expand All @@ -102,21 +124,53 @@ async function deploy() {
executionStrategies,
props.controller
);
} else {
result = await deployDependency(
props.networkId,
props.controller,
props.predictedSpaceAddress,
step.strategy
);
}
if (!result) {
failed.value = true;
return;
}
if (!result) {
failed.value = true;
return;
}
txIds.value['DEPLOYING_SPACE'] = result;
const { address, txId } = result;
const confirmedReceipt = await network.value.helpers.waitForTransaction(result);
if (confirmedReceipt.status === 0) throw new Error('Transaction failed');
txIds.value[step.id] = txId;
const stepNetwork = getStepNetwork(step);
const confirmedReceipt = await stepNetwork.helpers.waitForTransaction(txId);
if (confirmedReceipt.status === 0) throw new Error('Transaction failed');
currentStep.value = currentStep.value + 1;
} catch {
failed.value = true;
return;
if (step.id !== 'DEPLOYING_SPACE' && step.strategy.address === '') {
deployedExecutionStrategies.value.push({
...step.strategy,
deploy: undefined,
address
});
}
currentStep.value = currentStep.value + 1;
}
async function deploy(startIndex: number = 0) {
failed.value = false;
const stepsToProcess = steps.value.slice(startIndex);
for (const step of stepsToProcess) {
if (step.id === 'INDEXING_SPACE') continue;
try {
await deployStep(step);
} catch (e: unknown) {
console.log('e', e);
failed.value = true;
return;
}
}
try {
Expand All @@ -129,6 +183,14 @@ async function deploy() {
}
}
function getStepNetwork(step: Step) {
if ('strategy' in step && step.strategy.deployNetworkId) {
return getNetwork(step.strategy.deployNetworkId);
}
return network.value;
}
onMounted(() => deploy());
</script>

Expand All @@ -149,11 +211,14 @@ onMounted(() => deploy());
</div>
<div>
<h4 v-text="step.title" />
<a v-if="failed && i === currentStep" class="text-skin-text" @click="deploy(currentStep)">
Retry
</a>
<a
v-if="txIds[step.id]"
class="inline-flex items-center"
target="_blank"
:href="network.helpers.getExplorerUrl(txIds[step.id], 'transaction')"
:href="getStepNetwork(step).helpers.getExplorerUrl(txIds[step.id], 'transaction')"
>
{{ shorten(txIds[step.id]) }}
<IH-arrow-sm-right class="inline-block ml-1 -rotate-45" />
Expand All @@ -172,4 +237,10 @@ onMounted(() => deploy());
/>.
</div>
</div>
<ModalConnector
:open="connectorModalOpen"
:supported-connectors="connectorModalConnectors"
@close="handleConnectorClose"
@pick="handleConnectorPick"
/>
</template>
71 changes: 71 additions & 0 deletions apps/ui/src/components/Modal/Connector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script setup lang="ts">
import { getInjected } from '@snapshot-labs/lock/src/utils';
import connectors, { mapConnectorId, getConnectorIconUrl } from '@/helpers/connectors';
const win = window;
const injected = getInjected();
if (injected)
connectors['injected'] = {
...connectors['injected'],
...injected,
id: 'injected',
icon: connectors[mapConnectorId(injected.id)]?.icon ?? injected.icon
};
const props = defineProps<{
open: boolean;
supportedConnectors: string[];
}>();
const emit = defineEmits<{
(e: 'pick', connector: string): void;
(e: 'close'): void;
}>();
const { open } = toRefs(props);
const availableConnectors = computed(() => {
return Object.values(connectors).filter(connector => {
if (!props.supportedConnectors.includes(connector.id)) return false;
const hasNoType = !('type' in connector) || !connector.type;
const isActive =
'type' in connector &&
'root' in connector &&
connector.type === 'injected' &&
win[connector.root];
return hasNoType || isActive;
});
});
</script>

<template>
<UiModal :open="open" @close="emit('close')">
<template #header>
<h3 v-text="'Connect wallet'" />
</template>
<div>
<div class="m-4 space-y-2">
<a
v-for="connector in availableConnectors"
:key="connector.id"
target="_blank"
class="block"
@click="emit('pick', connector.id)"
>
<UiButton class="button-outline w-full flex justify-center items-center">
<img
:src="getConnectorIconUrl(connector.icon)"
height="28"
width="28"
class="mr-2 -mt-1"
:alt="connector.name"
/>
{{ connector.name }}
</UiButton>
</a>
</div>
</div>
</UiModal>
</template>
2 changes: 1 addition & 1 deletion apps/ui/src/composables/useActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export function useActions() {
predictedSpaceAddress: predictSpaceAddress(networkId, salt)
});

return receipt.txId;
return receipt;
}

async function updateMetadata(space: Space, metadata: SpaceMetadata) {
Expand Down
21 changes: 19 additions & 2 deletions apps/ui/src/networks/starknet/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,25 @@ export function createActions(
async predictSpaceAddress(web3: any, { salt }) {
return client.predictSpaceAddress({ account: web3.provider.account, saltNonce: salt });
},
async deployDependency() {
throw new Error('Not implemented');
async deployDependency(
web3: any,
params: {
controller: string;
spaceAddress: string;
strategy: StrategyConfig;
}
) {
if (!params.strategy.deploy) {
throw new Error('This strategy is not deployable');
}

return params.strategy.deploy(
client,
web3.getSigner(),
params.controller,
params.spaceAddress,
params.strategy.params
);
},
async createSpace(
web3: any,
Expand Down
Loading

0 comments on commit f72f4d7

Please sign in to comment.