Skip to content

Commit

Permalink
Redeploy counterfactual accounts (#344)
Browse files Browse the repository at this point in the history
* Hide close when not iframe

* Redeploy counterfactual accounts

* fix build
  • Loading branch information
broody authored Jun 7, 2024
1 parent c48d718 commit 52cdb73
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 65 deletions.
2 changes: 1 addition & 1 deletion packages/keychain/src/components/Container/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function Container({
onClick={close}
/>
)}

<Header chainId={chainId} onBack={onBack} hideAccount={hideAccount} />

<VStack
Expand Down
40 changes: 32 additions & 8 deletions packages/keychain/src/components/DeploymentRequired.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { Container } from "./Container";
import { useEffect, useState } from "react";
import { Status } from "utils/account";
import { Loading } from "./Loading";
import { Button, Link } from "@chakra-ui/react";
import { Button, Link, Text } from "@chakra-ui/react";
import { ExternalIcon } from "@cartridge/ui";
import { PortalBanner } from "./PortalBanner";
import { PortalFooter } from "./PortalFooter";

export function DeploymentRequired({
chainId,
controller,
Expand All @@ -25,30 +24,46 @@ export function DeploymentRequired({
const account = controller.account;
const [status, setStatus] = useState<Status>(account.status);
const [deployHash, setDeployHash] = useState<string>();
const [error, setError] = useState<Error>();

useEffect(() => {
const fetch = async () => {
if (account.status !== Status.DEPLOYING) {
return;
try {
switch (account.status) {
case Status.COUNTERFACTUAL: {
await account.requestDeployment();
break;
}
case Status.DEPLOYING: {
const hash = await account.getDeploymentTxn();

if (hash) {
setDeployHash(hash);
}
break;
}
case Status.DEPLOYED:
return;
}
} catch (e) {
setError(e);
}
const hash = await account.getDeploymentTxn();
setDeployHash(hash);
};

fetch();
}, [account]);

useEffect(() => {
const id = setInterval(async () => {
if (account.status !== Status.DEPLOYING) clearInterval(id);
if (account.status === Status.DEPLOYED) clearInterval(id);
setStatus(account.status);
await account.sync();
}, 2000);

return () => clearInterval(id);
}, [account, setStatus]);

if (status === Status.DEPLOYING) {
if (status !== Status.DEPLOYED) {
return (
<Container chainId={chainId} onLogout={onLogout}>
<PortalBanner
Expand All @@ -72,6 +87,15 @@ export function DeploymentRequired({
</Link>
)}

{error && (
<>
<Text>
We encounter an account deployment error: {error.message}
</Text>
<Text>Please come by discord and report this issue.</Text>
</>
)}

<PortalFooter>
<Button onClick={onClose}>close</Button>
</PortalFooter>
Expand Down
2 changes: 0 additions & 2 deletions packages/keychain/src/components/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { StarkscanUrl } from "utils/url";
import { CheckIcon, ExternalIcon, StarknetIcon, Loading } from "@cartridge/ui";
import { useController } from "hooks/controller";
import { useChainName } from "hooks/chain";
import Account from "utils/account";

export type TransactionState = "pending" | "success" | "error";

Expand All @@ -32,7 +31,6 @@ export function Transaction({
let result: TransactionState = "pending";
controller.account
.waitForTransaction(hash, {
...Account.waitForTransactionOptions,
retryInterval: 8000,
})
.then(() => {
Expand Down
12 changes: 11 additions & 1 deletion packages/keychain/src/components/connect/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,17 @@ export function Login({

setIsLoading(false);
},
[chainId, rpcUrl, origin, policies, expiresAt, isSlot, log],
[
chainId,
rpcUrl,
origin,
policies,
expiresAt,
mode,
log,
onSuccess,
setController,
],
);

return (
Expand Down
16 changes: 6 additions & 10 deletions packages/keychain/src/components/connect/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import { useCallback, useEffect, useState } from "react";
import { DeployAccountDocument, useAccountQuery } from "generated/graphql";
import Controller from "utils/controller";
import { Status } from "utils/account";
import { client } from "utils/graphql";
import { PopupCenter } from "utils/url";
import { FormValues, SignupProps } from "./types";
Expand Down Expand Up @@ -122,6 +121,12 @@ function Form({
cacheTime: 10000000,
refetchInterval: (data) => (!data ? 1000 : undefined),
onSuccess: async (data) => {
// Deploy account
await client.request(DeployAccountDocument, {
id: values.username,
chainId: `starknet:${shortString.decodeShortString(chainId)}`,
});

const {
account: {
credentials: {
Expand All @@ -140,16 +145,7 @@ function Form({
credentialId,
});

controller.account.status = Status.DEPLOYING;

await client.request(DeployAccountDocument, {
id: values.username,
chainId: `starknet:${shortString.decodeShortString(chainId)}`,
});

controller.store();
await controller.account.sync();

setController(controller);

if (onSuccess) {
Expand Down
63 changes: 27 additions & 36 deletions packages/keychain/src/utils/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import {
InvokeFunctionResponse,
TypedData,
BigNumberish,
waitForTransactionOptions,
TransactionFinalityStatus,
InvocationsDetails,
num,
TransactionExecutionStatus,
shortString,
} from "starknet";
import {
AccountContractDocument,
Expand All @@ -27,6 +27,7 @@ import selectors from "./selectors";
import Storage from "./storage";
import { CartridgeAccount } from "@cartridge/account-wasm";
import { Session } from "@cartridge/controller";
import { VERSION } from "./controller";

const EST_FEE_MULTIPLIER = 2n;

Expand All @@ -40,12 +41,14 @@ class Account extends BaseAccount {
rpc: RpcProvider;
private selector: string;
chainId: string;
username: string;
cartridge: CartridgeAccount;

constructor(
chainId: string,
nodeUrl: string,
address: string,
username: string,
signer: SignerInterface,
webauthn: {
rpId: string;
Expand All @@ -57,8 +60,9 @@ class Account extends BaseAccount {
super({ nodeUrl }, address, signer);

this.rpc = new RpcProvider({ nodeUrl });
this.selector = selectors["0.0.1"].deployment(address, chainId);
this.selector = selectors[VERSION].deployment(address, chainId);
this.chainId = chainId;
this.username = username;
this.cartridge = CartridgeAccount.new(
nodeUrl,
chainId,
Expand All @@ -69,31 +73,13 @@ class Account extends BaseAccount {
webauthn.publicKey,
);

const state = Storage.get(this.selector);
if (!state || Date.now() - state.syncing > 5000) {
this.sync();
return;
}
}

static get waitForTransactionOptions(): waitForTransactionOptions {
return {
retryInterval: 1000,
successStates: [
TransactionFinalityStatus.ACCEPTED_ON_L1,
TransactionFinalityStatus.ACCEPTED_ON_L2,
],
errorStates: [
TransactionFinalityStatus.ACCEPTED_ON_L1,
TransactionFinalityStatus.ACCEPTED_ON_L2,
],
};
this.sync();
}

get status() {
const state = Storage.get(this.selector);
if (!state || !state.status) {
return Status.COUNTERFACTUAL;
return Status.DEPLOYING;
}

return state.status;
Expand All @@ -105,21 +91,28 @@ class Account extends BaseAccount {
});
}

async getDeploymentTxn(): Promise<string | undefined> {
let chainId = this.chainId.substring(2);
chainId = Buffer.from(chainId, "hex").toString("ascii");
async requestDeployment(): Promise<void> {
await client.request(AccountContractDocument, {
id: this.username,
chainId: `starknet:${shortString.decodeShortString(this.chainId)}`,
});

this.status = Status.DEPLOYING;
}

async getDeploymentTxn(): Promise<string | undefined> {
try {
const data: AccountContractQuery = await client.request(
AccountContractDocument,
{
id: `starknet:${chainId}:${this.address}`,
id: `starknet:${shortString.decodeShortString(this.chainId)}:${
this.address
}`,
},
);

if (!data?.contract?.deployTransaction?.id) {
console.error("could not find deployment txn");
return;
throw new Error("deployment txn not found");
}

return data.contract.deployTransaction.id.split("/")[1];
Expand All @@ -133,19 +126,17 @@ class Account extends BaseAccount {
}

async sync() {
Storage.update(this.selector, {
syncing: Date.now(),
});

try {
switch (this.status) {
case Status.DEPLOYING:
case Status.COUNTERFACTUAL: {
case Status.DEPLOYING: {
const hash = await this.getDeploymentTxn();
if (!hash) return;
if (!hash) {
this.status = Status.COUNTERFACTUAL;
return;
}

const receipt = await this.rpc.getTransactionReceipt(hash);
if (receipt.isReverted() || receipt.isRejected()) {
// TODO: Handle redeployment
this.status = Status.COUNTERFACTUAL;
return;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/keychain/src/utils/connection/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export function executeFactory({
});
}
const account = controller.account;
if (account.status === Status.DEPLOYING) {
if (
account.status === Status.COUNTERFACTUAL ||
account.status === Status.DEPLOYING
) {
return Promise.resolve({
code: ResponseCodes.NOT_ALLOWED,
message: "Account is deploying.",
Expand Down
19 changes: 13 additions & 6 deletions packages/keychain/src/utils/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,19 @@ export default class Controller {
this.rpcUrl = rpcUrl;
this.publicKey = publicKey;
this.credentialId = credentialId;
this.account = new Account(chainId, rpcUrl, address, this.signer, {
rpId: process.env.NEXT_PUBLIC_RP_ID,
origin: process.env.NEXT_PUBLIC_ORIGIN,
credentialId,
publicKey,
});
this.account = new Account(
chainId,
rpcUrl,
address,
username,
this.signer,
{
rpId: process.env.NEXT_PUBLIC_RP_ID,
origin: process.env.NEXT_PUBLIC_ORIGIN,
credentialId,
publicKey,
},
);
}

async getUser() {
Expand Down

0 comments on commit 52cdb73

Please sign in to comment.