From 9fe75f1d1d08f2b8be81ed88673a53cf5b59868d Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 4 Oct 2023 18:50:21 +0800 Subject: [PATCH 1/9] feat: change account contract to cairo 1 (#152) * feat: change account contract to cairo 1 - update recover account to return correct address - update create account to use isAccountAddressDeployed method - update est gas fee to use new accountClassHash - add upgradeRequired in to acc object - fix test case * fix: update incorrect name --------- Co-authored-by: Amine Harty --- packages/starknet-snap/snap.manifest.json | 2 +- packages/starknet-snap/src/addErc20Token.ts | 5 +- packages/starknet-snap/src/createAccount.ts | 28 +- packages/starknet-snap/src/estimateFee.ts | 3 +- packages/starknet-snap/src/recoverAccounts.ts | 23 +- packages/starknet-snap/src/signMessage.ts | 7 +- packages/starknet-snap/src/types/snapState.ts | 2 + packages/starknet-snap/src/utils/constants.ts | 13 +- packages/starknet-snap/src/utils/snapUtils.ts | 1 + .../starknet-snap/src/utils/starknetUtils.ts | 136 +++++++++- packages/starknet-snap/test/constants.test.ts | 33 ++- .../test/src/createAccount.test.ts | 134 ++++++---- .../test/src/estimateFee.test.ts | 14 +- .../test/src/recoverAccounts.test.ts | 177 ++++++++++--- .../test/src/sendTransaction.test.ts | 56 ++-- .../test/utils/starknetUtils.test.ts | 247 +++++++++++++++++- 16 files changed, 690 insertions(+), 191 deletions(-) diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index 4ab8e3de..f2e49c07 100644 --- a/packages/starknet-snap/snap.manifest.json +++ b/packages/starknet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/ConsenSys/starknet-snap.git" }, "source": { - "shasum": "+VwOFJaGNMcEVuIMh+hlc5DCpqPxwbflYvfVdhY8hT0=", + "shasum": "6MAPpaepoUn4jcXUhYKIF+XnYV1SGbudlJ+Qa+knr5g=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/src/addErc20Token.ts b/packages/starknet-snap/src/addErc20Token.ts index a04b62c6..72f5e22b 100644 --- a/packages/starknet-snap/src/addErc20Token.ts +++ b/packages/starknet-snap/src/addErc20Token.ts @@ -10,6 +10,7 @@ import { import { DEFAULT_DECIMAL_PLACES } from './utils/constants'; import { DialogType } from '@metamask/rpc-methods'; import { heading, panel, text, copyable } from '@metamask/snaps-ui'; +import { logger } from './utils/logger'; export async function addErc20Token(params: ApiParams) { try { @@ -61,10 +62,10 @@ export async function addErc20Token(params: ApiParams) { await upsertErc20Token(erc20Token, wallet, saveMutex); - console.log(`addErc20Token:\nerc20Token: ${toJson(erc20Token)}`); + logger.log(`addErc20Token:\nerc20Token: ${toJson(erc20Token)}`); return erc20Token; } catch (err) { - console.error(`Problem found: ${err}`); + logger.error(`Problem found: ${err}`); throw err; } } diff --git a/packages/starknet-snap/src/createAccount.ts b/packages/starknet-snap/src/createAccount.ts index 61d9c79b..51546ea2 100644 --- a/packages/starknet-snap/src/createAccount.ts +++ b/packages/starknet-snap/src/createAccount.ts @@ -3,9 +3,9 @@ import { getKeysFromAddressIndex, getAccContractAddressAndCallData, deployAccount, - callContract, + getBalance, estimateAccountDeployFee, - getSigner, + isAccountAddressDeployed, } from './utils/starknetUtils'; import { getEtherErc20Token, @@ -37,18 +37,18 @@ export async function createAccount(params: ApiParams, silentMode = false) { addressIndex: addressIndexInUsed, derivationPath, } = await getKeysFromAddressIndex(keyDeriver, network.chainId, state, addressIndex); + const { address: contractAddress, callData: contractCallData } = getAccContractAddressAndCallData( network.accountClassHash, publicKey, ); + logger.log( `createAccount:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\naddressIndex = ${addressIndexInUsed}`, ); let failureReason = ''; let estimateDeployFee: EstimateFee; - let signerAssigned = true; - let signer = ''; if (deploy) { if (!silentMode) { @@ -71,24 +71,16 @@ export async function createAccount(params: ApiParams, silentMode = false) { }; } - try { - signer = await getSigner(contractAddress, network); - logger.log(`createAccount:\ngetSigner: contractAddress = ${contractAddress}, signerPublicKey= ${signer}`); - failureReason = 'The account address had already been deployed'; - } catch (err) { - signerAssigned = false; - logger.log(`createAccount:\ngetSigner: err in get signer: ${toJson(err)}`); - } + const signerAssigned = await isAccountAddressDeployed(network, contractAddress); if (!signerAssigned) { try { - const getBalanceResp = await callContract( - network, + const balance = await getBalance( getEtherErc20Token(state, network.chainId)?.address, - 'balanceOf', - [num.toBigInt(contractAddress).toString(10)], + num.toBigInt(contractAddress).toString(10), + network, ); - logger.log(`createAccount:\ngetBalanceResp: ${toJson(getBalanceResp)}`); + logger.log(`createAccount:\ngetBalanceResp: ${balance}`); estimateDeployFee = await estimateAccountDeployFee( network, contractAddress, @@ -97,7 +89,7 @@ export async function createAccount(params: ApiParams, silentMode = false) { privateKey, ); logger.log(`createAccount:\nestimateDeployFee: ${toJson(estimateDeployFee)}`); - if (Number(getBalanceResp.result[0]) < Number(estimateDeployFee.suggestedMaxFee)) { + if (Number(balance) < Number(estimateDeployFee.suggestedMaxFee)) { const gasFeeStr = ethers.utils.formatUnits(estimateDeployFee.suggestedMaxFee.toString(10), 18); const gasFeeFloat = parseFloat(gasFeeStr).toFixed(6); // 6 decimal places for ether const gasFeeInEther = Number(gasFeeFloat) === 0 ? '0.000001' : gasFeeFloat; diff --git a/packages/starknet-snap/src/estimateFee.ts b/packages/starknet-snap/src/estimateFee.ts index 0282e3ec..04662634 100644 --- a/packages/starknet-snap/src/estimateFee.ts +++ b/packages/starknet-snap/src/estimateFee.ts @@ -13,7 +13,6 @@ import { isAccountDeployed, } from './utils/starknetUtils'; -import { PROXY_CONTRACT_HASH } from './utils/constants'; import { logger } from './utils/logger'; export async function estimateFee(params: ApiParams) { @@ -71,7 +70,7 @@ export async function estimateFee(params: ApiParams) { if (!accountDeployed) { const { callData } = getAccContractAddressAndCallData(network.accountClassHash, publicKey); const deployAccountpayload = { - classHash: PROXY_CONTRACT_HASH, + classHash: network.accountClassHash, contractAddress: senderAddress, constructorCalldata: callData, addressSalt: publicKey, diff --git a/packages/starknet-snap/src/recoverAccounts.ts b/packages/starknet-snap/src/recoverAccounts.ts index ad06b4c3..d33454f6 100644 --- a/packages/starknet-snap/src/recoverAccounts.ts +++ b/packages/starknet-snap/src/recoverAccounts.ts @@ -1,6 +1,6 @@ import { toJson } from './utils/serializer'; import { num } from 'starknet'; -import { getSigner, getKeysFromAddressIndex, getAccContractAddressAndCallData } from './utils/starknetUtils'; +import { getKeysFromAddressIndex, getCorrectContractAddress, isUpgradeRequired } from './utils/starknetUtils'; import { getNetworkFromChainId, getValidNumber, upsertAccount } from './utils/snapUtils'; import { AccContract } from './types/snapState'; import { ApiParams, RecoverAccountsRequestParams } from './types/snapApi'; @@ -45,20 +45,18 @@ export async function recoverAccounts(params: ApiParams) { state, i, ); - const { address: contractAddress } = getAccContractAddressAndCallData(network.accountClassHash, publicKey); + const { address: contractAddress, signerPubKey: signerPublicKey } = await getCorrectContractAddress( + network, + publicKey, + ); logger.log(`recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}`); - let signerPublicKey = ''; - - try { - signerPublicKey = await getSigner(contractAddress, network); - logger.log(`recoverAccounts: index ${i}\nsignerPublicKey: ${signerPublicKey}`); - } catch (err) { - logger.log(`recoverAccounts: index ${i}\nerr in get signer: ${toJson(err)}`); - signerPublicKey = ''; - } - + let _isUpgradeRequired = false; if (signerPublicKey) { + _isUpgradeRequired = await isUpgradeRequired(network, contractAddress); + logger.log( + `recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\nisUpgradeRequired = ${_isUpgradeRequired}`, + ); if (num.toBigInt(signerPublicKey) === num.toBigInt(publicKey)) { logger.log(`recoverAccounts: index ${i} matched\npublicKey: ${publicKey}`); } @@ -75,6 +73,7 @@ export async function recoverAccounts(params: ApiParams) { derivationPath, deployTxnHash: '', chainId: network.chainId, + upgradeRequired: _isUpgradeRequired, }; logger.log(`recoverAccounts: index ${i}\nuserAccount: ${toJson(userAccount)}`); diff --git a/packages/starknet-snap/src/signMessage.ts b/packages/starknet-snap/src/signMessage.ts index 7afaa0ae..ba8660c5 100644 --- a/packages/starknet-snap/src/signMessage.ts +++ b/packages/starknet-snap/src/signMessage.ts @@ -52,8 +52,11 @@ export async function signMessage(params: ApiParams) { const typedDataSignature = getTypedDataMessageSignature(signerPrivateKey, typedDataMessage, signerAddress); - logger.log(`signMessage:\ntypedDataSignature: ${toJson(typedDataSignature)}`); - return typedDataSignature.toDERHex(); + const result = typedDataSignature.toDERHex(); + + logger.log(`signMessage:\ntypedDataSignature: ${result}`); + + return result; } catch (err) { logger.error(`Problem found: ${err}`); throw err; diff --git a/packages/starknet-snap/src/types/snapState.ts b/packages/starknet-snap/src/types/snapState.ts index 96c5d179..b149e319 100644 --- a/packages/starknet-snap/src/types/snapState.ts +++ b/packages/starknet-snap/src/types/snapState.ts @@ -15,6 +15,7 @@ export interface AccContract { derivationPath: string; deployTxnHash: string; // in hex chainId: string; // in hex + upgradeRequired?: boolean; } export interface Erc20Token { @@ -32,6 +33,7 @@ export interface Network { nodeUrl: string; voyagerUrl: string; accountClassHash: string; // in hex + accountClassHashV0?: string; // in hex useOldAccounts?: boolean; } diff --git a/packages/starknet-snap/src/utils/constants.ts b/packages/starknet-snap/src/utils/constants.ts index 923203eb..bc44de46 100644 --- a/packages/starknet-snap/src/utils/constants.ts +++ b/packages/starknet-snap/src/utils/constants.ts @@ -19,7 +19,8 @@ export const STARKNET_MAINNET_NETWORK: Network = { baseUrl: 'https://alpha-mainnet.starknet.io', nodeUrl: 'https://starknet-mainnet.infura.io/v3/60c7253fb48147658095fe0460ac9ee9', voyagerUrl: 'https://voyager.online', - accountClassHash: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo + accountClassHashV0: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo + accountClassHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', }; export const STARKNET_TESTNET_NETWORK: Network = { @@ -28,7 +29,8 @@ export const STARKNET_TESTNET_NETWORK: Network = { baseUrl: 'https://alpha4.starknet.io', nodeUrl: 'https://starknet-goerli.infura.io/v3/60c7253fb48147658095fe0460ac9ee9', voyagerUrl: 'https://goerli.voyager.online', - accountClassHash: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo + accountClassHashV0: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo + accountClassHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', }; export const STARKNET_INTEGRATION_NETWORK: Network = { @@ -37,7 +39,8 @@ export const STARKNET_INTEGRATION_NETWORK: Network = { baseUrl: 'https://external.integration.starknet.io', nodeUrl: '', voyagerUrl: '', - accountClassHash: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo + accountClassHashV0: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo + accountClassHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', }; export const ETHER_MAINNET: Erc20Token = { @@ -115,4 +118,6 @@ export const TEST_TOKEN_TESTNET: Erc20Token = { export const PRELOADED_TOKENS = [ETHER_MAINNET, ETHER_TESTNET]; export const PRELOADED_NETWORKS = [STARKNET_MAINNET_NETWORK, STARKNET_TESTNET_NETWORK, STARKNET_INTEGRATION_NETWORK]; -export const PROXY_CONTRACT_HASH = '0x25ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918'; // from argent-x repo +export const PROXY_CONTRACT_HASH = '0x25ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918'; // for cairo 0 proxy contract + +export const MIN_ACC_CONTRACT_VERSION = [0, 3, 0]; diff --git a/packages/starknet-snap/src/utils/snapUtils.ts b/packages/starknet-snap/src/utils/snapUtils.ts index 785afdbf..4d87f282 100644 --- a/packages/starknet-snap/src/utils/snapUtils.ts +++ b/packages/starknet-snap/src/utils/snapUtils.ts @@ -270,6 +270,7 @@ export async function upsertAccount(userAccount: AccContract, wallet, mutex: Mut storedAccount.derivationPath = userAccount.derivationPath; storedAccount.publicKey = userAccount.publicKey; storedAccount.deployTxnHash = userAccount.deployTxnHash || storedAccount.deployTxnHash; + storedAccount.upgradeRequired = userAccount.upgradeRequired; } await wallet.request({ diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 3a03b8cd..1b41f43e 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -24,7 +24,7 @@ import { } from 'starknet'; import type { Hex } from '@noble/curves/abstract/utils'; import { Network, SnapState, Transaction, TransactionType } from '../types/snapState'; -import { PROXY_CONTRACT_HASH, TRANSFER_SELECTOR_HEX } from './constants'; +import { PROXY_CONTRACT_HASH, TRANSFER_SELECTOR_HEX, MIN_ACC_CONTRACT_VERSION } from './constants'; import { getAddressKey } from './keyPair'; import { getAccount, getAccounts, getTransactionFromVoyagerUrl, getTransactionsFromVoyagerUrl } from './snapUtils'; import { logger } from './logger'; @@ -80,7 +80,7 @@ export const estimateFee = async ( txnInvocation: Call | Call[], ): Promise => { const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey); + const account = new Account(provider, senderAddress, privateKey, '1'); return account.estimateInvokeFee(txnInvocation, { blockIdentifier: 'latest' }); }; @@ -93,7 +93,7 @@ export const estimateFeeBulk = async ( // ensure always calling the sequencer endpoint since the rpc endpoint and // starknet.js are not supported yet. const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey); + const account = new Account(provider, senderAddress, privateKey, '1'); return account.estimateFeeBulk(txnInvocation, { blockIdentifier: 'latest' }); }; @@ -106,7 +106,7 @@ export const executeTxn = async ( nonce?: number, ): Promise => { const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey); + const account = new Account(provider, senderAddress, privateKey, '1'); return account.execute(txnInvocation, undefined, { nonce, maxFee }); }; @@ -119,9 +119,9 @@ export const deployAccount = async ( maxFee: num.BigNumberish, ): Promise => { const provider = getProvider(network); - const account = new Account(provider, contractAddress, privateKey); + const account = new Account(provider, contractAddress, privateKey, '1'); const deployAccountPayload = { - classHash: PROXY_CONTRACT_HASH, + classHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', contractAddress: contractAddress, constructorCalldata: contractCallData, addressSalt, @@ -137,9 +137,9 @@ export const estimateAccountDeployFee = async ( privateKey: string | Uint8Array, ): Promise => { const provider = getProvider(network); - const account = new Account(provider, contractAddress, privateKey); + const account = new Account(provider, contractAddress, privateKey, '1'); const deployAccountPayload = { - classHash: PROXY_CONTRACT_HASH, + classHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', contractAddress: contractAddress, constructorCalldata: contractCallData, addressSalt, @@ -152,6 +152,21 @@ export const getSigner = async (userAccAddress: string, network: Network): Promi return resp.result[0]; }; +export const getVersion = async (userAccAddress: string, network: Network): Promise => { + const resp = await callContract(network, userAccAddress, 'getVersion'); + return resp.result[0]; +}; + +export const getOwner = async (userAccAddress: string, network: Network): Promise => { + const resp = await callContract(network, userAccAddress, 'get_owner'); + return resp.result[0]; +}; + +export const getBalance = async (address: string, tokenAddress: string, network: Network) => { + const resp = await callContract(network, tokenAddress, 'balanceOf', [num.toBigInt(address).toString(10)]); + return resp.result[0]; +}; + export const getTransactionStatus = async (transactionHash: num.BigNumberish, network: Network) => { const provider = getProvider(network); const receipt = (await provider.getTransactionReceipt(transactionHash)) as RpcV4GetTransactionReceiptResponse; @@ -283,7 +298,7 @@ export const getMassagedTransactions = async ( senderAddress: txnResp.sender_address || txnResp.contract_address || txn.contract_address || '', contractAddress: txnResp.calldata?.[1] || txnResp.contract_address || txn.contract_address || '', contractFuncName: num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex ? 'transfer' : '', - contractCallData: txnResp.calldata?.slice(6, txnResp.calldata?.length - 1) || [], + contractCallData: txnResp.calldata || [], timestamp: txn.timestamp, status: '', //DEPRECATION finalityStatus: statusResp.finalityStatus || '', @@ -377,6 +392,28 @@ export const getNextAddressIndex = (chainId: string, state: SnapState, derivatio }; export const getAccContractAddressAndCallData = (accountClassHash: string, publicKey) => { + const constructorCallData = { + signer: publicKey, + guardian: '0', + }; + + let address = hash.calculateContractAddressFromHash( + constructorCallData.signer, + accountClassHash, + constructorCallData, + 0, + ); + + if (address.length < 66) { + address = address.replace('0x', '0x' + '0'.repeat(66 - address.length)); + } + return { + address, + callData: CallData.compile(constructorCallData), + }; +}; + +export const getAccContractAddressAndCallDataCairo0 = (accountClassHash: string, publicKey) => { const callData = CallData.compile({ implementation: accountClassHash, selector: hash.getSelectorFromName('initialize'), @@ -420,7 +457,7 @@ export const getKeysFromAddress = async ( if (!isNaN(addressIndex)) { return getKeysFromAddressIndex(keyDeriver, network.chainId, state, addressIndex); } - console.log(`getNextAddressIndex:\nAddress not found: ${address}`); + logger.log(`getNextAddressIndex:\nAddress not found: ${address}`); throw new Error(`Address not found: ${address}`); }; @@ -447,15 +484,36 @@ export const getKeysFromAddressIndex = async ( }; }; +export const isAccountDeployedCairo0 = async (network: Network, publicKey: string) => { + const { address } = getAccContractAddressAndCallDataCairo0(network.accountClassHash, publicKey); + return isAccountAddressDeployed(network, address, 0); +}; + export const isAccountDeployed = async (network: Network, publicKey: string) => { + const { address } = getAccContractAddressAndCallData(network.accountClassHash, publicKey); + return isAccountAddressDeployed(network, address, 1); +}; + +export const isAccountAddressDeployed = async (network: Network, address: string, cairoVersion = 1) => { let accountDeployed = true; try { - const { address: signerContractAddress } = getAccContractAddressAndCallData(network.accountClassHash, publicKey); - await getSigner(signerContractAddress, network); + switch (cairoVersion) { + case 0: + await getSigner(address, network); + break; + case 1: + await getOwner(address, network); + break; + default: + throw new Error(`Not supported cairo version ${cairoVersion}`); + } } catch (err) { + if (!err.message.includes('Contract not found')) { + throw err; + } accountDeployed = false; } - logger.log(`isAccountDeployed: ${accountDeployed}`); + return accountDeployed; }; @@ -481,3 +539,55 @@ export const validateAndParseAddress = (address: num.BigNumberish, length = 63) if (trimmedAddress.length !== length) throw new Error(`Address ${address} has an invalid length`); return _validateAndParseAddressFn(address); }; + +export const isUpgradeRequired = async (network: Network, cairo0address: string) => { + try { + logger.log(`isUpgradeRequired: cairo0address = ${cairo0address}`); + const version = await getVersion(cairo0address, network); + const versionArr = version.split('.'); + return Number(versionArr[1]) < MIN_ACC_CONTRACT_VERSION[1]; + } catch (err) { + if (!err.message.includes('Contract not found')) { + throw err; + } + return false; + } +}; + +export const getCorrectContractAddress = async (network: Network, publicKey: string) => { + const { address: contractAddress } = getAccContractAddressAndCallData(network.accountClassHash, publicKey); + const { address: contractAddressCairo0 } = getAccContractAddressAndCallDataCairo0( + network.accountClassHashV0, + publicKey, + ); + let pk = ''; + logger.log( + `getContractAddressByKey: contractAddressCairo1 = ${contractAddress}\ncontractAddressCairo0 = ${contractAddressCairo0}\npublicKey = ${publicKey}`, + ); + try { + pk = await getOwner(contractAddress, network); + logger.log(`getContractAddressByKey: cairo 1 contract found`); + } catch (err) { + if (!err.message.includes('Contract not found')) { + throw err; + } + logger.log(`getContractAddressByKey: cairo 1 contract not found`); + try { + pk = await getSigner(contractAddressCairo0, network); + logger.log(`getContractAddressByKey: cairo 0 contract found`); + return { + address: contractAddressCairo0, + signerPubKey: pk, + }; + } catch (err) { + if (!err.message.includes('Contract not found')) { + throw err; + } + logger.log(`getContractAddressByKey: cairo 0 contract not found`); + } + } + return { + address: contractAddress, + signerPubKey: pk, + }; +}; diff --git a/packages/starknet-snap/test/constants.test.ts b/packages/starknet-snap/test/constants.test.ts index 8e1107b4..9b02520b 100644 --- a/packages/starknet-snap/test/constants.test.ts +++ b/packages/starknet-snap/test/constants.test.ts @@ -18,6 +18,7 @@ export const invalidNetwork: Network = { nodeUrl: '', voyagerUrl: '', accountClassHash: '', + accountClassHashV0: '', }; export const account1: AccContract = { @@ -87,7 +88,7 @@ export const token3: Erc20Token = { export const signature1 = '3044022001bbc0696d02f85696608c9029973d7d5cf714be2a8188424578c40016262fff022004e388edeb3ceb1fd023b165c9a91cc39b97d58f77beb53b6b90ee9261d9f90c'; export const signature2 = - '304402200510bd78f928984364253c04201185ab6ccc386278c8fe1aeda0deab7a476e3f02200442916a82f917f520071da038d6dc3eb4446824ce26893355ad4c4a9343729c'; + '30440220052956ac852275b6004c4e8042450f6dce83059f068029b037cc47338c80d062022002bc0e712f03e341bb3532fc356b779d84fcb4dbfe8ed34de2db66e121971d92'; // Derived from seed phrase: "dog simple gown ankle release anger local pulp rose river approve miracle" export const bip44Entropy: JsonBIP44CoinTypeNode = { @@ -213,7 +214,18 @@ export const RejectedTxn2: Transaction = { export const unsettedTransactionInMassagedTxn: Transaction = { chainId: STARKNET_TESTNET_NETWORK.chainId, contractAddress: '0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractCallData: ['0x14361d05e560796ad3152e083b609f5205f3bd76039327326746ba7f769a666', '0xde0b6b3a7640000', '0x0'], + contractCallData: [ + '0x1', + '0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e', + '0x0', + '0x3', + '0x3', + '0x14361d05e560796ad3152e083b609f5205f3bd76039327326746ba7f769a666', + '0xde0b6b3a7640000', + '0x0', + '0x1', + ], contractFuncName: 'transfer', senderAddress: '0x5a98ec74a40383cf99896bfea2ec5e6aad16c7eed50025a5f569d585ebb13a2', timestamp: 1655109666, @@ -336,8 +348,8 @@ export const estimateFeeResp2 = { suggestedMaxFee: num.toBigInt('0x14a5d6744ed9'), }; -export const unfoundUserAddress = '0x018dfa1955a0154524203f81c5668d6a78c708375ee8908dcb55a49c6ec87190'; -export const unfoundUserPrivateKey = '0x610d87a5c02459f8643f9ad6a9bc70597d1a8a0ab4d645346b7eadc5266ad4d'; +export const unfoundUserAddress = '0x07bb462e1cf8f2eead6a86464fecac1fad84b66da8078fe39d24bdf7c8504070'; +export const unfoundUserPrivateKey = '0x38d36fc25592257d913d143d37e12533dba9f6721db6fa954ed513b0dc3d68b'; export const unfoundUserPublicKey = '0x4b36a2b0a1e9d2af3416914798de776e37d9e0ab9a50d2dec30485dca64bb8'; export const foundUserPrivateKey = '0x3cddbb7f3694ce84bd9598820834015d979d78e63474a5b00e59b41b0563f4e'; @@ -677,7 +689,18 @@ export const expectedMassagedTxns: Transaction[] = [ senderAddress: '0x5a98ec74a40383cf99896bfea2ec5e6aad16c7eed50025a5f569d585ebb13a2', contractAddress: '0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', contractFuncName: 'transfer', - contractCallData: ['0x14361d05e560796ad3152e083b609f5205f3bd76039327326746ba7f769a666', '0xde0b6b3a7640000', '0x0'], + contractCallData: [ + '0x1', + '0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e', + '0x0', + '0x3', + '0x3', + '0x14361d05e560796ad3152e083b609f5205f3bd76039327326746ba7f769a666', + '0xde0b6b3a7640000', + '0x0', + '0x1', + ], timestamp: 1655109666, status: '', finalityStatus: 'ACCEPTED_ON_L1', diff --git a/packages/starknet-snap/test/src/createAccount.test.ts b/packages/starknet-snap/test/src/createAccount.test.ts index 72d56f85..88a9de7b 100644 --- a/packages/starknet-snap/test/src/createAccount.test.ts +++ b/packages/starknet-snap/test/src/createAccount.test.ts @@ -17,7 +17,6 @@ import { estimateDeployFeeResp, getBalanceResp, account1, - account2, estimateDeployFeeResp2, estimateDeployFeeResp3, } from '../constants.test'; @@ -31,7 +30,7 @@ const sandbox = sinon.createSandbox(); describe('Test function: createAccount', function () { this.timeout(10000); const walletStub = new WalletMock(); - const state: SnapState = { + let state: SnapState = { accContracts: [], erc20Tokens: [], networks: [STARKNET_MAINNET_NETWORK, STARKNET_TESTNET_NETWORK], @@ -55,10 +54,16 @@ describe('Test function: createAccount', function () { afterEach(function () { walletStub.reset(); sandbox.restore(); + state = { + accContracts: [], + erc20Tokens: [], + networks: [STARKNET_MAINNET_NETWORK, STARKNET_TESTNET_NETWORK], + transactions: [], + }; }); it('should only return derived address without sending deploy txn correctly in mainnet if deploy is false', async function () { - sandbox.stub(utils, 'getSigner').throws(new Error()); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); const requestObject: CreateAccountRequestParams = { chainId: STARKNET_MAINNET_NETWORK.chainId, }; @@ -84,9 +89,9 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyMainnetResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; @@ -115,12 +120,14 @@ describe('Test function: createAccount', function () { }); it('should create and store an user account of specific address index with proxy in state correctly in mainnet', async function () { + state.accContracts.push(account1); + state.transactions.push(createAccountProxyTxn); sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyMainnetResp2; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; @@ -153,9 +160,9 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; @@ -172,34 +179,39 @@ describe('Test function: createAccount', function () { expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(4); expect(result.address).to.be.eq(createAccountProxyResp.contract_address); expect(result.transaction_hash).to.be.eq(createAccountProxyResp.transaction_hash); - expect(state.accContracts.length).to.be.eq(3); - expect(state.accContracts[2].address).to.be.eq(createAccountProxyResp.contract_address); - expect(state.accContracts[2].deployTxnHash).to.be.eq(createAccountProxyResp.transaction_hash); - expect(state.accContracts[2].publicKey).to.be.eq(expectedPublicKey); - expect(state.accContracts[2].addressSalt).to.be.eq(expectedPublicKey); - expect(state.transactions.length).to.be.eq(3); + expect(state.accContracts.length).to.be.eq(1); + expect(state.accContracts[0].address).to.be.eq(createAccountProxyResp.contract_address); + expect(state.accContracts[0].deployTxnHash).to.be.eq(createAccountProxyResp.transaction_hash); + expect(state.accContracts[0].publicKey).to.be.eq(expectedPublicKey); + expect(state.accContracts[0].addressSalt).to.be.eq(expectedPublicKey); + expect(state.transactions.length).to.be.eq(1); }); it('should not create any user account with proxy in state in SN_GOERLI if not in silentMode and user rejected', async function () { + sandbox.stub(utils, 'getAccContractAddressAndCallData').callsFake(() => { + return { + address: account1.address, + callData: [], + }; + }); walletStub.rpcStubs.snap_dialog.resolves(false); const requestObject: CreateAccountRequestParams = { deploy: true }; apiParams.requestParams = requestObject; + const result = await createAccount(apiParams); expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(0); - expect(result.address).to.be.eq(account2.address); - expect(state.accContracts.length).to.be.eq(3); - expect(state.transactions.length).to.be.eq(3); + expect(result.address).to.be.eq(account1.address); + expect(state.accContracts.length).to.be.eq(0); + expect(state.transactions.length).to.be.eq(0); }); - it('should not create any user account with proxy in state in SN_GOERLI if account already initialized with a signer', async function () { + it('should not create any user account with proxy in state in SN_GOERLI if account already deployed', async function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; @@ -210,17 +222,17 @@ describe('Test function: createAccount', function () { expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(4); expect(result.address).to.be.eq(createAccountProxyResp.contract_address); expect(result.transaction_hash).to.be.eq(createAccountProxyResp.transaction_hash); - expect(state.accContracts.length).to.be.eq(3); - expect(state.transactions.length).to.be.eq(3); + expect(state.accContracts.length).to.be.eq(1); + expect(state.transactions.length).to.be.eq(1); }); it('should not create any user account with proxy in state in SN_GOERLI if account does not have enough ETH balance', async function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp2; @@ -228,20 +240,20 @@ describe('Test function: createAccount', function () { const requestObject: CreateAccountRequestParams = { deploy: true }; apiParams.requestParams = requestObject; const result = await createAccount(apiParams); - expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(3); + expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(4); expect(result.address).to.be.eq(createAccountProxyResp.contract_address); expect(result.transaction_hash).to.be.eq(createAccountProxyResp.transaction_hash); - expect(state.accContracts.length).to.be.eq(3); - expect(state.transactions.length).to.be.eq(3); + expect(state.accContracts.length).to.be.eq(1); + expect(state.transactions.length).to.be.eq(1); }); it('should not create any user account with proxy in state in SN_GOERLI if account does not have enough ETH balance for suggestedMaxFee > 0.000001 ETH', async function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp3; @@ -249,37 +261,37 @@ describe('Test function: createAccount', function () { const requestObject: CreateAccountRequestParams = { deploy: true }; apiParams.requestParams = requestObject; const result = await createAccount(apiParams); - expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(3); + expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(4); expect(result.address).to.be.eq(createAccountProxyResp.contract_address); expect(result.transaction_hash).to.be.eq(createAccountProxyResp.transaction_hash); - expect(state.accContracts.length).to.be.eq(3); - expect(state.transactions.length).to.be.eq(3); + expect(state.accContracts.length).to.be.eq(1); + expect(state.transactions.length).to.be.eq(1); }); it('should not create any user account with proxy in state in SN_GOERLI if get account ETH balance throws error', async function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); - sandbox.stub(utils, 'callContract').throws(new Error()); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'getBalance').throws(new Error()); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp2; }); const requestObject: CreateAccountRequestParams = { deploy: true }; apiParams.requestParams = requestObject; const result = await createAccount(apiParams); - expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(3); + expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(4); expect(result.address).to.be.eq(createAccountProxyResp.contract_address); expect(result.transaction_hash).to.be.eq(createAccountProxyResp.transaction_hash); - expect(state.accContracts.length).to.be.eq(3); - expect(state.transactions.length).to.be.eq(3); + expect(state.accContracts.length).to.be.eq(1); + expect(state.transactions.length).to.be.eq(1); }); it('should skip upsert account and transaction if deployTxn response code has no transaction_hash in SN_GOERLI', async function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountFailedProxyResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; }); @@ -289,8 +301,8 @@ describe('Test function: createAccount', function () { expect(walletStub.rpcStubs.snap_manageState).to.have.been.callCount(0); expect(result.address).to.be.eq(createAccountFailedProxyResp.contract_address); expect(result.transaction_hash).to.be.eq(createAccountFailedProxyResp.transaction_hash); - expect(state.accContracts.length).to.be.eq(3); - expect(state.transactions.length).to.be.eq(3); + expect(state.accContracts.length).to.be.eq(0); + expect(state.transactions.length).to.be.eq(0); }); it('should throw error if upsertAccount failed', async function () { @@ -298,7 +310,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'getSigner').throws(new Error()); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; }); @@ -314,4 +326,26 @@ describe('Test function: createAccount', function () { expect(result).to.be.an('Error'); } }); + + it('should throw error if isAccountAddressDeployed failed', async function () { + const isAccountAddressDeployedStub = sandbox.stub(utils, 'isAccountAddressDeployed').throws(new Error()); + const deployAccountStub = sandbox.stub(utils, 'deployAccount'); + const estimateAccountDeployFeeStub = sandbox.stub(utils, 'estimateAccountDeployFee'); + const getBalanceStub = sandbox.stub(utils, 'getBalance'); + const requestObject: CreateAccountRequestParams = { deploy: true }; + apiParams.requestParams = requestObject; + + let result; + try { + await createAccount(apiParams); + } catch (err) { + result = err; + } finally { + expect(isAccountAddressDeployedStub).to.have.been.callCount(1); + expect(deployAccountStub).to.have.been.callCount(0); + expect(estimateAccountDeployFeeStub).to.have.been.callCount(0); + expect(getBalanceStub).to.have.been.callCount(0); + expect(result).to.be.an('Error'); + } + }); }); diff --git a/packages/starknet-snap/test/src/estimateFee.test.ts b/packages/starknet-snap/test/src/estimateFee.test.ts index 44fedaa0..7309f3d0 100644 --- a/packages/starknet-snap/test/src/estimateFee.test.ts +++ b/packages/starknet-snap/test/src/estimateFee.test.ts @@ -53,7 +53,7 @@ describe('Test function: estimateFee', function () { }); it('should estimate the fee correctly', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account2.publicKey; }); sandbox.stub(utils, 'estimateFee').callsFake(async () => { @@ -69,7 +69,7 @@ describe('Test function: estimateFee', function () { }); it('should estimate the fee including deploy txn correctly', async function () { - sandbox.stub(utils, 'getSigner').throws(new Error()); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'estimateFeeBulk').callsFake(async () => { return [estimateDeployFeeResp4, estimateFeeResp]; }); @@ -79,7 +79,7 @@ describe('Test function: estimateFee', function () { }); it('should estimate the fee without gas consumed and gas price correctly', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account2.publicKey; }); sandbox.stub(utils, 'estimateFee').callsFake(async () => { @@ -95,7 +95,7 @@ describe('Test function: estimateFee', function () { }); it('should throw error if estimateFee failed', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account2.publicKey; }); sandbox.stub(utils, 'estimateFeeBulk').throws(new Error()); @@ -112,7 +112,7 @@ describe('Test function: estimateFee', function () { }); it('should throw an error if the function name is undefined', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account2.publicKey; }); apiParams.requestParams = { @@ -132,7 +132,7 @@ describe('Test function: estimateFee', function () { }); it('should throw an error if the contract address is invalid', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account2.publicKey; }); apiParams.requestParams = { @@ -152,7 +152,7 @@ describe('Test function: estimateFee', function () { }); it('should throw an error if the sender address is invalid', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account2.publicKey; }); apiParams.requestParams = { diff --git a/packages/starknet-snap/test/src/recoverAccounts.test.ts b/packages/starknet-snap/test/src/recoverAccounts.test.ts index 178c1805..36cc4ff9 100644 --- a/packages/starknet-snap/test/src/recoverAccounts.test.ts +++ b/packages/starknet-snap/test/src/recoverAccounts.test.ts @@ -27,7 +27,7 @@ const sandbox = sinon.createSandbox(); describe('Test function: recoverAccounts', function () { this.timeout(5000); const walletStub = new WalletMock(); - const state: SnapState = { + let state: SnapState = { accContracts: [], erc20Tokens: [], networks: [STARKNET_TESTNET_NETWORK, STARKNET_MAINNET_NETWORK, INVALID_NETWORK], @@ -50,19 +50,32 @@ describe('Test function: recoverAccounts', function () { afterEach(function () { walletStub.reset(); sandbox.restore(); + state = { + accContracts: [], + erc20Tokens: [], + networks: [STARKNET_TESTNET_NETWORK, STARKNET_MAINNET_NETWORK, INVALID_NETWORK], + transactions: [], + }; }); it('should recover accounts in mainnet correctly', async function () { - state.accContracts = []; const maxScanned = 5; const maxMissed = 3; const validPublicKeys = 2; - const getSignerStub = sandbox.stub(utils, 'getSigner'); - for (let i = 0; i < validPublicKeys; i++) { - getSignerStub.onCall(i).resolves(mainnetPublicKeys[i]); + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + isUpgradeRequiredStub.resolves(false); + for (let i = 0; i < maxScanned; i++) { + if (i < validPublicKeys) { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i] }); + } else { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO) }); + } } - getSignerStub.onCall(validPublicKeys).resolves(num.toHex(constants.ZERO)); - getSignerStub.resolves(num.toHex(constants.ZERO)); const requestObject: RecoverAccountsRequestParams = { startScanIndex: 0, @@ -74,6 +87,8 @@ describe('Test function: recoverAccounts', function () { const result = await recoverAccounts(apiParams); const expectedCalledTimes = validPublicKeys + maxMissed; + + expect(isUpgradeRequiredStub.callCount).to.be.eq(expectedCalledTimes); expect(walletStub.rpcStubs.snap_manageState.callCount).to.be.eq(expectedCalledTimes * 2); expect(result.length).to.be.eq(expectedCalledTimes); expect(state.accContracts.map((acc) => acc.address)).to.be.eql(mainnetAccAddresses.slice(0, expectedCalledTimes)); @@ -87,15 +102,23 @@ describe('Test function: recoverAccounts', function () { }); it('should recover accounts in SN_GOERLI correctly', async function () { - state.accContracts = []; const maxScanned = 5; const maxMissed = 3; const validPublicKeys = 2; - const getSignerStub = sandbox.stub(utils, 'getSigner'); - for (let i = 0; i < validPublicKeys; i++) { - getSignerStub.onCall(i).resolves(testnetPublicKeys[i]); + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + isUpgradeRequiredStub.resolves(false); + for (let i = 0; i < maxScanned; i++) { + if (i < validPublicKeys) { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: testnetAccAddresses[i], signerPubKey: testnetPublicKeys[i] }); + } else { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: testnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO) }); + } } - getSignerStub.throws(new Error()); const requestObject: RecoverAccountsRequestParams = { startScanIndex: 0, @@ -106,6 +129,8 @@ describe('Test function: recoverAccounts', function () { apiParams.requestParams = requestObject; const result = await recoverAccounts(apiParams); const expectedCalledTimes = validPublicKeys + maxMissed; + + expect(isUpgradeRequiredStub.callCount).to.be.eq(expectedCalledTimes); expect(walletStub.rpcStubs.snap_manageState.callCount).to.be.eq(expectedCalledTimes * 2); expect(result.length).to.be.eq(expectedCalledTimes); expect(state.accContracts.map((acc) => acc.address)).to.be.eql(testnetAccAddresses.slice(0, expectedCalledTimes)); @@ -118,48 +143,128 @@ describe('Test function: recoverAccounts', function () { expect(state.accContracts.length).to.be.eq(expectedCalledTimes); }); - it('should recover accounts in SN_GOERLI with same parameters correctly', async function () { + it('should recover accounts with upgrade attr when account required upgrade', async function () { const maxScanned = 5; const maxMissed = 3; const validPublicKeys = 2; - const getSignerStub = sandbox.stub(utils, 'getSigner'); - for (let i = 0; i < validPublicKeys; i++) { - getSignerStub.onCall(i).resolves(testnetPublicKeys[i]); + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + const isUpgradeRequiredExpectedResult = []; + for (let i = 0; i < maxScanned; i++) { + if (i < validPublicKeys) { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i] }); + isUpgradeRequiredStub.onCall(i).resolves(true); + isUpgradeRequiredExpectedResult.push(true); + } else { + getCorrectContractAddressStub.onCall(i).resolves({ address: mainnetAccAddresses[i], signerPubKey: '' }); + isUpgradeRequiredStub.onCall(i).resolves(false); + isUpgradeRequiredExpectedResult.push(false); + } } - getSignerStub.throws(new Error()); const requestObject: RecoverAccountsRequestParams = { startScanIndex: 0, maxScanned, maxMissed, + chainId: STARKNET_MAINNET_NETWORK.chainId, }; - apiParams.requestParams = requestObject; + const result = await recoverAccounts(apiParams); const expectedCalledTimes = validPublicKeys + maxMissed; - expect(walletStub.rpcStubs.snap_manageState.callCount).to.be.eq(expectedCalledTimes); + + expect(isUpgradeRequiredStub.callCount).to.be.eq(validPublicKeys); + expect(walletStub.rpcStubs.snap_manageState.callCount).to.be.eq(expectedCalledTimes * 2); expect(result.length).to.be.eq(expectedCalledTimes); - expect(state.accContracts.map((acc) => acc.address)).to.be.eql(testnetAccAddresses.slice(0, expectedCalledTimes)); - expect(state.accContracts.map((acc) => acc.addressSalt)).to.be.eql(testnetPublicKeys.slice(0, expectedCalledTimes)); - expect( - state.accContracts - .filter((acc) => acc.publicKey && acc.publicKey !== num.toHex(constants.ZERO)) - .map((acc) => acc.publicKey), - ).to.be.eql(testnetPublicKeys.slice(0, validPublicKeys)); + expect(state.accContracts.map((acc) => acc.upgradeRequired)).to.be.eql(isUpgradeRequiredExpectedResult); expect(state.accContracts.length).to.be.eq(expectedCalledTimes); }); + it('should throw error if getCorrectContractAddress throw error', async function () { + const maxScanned = 5; + const maxMissed = 3; + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + getCorrectContractAddressStub.callsFake(async () => { + throw new Error('network error'); + }); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + const requestObject: RecoverAccountsRequestParams = { + startScanIndex: 0, + maxScanned, + maxMissed, + chainId: STARKNET_MAINNET_NETWORK.chainId, + }; + apiParams.requestParams = requestObject; + + let result = null; + + try { + await recoverAccounts(apiParams); + } catch (e) { + result = e; + } finally { + expect(getCorrectContractAddressStub.callCount).to.be.eq(1); + expect(isUpgradeRequiredStub.callCount).to.be.eq(0); + expect(walletStub.rpcStubs.snap_manageState.callCount).to.be.eq(0); + expect(result).to.be.an('Error'); + expect(result.message).to.be.eq('network error'); + } + }); + + it('should throw error if isUpgradeRequired throw error', async function () { + const maxScanned = 5; + const maxMissed = 3; + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + getCorrectContractAddressStub.resolves({ address: mainnetAccAddresses[0], signerPubKey: mainnetPublicKeys[0] }); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + isUpgradeRequiredStub.callsFake(async () => { + throw new Error('network error'); + }); + + const requestObject: RecoverAccountsRequestParams = { + startScanIndex: 0, + maxScanned, + maxMissed, + chainId: STARKNET_MAINNET_NETWORK.chainId, + }; + apiParams.requestParams = requestObject; + + let result = null; + + try { + await recoverAccounts(apiParams); + } catch (e) { + result = e; + } finally { + expect(getCorrectContractAddressStub.callCount).to.be.eq(1); + expect(isUpgradeRequiredStub.callCount).to.be.eq(1); + expect(walletStub.rpcStubs.snap_manageState.callCount).to.be.eq(0); + expect(result).to.be.an('Error'); + expect(result.message).to.be.eq('network error'); + } + }); + it('should throw error if upsertAccount failed', async function () { sandbox.stub(snapUtils, 'upsertAccount').throws(new Error()); const maxScanned = 5; const maxMissed = 3; const validPublicKeys = 2; - const getSignerStub = sandbox.stub(utils, 'getSigner'); - for (let i = 0; i < validPublicKeys; i++) { - getSignerStub.onCall(i).resolves(mainnetPublicKeys[i]); + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + isUpgradeRequiredStub.resolves(false); + for (let i = 0; i < maxScanned; i++) { + if (i < validPublicKeys) { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i] }); + } else { + getCorrectContractAddressStub + .onCall(i) + .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO) }); + } } - getSignerStub.onCall(validPublicKeys).resolves(num.toHex(constants.ZERO)); - getSignerStub.resolves(num.toHex(constants.ZERO)); const requestObject: RecoverAccountsRequestParams = { startScanIndex: 0, @@ -182,12 +287,10 @@ describe('Test function: recoverAccounts', function () { it('should show confirmation box with failure msg if network accountClassHash is missing', async function () { const maxScanned = 5; const maxMissed = 3; - const validPublicKeys = 2; - const getSignerStub = sandbox.stub(utils, 'getSigner'); - for (let i = 0; i < validPublicKeys; i++) { - getSignerStub.onCall(i).resolves(testnetPublicKeys[i]); - } - getSignerStub.throws(new Error()); + const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired'); + getCorrectContractAddressStub.resolves({ address: mainnetAccAddresses[0], signerPubKey: mainnetPublicKeys[0] }); + isUpgradeRequiredStub.resolves(false); const requestObject: RecoverAccountsRequestParams = { startScanIndex: 0, diff --git a/packages/starknet-snap/test/src/sendTransaction.test.ts b/packages/starknet-snap/test/src/sendTransaction.test.ts index f118dbfb..1a9edb0d 100644 --- a/packages/starknet-snap/test/src/sendTransaction.test.ts +++ b/packages/starknet-snap/test/src/sendTransaction.test.ts @@ -70,9 +70,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transaction for transferring 10 tokens correctly', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', contractFuncName: 'transfer', @@ -87,12 +85,12 @@ describe('Test function: sendTransaction', function () { }); it('should trigger a deploy txn and send a transaction for transferring 10 tokens correctly', async function () { - sandbox.stub(utils, 'getSigner').throws(new Error()); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'callContract').callsFake(async () => { - return getBalanceResp; + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; }); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp; @@ -111,9 +109,7 @@ describe('Test function: sendTransaction', function () { }); it('should return false if user rejected to sign the transaction', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); walletStub.rpcStubs.snap_dialog.resolves(false); const requestObject: SendTransactionRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', @@ -129,9 +125,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transaction for transferring 10 tokens but not update snap state if transaction_hash is missing from response', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); executeTxnResp = sendTransactionFailedResp; const requestObject: SendTransactionRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', @@ -147,9 +141,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transaction with given max fee for transferring 10 tokens correctly', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', contractFuncName: 'transfer', @@ -165,9 +157,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transfer transaction for empty call data', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', contractFuncName: 'transfer', @@ -182,9 +172,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transaction for empty call data', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: account1.address, contractFuncName: 'get_signer', @@ -200,9 +188,7 @@ describe('Test function: sendTransaction', function () { it('should use heading, text and copyable component', async function () { executeTxnResp = sendTransactionFailedResp; - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: account1.address, contractFuncName: 'get_signer', @@ -265,9 +251,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transaction for transferring 10 tokens from an unfound user correctly', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', contractFuncName: 'transfer', @@ -282,9 +266,7 @@ describe('Test function: sendTransaction', function () { }); it('should send a transaction for transferring 10 tokens (token of 10 decimal places) from an unfound user correctly', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account1.publicKey; - }); + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); const requestObject: SendTransactionRequestParams = { contractAddress: '0x06a09ccb1caaecf3d9683efe335a667b2169a409d19c589ba1eb771cd210af75', contractFuncName: 'transfer', @@ -299,7 +281,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw error if upsertTransaction failed', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); sandbox.stub(snapUtils, 'upsertTransaction').throws(new Error()); @@ -322,7 +304,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw an error if contract address is undefined', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); const requestObject: SendTransactionRequestParams = { @@ -343,7 +325,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw an error if function name is undefined', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); const requestObject: SendTransactionRequestParams = { @@ -364,7 +346,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw an error if sender address is undefined', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); const requestObject: SendTransactionRequestParams = { @@ -385,7 +367,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw an error if contract address is invalid', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); const requestObject: SendTransactionRequestParams = { @@ -406,7 +388,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw an error if sender address is invalid', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); const requestObject: SendTransactionRequestParams = { @@ -427,7 +409,7 @@ describe('Test function: sendTransaction', function () { }); it('should throw an error when call data entries can not be converted to a bigNumber', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { + sandbox.stub(utils, 'getOwner').callsFake(async () => { return account1.publicKey; }); const requestObject: SendTransactionRequestParams = { diff --git a/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index e4a70e1a..5c50fe51 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -13,7 +13,7 @@ import { account2, } from '../constants.test'; import { SnapState } from '../../src/types/snapState'; -import { Calldata } from 'starknet'; +import { Calldata, num } from 'starknet'; chai.use(sinonChai); const sandbox = sinon.createSandbox(); @@ -123,3 +123,248 @@ describe('Test function: validateAndParseAddress', function () { ); }); }); + +describe('Test function: getVersion', function () { + let callContractStub: sinon.SinonStub; + const expected = '0.3.0'; + + beforeEach(function () { + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ({ result: [expected] })); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should trigger callContract correct', async function () { + const result = await utils.getVersion(account1.address, STARKNET_TESTNET_NETWORK); + expect(result).to.be.eq(expected); + expect(callContractStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address, 'getVersion'); + }); +}); + +describe('Test function: getOwner', function () { + let callContractStub: sinon.SinonStub; + const expected = 'pk'; + + beforeEach(function () { + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ({ result: [expected] })); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should trigger callContract correct', async function () { + const result = await utils.getOwner(account1.address, STARKNET_TESTNET_NETWORK); + expect(result).to.be.eq(expected); + expect(callContractStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address, 'get_owner'); + }); +}); + +describe('Test function: getBalance', function () { + let callContractStub: sinon.SinonStub; + const expected = 'pk'; + + beforeEach(function () { + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ({ result: [expected] })); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should trigger callContract correct', async function () { + const result = await utils.getBalance(account1.address, account1.address, STARKNET_TESTNET_NETWORK); + expect(result).to.be.eq(expected); + expect(callContractStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address, 'balanceOf', [ + num.toBigInt(account1.address).toString(10), + ]); + }); +}); + +describe('Test function: isUpgradeRequired', function () { + const walletStub = new WalletMock(); + const userAddress = '0x27f204588cadd08a7914f6a9808b34de0cbfc4cb53aa053663e7fd3a34dbc26'; + + afterEach(function () { + walletStub.reset(); + sandbox.restore(); + }); + + it('should return true when upgrade is required', async function () { + sandbox.stub(utils, 'getVersion').callsFake(async () => '0.2.3'); + const result = await utils.isUpgradeRequired(STARKNET_TESTNET_NETWORK, userAddress); + expect(result).to.be.eq(true); + }); + + it('should return false when upgrade is not required', async function () { + sandbox.stub(utils, 'getVersion').callsFake(async () => '0.3.0'); + const result = await utils.isUpgradeRequired(STARKNET_TESTNET_NETWORK, userAddress); + expect(result).to.be.eq(false); + }); + + it('should return false when contract is not deployed', async function () { + sandbox.stub(utils, 'getVersion').callsFake(async () => { + throw new Error('Contract not found'); + }); + const result = await utils.isUpgradeRequired(STARKNET_TESTNET_NETWORK, userAddress); + expect(result).to.be.eq(false); + }); + + it('should throw err when getVersion is throwing unknown error', async function () { + sandbox.stub(utils, 'getVersion').callsFake(async () => { + throw new Error('network error'); + }); + let result = null; + try { + await utils.isUpgradeRequired(STARKNET_TESTNET_NETWORK, userAddress); + } catch (e) { + result = e; + } finally { + expect(result).to.be.an('Error'); + expect(result?.message).to.be.eq('network error'); + } + }); +}); + +describe('Test function: getCorrectContractAddress', function () { + const walletStub = new WalletMock(); + let getAccContractAddressAndCallDataStub: sinon.SinonStub; + let getAccContractAddressAndCallDataCairo0Stub: sinon.SinonStub; + let getOwnerStub: sinon.SinonStub; + let getSignerStub: sinon.SinonStub; + const PK = 'pk'; + + beforeEach(function () { + getAccContractAddressAndCallDataStub = sandbox + .stub(utils, 'getAccContractAddressAndCallData') + .callsFake(() => ({ address: account1.address, callData: [] as Calldata })); + getAccContractAddressAndCallDataCairo0Stub = sandbox + .stub(utils, 'getAccContractAddressAndCallDataCairo0') + .callsFake(() => ({ address: account2.address, callData: [] as Calldata })); + getOwnerStub = sandbox.stub(utils, 'getOwner').callsFake(async () => PK); + getSignerStub = sandbox.stub(utils, 'getSigner').callsFake(async () => PK); + }); + afterEach(function () { + walletStub.reset(); + sandbox.restore(); + }); + + it('should permutation both Cairo0 and Cario1 address', async function () { + await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + expect(getAccContractAddressAndCallDataStub).to.have.been.calledOnceWith( + STARKNET_TESTNET_NETWORK.accountClassHash, + PK, + ); + expect(getAccContractAddressAndCallDataCairo0Stub).to.have.been.calledOnceWith( + STARKNET_TESTNET_NETWORK.accountClassHashV0, + PK, + ); + }); + + it('should return Cairo1 address with pubic key when Cario1 deployed', async function () { + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + expect(getOwnerStub).to.have.been.calledOnceWith(account1.address, STARKNET_TESTNET_NETWORK); + expect(getSignerStub).to.have.been.callCount(0); + expect(result.address).to.be.eq(account1.address); + expect(result.signerPubKey).to.be.eq(PK); + }); + + it('should return Cairo0 address with pubic key when Cario1 not deployed', async function () { + sandbox.restore(); + getAccContractAddressAndCallDataStub = sandbox + .stub(utils, 'getAccContractAddressAndCallData') + .callsFake(() => ({ address: account1.address, callData: [] as Calldata })); + getAccContractAddressAndCallDataCairo0Stub = sandbox + .stub(utils, 'getAccContractAddressAndCallDataCairo0') + .callsFake(() => ({ address: account2.address, callData: [] as Calldata })); + getSignerStub = sandbox.stub(utils, 'getSigner').callsFake(async () => PK); + getOwnerStub = sandbox.stub(utils, 'getOwner').callsFake(async () => { + throw new Error('Contract not found'); + }); + + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + expect(getOwnerStub).to.have.been.calledOnceWith(account1.address, STARKNET_TESTNET_NETWORK); + expect(getSignerStub).to.have.been.calledOnceWith(account2.address, STARKNET_TESTNET_NETWORK); + expect(result.address).to.be.eq(account2.address); + expect(result.signerPubKey).to.be.eq(PK); + }); + + it('should return Cairo1 address with no pubic key when Cario1 and Cario0 not deployed', async function () { + sandbox.restore(); + getAccContractAddressAndCallDataStub = sandbox + .stub(utils, 'getAccContractAddressAndCallData') + .callsFake(() => ({ address: account1.address, callData: [] as Calldata })); + getAccContractAddressAndCallDataCairo0Stub = sandbox + .stub(utils, 'getAccContractAddressAndCallDataCairo0') + .callsFake(() => ({ address: account2.address, callData: [] as Calldata })); + getSignerStub = sandbox.stub(utils, 'getSigner').callsFake(async () => { + throw new Error('Contract not found'); + }); + getOwnerStub = sandbox.stub(utils, 'getOwner').callsFake(async () => { + throw new Error('Contract not found'); + }); + + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + expect(getOwnerStub).to.have.been.calledOnceWith(account1.address, STARKNET_TESTNET_NETWORK); + expect(getSignerStub).to.have.been.calledOnceWith(account2.address, STARKNET_TESTNET_NETWORK); + expect(result.address).to.be.eq(account1.address); + expect(result.signerPubKey).to.be.eq(''); + }); + + it('should throw error when getOwner is throwing unknown error', async function () { + sandbox.restore(); + getAccContractAddressAndCallDataStub = sandbox + .stub(utils, 'getAccContractAddressAndCallData') + .callsFake(() => ({ address: account1.address, callData: [] as Calldata })); + getAccContractAddressAndCallDataCairo0Stub = sandbox + .stub(utils, 'getAccContractAddressAndCallDataCairo0') + .callsFake(() => ({ address: account2.address, callData: [] as Calldata })); + getSignerStub = sandbox.stub(utils, 'getSigner').callsFake(async () => { + throw new Error('network error for getSigner'); + }); + getOwnerStub = sandbox.stub(utils, 'getOwner').callsFake(async () => { + throw new Error('network error for getOwner'); + }); + let result = null; + try { + await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + } catch (e) { + result = e; + } finally { + expect(getOwnerStub).to.have.been.calledOnceWith(account1.address, STARKNET_TESTNET_NETWORK); + expect(getSignerStub).to.have.been.callCount(0); + expect(result).to.be.an('Error'); + expect(result?.message).to.be.eq('network error for getOwner'); + } + }); + + it('should throw error when getSigner is throwing unknown error', async function () { + sandbox.restore(); + getAccContractAddressAndCallDataStub = sandbox + .stub(utils, 'getAccContractAddressAndCallData') + .callsFake(() => ({ address: account1.address, callData: [] as Calldata })); + getAccContractAddressAndCallDataCairo0Stub = sandbox + .stub(utils, 'getAccContractAddressAndCallDataCairo0') + .callsFake(() => ({ address: account2.address, callData: [] as Calldata })); + getSignerStub = sandbox.stub(utils, 'getSigner').callsFake(async () => { + throw new Error('network error for getSigner'); + }); + getOwnerStub = sandbox.stub(utils, 'getOwner').callsFake(async () => { + throw new Error('Contract not found'); + }); + + let result = null; + try { + await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + } catch (e) { + result = e; + } finally { + expect(getOwnerStub).to.have.been.calledOnceWith(account1.address, STARKNET_TESTNET_NETWORK); + expect(getSignerStub).to.have.been.calledOnceWith(account2.address, STARKNET_TESTNET_NETWORK); + expect(result).to.be.an('Error'); + expect(result?.message).to.be.eq('network error for getSigner'); + } + }); +}); From 1db69553e0be401ecf98d1c1fa366331163c509f Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:38:30 +0800 Subject: [PATCH 2/9] feat: sf-542 block cairo0 (#153) * feat: change account contract to cairo 1 - update recover account to return correct address - update create account to use isAccountAddressDeployed method - update est gas fee to use new accountClassHash - add upgradeRequired in to acc object - fix test case * feat: add blocking on snap method - add blocking on snap methods - add test case * fix: update incorrect name * fix: lint style * fix: bug in merge conflict * fix: update yarn lock --- packages/starknet-snap/snap.manifest.json | 2 +- packages/starknet-snap/src/estimateFee.ts | 25 +- .../starknet-snap/src/extractPrivateKey.ts | 21 +- .../starknet-snap/src/extractPublicKey.ts | 6 +- packages/starknet-snap/src/sendTransaction.ts | 16 +- packages/starknet-snap/src/signMessage.ts | 29 +- .../starknet-snap/src/utils/starknetUtils.ts | 8 +- .../starknet-snap/src/verifySignedMessage.ts | 30 +- .../test/src/estimateFee.test.ts | 244 +++--- .../test/src/extractPrivateKey.test.ts | 217 +++-- .../test/src/extractPublicKey.test.ts | 184 +++-- .../test/src/sendTransaction.test.ts | 738 ++++++++++-------- .../test/src/signMessage.test.ts | 281 ++++--- .../test/src/verifySignedMessage.test.ts | 206 +++-- yarn.lock | 4 +- 15 files changed, 1184 insertions(+), 827 deletions(-) diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index f2e49c07..d8bfd5b1 100644 --- a/packages/starknet-snap/snap.manifest.json +++ b/packages/starknet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/ConsenSys/starknet-snap.git" }, "source": { - "shasum": "6MAPpaepoUn4jcXUhYKIF+XnYV1SGbudlJ+Qa+knr5g=", + "shasum": "yqSLTJy0WyWa+iX4Yx61DMnY7hrA8BdnBJwNEOAy2I8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/src/estimateFee.ts b/packages/starknet-snap/src/estimateFee.ts index 04662634..13f376dc 100644 --- a/packages/starknet-snap/src/estimateFee.ts +++ b/packages/starknet-snap/src/estimateFee.ts @@ -11,6 +11,7 @@ import { estimateFeeBulk, addFeesFromAllTransactions, isAccountDeployed, + isUpgradeRequired, } from './utils/starknetUtils'; import { logger } from './utils/logger'; @@ -19,8 +20,13 @@ export async function estimateFee(params: ApiParams) { try { const { state, keyDeriver, requestParams } = params; const requestParamsObj = requestParams as EstimateFeeRequestParams; + const contractAddress = requestParamsObj.contractAddress; + const contractFuncName = requestParamsObj.contractFuncName; + const contractCallData = getCallDataArray(requestParamsObj.contractCallData); + const senderAddress = requestParamsObj.senderAddress; + const network = getNetworkFromChainId(state, requestParamsObj.chainId); - if (!requestParamsObj.contractAddress || !requestParamsObj.senderAddress || !requestParamsObj.contractFuncName) { + if (!contractAddress || !requestParamsObj.senderAddress || !contractFuncName) { throw new Error( `The given contract address, sender address, and function name need to be non-empty string, got: ${toJson( requestParamsObj, @@ -29,21 +35,20 @@ export async function estimateFee(params: ApiParams) { } try { - validateAndParseAddress(requestParamsObj.contractAddress); + validateAndParseAddress(contractAddress); } catch (err) { - throw new Error(`The given contract address is invalid: ${requestParamsObj.contractAddress}`); + throw new Error(`The given contract address is invalid: ${contractAddress}`); } try { - validateAndParseAddress(requestParamsObj.senderAddress); + validateAndParseAddress(senderAddress); } catch (err) { - throw new Error(`The given sender address is invalid: ${requestParamsObj.senderAddress}`); + throw new Error(`The given sender address is invalid: ${senderAddress}`); + } + + if (await isUpgradeRequired(network, senderAddress)) { + throw new Error('Upgrade required'); } - const contractAddress = requestParamsObj.contractAddress; - const contractFuncName = requestParamsObj.contractFuncName; - const contractCallData = getCallDataArray(requestParamsObj.contractCallData); - const senderAddress = requestParamsObj.senderAddress; - const network = getNetworkFromChainId(state, requestParamsObj.chainId); const { privateKey: senderPrivateKey, publicKey } = await getKeysFromAddress( keyDeriver, network, diff --git a/packages/starknet-snap/src/extractPrivateKey.ts b/packages/starknet-snap/src/extractPrivateKey.ts index f958d030..60252c03 100644 --- a/packages/starknet-snap/src/extractPrivateKey.ts +++ b/packages/starknet-snap/src/extractPrivateKey.ts @@ -2,7 +2,7 @@ import { toJson } from './utils/serializer'; import { validateAndParseAddress } from '../src/utils/starknetUtils'; import { ApiParams, ExtractPrivateKeyRequestParams } from './types/snapApi'; import { getNetworkFromChainId } from './utils/snapUtils'; -import { getKeysFromAddress } from './utils/starknetUtils'; +import { getKeysFromAddress, isUpgradeRequired } from './utils/starknetUtils'; import { DialogType } from '@metamask/rpc-methods'; import { copyable, panel, text } from '@metamask/snaps-ui'; import { logger } from './utils/logger'; @@ -11,17 +11,20 @@ export async function extractPrivateKey(params: ApiParams) { try { const { state, wallet, keyDeriver, requestParams } = params; const requestParamsObj = requestParams as ExtractPrivateKeyRequestParams; - - if (!requestParamsObj.userAddress) { - throw new Error( - `The given user address need to be non-empty string, got: ${toJson(requestParamsObj.userAddress)}`, - ); + const network = getNetworkFromChainId(state, requestParamsObj.chainId); + const userAddress = requestParamsObj.userAddress; + if (!userAddress) { + throw new Error(`The given user address need to be non-empty string, got: ${toJson(userAddress)}`); } try { - validateAndParseAddress(requestParamsObj.userAddress); + validateAndParseAddress(userAddress); } catch (err) { - throw new Error(`The given user address is invalid: ${requestParamsObj.userAddress}`); + throw new Error(`The given user address is invalid: ${userAddress}`); + } + + if (await isUpgradeRequired(network, userAddress)) { + throw new Error('Upgrade required'); } const response = await wallet.request({ @@ -33,8 +36,6 @@ export async function extractPrivateKey(params: ApiParams) { }); if (response === true) { - const userAddress = requestParamsObj.userAddress; - const network = getNetworkFromChainId(state, requestParamsObj.chainId); const { privateKey: userPrivateKey } = await getKeysFromAddress(keyDeriver, network, state, userAddress); await wallet.request({ diff --git a/packages/starknet-snap/src/extractPublicKey.ts b/packages/starknet-snap/src/extractPublicKey.ts index 7b9e5960..1fa67fe5 100644 --- a/packages/starknet-snap/src/extractPublicKey.ts +++ b/packages/starknet-snap/src/extractPublicKey.ts @@ -1,6 +1,6 @@ import { toJson } from './utils/serializer'; import { constants, num } from 'starknet'; -import { validateAndParseAddress } from '../src/utils/starknetUtils'; +import { validateAndParseAddress, isUpgradeRequired } from '../src/utils/starknetUtils'; import { ApiParams, ExtractPublicKeyRequestParams } from './types/snapApi'; import { getAccount, getNetworkFromChainId } from './utils/snapUtils'; import { getKeysFromAddress } from './utils/starknetUtils'; @@ -26,6 +26,10 @@ 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'); + } + let userPublicKey; const accContract = getAccount(state, userAddress, network.chainId); if (!accContract?.publicKey || num.toBigInt(accContract.publicKey) === constants.ZERO) { diff --git a/packages/starknet-snap/src/sendTransaction.ts b/packages/starknet-snap/src/sendTransaction.ts index 4b6b0c2c..7ecf1fca 100644 --- a/packages/starknet-snap/src/sendTransaction.ts +++ b/packages/starknet-snap/src/sendTransaction.ts @@ -4,7 +4,13 @@ import { validateAndParseAddress } from '../src/utils/starknetUtils'; import { estimateFee } from './estimateFee'; import { Transaction, TransactionStatus, VoyagerTransactionType } from './types/snapState'; import { getNetworkFromChainId, getSigningTxnText, upsertTransaction } from './utils/snapUtils'; -import { getKeysFromAddress, getCallDataArray, executeTxn, isAccountDeployed } from './utils/starknetUtils'; +import { + getKeysFromAddress, + getCallDataArray, + executeTxn, + isAccountDeployed, + isUpgradeRequired, +} from './utils/starknetUtils'; import { ApiParams, SendTransactionRequestParams } from './types/snapApi'; import { createAccount } from './createAccount'; import { DialogType } from '@metamask/rpc-methods'; @@ -40,6 +46,11 @@ export async function sendTransaction(params: ApiParams) { const contractCallData = getCallDataArray(requestParamsObj.contractCallData); const senderAddress = requestParamsObj.senderAddress; const network = getNetworkFromChainId(state, requestParamsObj.chainId); + + if (await isUpgradeRequired(network, senderAddress)) { + throw new Error('Upgrade required'); + } + const { privateKey: senderPrivateKey, publicKey, @@ -120,7 +131,8 @@ export async function sendTransaction(params: ApiParams) { try { return num.toHex(num.toBigInt(data)); } catch (e) { - throw new Error(`contractCallData could not be converted, ${e.message || e}`); + //data is already send to chain, hence we should not throw error + return '0x0'; } }), finalityStatus: TransactionStatus.RECEIVED, diff --git a/packages/starknet-snap/src/signMessage.ts b/packages/starknet-snap/src/signMessage.ts index ba8660c5..1c4cf2ca 100644 --- a/packages/starknet-snap/src/signMessage.ts +++ b/packages/starknet-snap/src/signMessage.ts @@ -1,6 +1,6 @@ import { toJson } from './utils/serializer'; import typedDataExample from './typedData/typedDataExample.json'; -import { getTypedDataMessageSignature, getKeysFromAddress } from './utils/starknetUtils'; +import { getTypedDataMessageSignature, getKeysFromAddress, isUpgradeRequired } from './utils/starknetUtils'; import { getNetworkFromChainId } from './utils/snapUtils'; import { ApiParams, SignMessageRequestParams } from './types/snapApi'; import { validateAndParseAddress } from '../src/utils/starknetUtils'; @@ -12,19 +12,6 @@ export async function signMessage(params: ApiParams) { try { const { state, wallet, keyDeriver, requestParams } = params; const requestParamsObj = requestParams as SignMessageRequestParams; - - if (!requestParamsObj.signerAddress) { - throw new Error( - `The given signer address need to be non-empty string, got: ${toJson(requestParamsObj.signerAddress)}`, - ); - } - - try { - validateAndParseAddress(requestParamsObj.signerAddress); - } catch (err) { - throw new Error(`The given signer address is invalid: ${requestParamsObj.signerAddress}`); - } - const signerAddress = requestParamsObj.signerAddress; const typedDataMessage = requestParamsObj.typedDataMessage ? JSON.parse(requestParamsObj.typedDataMessage) @@ -33,6 +20,20 @@ export async function signMessage(params: ApiParams) { logger.log(`signMessage:\nsignerAddress: ${signerAddress}\ntypedDataMessage: ${toJson(typedDataMessage)}`); + if (!signerAddress) { + throw new Error(`The given signer address need to be non-empty string, got: ${toJson(signerAddress)}`); + } + + try { + validateAndParseAddress(signerAddress); + } catch (err) { + throw new Error(`The given signer address is invalid: ${signerAddress}`); + } + + if (await isUpgradeRequired(network, signerAddress)) { + throw new Error('Upgrade required'); + } + const response = await wallet.request({ method: 'snap_dialog', params: { diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 1b41f43e..d81fc05e 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -540,10 +540,10 @@ export const validateAndParseAddress = (address: num.BigNumberish, length = 63) return _validateAndParseAddressFn(address); }; -export const isUpgradeRequired = async (network: Network, cairo0address: string) => { +export const isUpgradeRequired = async (network: Network, address: string) => { try { - logger.log(`isUpgradeRequired: cairo0address = ${cairo0address}`); - const version = await getVersion(cairo0address, network); + logger.log(`isUpgradeRequired: address = ${address}`); + const version = await getVersion(address, network); const versionArr = version.split('.'); return Number(versionArr[1]) < MIN_ACC_CONTRACT_VERSION[1]; } catch (err) { @@ -562,7 +562,7 @@ export const getCorrectContractAddress = async (network: Network, publicKey: str ); let pk = ''; logger.log( - `getContractAddressByKey: contractAddressCairo1 = ${contractAddress}\ncontractAddressCairo0 = ${contractAddressCairo0}\npublicKey = ${publicKey}`, + `getContractAddressByKey: contractAddressCario1 = ${contractAddress}\ncontractAddressCairo0 = ${contractAddressCairo0}\npublicKey = ${publicKey}`, ); try { pk = await getOwner(contractAddress, network); diff --git a/packages/starknet-snap/src/verifySignedMessage.ts b/packages/starknet-snap/src/verifySignedMessage.ts index e3475d6a..8917852e 100644 --- a/packages/starknet-snap/src/verifySignedMessage.ts +++ b/packages/starknet-snap/src/verifySignedMessage.ts @@ -4,6 +4,7 @@ import { verifyTypedDataMessageSignature, getFullPublicKeyPairFromPrivateKey, getKeysFromAddress, + isUpgradeRequired, } from './utils/starknetUtils'; import { getNetworkFromChainId } from './utils/snapUtils'; import { ApiParams, VerifySignedMessageRequestParams } from './types/snapApi'; @@ -14,19 +15,6 @@ export async function verifySignedMessage(params: ApiParams) { try { const { state, keyDeriver, requestParams } = params; const requestParamsObj = requestParams as VerifySignedMessageRequestParams; - - if (!requestParamsObj.signerAddress || !requestParamsObj.signature) { - throw new Error( - `The given signer address and signature need to be non-empty string, got: ${toJson(requestParamsObj)}`, - ); - } - - try { - validateAndParseAddress(requestParamsObj.signerAddress); - } catch (err) { - throw new Error(`The given signer address is invalid: ${requestParamsObj.signerAddress}`); - } - const verifySignerAddress = requestParamsObj.signerAddress; const verifySignature = requestParamsObj.signature; const verifyTypedDataMessage = requestParamsObj.typedDataMessage @@ -40,6 +28,22 @@ export async function verifySignedMessage(params: ApiParams) { )}`, ); + if (!verifySignerAddress || !verifySignature) { + throw new Error( + `The given signer address and signature need to be non-empty string, got: ${toJson(requestParamsObj)}`, + ); + } + + try { + validateAndParseAddress(verifySignerAddress); + } catch (err) { + throw new Error(`The given signer address is invalid: ${verifySignerAddress}`); + } + + if (await isUpgradeRequired(network, verifySignerAddress)) { + throw new Error('Upgrade required'); + } + const { privateKey: signerPrivateKey } = await getKeysFromAddress(keyDeriver, network, state, verifySignerAddress); const fullPublicKey = getFullPublicKeyPairFromPrivateKey(signerPrivateKey); diff --git a/packages/starknet-snap/test/src/estimateFee.test.ts b/packages/starknet-snap/test/src/estimateFee.test.ts index 7309f3d0..09cc27fe 100644 --- a/packages/starknet-snap/test/src/estimateFee.test.ts +++ b/packages/starknet-snap/test/src/estimateFee.test.ts @@ -7,13 +7,7 @@ import { estimateFee } from '../../src/estimateFee'; import { SnapState } from '../../src/types/snapState'; import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; -import { - account2, - estimateDeployFeeResp4, - estimateFeeResp, - estimateFeeResp2, - getBip44EntropyStub, -} from '../constants.test'; +import { account2, estimateDeployFeeResp4, estimateFeeResp, getBip44EntropyStub } from '../constants.test'; import { Mutex } from 'async-mutex'; import { ApiParams, EstimateFeeRequestParams } from '../../src/types/snapApi'; @@ -52,122 +46,154 @@ describe('Test function: estimateFee', function () { sandbox.restore(); }); - it('should estimate the fee correctly', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account2.publicKey; - }); - sandbox.stub(utils, 'estimateFee').callsFake(async () => { - return estimateFeeResp; + describe('when request param validation fail', function () { + let invalidRequest = Object.assign({}, requestObject); + + afterEach(async function () { + invalidRequest = Object.assign({}, requestObject); }); - // The following will be commented out later when starknet.js - // supports estimateFeeBulk in rpc mode - // sandbox.stub(utils, 'estimateFeeBulk').callsFake(async () => { - // return [estimateFeeResp]; - // }); - const result = await estimateFee(apiParams); - expect(result.suggestedMaxFee).to.be.eq(estimateFeeResp.suggestedMaxFee.toString(10)); - }); - it('should estimate the fee including deploy txn correctly', async function () { - sandbox.stub(utils, 'isAccountDeployed').resolves(false); - sandbox.stub(utils, 'estimateFeeBulk').callsFake(async () => { - return [estimateDeployFeeResp4, estimateFeeResp]; + it('should throw an error if the function name is undefined', async function () { + invalidRequest.contractFuncName = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await estimateFee(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } }); - const expectedSuggestedMaxFee = estimateDeployFeeResp4.suggestedMaxFee + estimateFeeResp.suggestedMaxFee; - const result = await estimateFee(apiParams); - expect(result.suggestedMaxFee).to.be.eq(expectedSuggestedMaxFee.toString(10)); - }); - it('should estimate the fee without gas consumed and gas price correctly', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account2.publicKey; + it('should throw an error if the contract address is invalid', async function () { + invalidRequest.contractAddress = 'wrongAddress'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await estimateFee(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } }); - sandbox.stub(utils, 'estimateFee').callsFake(async () => { - return estimateFeeResp2; + + it('should throw an error if the sender address is invalid', async function () { + invalidRequest.senderAddress = 'wrongAddress'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await estimateFee(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } }); - // The following will be commented out later when starknet.js - // supports estimateFeeBulk in rpc mode - // sandbox.stub(utils, 'estimateFeeBulk').callsFake(async () => { - // return [estimateFeeResp2]; - // }); - const result = await estimateFee(apiParams); - expect(result.suggestedMaxFee).to.be.eq(estimateFeeResp.suggestedMaxFee.toString(10)); }); - it('should throw error if estimateFee failed', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account2.publicKey; + describe('when request param validation pass', function () { + beforeEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); }); - sandbox.stub(utils, 'estimateFeeBulk').throws(new Error()); - apiParams.requestParams = requestObject; - - let result; - try { - await estimateFee(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); - it('should throw an error if the function name is undefined', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account2.publicKey; + afterEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); }); - apiParams.requestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: undefined, - contractCallData: '0x7426b2da7a8978e0d472d64f15f984d658226cb55a4fd8aa7689688a7eab37b', - senderAddress: account2.address, - }; - let result; - try { - result = await estimateFee(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); - it('should throw an error if the contract address is invalid', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account2.publicKey; + describe('when account require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + }); + + it('should throw error if upgrade required', async function () { + let result; + try { + result = await estimateFee(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account2.address); + expect(result).to.be.an('Error'); + } + }); }); - apiParams.requestParams = { - contractAddress: 'wrongAddress', - contractFuncName: 'balanceOf', - contractCallData: '0x7426b2da7a8978e0d472d64f15f984d658226cb55a4fd8aa7689688a7eab37b', - senderAddress: account2.address, - }; - let result; - try { - result = await estimateFee(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); - it('should throw an error if the sender address is invalid', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account2.publicKey; + describe('when account is not require upgrade', function () { + let estimateFeeBulkStub: sinon.SinonStub; + let estimateFeeStub: sinon.SinonStub; + + beforeEach(async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + }); + + describe('when account is deployed', function () { + beforeEach(async function () { + estimateFeeBulkStub = sandbox.stub(utils, 'estimateFeeBulk'); + sandbox.stub(utils, 'isAccountDeployed').resolves(true); + }); + + it('should estimate the fee correctly', async function () { + estimateFeeStub = sandbox.stub(utils, 'estimateFee').resolves(estimateFeeResp); + + const result = await estimateFee(apiParams); + expect(result.suggestedMaxFee).to.be.eq(estimateFeeResp.suggestedMaxFee.toString(10)); + expect(estimateFeeStub).callCount(1); + expect(estimateFeeBulkStub).callCount(0); + }); + + it('should throw error if estimateFee failed', async function () { + estimateFeeStub = sandbox.stub(utils, 'estimateFee').throws('Error'); + apiParams.requestParams = requestObject; + + let result; + try { + await estimateFee(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(estimateFeeStub).callCount(1); + expect(estimateFeeBulkStub).callCount(0); + } + }); + }); + + describe('when account is not deployed', function () { + beforeEach(async function () { + estimateFeeStub = sandbox.stub(utils, 'estimateFee'); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); + }); + + it('should estimate the fee including deploy txn correctly', async function () { + estimateFeeBulkStub = sandbox + .stub(utils, 'estimateFeeBulk') + .resolves([estimateFeeResp, estimateDeployFeeResp4]); + const expectedSuggestedMaxFee = estimateDeployFeeResp4.suggestedMaxFee + estimateFeeResp.suggestedMaxFee; + const result = await estimateFee(apiParams); + expect(result.suggestedMaxFee).to.be.eq(expectedSuggestedMaxFee.toString(10)); + expect(estimateFeeStub).callCount(0); + expect(estimateFeeBulkStub).callCount(1); + }); + + it('should throw error if estimateFee failed', async function () { + estimateFeeBulkStub = sandbox.stub(utils, 'estimateFeeBulk').throws('Error'); + apiParams.requestParams = requestObject; + + let result; + try { + await estimateFee(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(estimateFeeStub).callCount(0); + expect(estimateFeeBulkStub).callCount(1); + } + }); + }); }); - apiParams.requestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'balanceOf', - contractCallData: '0x7426b2da7a8978e0d472d64f15f984d658226cb55a4fd8aa7689688a7eab37b', - senderAddress: 'wrongAddress', - }; - let result; - try { - result = await estimateFee(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } }); }); diff --git a/packages/starknet-snap/test/src/extractPrivateKey.test.ts b/packages/starknet-snap/test/src/extractPrivateKey.test.ts index 7345723a..34743b98 100644 --- a/packages/starknet-snap/test/src/extractPrivateKey.test.ts +++ b/packages/starknet-snap/test/src/extractPrivateKey.test.ts @@ -22,6 +22,7 @@ describe('Test function: extractPrivateKey', function () { networks: [STARKNET_TESTNET_NETWORK], transactions: [], }; + const apiParams: ApiParams = { state, requestParams: {}, @@ -29,6 +30,10 @@ describe('Test function: extractPrivateKey', function () { saveMutex: new Mutex(), }; + const requestObject: ExtractPrivateKeyRequestParams = { + userAddress: account1.address, + }; + beforeEach(async function () { walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); apiParams.keyDeriver = await getAddressKeyDeriver(walletStub); @@ -39,87 +44,147 @@ describe('Test function: extractPrivateKey', function () { sandbox.restore(); }); - it('should get the private key of the specified user account correctly', async function () { - walletStub.rpcStubs.snap_dialog.resolves(true); - const requestObject: ExtractPrivateKeyRequestParams = { - userAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await extractPrivateKey(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledTwice; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.equal(null); - }); + describe('when request param validation fail', function () { + let invalidRequest = Object.assign({}, requestObject); - it('should get the private key of the unfound user account correctly', async function () { - walletStub.rpcStubs.snap_dialog.resolves(true); - const requestObject: ExtractPrivateKeyRequestParams = { - userAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - const result = await extractPrivateKey(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledTwice; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(null); - }); + afterEach(async function () { + invalidRequest = Object.assign({}, requestObject); + }); - it('should not get the private key of the specified user account if user rejected', async function () { - walletStub.rpcStubs.snap_dialog.resolves(false); - const requestObject: ExtractPrivateKeyRequestParams = { - userAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await extractPrivateKey(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.equal(null); - }); + it('should throw an error if the user address is undefined', async function () { + invalidRequest.userAddress = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await extractPrivateKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); - it('should throw error if getKeysFromAddress failed', async function () { - sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); - walletStub.rpcStubs.snap_dialog.resolves(true); - const requestObject: ExtractPrivateKeyRequestParams = { - userAddress: account1.address, - }; - apiParams.requestParams = requestObject; - - let result; - try { - await extractPrivateKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + it('should throw an error if the user address is invalid', async function () { + invalidRequest.userAddress = 'wrongAddress'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await extractPrivateKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); }); - it('should throw an error if the user address is undefined', async function () { - const requestObject: ExtractPrivateKeyRequestParams = { - userAddress: undefined, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await extractPrivateKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + describe('when request param validation pass', function () { + beforeEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + afterEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + describe('when require upgrade checking fail', function () { + it('should throw error', async function () { + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').throws('network error'); + let result; + try { + result = await extractPrivateKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + }); + + it('should throw error if upgrade required', async function () { + let result; + try { + result = await extractPrivateKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account is not require upgrade', function () { + beforeEach(async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + }); + + describe('when account is cairo 0', function () { + //TODO + }); + + describe('when account is cairo 1', function () { + it('should get the private key of the specified user account correctly', async function () { + walletStub.rpcStubs.snap_dialog.resolves(true); + const requestObject: ExtractPrivateKeyRequestParams = { + userAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await extractPrivateKey(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledTwice; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.equal(null); + }); + + it('should get the private key of the unfound user account correctly', async function () { + walletStub.rpcStubs.snap_dialog.resolves(true); + const requestObject: ExtractPrivateKeyRequestParams = { + userAddress: unfoundUserAddress, + }; + apiParams.requestParams = requestObject; + const result = await extractPrivateKey(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledTwice; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(null); + }); + + it('should not get the private key of the specified user account if user rejected', async function () { + walletStub.rpcStubs.snap_dialog.resolves(false); + const requestObject: ExtractPrivateKeyRequestParams = { + userAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await extractPrivateKey(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.equal(null); + }); + + it('should throw error if getKeysFromAddress failed', async function () { + sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); + walletStub.rpcStubs.snap_dialog.resolves(true); + const requestObject: ExtractPrivateKeyRequestParams = { + userAddress: account1.address, + }; + apiParams.requestParams = requestObject; - it('should throw an error if the user address is invalid', async function () { - const requestObject: ExtractPrivateKeyRequestParams = { - userAddress: 'wrongAddress', - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await extractPrivateKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + let result; + try { + await extractPrivateKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); + }); + }); }); }); diff --git a/packages/starknet-snap/test/src/extractPublicKey.test.ts b/packages/starknet-snap/test/src/extractPublicKey.test.ts index 0039af89..88614799 100644 --- a/packages/starknet-snap/test/src/extractPublicKey.test.ts +++ b/packages/starknet-snap/test/src/extractPublicKey.test.ts @@ -29,6 +29,10 @@ describe('Test function: extractPublicKey', function () { saveMutex: new Mutex(), }; + const requestObject: ExtractPublicKeyRequestParams = { + userAddress: account1.address, + }; + beforeEach(async function () { walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); apiParams.keyDeriver = await getAddressKeyDeriver(walletStub); @@ -39,70 +43,130 @@ describe('Test function: extractPublicKey', function () { sandbox.restore(); }); - it('should get the public key of the specified user account correctly', async function () { - const requestObject: ExtractPublicKeyRequestParams = { - userAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await extractPublicKey(apiParams); - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(account1.publicKey); - }); + describe('when request param validation fail', function () { + let invalidRequest = Object.assign({}, requestObject); - it('should get the public key of the unfound user account correctly', async function () { - const requestObject: ExtractPublicKeyRequestParams = { - userAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - const result = await extractPublicKey(apiParams); - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(unfoundUserPublicKey); - }); + afterEach(async function () { + invalidRequest = Object.assign({}, requestObject); + }); - it('should throw error if getKeysFromAddress failed', async function () { - sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); - const requestObject: ExtractPublicKeyRequestParams = { - userAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - - let result; - try { - await extractPublicKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + it('should throw error if param userAddress is undefined', async function () { + invalidRequest.userAddress = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await extractPublicKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); - it('should throw error if param userAddress is undefined', async function () { - const requestObject: ExtractPublicKeyRequestParams = { - userAddress: undefined, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await extractPublicKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + it('should throw error if param userAddress is invalid', async function () { + invalidRequest.userAddress = 'wrongAddress'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await extractPublicKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); }); - it('should throw error if param userAddress is invalid', async function () { - const requestObject: ExtractPublicKeyRequestParams = { - userAddress: 'wrongAddress', - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await extractPublicKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + describe('when request param validation pass', function () { + beforeEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + afterEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + describe('when require upgrade checking fail', function () { + it('should throw error', async function () { + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').throws('network error'); + let result; + try { + result = await extractPublicKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + }); + + it('should throw error if upgrade required', async function () { + let result; + try { + result = await extractPublicKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account is not require upgrade', function () { + beforeEach(async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + }); + + describe('when account is cairo 0', function () { + //TODO + }); + + describe('when account is cairo 1', function () { + it('should get the public key of the specified user account correctly', async function () { + const requestObject: ExtractPublicKeyRequestParams = { + userAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await extractPublicKey(apiParams); + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(account1.publicKey); + }); + + it('should get the public key of the unfound user account correctly', async function () { + const requestObject: ExtractPublicKeyRequestParams = { + userAddress: unfoundUserAddress, + }; + apiParams.requestParams = requestObject; + const result = await extractPublicKey(apiParams); + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(unfoundUserPublicKey); + }); + + it('should throw error if getKeysFromAddress failed', async function () { + sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); + const requestObject: ExtractPublicKeyRequestParams = { + userAddress: unfoundUserAddress, + }; + apiParams.requestParams = requestObject; + + let result; + try { + await extractPublicKey(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); + }); + }); }); }); diff --git a/packages/starknet-snap/test/src/sendTransaction.test.ts b/packages/starknet-snap/test/src/sendTransaction.test.ts index 1a9edb0d..4610bb52 100644 --- a/packages/starknet-snap/test/src/sendTransaction.test.ts +++ b/packages/starknet-snap/test/src/sendTransaction.test.ts @@ -7,7 +7,7 @@ import * as utils from '../../src/utils/starknetUtils'; import * as snapUtils from '../../src/utils/snapUtils'; import { SnapState } from '../../src/types/snapState'; import { sendTransaction } from '../../src/sendTransaction'; - +import * as estimateFeeSnap from '../../src/estimateFee'; import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; import { account1, @@ -45,23 +45,18 @@ describe('Test function: sendTransaction', function () { wallet: walletStub, saveMutex: new Mutex(), }; - let executeTxnResp; + + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: account1.address, + chainId: STARKNET_TESTNET_NETWORK.chainId, + }; beforeEach(async function () { walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); apiParams.keyDeriver = await getAddressKeyDeriver(walletStub); - sandbox.stub(utils, 'estimateFeeBulk').callsFake(async () => { - return [estimateFeeResp]; - }); - sandbox.stub(utils, 'estimateFee').callsFake(async () => { - return estimateFeeResp; - }); - executeTxnResp = sendTransactionResp; - sandbox.stub(utils, 'executeTxn').callsFake(async () => { - return executeTxnResp; - }); - walletStub.rpcStubs.snap_dialog.resolves(true); - walletStub.rpcStubs.snap_manageState.resolves(state); }); afterEach(function () { @@ -69,358 +64,411 @@ describe('Test function: sendTransaction', function () { sandbox.restore(); }); - it('should send a transaction for transferring 10 tokens correctly', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); - }); + describe('when request param validation fail', function () { + let invalidRequest: SendTransactionRequestParams = Object.assign({}, requestObject); - it('should trigger a deploy txn and send a transaction for transferring 10 tokens correctly', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); - sandbox.stub(utils, 'deployAccount').callsFake(async () => { - return createAccountProxyResp; + afterEach(function () { + invalidRequest = Object.assign({}, requestObject); + apiParams.requestParams = requestObject; }); - sandbox.stub(utils, 'getBalance').callsFake(async () => { - return getBalanceResp[0]; + + it('it should show error when request contractAddress is not given', async function () { + invalidRequest.contractAddress = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include( + 'The given contract address, sender address, and function name need to be non-empty string', + ); + } }); - sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { - return estimateDeployFeeResp; + + it('it should show error when request contractAddress is invalid', async function () { + invalidRequest.contractAddress = '0x0'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('The given contract address is invalid'); + } }); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); - }); - it('should return false if user rejected to sign the transaction', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - walletStub.rpcStubs.snap_dialog.resolves(false); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(false); - }); + it('it should show error when request senderAddress is not given', async function () { + invalidRequest.senderAddress = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include( + 'The given contract address, sender address, and function name need to be non-empty string', + ); + } + }); - it('should send a transaction for transferring 10 tokens but not update snap state if transaction_hash is missing from response', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - executeTxnResp = sendTransactionFailedResp; - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(sendTransactionFailedResp); - }); + it('it should show error when request contractAddress is invalid', async function () { + invalidRequest.senderAddress = '0x0'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('The given sender address is invalid'); + } + }); - it('should send a transaction with given max fee for transferring 10 tokens correctly', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: account1.address, - maxFee: '15135825227039', - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); - }); + it('it should show error when request contractFuncName is not given', async function () { + invalidRequest.contractFuncName = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include( + 'The given contract address, sender address, and function name need to be non-empty string', + ); + } + }); - it('should send a transfer transaction for empty call data', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: undefined, - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); + it('it should show error when request network not found', async function () { + invalidRequest.chainId = '0x0'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); }); - it('should send a transaction for empty call data', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: account1.address, - contractFuncName: 'get_signer', - contractCallData: undefined, - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); - }); + describe('when request param validation pass', function () { + beforeEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); - it('should use heading, text and copyable component', async function () { - executeTxnResp = sendTransactionFailedResp; - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: account1.address, - contractFuncName: 'get_signer', - contractCallData: '**foo**', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - await sendTransaction(apiParams); - const expectedDialogParams = { - type: 'confirmation', - content: { - type: 'panel', - children: [ - { type: 'heading', value: 'Do you want to sign this transaction ?' }, - { - type: 'text', - value: `**Signer Address:**`, - }, - { - type: 'copyable', - value: '0x04882a372da3dfe1c53170ad75893832469bf87b62b13e84662565c4a88f25cd', - }, - { - type: 'text', - value: `**Contract:**`, - }, - { - type: 'copyable', - value: '0x04882a372da3dfe1c53170ad75893832469bf87b62b13e84662565c4a88f25cd', - }, - { - type: 'text', - value: `**Call Data:**`, - }, - { - type: 'copyable', - value: '[**foo**]', - }, - { - type: 'text', - value: `**Estimated Gas Fee(ETH):**`, - }, - { - type: 'copyable', - value: '0.000022702500105945', - }, - { - type: 'text', - value: `**Network:**`, - }, - { - type: 'copyable', - value: 'Goerli Testnet', - }, - ], - }, - }; - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledWith(expectedDialogParams); - }); + afterEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); - it('should send a transaction for transferring 10 tokens from an unfound user correctly', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); - }); + describe('when require upgrade checking fail', function () { + it('should throw error', async function () { + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').throws('network error'); + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); - it('should send a transaction for transferring 10 tokens (token of 10 decimal places) from an unfound user correctly', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x06a09ccb1caaecf3d9683efe335a667b2169a409d19c589ba1eb771cd210af75', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - const result = await sendTransaction(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; - expect(result).to.be.eql(sendTransactionResp); - }); + describe('when account require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + }); - it('should throw error if upsertTransaction failed', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; + it('should throw error if upgrade required', async function () { + let result; + try { + result = await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); }); - sandbox.stub(snapUtils, 'upsertTransaction').throws(new Error()); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - - let result; - try { - await sendTransaction(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); - it('should throw an error if contract address is undefined', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; - }); - const requestObject: SendTransactionRequestParams = { - contractAddress: undefined, - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await sendTransaction(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + describe('when account is not require upgrade', function () { + let executeTxnResp; + let executeTxnStub: sinon.SinonStub; + beforeEach(async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + sandbox.stub(estimateFeeSnap, 'estimateFee').resolves({ + suggestedMaxFee: estimateFeeResp.suggestedMaxFee.toString(10), + overallFee: estimateFeeResp.overall_fee.toString(10), + gasConsumed: '0', + gasPrice: '0', + unit: 'wei', + includeDeploy: true, + }); + executeTxnResp = sendTransactionResp; + executeTxnStub = sandbox.stub(utils, 'executeTxn').resolves(executeTxnResp); + walletStub.rpcStubs.snap_manageState.resolves(state); + }); - it('should throw an error if function name is undefined', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; - }); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: undefined, - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: unfoundUserAddress, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await sendTransaction(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + it('should return false if user rejected to sign the transaction', async function () { + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + walletStub.rpcStubs.snap_dialog.resolves(false); + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(false); + }); - it('should throw an error if sender address is undefined', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; - }); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: undefined, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await sendTransaction(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + it('should use heading, text and copyable component', async function () { + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + executeTxnResp = sendTransactionFailedResp; + const requestObject: SendTransactionRequestParams = { + contractAddress: account1.address, + contractFuncName: 'get_signer', + contractCallData: '**foo**', + senderAddress: account1.address, + }; + apiParams.requestParams = requestObject; + await sendTransaction(apiParams); + const expectedDialogParams = { + type: 'confirmation', + content: { + type: 'panel', + children: [ + { type: 'heading', value: 'Do you want to sign this transaction ?' }, + { + type: 'text', + value: `**Signer Address:**`, + }, + { + type: 'copyable', + value: '0x04882a372da3dfe1c53170ad75893832469bf87b62b13e84662565c4a88f25cd', + }, + { + type: 'text', + value: `**Contract:**`, + }, + { + type: 'copyable', + value: '0x04882a372da3dfe1c53170ad75893832469bf87b62b13e84662565c4a88f25cd', + }, + { + type: 'text', + value: `**Call Data:**`, + }, + { + type: 'copyable', + value: '[**foo**]', + }, + { + type: 'text', + value: `**Estimated Gas Fee(ETH):**`, + }, + { + type: 'copyable', + value: '0.000022702500105945', + }, + { + type: 'text', + value: `**Network:**`, + }, + { + type: 'copyable', + value: 'Goerli Testnet', + }, + ], + }, + }; + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledWith(expectedDialogParams); + }); - it('should throw an error if contract address is invalid', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; - }); - const requestObject: SendTransactionRequestParams = { - contractAddress: 'wrongAddress', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await sendTransaction(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + describe('when account is cairo 0', function () { + //TODO + }); - it('should throw an error if sender address is invalid', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; - }); - const requestObject: SendTransactionRequestParams = { - contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', - contractFuncName: 'transfer', - contractCallData: '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', - senderAddress: 'wrongAddress', - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await sendTransaction(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + describe('when account is cairo 1', function () { + beforeEach(async function () { + walletStub.rpcStubs.snap_dialog.resolves(true); + }); + + describe('when account is deployed', function () { + beforeEach(async function () { + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + }); + + it('should send a transaction for transferring 10 tokens correctly', async function () { + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + + it('should send a transaction for transferring 10 tokens but not update snap state if transaction_hash is missing from response', async function () { + executeTxnStub.restore(); + executeTxnStub = sandbox.stub(utils, 'executeTxn').resolves(sendTransactionFailedResp); + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(sendTransactionFailedResp); + }); + + it('should send a transaction with given max fee for transferring 10 tokens correctly', async function () { + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: account1.address, + maxFee: '15135825227039', + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + + it('should send a transfer transaction for empty call data', async function () { + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: undefined, + senderAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + + it('should send a transaction for empty call data', async function () { + const requestObject: SendTransactionRequestParams = { + contractAddress: account1.address, + contractFuncName: 'get_signer', + contractCallData: undefined, + senderAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + + it('should send a transaction for transferring 10 tokens from an unfound user correctly', async function () { + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: unfoundUserAddress, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + + it('should send a transaction for transferring 10 tokens (token of 10 decimal places) from an unfound user correctly', async function () { + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x06a09ccb1caaecf3d9683efe335a667b2169a409d19c589ba1eb771cd210af75', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: unfoundUserAddress, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + + it('should throw error if upsertTransaction failed', async function () { + sandbox.stub(snapUtils, 'upsertTransaction').throws(new Error()); + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: unfoundUserAddress, + }; + apiParams.requestParams = requestObject; + + let result; + try { + await sendTransaction(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account is not deployed', function () { + beforeEach(async function () { + sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + }); - it('should throw an error when call data entries can not be converted to a bigNumber', async function () { - sandbox.stub(utils, 'getOwner').callsFake(async () => { - return account1.publicKey; + it('should send a transaction for transferring 10 tokens and a transaction for deploy correctly', async function () { + sandbox.stub(utils, 'deployAccount').callsFake(async () => { + return createAccountProxyResp; + }); + sandbox.stub(utils, 'getBalance').callsFake(async () => { + return getBalanceResp[0]; + }); + sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { + return estimateDeployFeeResp; + }); + const requestObject: SendTransactionRequestParams = { + contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', + contractFuncName: 'transfer', + contractCallData: + '0x0256d8f49882cc9366037415f48fa9fd2b5b7344ded7573ebfcef7c90e3e6b75,100000000000000000000,0', + senderAddress: account1.address, + }; + apiParams.requestParams = requestObject; + const result = await sendTransaction(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).to.have.been.called; + expect(result).to.be.eql(sendTransactionResp); + }); + }); + }); }); - const requestObject: SendTransactionRequestParams = { - contractAddress: account1.address, - contractFuncName: 'get_signer', - contractCallData: '**foo**', - senderAddress: account1.address, - }; - apiParams.requestParams = requestObject; - await expect(sendTransaction(apiParams)).to.be.rejectedWith( - 'contractCallData could not be converted, Cannot convert **foo** to a BigInt', - ); }); }); diff --git a/packages/starknet-snap/test/src/signMessage.test.ts b/packages/starknet-snap/test/src/signMessage.test.ts index ae85b905..b3d07835 100644 --- a/packages/starknet-snap/test/src/signMessage.test.ts +++ b/packages/starknet-snap/test/src/signMessage.test.ts @@ -32,6 +32,11 @@ describe('Test function: signMessage', function () { saveMutex: new Mutex(), }; + const requestObject: SignMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: toJson(typedDataExample), + }; + beforeEach(async function () { walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); apiParams.keyDeriver = await getAddressKeyDeriver(walletStub); @@ -43,120 +48,178 @@ describe('Test function: signMessage', function () { sandbox.restore(); }); - it('should sign a message from an user account correctly', async function () { - const requestObject: SignMessageRequestParams = { - signerAddress: account1.address, - typedDataMessage: undefined, // will use typedDataExample.json - }; - apiParams.requestParams = requestObject; - const result: boolean | string = await signMessage(apiParams); - const expectedDialogParams = { - type: 'confirmation', - content: { - type: 'panel', - children: [ - { type: 'heading', value: 'Do you want to sign this message ?' }, - - { - type: 'text', - value: `**Message:**`, - }, - { - type: 'copyable', - value: toJson(typedDataExample), - }, - { - type: 'text', - value: `**Signer address:**`, - }, - { - type: 'copyable', - value: `${account1.address}`, - }, - ], - }, - }; - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledWith(expectedDialogParams); - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(signature1); - }); + describe('when request param validation fail', function () { + let invalidRequest = Object.assign({}, requestObject); - it('should sign a message from an unfound user account correctly', async function () { - const requestObject: SignMessageRequestParams = { - signerAddress: unfoundUserAddress, - typedDataMessage: toJson(typedDataExample), - }; - apiParams.requestParams = requestObject; - const result = await signMessage(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(signature2); - }); + afterEach(async function () { + invalidRequest = Object.assign({}, requestObject); + apiParams.requestParams = requestObject; + }); - it('should throw error if getKeysFromAddress failed', async function () { - sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); - const requestObject: SignMessageRequestParams = { - signerAddress: account1.address, - typedDataMessage: undefined, // will use typedDataExample.json - }; - apiParams.requestParams = requestObject; - - let result; - try { - await signMessage(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - }); + it('should throw an error if the signerAddress is undefined', async function () { + invalidRequest.signerAddress = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await signMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); - it('should return false if the user not confirmed', async function () { - walletStub.rpcStubs.snap_dialog.resolves(false); - const requestObject: SignMessageRequestParams = { - signerAddress: account1.address, - typedDataMessage: undefined, // will use typedDataExample.json - }; - apiParams.requestParams = requestObject; - const result = await signMessage(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(false); + it('should throw an error if the signerAddress is an invalid address', async function () { + invalidRequest.signerAddress = 'wrongAddress'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await signMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); }); - it('should throw an error if the signerAddress is undefined', async function () { - const requestObject: SignMessageRequestParams = { - signerAddress: undefined, - typedDataMessage: toJson(typedDataExample), - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await signMessage(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + describe('when request param validation pass', function () { + beforeEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + afterEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + describe('when require upgrade checking fail', function () { + it('should throw error', async function () { + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').throws('network error'); + let result; + try { + result = await signMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + }); + + it('should throw error if upgrade required', async function () { + let result; + try { + result = await signMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account is not require upgrade', function () { + beforeEach(async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + }); + + it('should return false if the user not confirmed', async function () { + walletStub.rpcStubs.snap_dialog.resolves(false); + const requestObject: SignMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: undefined, // will use typedDataExample.json + }; + apiParams.requestParams = requestObject; + const result = await signMessage(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(false); + }); + + describe('when account is cairo 0', function () { + //TODO + }); + + describe('when account is cairo 1', function () { + it('should sign a message from an user account correctly', async function () { + const requestObject: SignMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: undefined, // will use typedDataExample.json + }; + apiParams.requestParams = requestObject; + const result: boolean | string = await signMessage(apiParams); + const expectedDialogParams = { + type: 'confirmation', + content: { + type: 'panel', + children: [ + { type: 'heading', value: 'Do you want to sign this message ?' }, + + { + type: 'text', + value: `**Message:**`, + }, + { + type: 'copyable', + value: toJson(typedDataExample), + }, + { + type: 'text', + value: `**Signer address:**`, + }, + { + type: 'copyable', + value: `${account1.address}`, + }, + ], + }, + }; + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledWith(expectedDialogParams); + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(signature1); + }); + + it('should sign a message from an unfound user account correctly', async function () { + const requestObject: SignMessageRequestParams = { + signerAddress: unfoundUserAddress, + typedDataMessage: toJson(typedDataExample), + }; + apiParams.requestParams = requestObject; + const result = await signMessage(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(signature2); + }); + + it('should throw error if getKeysFromAddress failed', async function () { + sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); + const requestObject: SignMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: undefined, // will use typedDataExample.json + }; + apiParams.requestParams = requestObject; - it('should throw an error if the signerAddress is an invalid address', async function () { - const invalidAddress = 'wrongAddress'; - const requestObject: SignMessageRequestParams = { - signerAddress: invalidAddress, - typedDataMessage: toJson(typedDataExample), - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await signMessage(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + let result; + try { + await signMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + }); + }); + }); }); }); diff --git a/packages/starknet-snap/test/src/verifySignedMessage.test.ts b/packages/starknet-snap/test/src/verifySignedMessage.test.ts index e87d5aa4..bffa615a 100644 --- a/packages/starknet-snap/test/src/verifySignedMessage.test.ts +++ b/packages/starknet-snap/test/src/verifySignedMessage.test.ts @@ -25,6 +25,7 @@ describe('Test function: verifySignedMessage', function () { networks: [STARKNET_TESTNET_NETWORK], transactions: [], }; + const apiParams: ApiParams = { state, requestParams: {}, @@ -32,6 +33,12 @@ describe('Test function: verifySignedMessage', function () { saveMutex: new Mutex(), }; + const requestObject: VerifySignedMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: undefined, // will use typedDataExample.json + signature: signature1, + }; + beforeEach(async function () { walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); apiParams.keyDeriver = await getAddressKeyDeriver(walletStub); @@ -42,81 +49,138 @@ describe('Test function: verifySignedMessage', function () { sandbox.restore(); }); - it('should verify a signed message from an user account correctly', async function () { - const requestObject: VerifySignedMessageRequestParams = { - signerAddress: account1.address, - typedDataMessage: undefined, // will use typedDataExample.json - signature: signature1, - }; - apiParams.requestParams = requestObject; - const result = await verifySignedMessage(apiParams); - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(true); - }); + describe('when request param validation fail', function () { + let invalidRequest = Object.assign({}, requestObject); - it('should verify a signed message from an unfound user account correctly', async function () { - const requestObject: VerifySignedMessageRequestParams = { - signerAddress: unfoundUserAddress, - typedDataMessage: toJson(typedDataExample), - signature: signature2, - }; - apiParams.requestParams = requestObject; - const result = await verifySignedMessage(apiParams); - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - expect(result).to.be.eql(true); - }); + afterEach(async function () { + invalidRequest = Object.assign({}, requestObject); + apiParams.requestParams = requestObject; + }); - it('should throw error if getKeysFromAddress failed', async function () { - sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); - const requestObject: VerifySignedMessageRequestParams = { - signerAddress: account1.address, - typedDataMessage: undefined, // will use typedDataExample.json - signature: signature1, - }; - apiParams.requestParams = requestObject; - - let result; - try { - await verifySignedMessage(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; - }); + it('should throw an error if the signerAddress is an invalid address', async function () { + invalidRequest.signerAddress = 'wrongAddress'; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await verifySignedMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); - it('should throw an error if the signerAddress is an invalid address', async function () { - const requestObject: VerifySignedMessageRequestParams = { - signerAddress: 'wrongAddress', - typedDataMessage: undefined, // will use typedDataExample.json - signature: signature1, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await verifySignedMessage(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + it('should throw an error if the signature is undefined', async function () { + invalidRequest.signature = undefined; + apiParams.requestParams = invalidRequest; + let result; + try { + result = await verifySignedMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + }); }); - it('should throw an error if the signature is undefined', async function () { - const requestObject: VerifySignedMessageRequestParams = { - signerAddress: account1.address, - typedDataMessage: undefined, // will use typedDataExample.json - signature: undefined, - }; - apiParams.requestParams = requestObject; - let result; - try { - result = await verifySignedMessage(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } + describe('when request param validation pass', function () { + beforeEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + afterEach(async function () { + apiParams.requestParams = Object.assign({}, requestObject); + }); + + describe('when require upgrade checking fail', function () { + it('should throw error', async function () { + const isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').throws('network error'); + let result; + try { + result = await verifySignedMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + }); + + it('should throw error if upgrade required', async function () { + let result; + try { + result = await verifySignedMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(result).to.be.an('Error'); + } + }); + }); + + describe('when account is not require upgrade', function () { + beforeEach(async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + }); + + describe('when account is cairo 0', function () { + //TODO + }); + + describe('when account is cairo 1', function () { + it('should verify a signed message from an user account correctly', async function () { + const requestObject: VerifySignedMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: undefined, // will use typedDataExample.json + signature: signature1, + }; + apiParams.requestParams = requestObject; + const result = await verifySignedMessage(apiParams); + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(true); + }); + + it('should verify a signed message from an unfound user account correctly', async function () { + const requestObject: VerifySignedMessageRequestParams = { + signerAddress: unfoundUserAddress, + typedDataMessage: toJson(typedDataExample), + signature: signature2, + }; + apiParams.requestParams = requestObject; + const result = await verifySignedMessage(apiParams); + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + expect(result).to.be.eql(true); + }); + + it('should throw error if getKeysFromAddress failed', async function () { + sandbox.stub(utils, 'getKeysFromAddress').throws(new Error()); + const requestObject: VerifySignedMessageRequestParams = { + signerAddress: account1.address, + typedDataMessage: undefined, // will use typedDataExample.json + signature: signature1, + }; + apiParams.requestParams = requestObject; + + let result; + try { + await verifySignedMessage(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + } + expect(walletStub.rpcStubs.snap_manageState).not.to.have.been.called; + }); + }); + }); }); }); diff --git a/yarn.lock b/yarn.lock index 66cf4e4c..fa514867 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1921,14 +1921,14 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.2.0 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=194891&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=f33260&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: async-mutex: ^0.3.2 ethereum-unit-converter: ^0.0.17 ethers: ^5.5.1 starknet: ^5.14.0 starknet_v4.22.0: "npm:starknet@4.22.0" - checksum: 630aa27078859add4e6f32f3946836d4469d5c4af9d1324e4df71cfa39fd99088a87de6e0aa71b80db066efe0716520aa7e15f63f17982f3bb7c75ba728c7f36 + checksum: 6457a8423d6468621a5550c2235517929c6d23012b1574ed6655a5224cdd9bf50328279079fdd66cc2b4fcdd3a017314a4429758a9051249fc2780800536d8f8 languageName: node linkType: hard From 42a1f9013ad5156a56ba7d7ce739f11a7e8f9d71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:45:40 +0800 Subject: [PATCH 3/9] chore(deps): bump word-wrap from 1.2.3 to 1.2.5 (#131) Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.5. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.5) --- updated-dependencies: - dependency-name: word-wrap dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index fa514867..6778a556 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24989,9 +24989,9 @@ __metadata: linkType: hard "word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": - version: 1.2.3 - resolution: "word-wrap@npm:1.2.3" - checksum: 30b48f91fcf12106ed3186ae4fa86a6a1842416df425be7b60485de14bec665a54a68e4b5156647dec3a70f25e84d270ca8bc8cd23182ed095f5c7206a938c1f + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: f93ba3586fc181f94afdaff3a6fef27920b4b6d9eaefed0f428f8e07adea2a7f54a5f2830ce59406c8416f033f86902b91eb824072354645eea687dff3691ccb languageName: node linkType: hard From 1a32b2918cc72b25189542afa141c2a94e592eae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:46:01 +0800 Subject: [PATCH 4/9] chore(deps): bump @adobe/css-tools from 4.2.0 to 4.3.1 (#132) Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.2.0 to 4.3.1. - [Changelog](https://github.com/adobe/css-tools/blob/main/History.md) - [Commits](https://github.com/adobe/css-tools/commits) --- updated-dependencies: - dependency-name: "@adobe/css-tools" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6778a556..0581f5ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6,9 +6,9 @@ __metadata: cacheKey: 8 "@adobe/css-tools@npm:^4.0.1": - version: 4.2.0 - resolution: "@adobe/css-tools@npm:4.2.0" - checksum: dc5cc92ba3d562e7ffddb79d6d222c7e00b65f255fd2725b3d71490ff268844be322f917415d8c4ab39eca646343b632058db8bd5b1d646193fcc94d1d3e420b + version: 4.3.1 + resolution: "@adobe/css-tools@npm:4.3.1" + checksum: ad43456379ff391132aff687ece190cb23ea69395e23c9b96690eeabe2468da89a4aaf266e4f8b6eaab53db3d1064107ce0f63c3a974e864f4a04affc768da3f languageName: node linkType: hard From be880cab2542a2b8135ddf5354ebce53efe1ac92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 18:28:28 +0800 Subject: [PATCH 5/9] chore(deps): bump semver from 7.3.8 to 7.5.2 (#130) Bumps [semver](https://github.com/npm/node-semver) from 7.3.8 to 7.5.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.3.8...v7.5.2) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/wallet-ui/package.json | 2 +- yarn.lock | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/wallet-ui/package.json b/packages/wallet-ui/package.json index fc80a729..03f7cd49 100644 --- a/packages/wallet-ui/package.json +++ b/packages/wallet-ui/package.json @@ -26,7 +26,7 @@ "react-qr-code": "^2.0.7", "react-redux": "^8.0.1", "redux-persist": "^6.0.0", - "semver": "^7.3.7", + "semver": "^7.5.2", "starknet": "^4.22.0", "styled-components": "^5.3.5", "toastr2": "^3.0.0-alpha.18", diff --git a/yarn.lock b/yarn.lock index 0581f5ad..c96d3337 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21712,6 +21712,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.2": + version: 7.5.4 + resolution: "semver@npm:7.5.4" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3 + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -24447,7 +24458,7 @@ __metadata: react-scripts: 5.0.1 redux-persist: ^6.0.0 rimraf: ^3.0.2 - semver: ^7.3.7 + semver: ^7.5.2 starknet: ^4.22.0 styled-components: ^5.3.5 toastr2: ^3.0.0-alpha.18 From 2ee8e1b0af58b2a4a9c02676ff368ddb862af31e Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Fri, 17 Nov 2023 16:35:44 +0800 Subject: [PATCH 6/9] feat: simplify snap logic --- packages/starknet-snap/src/createAccount.ts | 4 +- packages/starknet-snap/src/estimateFee.ts | 2 +- packages/starknet-snap/src/sendTransaction.ts | 13 ++-- packages/starknet-snap/src/types/snapState.ts | 1 - packages/starknet-snap/src/utils/constants.ts | 9 +-- .../starknet-snap/src/utils/starknetUtils.ts | 72 ++++++++++++------- packages/starknet-snap/test/constants.test.ts | 1 - .../test/src/createAccount.test.ts | 24 +++---- .../test/src/sendTransaction.test.ts | 6 +- 9 files changed, 75 insertions(+), 57 deletions(-) diff --git a/packages/starknet-snap/src/createAccount.ts b/packages/starknet-snap/src/createAccount.ts index 2bbe61df..18f4e1ea 100644 --- a/packages/starknet-snap/src/createAccount.ts +++ b/packages/starknet-snap/src/createAccount.ts @@ -5,7 +5,7 @@ import { deployAccount, getBalance, estimateAccountDeployFee, - isAccountAddressDeployed, + isAccountDeployed, } from './utils/starknetUtils'; import { getEtherErc20Token, @@ -72,7 +72,7 @@ export async function createAccount(params: ApiParams, silentMode = false) { }; } - const signerAssigned = await isAccountAddressDeployed(network, contractAddress); + const signerAssigned = await isAccountDeployed(network, contractAddress); if (!signerAssigned) { try { diff --git a/packages/starknet-snap/src/estimateFee.ts b/packages/starknet-snap/src/estimateFee.ts index 987b9de2..ce24c68d 100644 --- a/packages/starknet-snap/src/estimateFee.ts +++ b/packages/starknet-snap/src/estimateFee.ts @@ -65,7 +65,7 @@ export async function estimateFee(params: ApiParams) { logger.log(`estimateFee:\ntxnInvocation: ${toJson(txnInvocation)}`); //Estimate deploy account fee if the signer has not been deployed yet - const accountDeployed = await isAccountDeployed(network, publicKey); + const accountDeployed = await isAccountDeployed(network, senderAddress); let bulkTransactions: Invocations = [ { type: TransactionType.INVOKE, diff --git a/packages/starknet-snap/src/sendTransaction.ts b/packages/starknet-snap/src/sendTransaction.ts index 5c9ee72b..ce4f2bab 100644 --- a/packages/starknet-snap/src/sendTransaction.ts +++ b/packages/starknet-snap/src/sendTransaction.ts @@ -51,11 +51,12 @@ export async function sendTransaction(params: ApiParams) { throw new Error('Upgrade required'); } - const { - privateKey: senderPrivateKey, - publicKey, - addressIndex, - } = await getKeysFromAddress(keyDeriver, network, state, senderAddress); + const { privateKey: senderPrivateKey, addressIndex } = await getKeysFromAddress( + keyDeriver, + network, + state, + senderAddress, + ); let maxFee = requestParamsObj.maxFee ? num.toBigInt(requestParamsObj.maxFee) : constants.ZERO; if (maxFee === constants.ZERO) { const { suggestedMaxFee } = await estimateFee(params); @@ -88,7 +89,7 @@ export async function sendTransaction(params: ApiParams) { logger.log(`sendTransaction:\ntxnInvocation: ${toJson(txnInvocation)}\nmaxFee: ${maxFee.toString()}}`); - const accountDeployed = await isAccountDeployed(network, publicKey); + const accountDeployed = await isAccountDeployed(network, senderAddress); if (!accountDeployed) { //Deploy account before sending the transaction logger.log('sendTransaction:\nFirst transaction : send deploy transaction'); diff --git a/packages/starknet-snap/src/types/snapState.ts b/packages/starknet-snap/src/types/snapState.ts index dca1e0c9..041cfcbf 100644 --- a/packages/starknet-snap/src/types/snapState.ts +++ b/packages/starknet-snap/src/types/snapState.ts @@ -34,7 +34,6 @@ export interface Network { nodeUrl: string; voyagerUrl: string; accountClassHash: string; // in hex - accountClassHashV0?: string; // in hex useOldAccounts?: boolean; } diff --git a/packages/starknet-snap/src/utils/constants.ts b/packages/starknet-snap/src/utils/constants.ts index 3b62bcdf..a6f6496d 100644 --- a/packages/starknet-snap/src/utils/constants.ts +++ b/packages/starknet-snap/src/utils/constants.ts @@ -22,8 +22,7 @@ export const STARKNET_MAINNET_NETWORK: Network = { baseUrl: 'https://alpha-mainnet.starknet.io', nodeUrl: 'https://starknet-mainnet.infura.io/v3/60c7253fb48147658095fe0460ac9ee9', voyagerUrl: 'https://voyager.online', - accountClassHashV0: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo - accountClassHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', + accountClassHash: '', }; export const STARKNET_TESTNET_NETWORK: Network = { @@ -32,8 +31,7 @@ export const STARKNET_TESTNET_NETWORK: Network = { baseUrl: 'https://alpha4.starknet.io', nodeUrl: 'https://starknet-goerli.infura.io/v3/60c7253fb48147658095fe0460ac9ee9', voyagerUrl: 'https://goerli.voyager.online', - accountClassHashV0: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo - accountClassHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', + accountClassHash: '', }; export const STARKNET_INTEGRATION_NETWORK: Network = { @@ -42,8 +40,7 @@ export const STARKNET_INTEGRATION_NETWORK: Network = { baseUrl: 'https://external.integration.starknet.io', nodeUrl: '', voyagerUrl: '', - accountClassHashV0: '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2', // from argent-x repo - accountClassHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', + accountClassHash: '', }; export const ETHER_MAINNET: Erc20Token = { diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index c06de763..071d9ca0 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -421,6 +421,12 @@ export const getNextAddressIndex = (chainId: string, state: SnapState, derivatio return uninitializedAccount?.addressIndex ?? accounts.length; }; +/** + * calculate contract address by publicKey, supported for carioVersions [1] + * + * @param publicKey - address's publicKey. + * @returns - address and calldata. + */ export const getAccContractAddressAndCallData = (publicKey) => { const callData = CallData.compile({ signer: publicKey, @@ -438,6 +444,12 @@ export const getAccContractAddressAndCallData = (publicKey) => { }; }; +/** + * calculate contract address by publicKey, supported for carioVersions [0] + * + * @param publicKey - address's publicKey. + * @returns - address and calldata. + */ export const getAccContractAddressAndCallDataCairo0 = (publicKey) => { const callData = CallData.compile({ implementation: ACCOUNT_CLASS_HASH_V0, @@ -496,37 +508,23 @@ export const getKeysFromAddressIndex = async ( }; }; -export const isAccountDeployedCairo0 = async (network: Network, publicKey: string) => { - const { address } = getAccContractAddressAndCallDataCairo0(publicKey); - return isAccountAddressDeployed(network, address, 0); -}; - -export const isAccountDeployed = async (network: Network, publicKey: string) => { - const { address } = getAccContractAddressAndCallData(publicKey); - return isAccountAddressDeployed(network, address, 1); -}; - -export const isAccountAddressDeployed = async (network: Network, address: string, cairoVersion = 1) => { - let accountDeployed = true; +/** + * Check address is deployed by using getVersion, supported for carioVersions [0,1] + * + * @param network - Network. + * @param address - Input address. + * @returns - boolean. + */ +export const isAccountDeployed = async (network: Network, address: string) => { try { - switch (cairoVersion) { - case 0: - await getSigner(address, network); - break; - case 1: - await getOwner(address, network); - break; - default: - throw new Error(`Not supported cairo version ${cairoVersion}`); - } + await getVersion(address, network); + return true; } catch (err) { if (!err.message.includes('Contract not found')) { throw err; } - accountDeployed = false; + return false; } - - return accountDeployed; }; export const addFeesFromAllTransactions = (fees: EstimateFee[]): EstimateFee => { @@ -552,6 +550,16 @@ export const validateAndParseAddress = (address: num.BigNumberish, length = 63) return _validateAndParseAddressFn(address); }; +/** + * Find address index from the keyDeriver, supported for carioVersions [0,1] + * + * @param chainId - Network ChainId. + * @param address - Input address. + * @param keyDeriver - keyDeriver from MetaMask wallet. + * @param state - MetaMask Snap state. + * @param maxScan - Number of scaning in the keyDeriver. + * @returns - address index and cairoVersion. + */ export const findAddressIndex = async ( chainId: string, address: string, @@ -575,6 +583,13 @@ export const findAddressIndex = async ( throw new Error(`Address not found: ${address}`); }; +/** + * Check address needed upgrade by using getVersion and compare with MIN_ACC_CONTRACT_VERSION, supported for carioVersions [0,1] + * + * @param network - Network. + * @param address - Input address. + * @returns - boolean. + */ export const isUpgradeRequired = async (network: Network, address: string) => { try { logger.log(`isUpgradeRequired: address = ${address}`); @@ -590,6 +605,13 @@ export const isUpgradeRequired = async (network: Network, address: string) => { } }; +/** + * Get user address by public key, return address if the address has deployed, prioritize cario 1 over cario 0, supported for carioVersions [0,1] + * + * @param network - Network. + * @param publicKey - address's public key. + * @returns - address and address's public key. + */ export const getCorrectContractAddress = async (network: Network, publicKey: string) => { const { address: contractAddress } = getAccContractAddressAndCallData(publicKey); const { address: contractAddressCairo0 } = getAccContractAddressAndCallDataCairo0(publicKey); diff --git a/packages/starknet-snap/test/constants.test.ts b/packages/starknet-snap/test/constants.test.ts index b5d6c68c..d2c0cd0e 100644 --- a/packages/starknet-snap/test/constants.test.ts +++ b/packages/starknet-snap/test/constants.test.ts @@ -18,7 +18,6 @@ export const invalidNetwork: Network = { nodeUrl: '', voyagerUrl: '', accountClassHash: '', - accountClassHashV0: '', }; export const account1: AccContract = { diff --git a/packages/starknet-snap/test/src/createAccount.test.ts b/packages/starknet-snap/test/src/createAccount.test.ts index 2b9a8936..a2f9e00b 100644 --- a/packages/starknet-snap/test/src/createAccount.test.ts +++ b/packages/starknet-snap/test/src/createAccount.test.ts @@ -63,7 +63,7 @@ describe('Test function: createAccount', function () { }); it('should only return derived address without sending deploy txn correctly in mainnet if deploy is false', async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); const requestObject: CreateAccountRequestParams = { chainId: STARKNET_MAINNET_NETWORK.chainId, }; @@ -86,7 +86,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyMainnetResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'getBalance').callsFake(async () => { return getBalanceResp[0]; }); @@ -122,7 +122,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyMainnetResp2; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'getBalance').callsFake(async () => { return getBalanceResp[0]; }); @@ -157,7 +157,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'getBalance').callsFake(async () => { return getBalanceResp[0]; }); @@ -206,7 +206,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + sandbox.stub(utils, 'isAccountDeployed').resolves(true); sandbox.stub(utils, 'getBalance').callsFake(async () => { return getBalanceResp[0]; }); @@ -227,7 +227,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'getBalance').callsFake(async () => { return getBalanceResp[0]; }); @@ -248,7 +248,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'getBalance').callsFake(async () => { return getBalanceResp[0]; }); @@ -269,7 +269,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'getBalance').throws(new Error()); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { return estimateDeployFeeResp2; @@ -288,7 +288,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountFailedProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'callContract').resolves(getBalanceResp); sandbox.stub(utils, 'getSigner').throws(new Error()); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { @@ -309,7 +309,7 @@ describe('Test function: createAccount', function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); sandbox.stub(utils, 'callContract').resolves(getBalanceResp); sandbox.stub(utils, 'getSigner').throws(new Error()); sandbox.stub(utils, 'estimateAccountDeployFee').callsFake(async () => { @@ -328,8 +328,8 @@ describe('Test function: createAccount', function () { } }); - it('should throw error if isAccountAddressDeployed failed', async function () { - const isAccountAddressDeployedStub = sandbox.stub(utils, 'isAccountAddressDeployed').throws(new Error()); + it('should throw error if isAccountDeployed failed', async function () { + const isAccountAddressDeployedStub = sandbox.stub(utils, 'isAccountDeployed').throws(new Error()); const deployAccountStub = sandbox.stub(utils, 'deployAccount'); const estimateAccountDeployFeeStub = sandbox.stub(utils, 'estimateAccountDeployFee'); const getBalanceStub = sandbox.stub(utils, 'getBalance'); diff --git a/packages/starknet-snap/test/src/sendTransaction.test.ts b/packages/starknet-snap/test/src/sendTransaction.test.ts index c1e5ee43..96608e0e 100644 --- a/packages/starknet-snap/test/src/sendTransaction.test.ts +++ b/packages/starknet-snap/test/src/sendTransaction.test.ts @@ -226,7 +226,7 @@ describe('Test function: sendTransaction', function () { }); describe('when account is deployed', function () { beforeEach(async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + sandbox.stub(utils, 'isAccountDeployed').resolves(true); }); it('should send a transaction for transferring 10 tokens correctly', async function () { @@ -268,7 +268,7 @@ describe('Test function: sendTransaction', function () { describe('when account is deployed', function () { beforeEach(async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(true); + sandbox.stub(utils, 'isAccountDeployed').resolves(true); }); it('should send a transaction for transferring 10 tokens correctly', async function () { @@ -410,7 +410,7 @@ describe('Test function: sendTransaction', function () { describe('when account is not deployed', function () { beforeEach(async function () { - sandbox.stub(utils, 'isAccountAddressDeployed').resolves(false); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); }); it('should send a transaction for transferring 10 tokens and a transaction for deploy correctly', async function () { From b87e23df19168be693e220106a4426beccde9f2f Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:01:58 +0800 Subject: [PATCH 7/9] fix: replace hardcode class hash --- packages/starknet-snap/src/createAccount.ts | 2 -- packages/starknet-snap/src/utils/starknetUtils.ts | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/starknet-snap/src/createAccount.ts b/packages/starknet-snap/src/createAccount.ts index 18f4e1ea..b69ffff6 100644 --- a/packages/starknet-snap/src/createAccount.ts +++ b/packages/starknet-snap/src/createAccount.ts @@ -38,9 +38,7 @@ export async function createAccount(params: ApiParams, silentMode = false) { addressIndex: addressIndexInUsed, derivationPath, } = await getKeysFromAddressIndex(keyDeriver, network.chainId, state, addressIndex); - const { address: contractAddress, callData: contractCallData } = getAccContractAddressAndCallData(publicKey); - logger.log( `createAccount:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\naddressIndex = ${addressIndexInUsed}`, ); diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 071d9ca0..43944463 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -151,7 +151,7 @@ export const deployAccount = async ( const provider = getProvider(network); const account = new Account(provider, contractAddress, privateKey, '1'); const deployAccountPayload = { - classHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', + classHash: ACCOUNT_CLASS_HASH_V1, contractAddress: contractAddress, constructorCalldata: contractCallData, addressSalt, @@ -169,7 +169,7 @@ export const estimateAccountDeployFee = async ( const provider = getProvider(network); const account = new Account(provider, contractAddress, privateKey, '1'); const deployAccountPayload = { - classHash: '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003', + classHash: ACCOUNT_CLASS_HASH_V1, contractAddress: contractAddress, constructorCalldata: contractCallData, addressSalt, From ef4eb37876b911effee0eb3dd300fe1b25cde09b Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:55:48 +0800 Subject: [PATCH 8/9] fix: get version function --- packages/starknet-snap/src/utils/formatterUtils.ts | 9 +++++++++ packages/starknet-snap/src/utils/starknetUtils.ts | 6 +++++- packages/starknet-snap/test/utils/starknetUtils.test.ts | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 packages/starknet-snap/src/utils/formatterUtils.ts diff --git a/packages/starknet-snap/src/utils/formatterUtils.ts b/packages/starknet-snap/src/utils/formatterUtils.ts new file mode 100644 index 00000000..de0aabb8 --- /dev/null +++ b/packages/starknet-snap/src/utils/formatterUtils.ts @@ -0,0 +1,9 @@ +export const hexToString = (hex) => { + let str = ''; + for (let i = 0; i < hex.length; i += 2) { + const hexValue = hex.substr(i, 2); + const decimalValue = parseInt(hexValue, 16); + str += String.fromCharCode(decimalValue); + } + return str; +}; diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 43944463..2cb59311 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -46,6 +46,7 @@ import { getAddressKey } from './keyPair'; import { getAccount, getAccounts, getTransactionFromVoyagerUrl, getTransactionsFromVoyagerUrl } from './snapUtils'; import { logger } from './logger'; import { RpcV4GetTransactionReceiptResponse } from '../types/snapApi'; +import { hexToString } from './formatterUtils'; export const getCallDataArray = (callDataStr: string): string[] => { return (callDataStr ?? '') @@ -593,13 +594,16 @@ export const findAddressIndex = async ( export const isUpgradeRequired = async (network: Network, address: string) => { try { logger.log(`isUpgradeRequired: address = ${address}`); - const version = await getVersion(address, network); + const hexResp = await getVersion(address, network); + const version = hexToString(hexResp); + logger.log(`isUpgradeRequired: hexResp = ${hexResp}, version = ${version}`); const versionArr = version.split('.'); return Number(versionArr[1]) < MIN_ACC_CONTRACT_VERSION[1]; } catch (err) { if (!err.message.includes('Contract not found')) { throw err; } + logger.error(`isUpgradeRequired: error:`, err); //[TODO] if address is cario0 but not deployed we should throw error return false; } diff --git a/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index 4835126d..769ea889 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -193,13 +193,13 @@ describe('Test function: isUpgradeRequired', function () { }); it('should return true when upgrade is required', async function () { - sandbox.stub(utils, 'getVersion').callsFake(async () => '0.2.3'); + sandbox.stub(utils, 'getVersion').callsFake(async () => '0x302e322e33'); const result = await utils.isUpgradeRequired(STARKNET_TESTNET_NETWORK, userAddress); expect(result).to.be.eq(true); }); it('should return false when upgrade is not required', async function () { - sandbox.stub(utils, 'getVersion').callsFake(async () => '0.3.0'); + sandbox.stub(utils, 'getVersion').callsFake(async () => '0x302e332e30'); const result = await utils.isUpgradeRequired(STARKNET_TESTNET_NETWORK, userAddress); expect(result).to.be.eq(false); }); From bd45f5305879bf740335ca0cc5c6853360162dfa Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:17:21 +0800 Subject: [PATCH 9/9] fix: recover account for getting upgraded cairo 1 address --- .../starknet-snap/src/utils/starknetUtils.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 2cb59311..02a196de 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -623,6 +623,8 @@ export const getCorrectContractAddress = async (network: Network, publicKey: str logger.log( `getContractAddressByKey: contractAddressCario1 = ${contractAddress}\ncontractAddressCairo0 = ${contractAddressCairo0}\npublicKey = ${publicKey}`, ); + + //test if it is a cairo 1 account try { pk = await getOwner(contractAddress, network); logger.log(`getContractAddressByKey: cairo 1 contract found`); @@ -631,6 +633,23 @@ export const getCorrectContractAddress = async (network: Network, publicKey: str throw err; } logger.log(`getContractAddressByKey: cairo 1 contract not found`); + + //test if it is a upgraded cairo 0 account + try { + pk = await getOwner(contractAddressCairo0, network); + logger.log(`getContractAddressByKey: upgraded cairo 0 contract found`); + return { + address: contractAddressCairo0, + signerPubKey: pk, + }; + } catch (err) { + if (!err.message.includes('Contract not found')) { + throw err; + } + logger.log(`getContractAddressByKey: upgraded cairo 0 contract not found`); + } + + //test if it is a deployed cairo 0 account try { pk = await getSigner(contractAddressCairo0, network); logger.log(`getContractAddressByKey: cairo 0 contract found`); @@ -645,6 +664,8 @@ export const getCorrectContractAddress = async (network: Network, publicKey: str logger.log(`getContractAddressByKey: cairo 0 contract not found`); } } + + //return new/deployed cairo 1 account return { address: contractAddress, signerPubKey: pk,