Skip to content

Commit

Permalink
fix: non zero balance on non deployed cairo 0 account (#276)
Browse files Browse the repository at this point in the history
* fix: non zero balance on non deployed cairo 0 account (dapp-ui flow)

* feat: balance check in upgradecontract

* chore: lint + prettier

* feat: deploy / upgrade workflow separated

* test: rollback upgradeAccContract testing

* feat: finalized wallet-ui flow

* feat: check for required deploy in executeTxn

* test: check for required deploy in executeTxn

* chore: lint + prettier

* fix: review comment

* feat: replace upgradeRequired by getCorrectContractAddress to handle deploy and upgrade required

* fix: pr review comment

* refactor: upgrade / deploy requirement in non get-starknet api

* refactor: upgrade / deploy requirement in get-starknet api

* chore: lint + prettier

* refactor: added exceptions.ts file and utilities in snapUtils

* fix: eip 6963 detection on wallet-ui

* fix: ui workflow between deploy and upgrade

* fix: sync the account from snap homepage with recover (#282)

* fix: sync the address in snap homepage

* chore: add unit test

* fix: lower the test coverage

* chore: update var position on snap homepage

---------

Co-authored-by: khanti42 <[email protected]>

* fix: missing yarn.lock + pr review (#280)

* chore: add log level when building wallet ui (#284)

Co-authored-by: stanleyyuen <[email protected]>

---------

Co-authored-by: Stanley Yuen <[email protected]>
  • Loading branch information
khanti42 and stanleyyconsensys authored Jul 11, 2024
1 parent fcb83e1 commit d9beafe
Show file tree
Hide file tree
Showing 48 changed files with 1,320 additions and 348 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,21 @@ jobs:
echo "VERSION=${BASE}-dev-${HASH}-${DATE}"
echo "TAG=dev"
echo "ENV=dev"
echo "LOG_LEVEL=all"
} >> "$GITHUB_OUTPUT"
elif [[ $ENV == "staging" ]]; then
{
echo "VERSION=${BASE}-staging"
echo "TAG=staging"
echo "ENV=staging"
echo "LOG_LEVEL=off"
} >> "$GITHUB_OUTPUT"
elif [[ $ENV == "production" ]]; then
{
echo "VERSION=${BASE}"
echo "TAG=latest"
echo "ENV=prod"
echo "LOG_LEVEL=off"
} >> "$GITHUB_OUTPUT"
else
echo "Invalid environment"
Expand All @@ -67,6 +70,7 @@ jobs:
AWS_S3_URL: ${{ steps.prepare_parameters.outputs.AWS_S3_URL }}
GET_STARKNET_PUBLIC_PATH: ${{ steps.prepare_parameters.outputs.GET_STARKNET_PUBLIC_PATH }}
CACHE_KEY: ${{ github.sha }}-${{ steps.prepare_parameters.outputs.ENV }}
LOG_LEVEL: ${{ steps.prepare_parameters.outputs.LOG_LEVEL }}

install-build:
needs:
Expand Down Expand Up @@ -103,7 +107,7 @@ jobs:
echo "Building UI with version $VERSION"
REACT_APP_SNAP_VERSION="$VERSION" yarn workspace wallet-ui build
REACT_APP_DEBUG_LEVEL="${LOG_LEVEL}" REACT_APP_SNAP_VERSION="${VERSION}" yarn workspace wallet-ui build
echo "Building Get Starknet with GET_STARKNET_PUBLIC_PATH=$GET_STARKNET_PUBLIC_PATH"
Expand All @@ -114,6 +118,7 @@ jobs:
VOYAGER_API_KEY: ${{ secrets.VOYAGER_API_KEY }}
ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }}
GET_STARKNET_PUBLIC_PATH: ${{ needs.prepare-deployment.outputs.GET_STARKNET_PUBLIC_PATH }}
LOG_LEVEL: ${{ needs.prepare-deployment.outputs.LOG_LEVEL }}
- name: Cache Build
uses: actions/cache@v3
id: cache
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"clean": "yarn workspaces foreach --parallel --interlaced --verbose run clean",
"build": "yarn workspaces foreach --parallel --interlaced --verbose run build",
"lint": "yarn workspaces foreach --parallel --interlaced --verbose run lint",
"lint:fix": "yarn workspaces foreach --parallel --interlaced --verbose run lint:fix",
"start": "yarn workspaces foreach --parallel --interlaced --verbose run start",
"test": "yarn workspaces foreach --parallel --interlaced --verbose run test",
"prepare": "husky install"
Expand Down
4 changes: 2 additions & 2 deletions packages/starknet-snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"serve": "mm-snap serve",
"start": "mm-snap watch",
"test": "yarn run test:unit && yarn run cover:report",
"test:unit": "nyc --check-coverage --statements 80 --branches 80 --functions 80 --lines 80 mocha --colors -r ts-node/register \"test/**/*.test.ts\"",
"test:unit:one": "nyc --check-coverage --statements 80 --branches 80 --functions 80 --lines 80 mocha --colors -r ts-node/register"
"test:unit": "nyc --check-coverage --statements 70 --branches 70 --functions 70 --lines 70 mocha --colors -r ts-node/register \"test/**/*.test.ts\"",
"test:unit:one": "nyc --check-coverage --statements 70 --branches 70 --functions 70 --lines 70 mocha --colors -r ts-node/register"
},
"nyc": {
"exclude": [
Expand Down
67 changes: 52 additions & 15 deletions packages/starknet-snap/src/createAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@ import {
getAccContractAddressAndCallData,
deployAccount,
waitForTransaction,
getAccContractAddressAndCallDataLegacy,
estimateAccountDeployFee,
} from './utils/starknetUtils';
import {
getNetworkFromChainId,
getValidNumber,
upsertAccount,
upsertTransaction,
addDialogTxt,
getSendTxnText,
} from './utils/snapUtils';
import { AccContract, VoyagerTransactionType, Transaction, TransactionStatus } from './types/snapState';
import { ApiParams, CreateAccountRequestParams } from './types/snapApi';
import { heading, panel, text, DialogType } from '@metamask/snaps-sdk';
import { heading, panel, DialogType } from '@metamask/snaps-sdk';
import { logger } from './utils/logger';
import { CAIRO_VERSION, CAIRO_VERSION_LEGACY } from './utils/constants';
import { CairoVersion, EstimateFee, num } from 'starknet';

/**
* Create an starknet account.
Expand All @@ -24,11 +28,15 @@ import { logger } from './utils/logger';
* @param silentMode - The flag to disable the confirmation dialog from snap.
* @param waitMode - The flag to enable an determination by doing an recursive fetch to check if the deploy account status is on L2 or not. The wait mode is only useful when it compose with other txn together, it can make sure the deploy txn execute complete, avoiding the latter txn failed.
*/
export async function createAccount(params: ApiParams, silentMode = false, waitMode = false) {
export async function createAccount(
params: ApiParams,
silentMode = false,
waitMode = false,
cairoVersion: CairoVersion = CAIRO_VERSION,
) {
try {
const { state, wallet, saveMutex, keyDeriver, requestParams } = params;
const requestParamsObj = requestParams as CreateAccountRequestParams;

const addressIndex = getValidNumber(requestParamsObj.addressIndex, -1, 0);
const network = getNetworkFromChainId(state, requestParamsObj.chainId);
const deploy = !!requestParamsObj.deploy;
Expand All @@ -39,37 +47,64 @@ export async function createAccount(params: ApiParams, silentMode = false, waitM
addressIndex: addressIndexInUsed,
derivationPath,
} = await getKeysFromAddressIndex(keyDeriver, network.chainId, state, addressIndex);
const { address: contractAddress, callData: contractCallData } = getAccContractAddressAndCallData(publicKey);

const { address: contractAddress, callData: contractCallData } =
cairoVersion == CAIRO_VERSION_LEGACY
? getAccContractAddressAndCallDataLegacy(publicKey)
: getAccContractAddressAndCallData(publicKey);
logger.log(
`createAccount:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\naddressIndex = ${addressIndexInUsed}`,
);

if (deploy) {
if (!silentMode) {
const components = [];
addDialogTxt(components, 'Address', contractAddress);
addDialogTxt(components, 'Public Key', publicKey);
addDialogTxt(components, 'Address Index', addressIndex.toString());
logger.log(
`estimateAccountDeployFee:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\naddressIndex = ${addressIndexInUsed}`,
);

const estimateDeployFee: EstimateFee = await estimateAccountDeployFee(
network,
contractAddress,
contractCallData,
publicKey,
privateKey,
cairoVersion,
);
logger.log(`estimateAccountDeployFee:\nestimateDeployFee: ${toJson(estimateDeployFee)}`);
const maxFee = num.toBigInt(estimateDeployFee.suggestedMaxFee.toString(10) ?? '0');
const dialogComponents = getSendTxnText(
state,
contractAddress,
'deploy',
contractCallData,
contractAddress,
maxFee,
network,
);

const response = await wallet.request({
method: 'snap_dialog',
params: {
type: DialogType.Confirmation,
content: panel([
heading('Do you want to sign this deploy account transaction ?'),
text(`It will be signed with address: ${contractAddress}`),
...components,
]),
content: panel([heading('Do you want to sign this deploy transaction ?'), ...dialogComponents]),
},
});

if (!response)
return {
address: contractAddress,
};
}

// Deploy account will auto estimate the fee from the network if not provided
const deployResp = await deployAccount(network, contractAddress, contractCallData, publicKey, privateKey);
const deployResp = await deployAccount(
network,
contractAddress,
contractCallData,
publicKey,
privateKey,
cairoVersion,
);

if (deployResp.contract_address && deployResp.transaction_hash) {
const userAccount: AccContract = {
Expand All @@ -80,6 +115,8 @@ export async function createAccount(params: ApiParams, silentMode = false, waitM
derivationPath,
deployTxnHash: deployResp.transaction_hash,
chainId: network.chainId,
upgradeRequired: cairoVersion === CAIRO_VERSION_LEGACY,
deployRequired: false,
};

await upsertAccount(userAccount, wallet, saveMutex);
Expand Down
18 changes: 12 additions & 6 deletions packages/starknet-snap/src/declareContract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { toJson } from './utils/serializer';
import { ApiParams, DeclareContractRequestParams } from './types/snapApi';
import { getNetworkFromChainId, getDeclareSnapTxt, showUpgradeRequestModal } from './utils/snapUtils';
import { getKeysFromAddress, declareContract as declareContractUtil, isUpgradeRequired } from './utils/starknetUtils';
import { getNetworkFromChainId, getDeclareSnapTxt, showAccountRequireUpgradeOrDeployModal } from './utils/snapUtils';
import {
getKeysFromAddress,
declareContract as declareContractUtil,
validateAccountRequireUpgradeOrDeploy,
} from './utils/starknetUtils';
import { heading, panel, DialogType } from '@metamask/snaps-sdk';
import { logger } from './utils/logger';

Expand All @@ -14,11 +18,13 @@ export async function declareContract(params: ApiParams) {

const senderAddress = requestParamsObj.senderAddress;
const network = getNetworkFromChainId(state, requestParamsObj.chainId);
const { privateKey } = await getKeysFromAddress(keyDeriver, network, state, senderAddress);
const { privateKey, publicKey } = await getKeysFromAddress(keyDeriver, network, state, senderAddress);

if (await isUpgradeRequired(network, senderAddress)) {
await showUpgradeRequestModal(wallet);
throw new Error('Upgrade required');
try {
await validateAccountRequireUpgradeOrDeploy(network, senderAddress, publicKey);
} catch (e) {
await showAccountRequireUpgradeOrDeployModal(wallet, e);
throw e;
}

const snapComponents = getDeclareSnapTxt(
Expand Down
9 changes: 3 additions & 6 deletions packages/starknet-snap/src/estimateFee.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { toJson } from './utils/serializer';
import { Invocations, TransactionType } from 'starknet';
import { validateAndParseAddress } from '../src/utils/starknetUtils';
import { validateAccountRequireUpgradeOrDeploy, validateAndParseAddress } from '../src/utils/starknetUtils';
import { ApiParams, EstimateFeeRequestParams } from './types/snapApi';
import { getNetworkFromChainId } from './utils/snapUtils';
import {
Expand All @@ -11,7 +11,6 @@ import {
estimateFeeBulk,
addFeesFromAllTransactions,
isAccountDeployed,
isUpgradeRequired,
} from './utils/starknetUtils';
import { ACCOUNT_CLASS_HASH } from './utils/constants';
import { logger } from './utils/logger';
Expand Down Expand Up @@ -45,17 +44,15 @@ export async function estimateFee(params: ApiParams) {
throw new Error(`The given sender address is invalid: ${senderAddress}`);
}

if (await isUpgradeRequired(network, senderAddress)) {
throw new Error('Upgrade required');
}

const { privateKey: senderPrivateKey, publicKey } = await getKeysFromAddress(
keyDeriver,
network,
state,
senderAddress,
);

await validateAccountRequireUpgradeOrDeploy(network, senderAddress, publicKey);

const txnInvocation = {
contractAddress,
entrypoint: contractFuncName,
Expand Down
17 changes: 12 additions & 5 deletions packages/starknet-snap/src/executeTxn.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Invocations, TransactionType } from 'starknet';
import { getNetworkFromChainId, getTxnSnapTxt, addDialogTxt, showUpgradeRequestModal } from './utils/snapUtils';
import {
getNetworkFromChainId,
getTxnSnapTxt,
addDialogTxt,
showAccountRequireUpgradeOrDeployModal,
} from './utils/snapUtils';
import {
getKeysFromAddress,
executeTxn as executeTxnUtil,
isAccountDeployed,
estimateFeeBulk,
getAccContractAddressAndCallData,
addFeesFromAllTransactions,
isUpgradeRequired,
validateAccountRequireUpgradeOrDeploy,
} from './utils/starknetUtils';
import { ApiParams, ExecuteTxnRequestParams } from './types/snapApi';
import { createAccount } from './createAccount';
Expand All @@ -27,9 +32,11 @@ export async function executeTxn(params: ApiParams) {
addressIndex,
} = await getKeysFromAddress(keyDeriver, network, state, senderAddress);

if (await isUpgradeRequired(network, senderAddress)) {
await showUpgradeRequestModal(wallet);
throw new Error('Upgrade required');
try {
await validateAccountRequireUpgradeOrDeploy(network, senderAddress, publicKey);
} catch (e) {
await showAccountRequireUpgradeOrDeployModal(wallet, e);
throw e;
}

const txnInvocationArray = Array.isArray(requestParamsObj.txnInvocation)
Expand Down
11 changes: 4 additions & 7 deletions packages/starknet-snap/src/extractPrivateKey.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { toJson } from './utils/serializer';
import { validateAndParseAddress } from '../src/utils/starknetUtils';
import { validateAccountRequireUpgradeOrDeploy, validateAndParseAddress } from '../src/utils/starknetUtils';
import { ApiParams, ExtractPrivateKeyRequestParams } from './types/snapApi';
import { getNetworkFromChainId } from './utils/snapUtils';
import { getKeysFromAddress, isUpgradeRequired } from './utils/starknetUtils';
import { getKeysFromAddress } from './utils/starknetUtils';
import { copyable, panel, text, DialogType } from '@metamask/snaps-sdk';
import { logger } from './utils/logger';

Expand All @@ -22,9 +22,8 @@ export async function extractPrivateKey(params: ApiParams) {
throw new Error(`The given user address is invalid: ${userAddress}`);
}

if (await isUpgradeRequired(network, userAddress)) {
throw new Error('Upgrade required');
}
const { privateKey: userPrivateKey, publicKey } = await getKeysFromAddress(keyDeriver, network, state, userAddress);
await validateAccountRequireUpgradeOrDeploy(network, userAddress, publicKey);

const response = await wallet.request({
method: 'snap_dialog',
Expand All @@ -35,8 +34,6 @@ export async function extractPrivateKey(params: ApiParams) {
});

if (response === true) {
const { privateKey: userPrivateKey } = await getKeysFromAddress(keyDeriver, network, state, userAddress);

await wallet.request({
method: 'snap_dialog',
params: {
Expand Down
9 changes: 4 additions & 5 deletions packages/starknet-snap/src/extractPublicKey.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { toJson } from './utils/serializer';
import { constants, num } from 'starknet';
import { validateAndParseAddress, isUpgradeRequired } from '../src/utils/starknetUtils';
import { validateAndParseAddress, validateAccountRequireUpgradeOrDeploy } from '../src/utils/starknetUtils';
import { ApiParams, ExtractPublicKeyRequestParams } from './types/snapApi';
import { getAccount, getNetworkFromChainId } from './utils/snapUtils';
import { getKeysFromAddress } from './utils/starknetUtils';
Expand All @@ -26,15 +26,14 @@ export async function extractPublicKey(params: ApiParams) {
throw new Error(`The given user address is invalid: ${requestParamsObj.userAddress}`);
}

if (await isUpgradeRequired(network, userAddress)) {
throw new Error('Upgrade required');
}
// [TODO] logic below is redundant, getKeysFromAddress is doing the same
const { publicKey } = await getKeysFromAddress(keyDeriver, network, state, userAddress);
await validateAccountRequireUpgradeOrDeploy(network, userAddress, publicKey);

let userPublicKey;
const accContract = getAccount(state, userAddress, network.chainId);
if (!accContract?.publicKey || num.toBigInt(accContract.publicKey) === constants.ZERO) {
logger.log(`extractPublicKey: User address cannot be found or the signer public key is 0x0: ${userAddress}`);
const { publicKey } = await getKeysFromAddress(keyDeriver, network, state, userAddress);
userPublicKey = publicKey;
} else {
userPublicKey = accContract.publicKey;
Expand Down
Loading

0 comments on commit d9beafe

Please sign in to comment.