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 01/25] 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 02/25] 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 03/25] 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 04/25] 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 05/25] 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 06/25] 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 07/25] 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 08/25] 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 09/25] 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, From c166dde8689639d30c51705edd79265548a8ec3a Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:39:25 +0800 Subject: [PATCH 10/25] feat: sf 549 update all method to support upgraded address (#188) * fix: recoverAccount to avoid duplicate called on isUpgradeRequired * chore: hardcode cairo version 1 to constant CAIRO_VERSION * fix: findAddressIndex should return address if the address match either cairo0 or cairo{N} * fix: getCorrectContractAddress, use getVersion instead of getOwner to determine account has deployed or not * chore: getCorrectContractAddress to return upgrade required attribute --- packages/starknet-snap/src/estimateFees.ts | 2 +- packages/starknet-snap/src/recoverAccounts.ts | 12 +- packages/starknet-snap/src/utils/constants.ts | 4 + .../starknet-snap/src/utils/starknetUtils.ts | 135 ++++---- packages/starknet-snap/test/constants.test.ts | 4 +- .../test/src/estimateFee.test.ts | 193 +++++------ .../test/src/estimateFees.test.ts | 14 +- .../test/src/extractPrivateKey.test.ts | 106 +++--- .../test/src/extractPublicKey.test.ts | 72 ++--- .../test/src/recoverAccounts.test.ts | 95 +----- .../test/src/sendTransaction.test.ts | 111 +++---- .../test/src/signMessage.test.ts | 112 +++---- .../test/src/verifySignedMessage.test.ts | 86 +++-- .../test/utils/starknetUtils.test.ts | 306 +++++++++++++----- 14 files changed, 608 insertions(+), 644 deletions(-) diff --git a/packages/starknet-snap/src/estimateFees.ts b/packages/starknet-snap/src/estimateFees.ts index 624bbd5a..74888465 100644 --- a/packages/starknet-snap/src/estimateFees.ts +++ b/packages/starknet-snap/src/estimateFees.ts @@ -20,7 +20,7 @@ export async function estimateFees(params: ApiParams) { senderAddress, senderPrivateKey, requestParamsObj.invocations, - requestParamsObj.invocationDetails ? requestParamsObj.invocationDetails : undefined, + requestParamsObj.invocationDetails, ); return fees.map((fee) => ({ diff --git a/packages/starknet-snap/src/recoverAccounts.ts b/packages/starknet-snap/src/recoverAccounts.ts index d50f9e4a..94ae4896 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 { getKeysFromAddressIndex, getCorrectContractAddress, isUpgradeRequired } from './utils/starknetUtils'; +import { getKeysFromAddressIndex, getCorrectContractAddress } from './utils/starknetUtils'; import { getNetworkFromChainId, getValidNumber, upsertAccount } from './utils/snapUtils'; import { AccContract } from './types/snapState'; import { ApiParams, RecoverAccountsRequestParams } from './types/snapApi'; @@ -29,17 +29,15 @@ export async function recoverAccounts(params: ApiParams) { state, i, ); - const { address: contractAddress, signerPubKey: signerPublicKey } = await getCorrectContractAddress( + const { address: contractAddress, signerPubKey: signerPublicKey, upgradeRequired } = await getCorrectContractAddress( network, publicKey, ); - logger.log(`recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}`); + logger.log(`recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\nisUpgradeRequired = ${upgradeRequired}`); - let _isUpgradeRequired = false; if (signerPublicKey) { - _isUpgradeRequired = await isUpgradeRequired(network, contractAddress); logger.log( - `recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\nisUpgradeRequired = ${_isUpgradeRequired}`, + `recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\n`, ); if (num.toBigInt(signerPublicKey) === num.toBigInt(publicKey)) { logger.log(`recoverAccounts: index ${i} matched\npublicKey: ${publicKey}`); @@ -57,7 +55,7 @@ export async function recoverAccounts(params: ApiParams) { derivationPath, deployTxnHash: '', chainId: network.chainId, - upgradeRequired: _isUpgradeRequired, + upgradeRequired: upgradeRequired, }; logger.log(`recoverAccounts: index ${i}\nuserAccount: ${toJson(userAccount)}`); diff --git a/packages/starknet-snap/src/utils/constants.ts b/packages/starknet-snap/src/utils/constants.ts index edca36a0..8e3cdf1e 100644 --- a/packages/starknet-snap/src/utils/constants.ts +++ b/packages/starknet-snap/src/utils/constants.ts @@ -137,3 +137,7 @@ export const PRELOADED_NETWORKS = [STARKNET_MAINNET_NETWORK, STARKNET_TESTNET_NE export const PROXY_CONTRACT_HASH = '0x25ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918'; // for cairo 0 proxy contract export const MIN_ACC_CONTRACT_VERSION = [0, 3, 0]; + +export const CAIRO_VERSION = '1'; + +export const CAIRO_VERSION_LEGACY = '0'; \ No newline at end of file diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 02a196de..d6b93efe 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -32,6 +32,7 @@ import { Abi, DeclareSignerDetails, DeployAccountSignerDetails, + CairoVersion, } from 'starknet'; import type { Hex } from '@noble/curves/abstract/utils'; import { Network, SnapState, Transaction, TransactionType } from '../types/snapState'; @@ -41,6 +42,8 @@ import { MIN_ACC_CONTRACT_VERSION, ACCOUNT_CLASS_HASH_V0, ACCOUNT_CLASS_HASH_V1, + CAIRO_VERSION, + CAIRO_VERSION_LEGACY, } from './constants'; import { getAddressKey } from './keyPair'; import { getAccount, getAccounts, getTransactionFromVoyagerUrl, getTransactionsFromVoyagerUrl } from './snapUtils'; @@ -110,7 +113,7 @@ export const estimateFee = async ( txnInvocation: Call | Call[], ): Promise => { const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey, '1'); + const account = new Account(provider, senderAddress, privateKey, CAIRO_VERSION); return account.estimateInvokeFee(txnInvocation, { blockIdentifier: 'latest' }); }; @@ -124,7 +127,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, '1'); + const account = new Account(provider, senderAddress, privateKey, CAIRO_VERSION); return account.estimateFeeBulk(txnInvocation, invocationsDetails); }; @@ -137,7 +140,7 @@ export const executeTxn = async ( invocationsDetails?: InvocationsDetails, ): Promise => { const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey, '1'); + const account = new Account(provider, senderAddress, privateKey, CAIRO_VERSION); return account.execute(txnInvocation, abis, invocationsDetails); }; @@ -150,7 +153,7 @@ export const deployAccount = async ( maxFee: num.BigNumberish, ): Promise => { const provider = getProvider(network); - const account = new Account(provider, contractAddress, privateKey, '1'); + const account = new Account(provider, contractAddress, privateKey, CAIRO_VERSION); const deployAccountPayload = { classHash: ACCOUNT_CLASS_HASH_V1, contractAddress: contractAddress, @@ -168,7 +171,7 @@ export const estimateAccountDeployFee = async ( privateKey: string | Uint8Array, ): Promise => { const provider = getProvider(network); - const account = new Account(provider, contractAddress, privateKey, '1'); + const account = new Account(provider, contractAddress, privateKey, CAIRO_VERSION); const deployAccountPayload = { classHash: ACCOUNT_CLASS_HASH_V1, contractAddress: contractAddress, @@ -193,6 +196,10 @@ export const getOwner = async (userAccAddress: string, network: Network): Promis return resp.result[0]; }; +export const getContractOwner = async (userAccAddress: string, network: Network, version: CairoVersion): Promise => { + return version === '0' ? getSigner(userAccAddress, network) : getOwner(userAccAddress, network); +}; + 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]; @@ -423,7 +430,7 @@ export const getNextAddressIndex = (chainId: string, state: SnapState, derivatio }; /** - * calculate contract address by publicKey, supported for carioVersions [1] + * calculate contract address by publicKey * * @param publicKey - address's publicKey. * @returns - address and calldata. @@ -446,12 +453,12 @@ export const getAccContractAddressAndCallData = (publicKey) => { }; /** - * calculate contract address by publicKey, supported for carioVersions [0] + * calculate contract address by publicKey * * @param publicKey - address's publicKey. * @returns - address and calldata. */ -export const getAccContractAddressAndCallDataCairo0 = (publicKey) => { +export const getAccContractAddressAndCallDataLegacy = (publicKey) => { const callData = CallData.compile({ implementation: ACCOUNT_CLASS_HASH_V0, selector: hash.getSelectorFromName('initialize'), @@ -510,7 +517,7 @@ export const getKeysFromAddressIndex = async ( }; /** - * Check address is deployed by using getVersion, supported for carioVersions [0,1] + * Check address is deployed by using getVersion * * @param network - Network. * @param address - Input address. @@ -552,7 +559,7 @@ export const validateAndParseAddress = (address: num.BigNumberish, length = 63) }; /** - * Find address index from the keyDeriver, supported for carioVersions [0,1] + * Find address index from the keyDeriver * * @param chainId - Network ChainId. * @param address - Input address. @@ -571,9 +578,9 @@ export const findAddressIndex = async ( const bigIntAddress = num.toBigInt(address); for (let i = 0; i < maxScan; i++) { const { publicKey } = await getKeysFromAddressIndex(keyDeriver, chainId, state, i); - const { address: calculatedAddress } = getAccContractAddressAndCallData(publicKey); - const { address: calculatedAddressCairo0 } = getAccContractAddressAndCallDataCairo0(publicKey); - if (num.toBigInt(calculatedAddress) === bigIntAddress || num.toBigInt(calculatedAddressCairo0) === bigIntAddress) { + const { address: calculatedAddress, addressLegacy: calculatedAddressLegacy } = getPermutationAddresses(publicKey); + + if (num.toBigInt(calculatedAddress) === bigIntAddress || num.toBigInt(calculatedAddressLegacy) === bigIntAddress) { logger.log(`findAddressIndex:\nFound address in scan: ${i} ${address}`); return { index: i, @@ -585,7 +592,23 @@ export const findAddressIndex = async ( }; /** - * Check address needed upgrade by using getVersion and compare with MIN_ACC_CONTRACT_VERSION, supported for carioVersions [0,1] + * Get address permutation by public key + * + * @param pk - Public key. + * @returns - address and addressLegacy. + */ +export const getPermutationAddresses = (pk:string) => { + const { address } = getAccContractAddressAndCallData(pk); + const { address: addressLegacy } = getAccContractAddressAndCallDataLegacy(pk); + + return { + address, + addressLegacy + } +} + +/** + * Check address needed upgrade by using getVersion and compare with MIN_ACC_CONTRACT_VERSION * * @param network - Network. * @param address - Input address. @@ -595,80 +618,74 @@ export const isUpgradeRequired = async (network: Network, address: string) => { try { logger.log(`isUpgradeRequired: address = ${address}`); 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]; + return isGTEMinVersion(hexToString(hexResp)) ? false : true; } 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 + //[TODO] if address is cairo0 but not deployed we throw error return false; } }; /** - * Get user address by public key, return address if the address has deployed, prioritize cario 1 over cario 0, supported for carioVersions [0,1] + * Compare version number with MIN_ACC_CONTRACT_VERSION + * + * @param version - version, e.g (2.3.0). + * @returns - boolean. + */ +export const isGTEMinVersion = (version: string) => { + logger.log(`isGTEMinVersion: version = ${version}`); + const versionArr = version.split('.'); + return Number(versionArr[1]) >= MIN_ACC_CONTRACT_VERSION[1]; +}; + +/** + * Get user address by public key, return address if the address has deployed * * @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); - let pk = ''; + const {address: contractAddress, addressLegacy: contractAddressLegacy} = getPermutationAddresses(publicKey) + logger.log( - `getContractAddressByKey: contractAddressCario1 = ${contractAddress}\ncontractAddressCairo0 = ${contractAddressCairo0}\npublicKey = ${publicKey}`, + `getContractAddressByKey: contractAddress = ${contractAddress}\ncontractAddressLegacy = ${contractAddressLegacy}\npublicKey = ${publicKey}`, ); - //test if it is a cairo 1 account + let address = contractAddress; + let upgradeRequired = false; + let pk = ''; + try { - pk = await getOwner(contractAddress, network); - logger.log(`getContractAddressByKey: cairo 1 contract found`); - } catch (err) { - if (!err.message.includes('Contract not found')) { - throw err; + await getVersion(contractAddress, network); + pk = await getContractOwner(address, network, CAIRO_VERSION); + } catch (e) { + if (!e.message.includes('Contract not found')) { + throw e; } - logger.log(`getContractAddressByKey: cairo 1 contract not found`); + + logger.log(`getContractAddressByKey: cairo ${CAIRO_VERSION} contract cant found, try cairo ${CAIRO_VERSION_LEGACY}`); - //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; + const version = await getVersion(contractAddressLegacy, network); + upgradeRequired = isGTEMinVersion(hexToString(version)) ? false : true; + pk = await getContractOwner(contractAddressLegacy, network, upgradeRequired ? CAIRO_VERSION_LEGACY : CAIRO_VERSION); + address = contractAddressLegacy + } catch (e) { + if (!e.message.includes('Contract not found')) { + throw e; } - 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`); - return { - address: contractAddressCairo0, - signerPubKey: pk, - }; - } catch (err) { - if (!err.message.includes('Contract not found')) { - throw err; - } - logger.log(`getContractAddressByKey: cairo 0 contract not found`); + logger.log(`getContractAddressByKey: no deployed contract found, fallback to cairo ${CAIRO_VERSION}`); } } - //return new/deployed cairo 1 account return { - address: contractAddress, + address, signerPubKey: pk, + upgradeRequired: upgradeRequired, }; }; diff --git a/packages/starknet-snap/test/constants.test.ts b/packages/starknet-snap/test/constants.test.ts index d2c0cd0e..65f56781 100644 --- a/packages/starknet-snap/test/constants.test.ts +++ b/packages/starknet-snap/test/constants.test.ts @@ -60,7 +60,7 @@ export const account4: AccContract = { chainId: constants.StarknetChainId.SN_GOERLI, }; -export const Cario1Account1: AccContract = { +export const Cairo1Account1: AccContract = { address: '0x043e3d703b005b8367a9783fb680713349c519202aa01e9beb170bdf710ae20b', addressSalt: '0x019e59f349e1aa813ab4556c5836d0472e5e1ae82d1e5c3b3e8aabfeb290befd', addressIndex: 1, @@ -99,7 +99,7 @@ export const signature1 = export const signature2 = '30440220052956ac852275b6004c4e8042450f6dce83059f068029b037cc47338c80d062022002bc0e712f03e341bb3532fc356b779d84fcb4dbfe8ed34de2db66e121971d92'; -export const signature4Cario1SignMessage = [ +export const signature4Cairo1SignMessage = [ '2941323345698930086258187297320132789256148405011604592758945785805412997864', '1024747634926675542679366527128384456926978174336360356924884281219915547518', ]; diff --git a/packages/starknet-snap/test/src/estimateFee.test.ts b/packages/starknet-snap/test/src/estimateFee.test.ts index 8be863b6..8518ae79 100644 --- a/packages/starknet-snap/test/src/estimateFee.test.ts +++ b/packages/starknet-snap/test/src/estimateFee.test.ts @@ -9,7 +9,7 @@ import { ACCOUNT_CLASS_HASH_V1, STARKNET_TESTNET_NETWORK } from '../../src/utils import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { account2, - Cario1Account1, + Cairo1Account1, estimateDeployFeeResp4, estimateFeeResp, getBip44EntropyStub, @@ -136,135 +136,94 @@ describe('Test function: estimateFee', function () { beforeEach(async function () { sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + apiParams.requestParams = { + ...apiParams.requestParams, + senderAddress: Cairo1Account1.address, + }; }); - describe('when account is cario1 address', function () { + describe('when account is deployed', function () { beforeEach(async function () { - apiParams.requestParams = { - ...apiParams.requestParams, - senderAddress: Cario1Account1.address, - }; + estimateFeeBulkStub = sandbox.stub(utils, 'estimateFeeBulk'); + sandbox.stub(utils, 'isAccountDeployed').resolves(true); }); - 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 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); }); + }); - 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); - - const { privateKey, publicKey } = await utils.getKeysFromAddress( - apiParams.keyDeriver, - STARKNET_TESTNET_NETWORK, - state, - Cario1Account1.address, - ); - const { callData } = utils.getAccContractAddressAndCallData(publicKey); - const apiRequest = apiParams.requestParams as EstimateFeeRequestParams; + describe('when account is not deployed', function () { + beforeEach(async function () { + estimateFeeStub = sandbox.stub(utils, 'estimateFee'); + sandbox.stub(utils, 'isAccountDeployed').resolves(false); + }); - const expectedBulkTransaction = [ - { - type: TransactionType.DEPLOY_ACCOUNT, - payload: { - classHash: ACCOUNT_CLASS_HASH_V1, - contractAddress: Cario1Account1.address, - constructorCalldata: callData, - addressSalt: publicKey, - }, + 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); + + const { privateKey, publicKey } = await utils.getKeysFromAddress( + apiParams.keyDeriver, + STARKNET_TESTNET_NETWORK, + state, + Cairo1Account1.address, + ); + const { callData } = utils.getAccContractAddressAndCallData(publicKey); + const apiRequest = apiParams.requestParams as EstimateFeeRequestParams; + + const expectedBulkTransaction = [ + { + type: TransactionType.DEPLOY_ACCOUNT, + payload: { + classHash: ACCOUNT_CLASS_HASH_V1, + contractAddress: Cairo1Account1.address, + constructorCalldata: callData, + addressSalt: publicKey, }, - { - type: TransactionType.INVOKE, - payload: { - contractAddress: apiRequest.contractAddress, - entrypoint: apiRequest.contractFuncName, - calldata: utils.getCallDataArray(apiRequest.contractCallData), - }, + }, + { + type: TransactionType.INVOKE, + payload: { + contractAddress: apiRequest.contractAddress, + entrypoint: apiRequest.contractFuncName, + calldata: utils.getCallDataArray(apiRequest.contractCallData), }, - ]; + }, + ]; + + expect(result.suggestedMaxFee).to.be.eq(expectedSuggestedMaxFee.toString(10)); + expect(estimateFeeStub).callCount(0); + expect(estimateFeeBulkStub).callCount(1); + expect(estimateFeeBulkStub).to.be.calledWith( + STARKNET_TESTNET_NETWORK, + Cairo1Account1.address, + privateKey, + expectedBulkTransaction, + ); + }); - expect(result.suggestedMaxFee).to.be.eq(expectedSuggestedMaxFee.toString(10)); + 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); - expect(estimateFeeBulkStub).to.be.calledWith( - STARKNET_TESTNET_NETWORK, - Cario1Account1.address, - privateKey, - expectedBulkTransaction, - ); - }); - - 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); - } - }); - }); - }); - - describe('when account is cario0 address', function () { - 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 () { - //not support cario0 address if not deployed + } }); }); }); diff --git a/packages/starknet-snap/test/src/estimateFees.test.ts b/packages/starknet-snap/test/src/estimateFees.test.ts index 5e004d03..d481eb80 100644 --- a/packages/starknet-snap/test/src/estimateFees.test.ts +++ b/packages/starknet-snap/test/src/estimateFees.test.ts @@ -40,11 +40,9 @@ describe('Test function: estimateFees', function () { sandbox.restore(); }); - it('should estimate the fee including deploy txn correctly', async function () { + it('should estimate fees correctly', async function () { const feeResult = [estimateDeployFeeResp2, estimateDeployFeeResp3]; - sandbox.stub(utils, 'estimateFeeBulk').callsFake(async () => { - return feeResult; - }); + sandbox.stub(utils, 'estimateFeeBulk').resolves(feeResult); apiParams.requestParams = { senderAddress: account2.address, chainId: STARKNET_TESTNET_NETWORK.chainId, @@ -58,6 +56,9 @@ describe('Test function: estimateFees', function () { }, }, ], + invocationsDetails: { + nonce: '1' + } }; const expectedResult = feeResult.map((fee) => ({ overall_fee: fee.overall_fee.toString(10) || '0', @@ -65,14 +66,13 @@ describe('Test function: estimateFees', function () { gas_price: fee.gas_price.toString(10) || '0', suggestedMaxFee: fee.suggestedMaxFee.toString(10) || '0', })); + const result = await estimateFees(apiParams); + expect(result).to.eql(expectedResult); }); it('should throw error if estimateFee failed', async function () { - sandbox.stub(utils, 'getSigner').callsFake(async () => { - return account2.publicKey; - }); sandbox.stub(utils, 'estimateFeeBulk').throws(new Error()); apiParams.requestParams = { senderAddress: account2.address, diff --git a/packages/starknet-snap/test/src/extractPrivateKey.test.ts b/packages/starknet-snap/test/src/extractPrivateKey.test.ts index 34743b98..0326a8b3 100644 --- a/packages/starknet-snap/test/src/extractPrivateKey.test.ts +++ b/packages/starknet-snap/test/src/extractPrivateKey.test.ts @@ -126,64 +126,58 @@ describe('Test function: extractPrivateKey', function () { sandbox.stub(utils, 'isUpgradeRequired').resolves(false); }); - describe('when account is cairo 0', function () { - //TODO + 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 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; - - let result; - try { - await extractPrivateKey(apiParams); - } catch (err) { - result = err; - } finally { - expect(result).to.be.an('Error'); - } - }); + 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; + + 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 88614799..a7523fa7 100644 --- a/packages/starknet-snap/test/src/extractPublicKey.test.ts +++ b/packages/starknet-snap/test/src/extractPublicKey.test.ts @@ -125,47 +125,41 @@ describe('Test function: extractPublicKey', function () { sandbox.stub(utils, 'isUpgradeRequired').resolves(false); }); - describe('when account is cairo 0', function () { - //TODO + 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 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'); - } - }); + 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/recoverAccounts.test.ts b/packages/starknet-snap/test/src/recoverAccounts.test.ts index 27264081..1386b0f8 100644 --- a/packages/starknet-snap/test/src/recoverAccounts.test.ts +++ b/packages/starknet-snap/test/src/recoverAccounts.test.ts @@ -63,17 +63,16 @@ describe('Test function: recoverAccounts', function () { const maxMissed = 3; const validPublicKeys = 2; 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] }); + .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i], upgradeRequired: false }); } else { getCorrectContractAddressStub .onCall(i) - .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO) }); + .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO), upgradeRequired: false }); } } @@ -88,7 +87,6 @@ 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)); @@ -106,17 +104,16 @@ describe('Test function: recoverAccounts', function () { const maxMissed = 3; const validPublicKeys = 2; 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] }); + .resolves({ address: testnetAccAddresses[i], signerPubKey: testnetPublicKeys[i], upgradeRequired: false }); } else { getCorrectContractAddressStub .onCall(i) - .resolves({ address: testnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO) }); + .resolves({ address: testnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO), upgradeRequired: false }); } } @@ -130,7 +127,6 @@ 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(testnetAccAddresses.slice(0, expectedCalledTimes)); @@ -143,45 +139,6 @@ describe('Test function: recoverAccounts', function () { expect(state.accContracts.length).to.be.eq(expectedCalledTimes); }); - it('should recover accounts with upgrade attr when account required upgrade', async function () { - const maxScanned = 5; - const maxMissed = 3; - const validPublicKeys = 2; - 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); - } - } - - 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(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.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; @@ -213,56 +170,22 @@ describe('Test function: recoverAccounts', function () { } }); - 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 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] }); + .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i], upgradeRequired: false }); } else { getCorrectContractAddressStub .onCall(i) - .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO) }); + .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO), upgradeRequired: false }); } } diff --git a/packages/starknet-snap/test/src/sendTransaction.test.ts b/packages/starknet-snap/test/src/sendTransaction.test.ts index 96608e0e..8e3f0b81 100644 --- a/packages/starknet-snap/test/src/sendTransaction.test.ts +++ b/packages/starknet-snap/test/src/sendTransaction.test.ts @@ -21,7 +21,7 @@ import { token2, token3, unfoundUserAddress, - Cario1Account1, + Cairo1Account1, } from '../constants.test'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { Mutex } from 'async-mutex'; @@ -73,7 +73,7 @@ describe('Test function: sendTransaction', function () { apiParams.requestParams = requestObject; }); - it('it should show error when request contractAddress is not given', async function () { + it('should show error when request contractAddress is not given', async function () { invalidRequest.contractAddress = undefined; apiParams.requestParams = invalidRequest; let result; @@ -89,7 +89,7 @@ describe('Test function: sendTransaction', function () { } }); - it('it should show error when request contractAddress is invalid', async function () { + it('should show error when request contractAddress is invalid', async function () { invalidRequest.contractAddress = '0x0'; apiParams.requestParams = invalidRequest; let result; @@ -103,7 +103,7 @@ describe('Test function: sendTransaction', function () { } }); - it('it should show error when request senderAddress is not given', async function () { + it('should show error when request senderAddress is not given', async function () { invalidRequest.senderAddress = undefined; apiParams.requestParams = invalidRequest; let result; @@ -119,7 +119,7 @@ describe('Test function: sendTransaction', function () { } }); - it('it should show error when request contractAddress is invalid', async function () { + it('should show error when request contractAddress is invalid', async function () { invalidRequest.senderAddress = '0x0'; apiParams.requestParams = invalidRequest; let result; @@ -133,7 +133,7 @@ describe('Test function: sendTransaction', function () { } }); - it('it should show error when request contractFuncName is not given', async function () { + it('should show error when request contractFuncName is not given', async function () { invalidRequest.contractFuncName = undefined; apiParams.requestParams = invalidRequest; let result; @@ -149,7 +149,7 @@ describe('Test function: sendTransaction', function () { } }); - it('it should show error when request network not found', async function () { + it('should show error when request network not found', async function () { invalidRequest.chainId = '0x0'; apiParams.requestParams = invalidRequest; let result; @@ -172,84 +172,47 @@ describe('Test function: sendTransaction', function () { apiParams.requestParams = Object.assign({}, requestObject); }); - describe('when account is cairo 0', function () { - 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'); - } - }); + 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'); + } }); + }); - 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 sendTransaction(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); }); - describe('when account do not require upgrade', function () { - let executeTxnResp; - 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; - sandbox.stub(utils, 'executeTxn').resolves(executeTxnResp); - walletStub.rpcStubs.snap_manageState.resolves(state); - walletStub.rpcStubs.snap_dialog.resolves(true); - }); - describe('when account is deployed', function () { - beforeEach(async function () { - sandbox.stub(utils, 'isAccountDeployed').resolves(true); - }); - - it('should send a transaction for transferring 10 tokens correctly', async function () { - 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 is not deployed', function () { - // not support cario0 address not deployed account - }); + 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'); + } }); }); - describe('when account is cairo 1', function () { + describe('when account do not require upgrade', function () { let executeTxnResp; let executeTxnStub: sinon.SinonStub; beforeEach(async function () { apiParams.requestParams = { ...apiParams.requestParams, - senderAddress: Cario1Account1.address, + senderAddress: Cairo1Account1.address, }; sandbox.stub(utils, 'isUpgradeRequired').resolves(false); sandbox.stub(estimateFeeSnap, 'estimateFee').resolves({ @@ -413,7 +376,7 @@ describe('Test function: sendTransaction', function () { sandbox.stub(utils, 'isAccountDeployed').resolves(false); }); - it('should send a transaction for transferring 10 tokens and a transaction for deploy correctly', async function () { + it('send a transaction for transferring 10 tokens and a transaction for deploy correctly', async function () { sandbox.stub(utils, 'deployAccount').callsFake(async () => { return createAccountProxyResp; }); diff --git a/packages/starknet-snap/test/src/signMessage.test.ts b/packages/starknet-snap/test/src/signMessage.test.ts index 2174004f..0fd93173 100644 --- a/packages/starknet-snap/test/src/signMessage.test.ts +++ b/packages/starknet-snap/test/src/signMessage.test.ts @@ -8,12 +8,11 @@ import typedDataExample from '../../src/typedData/typedDataExample.json'; import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; import { account1, - Cario1Account1, + Cairo1Account1, getBip44EntropyStub, signature4SignMessageWithUnfoundAddress, unfoundUserAddress, - signature4SignMessage, - signature4Cario1SignMessage, + signature4Cairo1SignMessage, } from '../constants.test'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import * as utils from '../../src/utils/starknetUtils'; @@ -102,69 +101,69 @@ describe('Test function: signMessage', function () { apiParams.requestParams = Object.assign({}, requestObject); }); - describe('when account is cairo 0', function () { - 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'); - } - }); + it('skip dialog if enableAutherize is false or omit', async function () { + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + const paramsObject = apiParams.requestParams as SignMessageRequestParams; + + paramsObject.enableAutherize = false; + await signMessage(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.callCount(0); + + paramsObject.enableAutherize = undefined; + await signMessage(apiParams); + expect(walletStub.rpcStubs.snap_dialog).to.have.been.callCount(0); + + paramsObject.enableAutherize = true; + }); + + 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 require upgrade', function () { + let isUpgradeRequiredStub: sinon.SinonStub; + beforeEach(async function () { + isUpgradeRequiredStub = sandbox.stub(utils, 'isUpgradeRequired').resolves(true); }); - describe('when account is not require upgrade', function () { - beforeEach(async function () { - sandbox.stub(utils, 'isUpgradeRequired').resolves(false); - }); - - it('should sign a message from an user account correctly', async function () { - 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(signature4SignMessage); - }); + 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 cairo 1', function () { + describe('when account is not require upgrade', function () { beforeEach(async function () { apiParams.requestParams = { ...apiParams.requestParams, - signerAddress: Cario1Account1.address, + signerAddress: Cairo1Account1.address, }; sandbox.stub(utils, 'isUpgradeRequired').resolves(false); }); - + it('should sign a message from an user account correctly', async function () { 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(signature4Cario1SignMessage); + expect(result).to.be.eql(signature4Cairo1SignMessage); }); it('should sign a message from an unfound user account correctly', async function () { @@ -198,20 +197,5 @@ describe('Test function: signMessage', function () { expect(result).to.be.eql(false); }); }); - - it('should skip dialog if enableAutherize is false or omit', async function () { - sandbox.stub(utils, 'isUpgradeRequired').resolves(false); - const paramsObject = apiParams.requestParams as SignMessageRequestParams; - - paramsObject.enableAutherize = false; - await signMessage(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.callCount(0); - - paramsObject.enableAutherize = undefined; - await signMessage(apiParams); - expect(walletStub.rpcStubs.snap_dialog).to.have.been.callCount(0); - - paramsObject.enableAutherize = true; - }); }); }); diff --git a/packages/starknet-snap/test/src/verifySignedMessage.test.ts b/packages/starknet-snap/test/src/verifySignedMessage.test.ts index bffa615a..07c88c5e 100644 --- a/packages/starknet-snap/test/src/verifySignedMessage.test.ts +++ b/packages/starknet-snap/test/src/verifySignedMessage.test.ts @@ -132,54 +132,48 @@ describe('Test function: verifySignedMessage', function () { sandbox.stub(utils, 'isUpgradeRequired').resolves(false); }); - describe('when account is cairo 0', function () { - //TODO + 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 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; - }); + 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/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index 769ea889..84076c72 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -3,7 +3,7 @@ import sinon from 'sinon'; import sinonChai from 'sinon-chai'; import { WalletMock } from '../wallet.mock.test'; import * as utils from '../../src/utils/starknetUtils'; -import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; +import { STARKNET_TESTNET_NETWORK, CAIRO_VERSION, CAIRO_VERSION_LEGACY } from '../../src/utils/constants'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { getTxnFromVoyagerResp1, @@ -11,13 +11,67 @@ import { getBip44EntropyStub, account1, account2, + account3 } from '../constants.test'; import { SnapState } from '../../src/types/snapState'; import { Calldata, num } from 'starknet'; +import { hexToString } from '../../src/utils/formatterUtils'; chai.use(sinonChai); const sandbox = sinon.createSandbox(); +describe('Test function: findAddressIndex', function () { + const state: SnapState = { + accContracts: [], + erc20Tokens: [], + networks: [STARKNET_TESTNET_NETWORK], + transactions: [], + }; + + beforeEach(function () { + sandbox.stub(utils, 'getKeysFromAddressIndex').resolves({ + privateKey: 'pk', + publicKey: 'pubkey', + addressIndex: 1, + derivationPath: `m / bip32:1' / bip32:1' / bip32:1' / bip32:1'` + }) + sandbox.stub(utils, 'getPermutationAddresses').returns({ + address: account1.address, addressLegacy: account2.address}) + }) + + afterEach(function () { + sandbox.restore(); + }); + + it('should find address index', async function () { + const result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account1.address, 'div', state, 1) + expect(result).to.be.contains({ + index: 0, + cairoVersion: 1, + }) + }); + + it('should find address index address match account legacy', async function () { + const result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account2.address, 'div', state, 1) + expect(result).to.be.contains({ + index: 0, + cairoVersion: 0, + }) + }); + + it('should throw error if address not found', async function () { + let result = null; + try { + result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account3.address, 'div', state, 1) + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result?.message).to.be.eq(`Address not found: ${account3.address}`); + } + }); +}); + describe('Test function: callContract', function () { const walletStub = new WalletMock(); const userAddress = '0x27f204588cadd08a7914f6a9808b34de0cbfc4cb53aa053663e7fd3a34dbc26'; @@ -124,6 +178,31 @@ describe('Test function: validateAndParseAddress', function () { }); }); +describe('Test function: getPermutationAddresses', function () { + let getAccContractAddressAndCallDataStub: sinon.SinonStub; + let getAccContractAddressAndCallDataLegacy: sinon.SinonStub; + const PK = 'PK'; + + beforeEach(function () { + getAccContractAddressAndCallDataStub = sandbox.stub(utils, 'getAccContractAddressAndCallData').returns({address: account1.address, callData: [] as Calldata}); + getAccContractAddressAndCallDataLegacy = sandbox.stub(utils, 'getAccContractAddressAndCallDataLegacy').returns({address: account2.address, callData: [] as Calldata}); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should return all addresses', async function () { + const result = await utils.getPermutationAddresses(PK); + expect(result).to.be.contains({ + address: account1.address, + addressLegacy: account2.address, + }); + expect(getAccContractAddressAndCallDataStub).to.have.been.calledOnceWith(PK); + expect(getAccContractAddressAndCallDataLegacy).to.have.been.calledOnceWith(PK); + }); +}); + describe('Test function: getVersion', function () { let callContractStub: sinon.SinonStub; const expected = '0.3.0'; @@ -228,99 +307,88 @@ describe('Test function: isUpgradeRequired', function () { }); }); +describe('Test function: isGTEMinVersion', function () { + const cairoVersionHex = '0x302e332e30'; + const cairoVersionLegacyHex = '302e322e30a'; + + it(`should return true when version greater than or equal to min version`, function () { + expect(utils.isGTEMinVersion(hexToString(cairoVersionHex))).to.be.eq(true) + }) + + it(`should return false when version smaller than min version`, function () { + expect(utils.isGTEMinVersion(hexToString(cairoVersionLegacyHex))).to.be.eq(false) + }) +}) + +describe('Test function: getContractOwner', function () { + let getOwnerStub: sinon.SinonStub; + let getSignerStub: sinon.SinonStub; + + beforeEach(function () { + getOwnerStub = sandbox.stub(utils, 'getOwner') + getSignerStub = sandbox.stub(utils, 'getSigner') + }); + + afterEach(function () { + sandbox.restore(); + }); + + it(`should call getOwner when cairo version is ${CAIRO_VERSION}`, async function () { + await utils.getContractOwner(account1.address, STARKNET_TESTNET_NETWORK, CAIRO_VERSION) + + expect(getOwnerStub).to.have.been.callCount(1) + expect(getSignerStub).to.have.been.callCount(0) + }) + + it(`should call getSigner when cairo version is ${CAIRO_VERSION_LEGACY}`, async function () { + await utils.getContractOwner(account1.address, STARKNET_TESTNET_NETWORK, CAIRO_VERSION_LEGACY) + + expect(getOwnerStub).to.have.been.callCount(0) + expect(getSignerStub).to.have.been.callCount(1) + }) +}) + describe('Test function: getCorrectContractAddress', function () { const walletStub = new WalletMock(); let getAccContractAddressAndCallDataStub: sinon.SinonStub; - let getAccContractAddressAndCallDataCairo0Stub: sinon.SinonStub; + let getAccContractAddressAndCallDataLegacyStub: sinon.SinonStub; let getOwnerStub: sinon.SinonStub; let getSignerStub: sinon.SinonStub; + let getVersionStub: sinon.SinonStub; + const PK = 'pk'; + const cairoVersionHex = '0x302e332e30'; + const cairoVersionLegacyHex = '302e322e30a'; 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); + .returns(({ address: account1.address, callData: [] as Calldata })); + getAccContractAddressAndCallDataLegacyStub = sandbox + .stub(utils, 'getAccContractAddressAndCallDataLegacy') + .returns(({ address: account2.address, callData: [] as Calldata })); }); + afterEach(function () { walletStub.reset(); sandbox.restore(); }); - it('should permutation both Cairo0 and Cario1 address', async function () { + it(`should permutation both Cairo${CAIRO_VERSION_LEGACY} and Cairo${CAIRO_VERSION} address`, async function () { + sandbox.stub(utils, 'getOwner').callsFake(async () => PK); + sandbox.stub(utils, 'getSigner').callsFake(async () => PK); + sandbox.stub(utils, 'getVersion').callsFake(async () => cairoVersionHex); + await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); expect(getAccContractAddressAndCallDataStub).to.have.been.calledOnceWith(PK); - expect(getAccContractAddressAndCallDataCairo0Stub).to.have.been.calledOnceWith(PK); + expect(getAccContractAddressAndCallDataLegacyStub).to.have.been.calledOnceWith(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 })); + it('should throw error when getOwner is throwing unknown error', async function () { + sandbox.stub(utils, 'getVersion').resolves(cairoVersionHex); + getOwnerStub = sandbox.stub(utils, 'getOwner').rejects(new Error('network error for getOwner')); 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); @@ -335,19 +403,11 @@ describe('Test function: getCorrectContractAddress', function () { }); 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'); - }); + sandbox.stub(utils, 'getVersion') + .withArgs(account1.address, STARKNET_TESTNET_NETWORK).rejects(new Error('Contract not found')) + .withArgs(account2.address, STARKNET_TESTNET_NETWORK).resolves(cairoVersionLegacyHex); + + getSignerStub = sandbox.stub(utils, 'getSigner').rejects(new Error('network error for getSigner')); let result = null; try { @@ -355,10 +415,84 @@ describe('Test function: getCorrectContractAddress', function () { } 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'); } }); + + describe(`when contact is Cairo${CAIRO_VERSION} has deployed`, function () { + it(`should return Cairo${CAIRO_VERSION} address with pubic key`, async function () { + getVersionStub = sandbox.stub(utils, 'getVersion').resolves(cairoVersionHex); + getSignerStub = sandbox.stub(utils, 'getSigner').resolves(PK); + getOwnerStub = sandbox.stub(utils, 'getOwner').resolves(PK); + + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + expect(getVersionStub).to.have.been.calledOnceWith(account1.address, STARKNET_TESTNET_NETWORK); + 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); + expect(result.upgradeRequired).to.be.eq(false); + }); + }); + + describe(`when contact is Cairo${CAIRO_VERSION} has not deployed`, function () { + describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has deployed`, function () { + describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has upgraded`, function () { + it(`should return Cairo${CAIRO_VERSION_LEGACY} address with upgrade = false`, async function () { + sandbox.stub(utils, 'getVersion') + .withArgs(account1.address, STARKNET_TESTNET_NETWORK).rejects(new Error('Contract not found')) + .withArgs(account2.address, STARKNET_TESTNET_NETWORK).resolves(cairoVersionHex); + + getSignerStub = sandbox.stub(utils, 'getSigner').resolves(PK); + getOwnerStub = sandbox.stub(utils, 'getOwner').resolves(PK); + + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + + expect(getOwnerStub).to.have.been.calledOnceWith(account2.address, STARKNET_TESTNET_NETWORK); + expect(getSignerStub).to.have.been.callCount(0); + expect(result.address).to.be.eq(account2.address); + expect(result.signerPubKey).to.be.eq(PK); + expect(result.upgradeRequired).to.be.eq(false); + }); + }); + + describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has not upgraded`, function () { + it(`should return Cairo${CAIRO_VERSION_LEGACY} address with upgrade = true`, async function () { + sandbox.stub(utils, 'getVersion') + .withArgs(account1.address, STARKNET_TESTNET_NETWORK).rejects(new Error('Contract not found')) + .withArgs(account2.address, STARKNET_TESTNET_NETWORK).resolves(cairoVersionLegacyHex); + + getSignerStub = sandbox.stub(utils, 'getSigner').resolves(PK); + getOwnerStub = sandbox.stub(utils, 'getOwner').resolves(PK); + + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + + expect(getSignerStub).to.have.been.calledOnceWith(account2.address, STARKNET_TESTNET_NETWORK); + expect(getOwnerStub).to.have.been.callCount(0); + expect(result.address).to.be.eq(account2.address); + expect(result.signerPubKey).to.be.eq(PK); + expect(result.upgradeRequired).to.be.eq(true); + }); + }); + }); + + describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has not deployed`, function () { + it(`should return Cairo${CAIRO_VERSION} address with upgrade = false`, async function () { + sandbox.stub(utils, 'getVersion').rejects(new Error('Contract not found')); + + getSignerStub = sandbox.stub(utils, 'getSigner').resolves(PK); + getOwnerStub = sandbox.stub(utils, 'getOwner').resolves(PK); + + const result = await utils.getCorrectContractAddress(STARKNET_TESTNET_NETWORK, PK); + + expect(getSignerStub).to.have.been.callCount(0); + expect(getOwnerStub).to.have.been.callCount(0); + expect(result.address).to.be.eq(account1.address); + expect(result.signerPubKey).to.be.eq(''); + expect(result.upgradeRequired).to.be.eq(false); + }); + }); + }); }); From eb5f4d73d5e0938e2e27f666488dca4cfdae17a4 Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:48:29 +0800 Subject: [PATCH 11/25] feat: sf-540 add account contract upgrade api (#189) * fix: fix the get correct account method * feat: add cairo upgrade api * fix: rollback package.json * fix: est fee in upgrade contract --- packages/starknet-snap/index.html | 26 ++ .../openrpc/starknet_snap_api_openrpc.json | 43 +++ packages/starknet-snap/snap.manifest.json | 2 +- packages/starknet-snap/src/estimateFee.ts | 4 +- packages/starknet-snap/src/index.ts | 5 + packages/starknet-snap/src/recoverAccounts.ts | 15 +- packages/starknet-snap/src/types/snapApi.ts | 5 + .../starknet-snap/src/upgradeAccContract.ts | 121 ++++++++ packages/starknet-snap/src/utils/constants.ts | 6 +- .../starknet-snap/src/utils/starknetUtils.ts | 102 ++++--- .../test/src/estimateFee.test.ts | 4 +- .../test/src/estimateFees.test.ts | 4 +- .../test/src/recoverAccounts.test.ts | 22 +- .../test/src/signMessage.test.ts | 2 +- .../test/src/upgradeAccContract.test.ts | 285 ++++++++++++++++++ .../test/utils/starknetUtils.test.ts | 124 +++++--- 16 files changed, 670 insertions(+), 100 deletions(-) create mode 100644 packages/starknet-snap/src/upgradeAccContract.ts create mode 100644 packages/starknet-snap/test/src/upgradeAccContract.test.ts diff --git a/packages/starknet-snap/index.html b/packages/starknet-snap/index.html index 2913a5a9..5b8b031e 100644 --- a/packages/starknet-snap/index.html +++ b/packages/starknet-snap/index.html @@ -359,6 +359,15 @@

Hello, Snaps!

+
+
+ Upgrade Cairo Version from 0 to 1 + +
+ +
+
+ + + diff --git a/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json b/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json index 1f157d72..48c8da36 100644 --- a/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json +++ b/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json @@ -367,6 +367,49 @@ }, "errors": [] }, + { + "name": "starkNet_upgradeAccContract", + "summary": "Upgrade Account Contract", + "paramStructure": "by-name", + "params": [ + { + "name": "contractAddress", + "summary": "Address of the target contract", + "description": "Address of the target contract", + "required": true, + "schema": { + "$ref": "#/components/schemas/ADDRESS" + } + }, + { + "name": "maxFee", + "summary": "Maximum gas fee allowed from the sender", + "description": "Maximum gas fee allowed from the sender (if not given, the max fee will be automatically calculated)", + "required": false, + "schema": { + "$ref": "#/components/schemas/NUM_AS_HEX" + } + }, + { + "name": "chainId", + "summary": "Id of the target Starknet network", + "description": "Id of the target Starknet network (default to Starknet Goerli Testnet)", + "required": false, + "schema": { + "$ref": "#/components/schemas/CHAIN_ID" + } + } + ], + "result": { + "name": "result", + "summary": "Response from Starknet’s \"gateway/call_contract\" endpoint", + "description": "Response from Starknet’s \"gateway/call_contract\" endpoint", + "schema": { + "$ref": "#/components/schemas/SEND_TRANSACTION_RESULT" + } + }, + "errors": [] + }, { "name": "starkNet_getTransactionStatus", "summary": "Gets the status of a transaction", diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index d8bfd5b1..cbf3cfe8 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": "yqSLTJy0WyWa+iX4Yx61DMnY7hrA8BdnBJwNEOAy2I8=", + "shasum": "trcJhKhhrNafDtnlSm85pA8J64JXwQ0NXLwnBgn7NSE=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/src/estimateFee.ts b/packages/starknet-snap/src/estimateFee.ts index eb1a1c99..c917c6e4 100644 --- a/packages/starknet-snap/src/estimateFee.ts +++ b/packages/starknet-snap/src/estimateFee.ts @@ -13,7 +13,7 @@ import { isAccountDeployed, isUpgradeRequired, } from './utils/starknetUtils'; -import { ACCOUNT_CLASS_HASH_V1 } from './utils/constants'; +import { ACCOUNT_CLASS_HASH } from './utils/constants'; import { logger } from './utils/logger'; export async function estimateFee(params: ApiParams) { @@ -75,7 +75,7 @@ export async function estimateFee(params: ApiParams) { if (!accountDeployed) { const { callData } = getAccContractAddressAndCallData(publicKey); const deployAccountpayload = { - classHash: ACCOUNT_CLASS_HASH_V1, + classHash: ACCOUNT_CLASS_HASH, contractAddress: senderAddress, constructorCalldata: callData, addressSalt: publicKey, diff --git a/packages/starknet-snap/src/index.ts b/packages/starknet-snap/src/index.ts index f2fd8910..29749f51 100644 --- a/packages/starknet-snap/src/index.ts +++ b/packages/starknet-snap/src/index.ts @@ -38,6 +38,7 @@ import { estimateFees } from './estimateFees'; import { declareContract } from './declareContract'; import { signDeclareTransaction } from './signDeclareTransaction'; import { signDeployAccountTransaction } from './signDeployAccountTransaction'; +import { upgradeAccContract } from './upgradeAccContract'; import { logger } from './utils/logger'; declare const snap; @@ -198,6 +199,10 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ origin, request }) => apiParams.keyDeriver = await getAddressKeyDeriver(snap); return declareContract(apiParams); + case 'starkNet_upgradeAccContract': + apiParams.keyDeriver = await getAddressKeyDeriver(snap); + return upgradeAccContract(apiParams); + default: throw new Error('Method not found.'); } diff --git a/packages/starknet-snap/src/recoverAccounts.ts b/packages/starknet-snap/src/recoverAccounts.ts index 94ae4896..fb4cef06 100644 --- a/packages/starknet-snap/src/recoverAccounts.ts +++ b/packages/starknet-snap/src/recoverAccounts.ts @@ -29,16 +29,17 @@ export async function recoverAccounts(params: ApiParams) { state, i, ); - const { address: contractAddress, signerPubKey: signerPublicKey, upgradeRequired } = await getCorrectContractAddress( - network, - publicKey, + const { + address: contractAddress, + signerPubKey: signerPublicKey, + upgradeRequired, + } = await getCorrectContractAddress(network, publicKey); + logger.log( + `recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\nisUpgradeRequired = ${upgradeRequired}`, ); - logger.log(`recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\npublicKey = ${publicKey}\nisUpgradeRequired = ${upgradeRequired}`); if (signerPublicKey) { - logger.log( - `recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\n`, - ); + logger.log(`recoverAccounts: index ${i}:\ncontractAddress = ${contractAddress}\n`); if (num.toBigInt(signerPublicKey) === num.toBigInt(publicKey)) { logger.log(`recoverAccounts: index ${i} matched\npublicKey: ${publicKey}`); } diff --git a/packages/starknet-snap/src/types/snapApi.ts b/packages/starknet-snap/src/types/snapApi.ts index 37f80539..15a5ff9a 100644 --- a/packages/starknet-snap/src/types/snapApi.ts +++ b/packages/starknet-snap/src/types/snapApi.ts @@ -102,6 +102,11 @@ export interface SendTransactionRequestParams extends BaseRequestParams { maxFee?: string; } +export interface UpgradeTransactionRequestParams extends BaseRequestParams { + contractAddress: string; + maxFee?: string; +} + export interface GetValueRequestParams extends BaseRequestParams { contractAddress: string; contractFuncName: string; diff --git a/packages/starknet-snap/src/upgradeAccContract.ts b/packages/starknet-snap/src/upgradeAccContract.ts new file mode 100644 index 00000000..a8ce04da --- /dev/null +++ b/packages/starknet-snap/src/upgradeAccContract.ts @@ -0,0 +1,121 @@ +import { toJson } from './utils/serializer'; +import { num, constants, CallData } from 'starknet'; +import { Transaction, TransactionStatus, VoyagerTransactionType } from './types/snapState'; +import { + getKeysFromAddress, + validateAndParseAddress, + isUpgradeRequired, + executeTxn, + isAccountDeployed, + estimateFee, +} from './utils/starknetUtils'; +import { getNetworkFromChainId, upsertTransaction, getSendTxnText } from './utils/snapUtils'; +import { ApiParams, UpgradeTransactionRequestParams } from './types/snapApi'; +import { ACCOUNT_CLASS_HASH, CAIRO_VERSION_LEGACY } from './utils/constants'; +import { DialogType } from '@metamask/rpc-methods'; +import { heading, panel } from '@metamask/snaps-ui'; +import { logger } from './utils/logger'; + +export async function upgradeAccContract(params: ApiParams) { + try { + const { state, wallet, saveMutex, keyDeriver, requestParams } = params; + const requestParamsObj = requestParams as UpgradeTransactionRequestParams; + const contractAddress = requestParamsObj.contractAddress; + const chainId = requestParamsObj.chainId; + + if (!contractAddress) { + throw new Error(`The given contract address need to be non-empty string, got: ${toJson(requestParamsObj)}`); + } + try { + validateAndParseAddress(contractAddress); + } catch (err) { + throw new Error(`The given contract address is invalid: ${contractAddress}`); + } + + const network = getNetworkFromChainId(state, chainId); + + if (!(await isAccountDeployed(network, contractAddress))) { + throw new Error('Contract has not deployed'); + } + + if (!(await isUpgradeRequired(network, contractAddress))) { + throw new Error('Upgrade is not required'); + } + + const { privateKey } = await getKeysFromAddress(keyDeriver, network, state, contractAddress); + + const method = 'upgrade'; + + const calldata = CallData.compile({ + implementation: ACCOUNT_CLASS_HASH, + calldata: [0], + }); + + const txnInvocation = { + contractAddress, + entrypoint: method, + calldata, + }; + + let maxFee = requestParamsObj.maxFee ? num.toBigInt(requestParamsObj.maxFee) : constants.ZERO; + if (maxFee === constants.ZERO) { + const estFeeResp = await estimateFee(network, contractAddress, privateKey, txnInvocation, CAIRO_VERSION_LEGACY); + maxFee = num.toBigInt(estFeeResp.suggestedMaxFee.toString(10) ?? '0'); + } + + const dialogComponents = getSendTxnText(state, contractAddress, method, calldata, contractAddress, maxFee, network); + + const response = await wallet.request({ + method: 'snap_dialog', + params: { + type: DialogType.Confirmation, + content: panel([heading('Do you want to sign this transaction ?'), ...dialogComponents]), + }, + }); + + if (!response) return false; + + logger.log(`sendTransaction:\ntxnInvocation: ${toJson(txnInvocation)}\nmaxFee: ${maxFee.toString()}}`); + + const txnResp = await executeTxn( + network, + contractAddress, + privateKey, + txnInvocation, + undefined, + { + maxFee, + }, + CAIRO_VERSION_LEGACY, + ); + + logger.log(`sendTransaction:\ntxnResp: ${toJson(txnResp)}`); + + if (!txnResp?.transaction_hash) { + throw new Error(`Transaction hash is not found`); + } + + const txn: Transaction = { + txnHash: txnResp.transaction_hash, + txnType: VoyagerTransactionType.INVOKE, + chainId: network.chainId, + senderAddress: contractAddress, + contractAddress, + contractFuncName: 'upgrade', + contractCallData: CallData.compile(calldata), + finalityStatus: TransactionStatus.RECEIVED, + executionStatus: TransactionStatus.RECEIVED, + status: '', //DEPRECATED LATER + failureReason: '', + eventIds: [], + timestamp: Math.floor(Date.now() / 1000), + }; + + await upsertTransaction(txn, wallet, saveMutex); + + return txnResp; + } catch (err) { + logger.error(`Problem found: ${err}`); + throw err; + } +} diff --git a/packages/starknet-snap/src/utils/constants.ts b/packages/starknet-snap/src/utils/constants.ts index 8e3cdf1e..729b15be 100644 --- a/packages/starknet-snap/src/utils/constants.ts +++ b/packages/starknet-snap/src/utils/constants.ts @@ -13,8 +13,8 @@ export const MAXIMUM_TOKEN_SYMBOL_LENGTH = 16; export const TRANSFER_SELECTOR_HEX = '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e'; -export const ACCOUNT_CLASS_HASH_V0 = '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2'; // from argent-x repo -export const ACCOUNT_CLASS_HASH_V1 = '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003'; // from argent-x repo +export const ACCOUNT_CLASS_HASH_LEGACY = '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2'; // from argent-x repo +export const ACCOUNT_CLASS_HASH = '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003'; // from argent-x repo export const STARKNET_MAINNET_NETWORK: Network = { name: 'Starknet Mainnet', @@ -140,4 +140,4 @@ export const MIN_ACC_CONTRACT_VERSION = [0, 3, 0]; export const CAIRO_VERSION = '1'; -export const CAIRO_VERSION_LEGACY = '0'; \ No newline at end of file +export const CAIRO_VERSION_LEGACY = '0'; diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index d6b93efe..fb4e4ee3 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -40,8 +40,8 @@ import { PROXY_CONTRACT_HASH, TRANSFER_SELECTOR_HEX, MIN_ACC_CONTRACT_VERSION, - ACCOUNT_CLASS_HASH_V0, - ACCOUNT_CLASS_HASH_V1, + ACCOUNT_CLASS_HASH_LEGACY, + ACCOUNT_CLASS_HASH, CAIRO_VERSION, CAIRO_VERSION_LEGACY, } from './constants'; @@ -77,6 +77,16 @@ export const getProvider = (network: Network, forceSequencer = false): Provider return new Provider(providerParam); }; +export const getAccountInstance = ( + network: Network, + userAddress: string, + privateKey: string | Uint8Array, + cairoVersion?: CairoVersion, +): Account => { + const provider = getProvider(network); + return new Account(provider, userAddress, privateKey, cairoVersion ?? CAIRO_VERSION); +}; + export const callContract = async ( network: Network, contractAddress: string, @@ -100,10 +110,12 @@ export const declareContract = async ( privateKey: string | Uint8Array, contractPayload: DeclareContractPayload, transactionsDetail?: InvocationsDetails, + cairoVersion?: CairoVersion, ): Promise => { - const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey); - return account.declare(contractPayload, transactionsDetail); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).declare( + contractPayload, + transactionsDetail, + ); }; export const estimateFee = async ( @@ -111,10 +123,11 @@ export const estimateFee = async ( senderAddress: string, privateKey: string | Uint8Array, txnInvocation: Call | Call[], + cairoVersion?: CairoVersion, ): Promise => { - const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey, CAIRO_VERSION); - return account.estimateInvokeFee(txnInvocation, { blockIdentifier: 'latest' }); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).estimateInvokeFee(txnInvocation, { + blockIdentifier: 'latest', + }); }; export const estimateFeeBulk = async ( @@ -123,12 +136,12 @@ export const estimateFeeBulk = async ( privateKey: string | Uint8Array, txnInvocation: Invocations, invocationsDetails: EstimateFeeDetails = { blockIdentifier: 'latest' }, + cairoVersion?: CairoVersion, ): Promise => { - // 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, CAIRO_VERSION); - return account.estimateFeeBulk(txnInvocation, invocationsDetails); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).estimateFeeBulk( + txnInvocation, + invocationsDetails, + ); }; export const executeTxn = async ( @@ -138,10 +151,13 @@ export const executeTxn = async ( txnInvocation: Call | Call[], abis?: Abi[], invocationsDetails?: InvocationsDetails, + cairoVersion?: CairoVersion, ): Promise => { - const provider = getProvider(network); - const account = new Account(provider, senderAddress, privateKey, CAIRO_VERSION); - return account.execute(txnInvocation, abis, invocationsDetails); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).execute( + txnInvocation, + abis, + invocationsDetails, + ); }; export const deployAccount = async ( @@ -151,16 +167,17 @@ export const deployAccount = async ( addressSalt: num.BigNumberish, privateKey: string | Uint8Array, maxFee: num.BigNumberish, + cairoVersion?: CairoVersion, ): Promise => { - const provider = getProvider(network); - const account = new Account(provider, contractAddress, privateKey, CAIRO_VERSION); const deployAccountPayload = { - classHash: ACCOUNT_CLASS_HASH_V1, + classHash: ACCOUNT_CLASS_HASH, contractAddress: contractAddress, constructorCalldata: contractCallData, addressSalt, }; - return account.deployAccount(deployAccountPayload, { maxFee }); + return getAccountInstance(network, contractAddress, privateKey, cairoVersion).deployAccount(deployAccountPayload, { + maxFee, + }); }; export const estimateAccountDeployFee = async ( @@ -169,16 +186,17 @@ export const estimateAccountDeployFee = async ( contractCallData: RawCalldata, addressSalt: num.BigNumberish, privateKey: string | Uint8Array, + cairoVersion?: CairoVersion, ): Promise => { - const provider = getProvider(network); - const account = new Account(provider, contractAddress, privateKey, CAIRO_VERSION); const deployAccountPayload = { - classHash: ACCOUNT_CLASS_HASH_V1, + classHash: ACCOUNT_CLASS_HASH, contractAddress: contractAddress, constructorCalldata: contractCallData, addressSalt, }; - return account.estimateAccountDeployFee(deployAccountPayload); + return getAccountInstance(network, contractAddress, privateKey, cairoVersion).estimateAccountDeployFee( + deployAccountPayload, + ); }; export const getSigner = async (userAccAddress: string, network: Network): Promise => { @@ -196,7 +214,11 @@ export const getOwner = async (userAccAddress: string, network: Network): Promis return resp.result[0]; }; -export const getContractOwner = async (userAccAddress: string, network: Network, version: CairoVersion): Promise => { +export const getContractOwner = async ( + userAccAddress: string, + network: Network, + version: CairoVersion, +): Promise => { return version === '0' ? getSigner(userAccAddress, network) : getOwner(userAccAddress, network); }; @@ -441,7 +463,7 @@ export const getAccContractAddressAndCallData = (publicKey) => { guardian: '0', }); - let address = hash.calculateContractAddressFromHash(publicKey, ACCOUNT_CLASS_HASH_V1, callData, 0); + let address = hash.calculateContractAddressFromHash(publicKey, ACCOUNT_CLASS_HASH, callData, 0); if (address.length < 66) { address = address.replace('0x', '0x' + '0'.repeat(66 - address.length)); @@ -460,7 +482,7 @@ export const getAccContractAddressAndCallData = (publicKey) => { */ export const getAccContractAddressAndCallDataLegacy = (publicKey) => { const callData = CallData.compile({ - implementation: ACCOUNT_CLASS_HASH_V0, + implementation: ACCOUNT_CLASS_HASH_LEGACY, selector: hash.getSelectorFromName('initialize'), calldata: CallData.compile({ signer: publicKey, guardian: '0' }), }); @@ -597,15 +619,15 @@ export const findAddressIndex = async ( * @param pk - Public key. * @returns - address and addressLegacy. */ -export const getPermutationAddresses = (pk:string) => { +export const getPermutationAddresses = (pk: string) => { const { address } = getAccContractAddressAndCallData(pk); const { address: addressLegacy } = getAccContractAddressAndCallDataLegacy(pk); return { address, - addressLegacy - } -} + addressLegacy, + }; +}; /** * Check address needed upgrade by using getVersion and compare with MIN_ACC_CONTRACT_VERSION @@ -648,8 +670,8 @@ export const isGTEMinVersion = (version: string) => { * @returns - address and address's public key. */ export const getCorrectContractAddress = async (network: Network, publicKey: string) => { - const {address: contractAddress, addressLegacy: contractAddressLegacy} = getPermutationAddresses(publicKey) - + const { address: contractAddress, addressLegacy: contractAddressLegacy } = getPermutationAddresses(publicKey); + logger.log( `getContractAddressByKey: contractAddress = ${contractAddress}\ncontractAddressLegacy = ${contractAddressLegacy}\npublicKey = ${publicKey}`, ); @@ -665,14 +687,20 @@ export const getCorrectContractAddress = async (network: Network, publicKey: str if (!e.message.includes('Contract not found')) { throw e; } - - logger.log(`getContractAddressByKey: cairo ${CAIRO_VERSION} contract cant found, try cairo ${CAIRO_VERSION_LEGACY}`); + + logger.log( + `getContractAddressByKey: cairo ${CAIRO_VERSION} contract cant found, try cairo ${CAIRO_VERSION_LEGACY}`, + ); try { const version = await getVersion(contractAddressLegacy, network); upgradeRequired = isGTEMinVersion(hexToString(version)) ? false : true; - pk = await getContractOwner(contractAddressLegacy, network, upgradeRequired ? CAIRO_VERSION_LEGACY : CAIRO_VERSION); - address = contractAddressLegacy + pk = await getContractOwner( + contractAddressLegacy, + network, + upgradeRequired ? CAIRO_VERSION_LEGACY : CAIRO_VERSION, + ); + address = contractAddressLegacy; } catch (e) { if (!e.message.includes('Contract not found')) { throw e; diff --git a/packages/starknet-snap/test/src/estimateFee.test.ts b/packages/starknet-snap/test/src/estimateFee.test.ts index 8518ae79..a1b438d7 100644 --- a/packages/starknet-snap/test/src/estimateFee.test.ts +++ b/packages/starknet-snap/test/src/estimateFee.test.ts @@ -5,7 +5,7 @@ import { WalletMock } from '../wallet.mock.test'; import * as utils from '../../src/utils/starknetUtils'; import { estimateFee } from '../../src/estimateFee'; import { SnapState } from '../../src/types/snapState'; -import { ACCOUNT_CLASS_HASH_V1, STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; +import { ACCOUNT_CLASS_HASH, STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { account2, @@ -183,7 +183,7 @@ describe('Test function: estimateFee', function () { { type: TransactionType.DEPLOY_ACCOUNT, payload: { - classHash: ACCOUNT_CLASS_HASH_V1, + classHash: ACCOUNT_CLASS_HASH, contractAddress: Cairo1Account1.address, constructorCalldata: callData, addressSalt: publicKey, diff --git a/packages/starknet-snap/test/src/estimateFees.test.ts b/packages/starknet-snap/test/src/estimateFees.test.ts index d481eb80..b96cbdc8 100644 --- a/packages/starknet-snap/test/src/estimateFees.test.ts +++ b/packages/starknet-snap/test/src/estimateFees.test.ts @@ -57,8 +57,8 @@ describe('Test function: estimateFees', function () { }, ], invocationsDetails: { - nonce: '1' - } + nonce: '1', + }, }; const expectedResult = feeResult.map((fee) => ({ overall_fee: fee.overall_fee.toString(10) || '0', diff --git a/packages/starknet-snap/test/src/recoverAccounts.test.ts b/packages/starknet-snap/test/src/recoverAccounts.test.ts index 1386b0f8..0d195e82 100644 --- a/packages/starknet-snap/test/src/recoverAccounts.test.ts +++ b/packages/starknet-snap/test/src/recoverAccounts.test.ts @@ -63,7 +63,7 @@ describe('Test function: recoverAccounts', function () { const maxMissed = 3; const validPublicKeys = 2; const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); - + for (let i = 0; i < maxScanned; i++) { if (i < validPublicKeys) { getCorrectContractAddressStub @@ -72,7 +72,11 @@ describe('Test function: recoverAccounts', function () { } else { getCorrectContractAddressStub .onCall(i) - .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO), upgradeRequired: false }); + .resolves({ + address: mainnetAccAddresses[i], + signerPubKey: num.toHex(constants.ZERO), + upgradeRequired: false, + }); } } @@ -104,7 +108,7 @@ describe('Test function: recoverAccounts', function () { const maxMissed = 3; const validPublicKeys = 2; const getCorrectContractAddressStub = sandbox.stub(utils, 'getCorrectContractAddress'); - + for (let i = 0; i < maxScanned; i++) { if (i < validPublicKeys) { getCorrectContractAddressStub @@ -113,7 +117,11 @@ describe('Test function: recoverAccounts', function () { } else { getCorrectContractAddressStub .onCall(i) - .resolves({ address: testnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO), upgradeRequired: false }); + .resolves({ + address: testnetAccAddresses[i], + signerPubKey: num.toHex(constants.ZERO), + upgradeRequired: false, + }); } } @@ -185,7 +193,11 @@ describe('Test function: recoverAccounts', function () { } else { getCorrectContractAddressStub .onCall(i) - .resolves({ address: mainnetAccAddresses[i], signerPubKey: num.toHex(constants.ZERO), upgradeRequired: false }); + .resolves({ + address: mainnetAccAddresses[i], + signerPubKey: num.toHex(constants.ZERO), + upgradeRequired: false, + }); } } diff --git a/packages/starknet-snap/test/src/signMessage.test.ts b/packages/starknet-snap/test/src/signMessage.test.ts index 0fd93173..1f7e19d1 100644 --- a/packages/starknet-snap/test/src/signMessage.test.ts +++ b/packages/starknet-snap/test/src/signMessage.test.ts @@ -158,7 +158,7 @@ describe('Test function: signMessage', function () { }; sandbox.stub(utils, 'isUpgradeRequired').resolves(false); }); - + it('should sign a message from an user account correctly', async function () { const result = await signMessage(apiParams); expect(walletStub.rpcStubs.snap_dialog).to.have.been.calledOnce; diff --git a/packages/starknet-snap/test/src/upgradeAccContract.test.ts b/packages/starknet-snap/test/src/upgradeAccContract.test.ts new file mode 100644 index 00000000..ab3d9662 --- /dev/null +++ b/packages/starknet-snap/test/src/upgradeAccContract.test.ts @@ -0,0 +1,285 @@ +import chai, { expect } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import { WalletMock } from '../wallet.mock.test'; +import * as utils from '../../src/utils/starknetUtils'; +import * as snapUtils from '../../src/utils/snapUtils'; +import { SnapState, VoyagerTransactionType, TransactionStatus } from '../../src/types/snapState'; +import { upgradeAccContract } from '../../src/upgradeAccContract'; +import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; +import { account1, estimateFeeResp, getBip44EntropyStub, sendTransactionResp } from '../constants.test'; +import { getAddressKeyDeriver } from '../../src/utils/keyPair'; +import { Mutex } from 'async-mutex'; +import { ApiParams, UpgradeTransactionRequestParams } from '../../src/types/snapApi'; +import { CAIRO_VERSION_LEGACY, ACCOUNT_CLASS_HASH } from '../../src/utils/constants'; +import { CallData, num } from 'starknet'; + +chai.use(sinonChai); +chai.use(chaiAsPromised); +const sandbox = sinon.createSandbox(); + +describe('Test function: upgradeAccContract', function () { + this.timeout(5000); + let walletStub: WalletMock; + let apiParams: ApiParams; + let state: SnapState; + + beforeEach(async function () { + const requestObject = { + contractAddress: account1.address, + chainId: STARKNET_TESTNET_NETWORK.chainId, + }; + + walletStub = new WalletMock(); + walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); + + state = { + accContracts: [account1], + erc20Tokens: [], + networks: [STARKNET_TESTNET_NETWORK], + transactions: [], + }; + + apiParams = { + state, + requestParams: requestObject, + wallet: walletStub, + saveMutex: new Mutex(), + keyDeriver: await getAddressKeyDeriver(walletStub), + }; + }); + + afterEach(function () { + walletStub.reset(); + sandbox.restore(); + }); + + describe('when validation fail', function () { + it('should show error when request contractAddress is not given', async function () { + (apiParams.requestParams as UpgradeTransactionRequestParams).contractAddress = undefined; + + let result; + try { + result = await upgradeAccContract(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('The given contract address need to be non-empty string'); + } + }); + + it('should show error when request contractAddress is invalid', async function () { + (apiParams.requestParams as UpgradeTransactionRequestParams).contractAddress = '0x0'; + + let result; + try { + result = await upgradeAccContract(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('The given contract address is invalid'); + } + }); + + it('should show error when account is not deployed', async function () { + sandbox.stub(utils, 'isAccountDeployed').resolves(false); + + let result; + try { + result = await upgradeAccContract(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('Contract has not deployed'); + } + }); + + it('should show error when account is not required to upgrade', async function () { + sandbox.stub(utils, 'isAccountDeployed').resolves(true); + sandbox.stub(utils, 'isUpgradeRequired').resolves(false); + + let result; + try { + result = await upgradeAccContract(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('Upgrade is not required'); + } + }); + }); + + describe('when validation pass', function () { + let upsertTransactionStub: sinon.SinonStub; + let executeTxnStub: sinon.SinonStub; + let estimateFeeStub: sinon.SinonStub; + + beforeEach(async function () { + sandbox.stub(utils, 'isAccountDeployed').resolves(true); + sandbox.stub(utils, 'isUpgradeRequired').resolves(true); + sandbox.stub(utils, 'getKeysFromAddress').resolves({ + privateKey: 'pk', + publicKey: account1.publicKey, + addressIndex: account1.addressIndex, + derivationPath: `m / bip32:1' / bip32:1' / bip32:1' / bip32:1'`, + }); + upsertTransactionStub = sandbox.stub(snapUtils, 'upsertTransaction'); + executeTxnStub = sandbox.stub(utils, 'executeTxn'); + estimateFeeStub = sandbox.stub(utils, 'estimateFee'); + }); + + it('should use provided max fee to execute txn when max fee provided', async function () { + (apiParams.requestParams as UpgradeTransactionRequestParams).maxFee = '10000'; + walletStub.rpcStubs.snap_dialog.resolves(true); + executeTxnStub.resolves(sendTransactionResp); + + const address = (apiParams.requestParams as UpgradeTransactionRequestParams).contractAddress; + const calldata = CallData.compile({ + implementation: ACCOUNT_CLASS_HASH, + calldata: [0], + }); + + const txnInvocation = { + contractAddress: address, + entrypoint: 'upgrade', + calldata, + }; + + const result = await upgradeAccContract(apiParams); + + expect(executeTxnStub).to.calledOnce; + expect(executeTxnStub).to.calledWith( + STARKNET_TESTNET_NETWORK, + address, + 'pk', + txnInvocation, + undefined, + { + maxFee: num.toBigInt(10000), + }, + CAIRO_VERSION_LEGACY, + ); + expect(result).to.be.equal(sendTransactionResp); + }); + + it('should use calculated max fee to execute txn when max fee not provided', async function () { + walletStub.rpcStubs.snap_dialog.resolves(true); + executeTxnStub.resolves(sendTransactionResp); + estimateFeeStub.resolves(estimateFeeResp); + + const address = (apiParams.requestParams as UpgradeTransactionRequestParams).contractAddress; + const calldata = CallData.compile({ + implementation: ACCOUNT_CLASS_HASH, + calldata: [0], + }); + + const txnInvocation = { + contractAddress: address, + entrypoint: 'upgrade', + calldata, + }; + + const result = await upgradeAccContract(apiParams); + + expect(executeTxnStub).to.calledOnce; + expect(executeTxnStub).to.calledWith( + STARKNET_TESTNET_NETWORK, + address, + 'pk', + txnInvocation, + undefined, + { + maxFee: num.toBigInt(estimateFeeResp.suggestedMaxFee), + }, + CAIRO_VERSION_LEGACY, + ); + expect(result).to.be.equal(sendTransactionResp); + }); + + it('should return executed txn result when user accept to sign the transaction', async function () { + executeTxnStub.resolves(sendTransactionResp); + estimateFeeStub.resolves(estimateFeeResp); + walletStub.rpcStubs.snap_dialog.resolves(true); + + const result = await upgradeAccContract(apiParams); + + expect(result).to.be.equal(sendTransactionResp); + expect(walletStub.rpcStubs.snap_dialog).to.calledOnce; + expect(executeTxnStub).to.calledOnce; + }); + + it('should return false when user rejected to sign the transaction', async function () { + executeTxnStub.resolves(sendTransactionResp); + estimateFeeStub.resolves(estimateFeeResp); + walletStub.rpcStubs.snap_dialog.resolves(false); + + const result = await upgradeAccContract(apiParams); + + expect(result).to.be.equal(false); + expect(walletStub.rpcStubs.snap_dialog).to.calledOnce; + expect(executeTxnStub).to.not.called; + }); + + it('should return executed txn result when execute transaction success', async function () { + executeTxnStub.resolves(sendTransactionResp); + estimateFeeStub.resolves(estimateFeeResp); + walletStub.rpcStubs.snap_dialog.resolves(true); + + const result = await upgradeAccContract(apiParams); + + expect(result).to.be.equal(sendTransactionResp); + expect(walletStub.rpcStubs.snap_dialog).to.calledOnce; + expect(executeTxnStub).to.calledOnce; + }); + + it('should throw exception when execute transaction result null', async function () { + executeTxnStub.resolves(null); + estimateFeeStub.resolves(estimateFeeResp); + walletStub.rpcStubs.snap_dialog.resolves(true); + + let result; + try { + result = await upgradeAccContract(apiParams); + } catch (err) { + result = err; + } finally { + expect(result).to.be.an('Error'); + expect(result.message).to.be.include('Transaction hash is not found'); + } + }); + + it('should save transaction when execute transaction success', async function () { + executeTxnStub.resolves(sendTransactionResp); + estimateFeeStub.resolves(estimateFeeResp); + walletStub.rpcStubs.snap_dialog.resolves(true); + const address = (apiParams.requestParams as UpgradeTransactionRequestParams).contractAddress; + const calldata = CallData.compile({ + implementation: ACCOUNT_CLASS_HASH, + calldata: [0], + }); + const txn = { + txnHash: sendTransactionResp.transaction_hash, + txnType: VoyagerTransactionType.INVOKE, + chainId: STARKNET_TESTNET_NETWORK.chainId, + senderAddress: address, + contractAddress: address, + contractFuncName: 'upgrade', + contractCallData: CallData.compile(calldata), + finalityStatus: TransactionStatus.RECEIVED, + executionStatus: TransactionStatus.RECEIVED, + status: '', + failureReason: '', + eventIds: [], + }; + + const result = await upgradeAccContract(apiParams); + expect(result).to.be.equal(sendTransactionResp); + expect(upsertTransactionStub).to.calledOnceWith(sinon.match(txn)); + }); + }); +}); diff --git a/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index 84076c72..bed67d43 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -11,15 +11,44 @@ import { getBip44EntropyStub, account1, account2, - account3 + account3, } from '../constants.test'; import { SnapState } from '../../src/types/snapState'; -import { Calldata, num } from 'starknet'; +import { Calldata, num, Account, Provider } from 'starknet'; import { hexToString } from '../../src/utils/formatterUtils'; chai.use(sinonChai); const sandbox = sinon.createSandbox(); +describe('Test function: getAccountInstance', function () { + const provider = {} as Provider; + + beforeEach(function () { + sandbox.stub(utils, 'getProvider').returns(provider); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should return account instance with default cairo version', async function () { + const result = await utils.getAccountInstance(STARKNET_TESTNET_NETWORK, account1.address, account1.publicKey); + expect(result).to.be.instanceOf(Account); + expect(result.cairoVersion).to.equal(CAIRO_VERSION); + }); + + it('should return account instance with provided cairo version', async function () { + const result = await utils.getAccountInstance( + STARKNET_TESTNET_NETWORK, + account1.address, + account1.publicKey, + CAIRO_VERSION_LEGACY, + ); + expect(result).to.be.instanceOf(Account); + expect(result.cairoVersion).to.equal(CAIRO_VERSION_LEGACY); + }); +}); + describe('Test function: findAddressIndex', function () { const state: SnapState = { accContracts: [], @@ -33,36 +62,38 @@ describe('Test function: findAddressIndex', function () { privateKey: 'pk', publicKey: 'pubkey', addressIndex: 1, - derivationPath: `m / bip32:1' / bip32:1' / bip32:1' / bip32:1'` - }) + derivationPath: `m / bip32:1' / bip32:1' / bip32:1' / bip32:1'`, + }); sandbox.stub(utils, 'getPermutationAddresses').returns({ - address: account1.address, addressLegacy: account2.address}) - }) + address: account1.address, + addressLegacy: account2.address, + }); + }); afterEach(function () { sandbox.restore(); }); it('should find address index', async function () { - const result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account1.address, 'div', state, 1) + const result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account1.address, 'div', state, 1); expect(result).to.be.contains({ index: 0, cairoVersion: 1, - }) + }); }); it('should find address index address match account legacy', async function () { - const result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account2.address, 'div', state, 1) + const result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account2.address, 'div', state, 1); expect(result).to.be.contains({ index: 0, cairoVersion: 0, - }) + }); }); it('should throw error if address not found', async function () { let result = null; try { - result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account3.address, 'div', state, 1) + result = await utils.findAddressIndex(STARKNET_TESTNET_NETWORK.chainId, account3.address, 'div', state, 1); } catch (err) { result = err; } finally { @@ -184,8 +215,12 @@ describe('Test function: getPermutationAddresses', function () { const PK = 'PK'; beforeEach(function () { - getAccContractAddressAndCallDataStub = sandbox.stub(utils, 'getAccContractAddressAndCallData').returns({address: account1.address, callData: [] as Calldata}); - getAccContractAddressAndCallDataLegacy = sandbox.stub(utils, 'getAccContractAddressAndCallDataLegacy').returns({address: account2.address, callData: [] as Calldata}); + getAccContractAddressAndCallDataStub = sandbox + .stub(utils, 'getAccContractAddressAndCallData') + .returns({ address: account1.address, callData: [] as Calldata }); + getAccContractAddressAndCallDataLegacy = sandbox + .stub(utils, 'getAccContractAddressAndCallDataLegacy') + .returns({ address: account2.address, callData: [] as Calldata }); }); afterEach(function () { @@ -312,21 +347,21 @@ describe('Test function: isGTEMinVersion', function () { const cairoVersionLegacyHex = '302e322e30a'; it(`should return true when version greater than or equal to min version`, function () { - expect(utils.isGTEMinVersion(hexToString(cairoVersionHex))).to.be.eq(true) - }) + expect(utils.isGTEMinVersion(hexToString(cairoVersionHex))).to.be.eq(true); + }); it(`should return false when version smaller than min version`, function () { - expect(utils.isGTEMinVersion(hexToString(cairoVersionLegacyHex))).to.be.eq(false) - }) -}) + expect(utils.isGTEMinVersion(hexToString(cairoVersionLegacyHex))).to.be.eq(false); + }); +}); describe('Test function: getContractOwner', function () { let getOwnerStub: sinon.SinonStub; let getSignerStub: sinon.SinonStub; beforeEach(function () { - getOwnerStub = sandbox.stub(utils, 'getOwner') - getSignerStub = sandbox.stub(utils, 'getSigner') + getOwnerStub = sandbox.stub(utils, 'getOwner'); + getSignerStub = sandbox.stub(utils, 'getSigner'); }); afterEach(function () { @@ -334,19 +369,19 @@ describe('Test function: getContractOwner', function () { }); it(`should call getOwner when cairo version is ${CAIRO_VERSION}`, async function () { - await utils.getContractOwner(account1.address, STARKNET_TESTNET_NETWORK, CAIRO_VERSION) + await utils.getContractOwner(account1.address, STARKNET_TESTNET_NETWORK, CAIRO_VERSION); - expect(getOwnerStub).to.have.been.callCount(1) - expect(getSignerStub).to.have.been.callCount(0) - }) + expect(getOwnerStub).to.have.been.callCount(1); + expect(getSignerStub).to.have.been.callCount(0); + }); it(`should call getSigner when cairo version is ${CAIRO_VERSION_LEGACY}`, async function () { - await utils.getContractOwner(account1.address, STARKNET_TESTNET_NETWORK, CAIRO_VERSION_LEGACY) + await utils.getContractOwner(account1.address, STARKNET_TESTNET_NETWORK, CAIRO_VERSION_LEGACY); - expect(getOwnerStub).to.have.been.callCount(0) - expect(getSignerStub).to.have.been.callCount(1) - }) -}) + expect(getOwnerStub).to.have.been.callCount(0); + expect(getSignerStub).to.have.been.callCount(1); + }); +}); describe('Test function: getCorrectContractAddress', function () { const walletStub = new WalletMock(); @@ -363,10 +398,10 @@ describe('Test function: getCorrectContractAddress', function () { beforeEach(function () { getAccContractAddressAndCallDataStub = sandbox .stub(utils, 'getAccContractAddressAndCallData') - .returns(({ address: account1.address, callData: [] as Calldata })); + .returns({ address: account1.address, callData: [] as Calldata }); getAccContractAddressAndCallDataLegacyStub = sandbox .stub(utils, 'getAccContractAddressAndCallDataLegacy') - .returns(({ address: account2.address, callData: [] as Calldata })); + .returns({ address: account2.address, callData: [] as Calldata }); }); afterEach(function () { @@ -403,9 +438,12 @@ describe('Test function: getCorrectContractAddress', function () { }); it('should throw error when getSigner is throwing unknown error', async function () { - sandbox.stub(utils, 'getVersion') - .withArgs(account1.address, STARKNET_TESTNET_NETWORK).rejects(new Error('Contract not found')) - .withArgs(account2.address, STARKNET_TESTNET_NETWORK).resolves(cairoVersionLegacyHex); + sandbox + .stub(utils, 'getVersion') + .withArgs(account1.address, STARKNET_TESTNET_NETWORK) + .rejects(new Error('Contract not found')) + .withArgs(account2.address, STARKNET_TESTNET_NETWORK) + .resolves(cairoVersionLegacyHex); getSignerStub = sandbox.stub(utils, 'getSigner').rejects(new Error('network error for getSigner')); @@ -441,9 +479,12 @@ describe('Test function: getCorrectContractAddress', function () { describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has deployed`, function () { describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has upgraded`, function () { it(`should return Cairo${CAIRO_VERSION_LEGACY} address with upgrade = false`, async function () { - sandbox.stub(utils, 'getVersion') - .withArgs(account1.address, STARKNET_TESTNET_NETWORK).rejects(new Error('Contract not found')) - .withArgs(account2.address, STARKNET_TESTNET_NETWORK).resolves(cairoVersionHex); + sandbox + .stub(utils, 'getVersion') + .withArgs(account1.address, STARKNET_TESTNET_NETWORK) + .rejects(new Error('Contract not found')) + .withArgs(account2.address, STARKNET_TESTNET_NETWORK) + .resolves(cairoVersionHex); getSignerStub = sandbox.stub(utils, 'getSigner').resolves(PK); getOwnerStub = sandbox.stub(utils, 'getOwner').resolves(PK); @@ -460,9 +501,12 @@ describe('Test function: getCorrectContractAddress', function () { describe(`when when is Cairo${CAIRO_VERSION_LEGACY} has not upgraded`, function () { it(`should return Cairo${CAIRO_VERSION_LEGACY} address with upgrade = true`, async function () { - sandbox.stub(utils, 'getVersion') - .withArgs(account1.address, STARKNET_TESTNET_NETWORK).rejects(new Error('Contract not found')) - .withArgs(account2.address, STARKNET_TESTNET_NETWORK).resolves(cairoVersionLegacyHex); + sandbox + .stub(utils, 'getVersion') + .withArgs(account1.address, STARKNET_TESTNET_NETWORK) + .rejects(new Error('Contract not found')) + .withArgs(account2.address, STARKNET_TESTNET_NETWORK) + .resolves(cairoVersionLegacyHex); getSignerStub = sandbox.stub(utils, 'getSigner').resolves(PK); getOwnerStub = sandbox.stub(utils, 'getOwner').resolves(PK); From c9270ac88633173f87a6ea73fc9f89b899cadbb5 Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:20:11 +0800 Subject: [PATCH 12/25] feat: sf-539 UI upgrade account (#191) --- .../starknet-snap/src/utils/starknetUtils.ts | 6 +- .../test/src/recoverAccounts.test.ts | 36 ++-- .../test/src/sendTransaction.test.ts | 2 +- packages/wallet-ui/src/App.tsx | 6 +- .../src/components/pages/Home/Home.view.tsx | 7 +- .../TransactionListItem/types.ts | 7 +- .../UpgradeModel/UpgradeModel.stories.tsx | 23 +++ .../UpgradeModel/UpgradeModel.style.ts | 64 ++++++ .../UpgradeModel/UpgradeModel.view.tsx | 122 ++++++++++++ .../ui/organism/UpgradeModel/index.ts | 1 + .../wallet-ui/src/services/useStarkNetSnap.ts | 182 +++++++++++++++++- packages/wallet-ui/src/slices/modalSlice.ts | 7 +- packages/wallet-ui/src/types/index.ts | 2 +- packages/wallet-ui/src/utils/constants.ts | 2 + packages/wallet-ui/src/utils/utils.ts | 51 +++++ yarn.lock | 4 +- 16 files changed, 480 insertions(+), 42 deletions(-) create mode 100644 packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.stories.tsx create mode 100644 packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.style.ts create mode 100644 packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.view.tsx create mode 100644 packages/wallet-ui/src/components/ui/organism/UpgradeModel/index.ts diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index de03383e..029c5c04 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -340,6 +340,8 @@ export const getMassagedTransactions = async ( const bigIntTransferSelectorHex = num.toBigInt(TRANSFER_SELECTOR_HEX); let massagedTxns = await Promise.all( txns.map(async (txn) => { + logger.log(`getMassagedTransactions: txn:\n${toJson(txn)}`); + let txnResp: GetTransactionResponse; let statusResp; try { @@ -357,7 +359,8 @@ export const getMassagedTransactions = async ( chainId: network.chainId, 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' : '', + contractFuncName: + num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex ? 'transfer' : txn.operations ?? '', contractCallData: txnResp.calldata || [], timestamp: txn.timestamp, status: '', //DEPRECATION @@ -379,6 +382,7 @@ export const getMassagedTransactions = async ( massagedTxns = massagedTxns.filter( (massagedTxn) => num.toBigInt(massagedTxn.contractAddress) === bigIntContractAddress || + massagedTxn.contractFuncName === 'upgrade' || deployTxns.find((deployTxn) => deployTxn.hash === massagedTxn.txnHash), ); } diff --git a/packages/starknet-snap/test/src/recoverAccounts.test.ts b/packages/starknet-snap/test/src/recoverAccounts.test.ts index 46c4cabe..bf2be591 100644 --- a/packages/starknet-snap/test/src/recoverAccounts.test.ts +++ b/packages/starknet-snap/test/src/recoverAccounts.test.ts @@ -74,13 +74,11 @@ describe('Test function: recoverAccounts', function () { .onCall(i) .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i], upgradeRequired: false }); } else { - getCorrectContractAddressStub - .onCall(i) - .resolves({ - address: mainnetAccAddresses[i], - signerPubKey: num.toHex(constants.ZERO), - upgradeRequired: false, - }); + getCorrectContractAddressStub.onCall(i).resolves({ + address: mainnetAccAddresses[i], + signerPubKey: num.toHex(constants.ZERO), + upgradeRequired: false, + }); } } @@ -119,13 +117,11 @@ describe('Test function: recoverAccounts', function () { .onCall(i) .resolves({ address: testnetAccAddresses[i], signerPubKey: testnetPublicKeys[i], upgradeRequired: false }); } else { - getCorrectContractAddressStub - .onCall(i) - .resolves({ - address: testnetAccAddresses[i], - signerPubKey: num.toHex(constants.ZERO), - upgradeRequired: false, - }); + getCorrectContractAddressStub.onCall(i).resolves({ + address: testnetAccAddresses[i], + signerPubKey: num.toHex(constants.ZERO), + upgradeRequired: false, + }); } } @@ -195,13 +191,11 @@ describe('Test function: recoverAccounts', function () { .onCall(i) .resolves({ address: mainnetAccAddresses[i], signerPubKey: mainnetPublicKeys[i], upgradeRequired: false }); } else { - getCorrectContractAddressStub - .onCall(i) - .resolves({ - address: mainnetAccAddresses[i], - signerPubKey: num.toHex(constants.ZERO), - upgradeRequired: false, - }); + getCorrectContractAddressStub.onCall(i).resolves({ + address: mainnetAccAddresses[i], + signerPubKey: num.toHex(constants.ZERO), + upgradeRequired: false, + }); } } diff --git a/packages/starknet-snap/test/src/sendTransaction.test.ts b/packages/starknet-snap/test/src/sendTransaction.test.ts index 8e3f0b81..9271d3f1 100644 --- a/packages/starknet-snap/test/src/sendTransaction.test.ts +++ b/packages/starknet-snap/test/src/sendTransaction.test.ts @@ -361,7 +361,7 @@ describe('Test function: sendTransaction', function () { }, { type: 'copyable', - value: 'Goerli Testnet', + value: 'Goerli Testnet (deprecated soon)', }, ], }, diff --git a/packages/wallet-ui/src/App.tsx b/packages/wallet-ui/src/App.tsx index 84bce3f1..bdf29930 100644 --- a/packages/wallet-ui/src/App.tsx +++ b/packages/wallet-ui/src/App.tsx @@ -14,6 +14,7 @@ import { ConnectModal } from 'components/ui/organism/ConnectModal'; import { PopIn } from 'components/ui/molecule/PopIn'; import { LoadingBackdrop } from 'components/ui/molecule/LoadingBackdrop'; import { ConnectInfoModal } from 'components/ui/organism/ConnectInfoModal'; +import { UpgradeModel } from 'components/ui/organism/UpgradeModel'; import 'toastr2/dist/toastr.min.css'; import { NoMetamaskModal } from 'components/ui/organism/NoMetamaskModal'; import { MinVersionModal } from './components/ui/organism/MinVersionModal'; @@ -24,7 +25,7 @@ library.add(fas, far); function App() { const { initSnap, getWalletData, checkConnection } = useStarkNetSnap(); const { connected, forceReconnect, provider } = useAppSelector((state) => state.wallet); - const { infoModalVisible, minVersionModalVisible } = useAppSelector((state) => state.modals); + const { infoModalVisible, minVersionModalVisible, upgradeModalVisible } = useAppSelector((state) => state.modals); const { loader } = useAppSelector((state) => state.UI); const networks = useAppSelector((state) => state.networks); const { accounts } = useAppSelector((state) => state.wallet); @@ -71,6 +72,9 @@ function App() { + + + {loading && {loader.loadingMessage}} diff --git a/packages/wallet-ui/src/components/pages/Home/Home.view.tsx b/packages/wallet-ui/src/components/pages/Home/Home.view.tsx index 318e190a..7be15eb6 100644 --- a/packages/wallet-ui/src/components/pages/Home/Home.view.tsx +++ b/packages/wallet-ui/src/components/pages/Home/Home.view.tsx @@ -10,14 +10,15 @@ interface Props { export const HomeView = ({ address }: Props) => { const { erc20TokenBalanceSelected, transactions } = useAppSelector((state) => state.wallet); const loader = useAppSelector((state) => state.UI.loader); + const { upgradeModalVisible } = useAppSelector((state) => state.modals); return ( - {Object.keys(erc20TokenBalanceSelected).length > 0 &&
} - - {Object.keys(transactions).length === 0 && !loader.isLoading && ( + {!upgradeModalVisible && Object.keys(erc20TokenBalanceSelected).length > 0 &&
} + {!upgradeModalVisible && } + {!upgradeModalVisible && Object.keys(transactions).length === 0 && !loader.isLoading && ( You have no transactions )} diff --git a/packages/wallet-ui/src/components/ui/molecule/TransactionsList/TransactionListItem/types.ts b/packages/wallet-ui/src/components/ui/molecule/TransactionsList/TransactionListItem/types.ts index db89eeb7..856d9435 100644 --- a/packages/wallet-ui/src/components/ui/molecule/TransactionsList/TransactionListItem/types.ts +++ b/packages/wallet-ui/src/components/ui/molecule/TransactionsList/TransactionListItem/types.ts @@ -21,6 +21,8 @@ export const getTxnName = (transaction: Transaction): string => { if (transaction.txnType.toLowerCase() === VoyagerTransactionType.INVOKE) { if (transaction.contractFuncName.toLowerCase() === 'transfer') { return 'Send'; + } else if (transaction.contractFuncName.toLowerCase() === 'upgrade') { + return 'Upgrade Account'; } } else if (transaction.txnType.toLowerCase() === VoyagerTransactionType.DEPLOY) { return 'Deploy'; @@ -92,7 +94,10 @@ export const getTxnValues = (transaction: Transaction, decimals: number = 18, to switch (txnName) { case 'Send': case 'Receive': - txnValue = ethers.utils.formatUnits(transaction.contractCallData[1].toString(), decimals); + txnValue = ethers.utils.formatUnits( + transaction.contractCallData[transaction.contractCallData.length - 2].toString(), + decimals, + ); txnUsdValue = (parseFloat(txnValue) * toUsdRate).toFixed(2); break; default: diff --git a/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.stories.tsx b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.stories.tsx new file mode 100644 index 00000000..4e3f0acb --- /dev/null +++ b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.stories.tsx @@ -0,0 +1,23 @@ +import { Meta } from '@storybook/react'; +import { useState } from 'react'; +import { PopIn } from 'components/ui/molecule/PopIn'; +import { UpgradeModelView } from './UpgradeModel.view'; + +export default { + title: 'Organism/UpgradeModel', + component: UpgradeModelView, +} as Meta; + +export const ContentOnly = () => ; + +export const WithModal = () => { + let [isOpen, setIsOpen] = useState(false); + return ( + <> + + + + + + ); +}; diff --git a/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.style.ts b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.style.ts new file mode 100644 index 00000000..4627b0db --- /dev/null +++ b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.style.ts @@ -0,0 +1,64 @@ +import styled from 'styled-components'; +import starknetSrc from 'assets/images/starknet-logo.svg'; +import { Button } from 'components/ui/atom/Button'; + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + background-color: ${(props) => props.theme.palette.grey.white}; + width: ${(props) => props.theme.modal.base}; + padding: ${(props) => props.theme.spacing.base}; + padding-top: 40px; + border-radius: 8px; + align-items: center; +`; + +export const StarknetLogo = styled.img.attrs(() => ({ + src: starknetSrc, +}))` + width: 158px; + height: 32px; + margin-bottom: 32px; +`; + +export const Title = styled.div` + text-align: center; + font-weight: ${(props) => props.theme.typography.h3.fontWeight}; + font-size: ${(props) => props.theme.typography.h3.fontSize}; + font-family: ${(props) => props.theme.typography.h3.fontFamily}; + line-height: ${(props) => props.theme.typography.h3.lineHeight}; + margin-bottom: 8px; +`; + +export const Description = styled.div` + font-size: ${(props) => props.theme.typography.p2.fontSize}; + color: ${(props) => props.theme.palette.grey.grey1}; +`; + +export const DescriptionCentered = styled(Description)` + text-align: center; + width: 264px; +`; + +export const Txnlink = styled.div` + margin-top: 12px; + font-size: ${(props) => props.theme.typography.p2.fontSize}; + color: ${(props) => props.theme.palette.primary.main}; + font-weight: ${(props) => props.theme.typography.bold.fontWeight}; + font-family: ${(props) => props.theme.typography.bold.fontFamily}; + text-decoration: underline; + cursor: pointer; +`; + +export const UpgradeButton = styled(Button).attrs((props) => ({ + textStyle: { + fontSize: props.theme.typography.p1.fontSize, + fontWeight: 900, + }, + upperCaseOnly: false, + backgroundTransparent: true, +}))` + box-shadow: 0px 14px 24px -6px rgba(106, 115, 125, 0.2); + padding-top: 16px; + padding-bottom: 16px; +`; diff --git a/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.view.tsx b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.view.tsx new file mode 100644 index 00000000..55602fdb --- /dev/null +++ b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/UpgradeModel.view.tsx @@ -0,0 +1,122 @@ +import { useEffect, useState } from 'react'; +import { useStarkNetSnap } from 'services'; +import { useAppSelector, useAppDispatch } from 'hooks/redux'; +import Toastr from 'toastr2'; + +import { setUpgradeModalVisible } from 'slices/modalSlice'; +import { openExplorerTab, shortenAddress } from '../../../../utils/utils'; +import { UpgradeButton, StarknetLogo, Title, Wrapper, DescriptionCentered, Txnlink } from './UpgradeModel.style'; + +interface Props { + address: string; +} + +enum Stage { + INIT = 0, + WAITING_FOR_TXN = 1, + SUCCESS = 2, + FAIL = 3, +} + +export const UpgradeModelView = ({ address }: Props) => { + const dispatch = useAppDispatch(); + const { upgradeAccount, waitForAccountUpdate } = useStarkNetSnap(); + const [txnHash, setTxnHash] = useState(''); + const [stage, setStage] = useState(Stage.INIT); + const networks = useAppSelector((state) => state.networks); + const chainId = networks?.items[networks.activeNetwork]?.chainId; + const toastr = new Toastr(); + + const onUpgrade = async () => { + try { + const resp = await upgradeAccount(address, '0', chainId); + + if (resp === false) { + return; + } + + if (resp.transaction_hash) { + setTxnHash(resp.transaction_hash); + } else { + throw new Error('no transaction hash'); + } + } catch (err) { + //eslint-disable-next-line no-console + console.error(err); + toastr.error(`Upgrade account failed`); + } + }; + + useEffect(() => { + if (txnHash) { + setStage(Stage.WAITING_FOR_TXN); + waitForAccountUpdate(txnHash, address, chainId) + .then((resp) => { + setStage(resp === true ? Stage.SUCCESS : Stage.FAIL); + }) + .catch(() => { + setStage(Stage.FAIL); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [txnHash, address, chainId]); + + useEffect(() => { + if (stage === Stage.SUCCESS) { + toastr.success(`Account upgraded successfully`); + dispatch(setUpgradeModalVisible(false)); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [stage, dispatch]); + + const renderComponent = () => { + switch (stage) { + case Stage.INIT: + return ( + <> + + A new version of the smart contract
+ is necessary to proceed with the Snap. +
+
+ New enhancements will come with
+ this version. +
+
+ Click on the "Upgrade" button to install it. +
+ Thank you! +
+ Upgrade + + ); + case Stage.WAITING_FOR_TXN: + return Waiting for transaction to be complete.; + case Stage.SUCCESS: + return Account upgraded successfully.; + default: + return ( + + Transaction Hash:
{' '} + openExplorerTab(txnHash, 'tx', chainId)}>{shortenAddress(txnHash)} +
+ Unfortunately, you reached the maximum number of upgrade tentatives allowed. +
+
+ Please try again in a couple of hours. +
+
+ Thank you for your comprehension. +
+ ); + } + }; + + return ( + + + Upgrade Account + {renderComponent()} + + ); +}; diff --git a/packages/wallet-ui/src/components/ui/organism/UpgradeModel/index.ts b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/index.ts new file mode 100644 index 00000000..632833b1 --- /dev/null +++ b/packages/wallet-ui/src/components/ui/organism/UpgradeModel/index.ts @@ -0,0 +1 @@ +export { UpgradeModelView as UpgradeModel } from './UpgradeModel.view'; diff --git a/packages/wallet-ui/src/services/useStarkNetSnap.ts b/packages/wallet-ui/src/services/useStarkNetSnap.ts index f1130384..04ed4b2b 100644 --- a/packages/wallet-ui/src/services/useStarkNetSnap.ts +++ b/packages/wallet-ui/src/services/useStarkNetSnap.ts @@ -1,4 +1,4 @@ -import { setInfoModalVisible, setMinVersionModalVisible } from 'slices/modalSlice'; +import { setInfoModalVisible, setMinVersionModalVisible, setUpgradeModalVisible } from 'slices/modalSlice'; import { setNetworks } from 'slices/networkSlice'; import { useAppDispatch, useAppSelector } from 'hooks/redux'; import { @@ -11,7 +11,7 @@ import { setForceReconnect, } from '../slices/walletSlice'; import Toastr from 'toastr2'; -import { addMissingPropertiesToToken } from '../utils/utils'; +import { addMissingPropertiesToToken, hexToString, retry, isGTEMinVersion } from '../utils/utils'; import { setWalletConnection } from '../slices/walletSlice'; import { Network, VoyagerTransactionType } from '../types'; import { Account } from '../types'; @@ -31,6 +31,9 @@ export const useStarkNetSnap = () => { const snapVersion = process.env.REACT_APP_SNAP_VERSION ? process.env.REACT_APP_SNAP_VERSION : '*'; const minSnapVersion = process.env.REACT_APP_MIN_SNAP_VERSION ? process.env.REACT_APP_MIN_SNAP_VERSION : '2.0.1'; const debugLevel = process.env.REACT_APP_DEBUG_LEVEL !== undefined ? process.env.REACT_APP_DEBUG_LEVEL : 'all'; + const START_SCAN_INDEX = 0; + const MAX_SCANNED = 1; + const MAX_MISSED = 1; const defaultParam = { debugLevel, @@ -114,10 +117,12 @@ export const useStarkNetSnap = () => { return tokens; }; - const recoverAccounts = async (chainId: string) => { - const START_SCAN_INDEX = 0; - const MAX_SCANNED = 1; - const MAX_MISSED = 1; + const recoverAccounts = async ( + chainId: string, + start: number = START_SCAN_INDEX, + maxScan: number = MAX_SCANNED, + maxMiss: number = MAX_MISSED, + ) => { const scannedAccounts = (await provider.request({ method: 'wallet_invokeSnap', params: { @@ -126,9 +131,9 @@ export const useStarkNetSnap = () => { method: 'starkNet_recoverAccounts', params: { ...defaultParam, - startScanIndex: START_SCAN_INDEX, - maxScanned: MAX_SCANNED, - maxMissed: MAX_MISSED, + startScanIndex: start, + maxScanned: maxScan, + maxMissed: maxMiss, chainId, }, }, @@ -163,7 +168,7 @@ export const useStarkNetSnap = () => { method: 'starkNet_createAccount', params: { ...defaultParam, - addressIndex: 0, + addressIndex: START_SCAN_INDEX, chainId, deploy: false, }, @@ -229,9 +234,14 @@ export const useStarkNetSnap = () => { } const tokens = await getTokens(chainId); let acc: Account[] | Account = await recoverAccounts(chainId); + let upgradeRequired = false; + if (!acc || acc.length === 0 || !acc[0].publicKey) { acc = await addAccount(chainId); + } else { + upgradeRequired = (Array.isArray(acc) ? acc[0].upgradeRequired : (acc as Account).upgradeRequired) ?? false; } + const tokenBalances = await Promise.all( tokens.map(async (token) => { const accountAddr = Array.isArray(acc) ? acc[0].address : acc.address; @@ -259,6 +269,7 @@ export const useStarkNetSnap = () => { if (!Array.isArray(acc)) { dispatch(setInfoModalVisible(true)); } + dispatch(setUpgradeModalVisible(upgradeRequired)); dispatch(disableLoading()); }; @@ -358,6 +369,80 @@ export const useStarkNetSnap = () => { } } + const getTransactionStatus = async (transactionHash: string, chainId: string) => { + try { + const response = await provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId, + request: { + method: 'starkNet_getTransactionStatus', + params: { + ...defaultParam, + transactionHash, + chainId, + }, + }, + }, + }); + return response; + } catch (err) { + //eslint-disable-next-line no-console + console.error(err); + } + }; + + const readContract = async (contractAddress: string, contractFuncName: string) => { + try { + const response = await provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId, + request: { + method: 'starkNet_getValue', + params: { + ...defaultParam, + contractAddress, + contractFuncName, + }, + }, + }, + }); + return response; + } catch (err) { + //eslint-disable-next-line no-console + console.error(err); + } + }; + + const upgradeAccount = async (contractAddress: string, maxFee: string, chainId: string) => { + dispatch(enableLoadingWithMessage('Upgrading account...')); + try { + const response = await provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId, + request: { + method: 'starkNet_upgradeAccContract', + params: { + ...defaultParam, + contractAddress, + maxFee, + chainId, + }, + }, + }, + }); + dispatch(disableLoading()); + return response; + } catch (err) { + dispatch(disableLoading()); + //eslint-disable-next-line no-console + console.error(err); + throw err; + } + }; + const getTransactions = async ( senderAddress: string, contractAddress: string, @@ -532,6 +617,77 @@ export const useStarkNetSnap = () => { } }; + const waitForTransaction = async (transactionHash: string, chainId: string) => { + let txStatus; + const successStates = ['ACCEPTED_ON_L2', 'ACCEPTED_ON_L1']; + const errorStates = ['REJECTED', 'NOT_RECEIVED']; + + const executeFn = async () => { + txStatus = await getTransactionStatus(transactionHash, chainId); + + if (!txStatus || !('executionStatus' in txStatus) || !('finalityStatus' in txStatus)) { + return false; + } + + if (txStatus.finalityStatus && successStates.includes(txStatus.finalityStatus)) { + return true; + } else if (txStatus.executionStatus && errorStates.includes(txStatus.executionStatus)) { + const message = txStatus.executionStatus; + throw new Error(message); + } + + return false; + }; + + await retry(executeFn); + + return txStatus; + }; + + const waitForAccountUpdate = async (transactionHash: string, accountAddress: string, chainId: string) => { + dispatch(enableLoadingWithMessage('Waiting for transaction finalize...')); + const toastr = new Toastr(); + let result = false; + + try { + // read transaction to check if the txn is ready + await waitForTransaction(transactionHash, chainId); + } catch (e) { + //eslint-disable-next-line no-console + console.log(`error while wait for transaction: ${e}`); + } + + try { + const executeFn = async (): Promise => { + // read contract to check if upgrade is required + const resp = await readContract(accountAddress, 'getVersion'); + if (!resp || !resp[0]) { + return false; + } + + if (!isGTEMinVersion(hexToString(resp[0]))) { + return false; + } + + // recover accounts to update snap state + await recoverAccounts(chainId); + return true; + }; + + result = await retry(executeFn, { + maxAttempts: 20, + }); + } catch (e: any) { + //eslint-disable-next-line no-console + console.log(`error while processing waitForAccountUpdate: ${e}`); + toastr.error('Snap is unable to verify the contract upgrade process'); + } + + dispatch(disableLoading()); + + return result; + }; + const switchNetwork = async (chainId: string) => { dispatch(enableLoadingWithMessage('Switching Network...')); try { @@ -605,7 +761,12 @@ export const useStarkNetSnap = () => { getPrivateKeyFromAddress, estimateFees, sendTransaction, + upgradeAccount, getTransactions, + getTransactionStatus, + recoverAccounts, + waitForTransaction, + waitForAccountUpdate, updateTokenBalance, getTokenBalance, addErc20Token, @@ -614,6 +775,7 @@ export const useStarkNetSnap = () => { initSnap, getWalletData, refreshTokensUSDPrice, + readContract, switchNetwork, getCurrentNetwork, getStarkName, diff --git a/packages/wallet-ui/src/slices/modalSlice.ts b/packages/wallet-ui/src/slices/modalSlice.ts index c4dcda5b..329e8314 100644 --- a/packages/wallet-ui/src/slices/modalSlice.ts +++ b/packages/wallet-ui/src/slices/modalSlice.ts @@ -3,11 +3,13 @@ import { createSlice } from '@reduxjs/toolkit'; export interface modalState { infoModalVisible: boolean; minVersionModalVisible: boolean; + upgradeModalVisible: boolean; } const initialState: modalState = { infoModalVisible: false, minVersionModalVisible: false, + upgradeModalVisible: false, }; export const modalSlice = createSlice({ @@ -18,12 +20,15 @@ export const modalSlice = createSlice({ setInfoModalVisible: (state, { payload }) => { state.infoModalVisible = payload; }, + setUpgradeModalVisible: (state, { payload }) => { + state.upgradeModalVisible = payload; + }, setMinVersionModalVisible: (state, { payload }) => { state.minVersionModalVisible = payload; }, }, }); -export const { setInfoModalVisible, setMinVersionModalVisible } = modalSlice.actions; +export const { setInfoModalVisible, setMinVersionModalVisible, setUpgradeModalVisible } = modalSlice.actions; export default modalSlice.reducer; diff --git a/packages/wallet-ui/src/types/index.ts b/packages/wallet-ui/src/types/index.ts index cc65ed4a..5a5855e3 100644 --- a/packages/wallet-ui/src/types/index.ts +++ b/packages/wallet-ui/src/types/index.ts @@ -1,7 +1,7 @@ import * as Types from '@consensys/starknet-snap/src/types/snapState'; import { BigNumber } from 'ethers'; -export type Account = Pick; +export type Account = Pick; export type Network = Pick; export interface Erc20TokenBalance extends Types.Erc20Token { diff --git a/packages/wallet-ui/src/utils/constants.ts b/packages/wallet-ui/src/utils/constants.ts index b6f81131..4c19cef2 100644 --- a/packages/wallet-ui/src/utils/constants.ts +++ b/packages/wallet-ui/src/utils/constants.ts @@ -70,3 +70,5 @@ export const TRANSACTIONS_REFRESH_FREQUENCY = 60000; export const TOKEN_BALANCE_REFRESH_FREQUENCY = 60000; export const TIMEOUT_DURATION = 10000; + +export const MIN_ACC_CONTRACT_VERSION = [0, 3, 0]; diff --git a/packages/wallet-ui/src/utils/utils.ts b/packages/wallet-ui/src/utils/utils.ts index f7ce4141..31c4acd0 100644 --- a/packages/wallet-ui/src/utils/utils.ts +++ b/packages/wallet-ui/src/utils/utils.ts @@ -7,6 +7,7 @@ import { STARKNET_SEPOLIA_TESTNET_EXPLORER, SEPOLIA_CHAINID, TIMEOUT_DURATION, + MIN_ACC_CONTRACT_VERSION, } from './constants'; import { Erc20Token, Erc20TokenBalance } from 'types'; import { constants } from 'starknet'; @@ -126,6 +127,56 @@ export const fetchWithTimeout = async (resource: string, options = { timeout: TI return response; }; +export const isGTEMinVersion = (version: string) => { + const versionArr = version.split('.'); + return Number(versionArr[1]) >= MIN_ACC_CONTRACT_VERSION[1]; +}; + +export const hexToString = (hex: string): string => { + 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; +}; + +export const wait = (delay: number) => { + return new Promise((res) => { + setTimeout(res, delay); + }); +}; + +export const retry = async ( + fn: () => Promise, + options?: { delay?: number; maxAttempts?: number; onFailedAttempt?: CallableFunction }, +): Promise => { + let retry = options?.maxAttempts ?? 10; + const delay = options?.delay ?? 1000; + + while (retry > 0) { + try { + // read contract to check if upgrade is required + const result = await fn(); + if (result) { + return result; + } + } catch (e) { + if (options?.onFailedAttempt && typeof options?.onFailedAttempt === 'function') { + options.onFailedAttempt(e); + } else { + //eslint-disable-next-line no-console + console.log(`error while processing retry: ${e}`); + } + } finally { + await wait(delay); + retry -= 1; + } + } + return false; +}; + export const shortenDomain = (domain: string, maxLength = 18) => { if (!domain) return ''; const ellipsis = '...'; diff --git a/yarn.lock b/yarn.lock index b9ff49af..d67e9376 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2034,14 +2034,14 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.4.0 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=ffefb9&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=6a2679&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: 7b4d61c524329726663d9a91210c1f91a03fd101b586c81835c1ba2912223c5c75403eb810e014fc2f3090d7ef34f20600e46f2873da9723bf644e48139423be + checksum: d36b61680e88ec96b3ba3f4003dc1427b363583bc55a03b774c995f1e029b4df9d8b2e50df2f11f3c99778ae70ccc137c17dfd69f3661876a8cded1fee390241 languageName: node linkType: hard From b0c48179480ae1de9367238ba01694bf5c89c3a7 Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:48:25 +0800 Subject: [PATCH 13/25] fix: test (#203) --- packages/starknet-snap/src/utils/constants.ts | 2 +- packages/starknet-snap/test/constants.test.ts | 14 +++++++------- .../test/src/verifySignedMessage.test.ts | 16 +--------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/starknet-snap/src/utils/constants.ts b/packages/starknet-snap/src/utils/constants.ts index 2ffd4ff0..9825055f 100644 --- a/packages/starknet-snap/src/utils/constants.ts +++ b/packages/starknet-snap/src/utils/constants.ts @@ -14,7 +14,7 @@ export const MAXIMUM_TOKEN_SYMBOL_LENGTH = 16; export const TRANSFER_SELECTOR_HEX = '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e'; export const ACCOUNT_CLASS_HASH_LEGACY = '0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2'; // from argent-x repo -export const ACCOUNT_CLASS_HASH = '0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003'; // from argent-x repo +export const ACCOUNT_CLASS_HASH = '0x29927c8af6bccf3f6fda035981e765a7bdbf18a2dc0d630494f8758aa908e2b'; // from argent-x repo export const STARKNET_MAINNET_NETWORK: Network = { name: 'Starknet Mainnet', diff --git a/packages/starknet-snap/test/constants.test.ts b/packages/starknet-snap/test/constants.test.ts index 974db05e..58440033 100644 --- a/packages/starknet-snap/test/constants.test.ts +++ b/packages/starknet-snap/test/constants.test.ts @@ -65,7 +65,7 @@ export const account4: AccContract = { }; export const Cairo1Account1: AccContract = { - address: '0x043e3d703b005b8367a9783fb680713349c519202aa01e9beb170bdf710ae20b', + address: '0x0404d766fd6db2c23177e5ea289af99e81e5c4a7badae588950ad0f8572c49b9', addressSalt: '0x019e59f349e1aa813ab4556c5836d0472e5e1ae82d1e5c3b3e8aabfeb290befd', addressIndex: 1, derivationPath: "m / bip32:44' / bip32:9004' / bip32:0' / bip32:0", @@ -104,16 +104,16 @@ export const signature2 = '30440220052956ac852275b6004c4e8042450f6dce83059f068029b037cc47338c80d062022002bc0e712f03e341bb3532fc356b779d84fcb4dbfe8ed34de2db66e121971d92'; export const signature4Cairo1SignMessage = [ - '2941323345698930086258187297320132789256148405011604592758945785805412997864', - '1024747634926675542679366527128384456926978174336360356924884281219915547518', + '1011319195017091294626264310379704228916787561535736421454347559326036897966', + '953080452563745534645084375499931001089185216376442413556466035688849743177', ]; export const signature4SignMessage = [ '784041227270069705374122994163964526105670242785431143890307285886848872447', '2211270729821731368290303126976610283184761443640531855459727543936510195980', ]; export const signature4SignMessageWithUnfoundAddress = [ - '2334603173889607172621639019166048758876763440301330097513705152117706117218', - '1236892621218974511001789530309582149943950152899985052022949903736308702610', + '1011319195017091294626264310379704228916787561535736421454347559326036897966', + '953080452563745534645084375499931001089185216376442413556466035688849743177', ]; export const signature3 = [ @@ -406,9 +406,9 @@ export const estimateFeeResp2 = { suggestedMaxFee: num.toBigInt('0x14a5d6744ed9'), }; -export const unfoundUserAddress = '0x07bb462e1cf8f2eead6a86464fecac1fad84b66da8078fe39d24bdf7c8504070'; +export const unfoundUserAddress = '0x0404d766fd6db2c23177e5ea289af99e81e5c4a7badae588950ad0f8572c49b9'; export const unfoundUserPrivateKey = '0x38d36fc25592257d913d143d37e12533dba9f6721db6fa954ed513b0dc3d68b'; -export const unfoundUserPublicKey = '0x4b36a2b0a1e9d2af3416914798de776e37d9e0ab9a50d2dec30485dca64bb8'; +export const unfoundUserPublicKey = '0x154c7b20442ee954f50831702ca844ec185ad484c21719575d351583deec90b'; export const foundUserPrivateKey = '0x3cddbb7f3694ce84bd9598820834015d979d78e63474a5b00e59b41b0563f4e'; export const testnetPublicKeys = [ diff --git a/packages/starknet-snap/test/src/verifySignedMessage.test.ts b/packages/starknet-snap/test/src/verifySignedMessage.test.ts index 07c88c5e..a54c1492 100644 --- a/packages/starknet-snap/test/src/verifySignedMessage.test.ts +++ b/packages/starknet-snap/test/src/verifySignedMessage.test.ts @@ -1,13 +1,11 @@ -import { toJson } from '../../src/utils/serializer'; import chai, { expect } from 'chai'; import sinon from 'sinon'; import sinonChai from 'sinon-chai'; import { WalletMock } from '../wallet.mock.test'; import { SnapState } from '../../src/types/snapState'; -import typedDataExample from '../../src/typedData/typedDataExample.json'; import { verifySignedMessage } from '../../src/verifySignedMessage'; import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; -import { account1, getBip44EntropyStub, signature1, signature2, unfoundUserAddress } from '../constants.test'; +import { account1, getBip44EntropyStub, signature1 } from '../constants.test'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import * as utils from '../../src/utils/starknetUtils'; import { Mutex } from 'async-mutex'; @@ -144,18 +142,6 @@ describe('Test function: verifySignedMessage', function () { 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 = { From c8276d09e43427d21c80c106582427328f357f2f Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:08:28 +0800 Subject: [PATCH 14/25] chore: rebase --- packages/starknet-snap/snap.manifest.json | 2 +- .../starknet-snap/src/upgradeAccContract.ts | 2 +- yarn.lock | 24 +++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index 5a73fd31..3a61d861 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": "A3cyal6PKAzEQQ3ptdBUK106XMp5jQVh4kydh87u7AA=", + "shasum": "ZwPyHnhMDYijYmftGu7W4ICZevMlPk8x2fZdX0mWiFA=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/src/upgradeAccContract.ts b/packages/starknet-snap/src/upgradeAccContract.ts index a8ce04da..d9f3bd11 100644 --- a/packages/starknet-snap/src/upgradeAccContract.ts +++ b/packages/starknet-snap/src/upgradeAccContract.ts @@ -13,7 +13,7 @@ import { getNetworkFromChainId, upsertTransaction, getSendTxnText } from './util import { ApiParams, UpgradeTransactionRequestParams } from './types/snapApi'; import { ACCOUNT_CLASS_HASH, CAIRO_VERSION_LEGACY } from './utils/constants'; import { DialogType } from '@metamask/rpc-methods'; -import { heading, panel } from '@metamask/snaps-ui'; +import { heading, panel } from '@metamask/snaps-sdk'; import { logger } from './utils/logger'; export async function upgradeAccContract(params: ApiParams) { diff --git a/yarn.lock b/yarn.lock index 34481b57..7b298c0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3155,7 +3155,7 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.5.2 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=180251&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=a45d25&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: "@metamask/snaps-sdk": 3.0.1 async-mutex: ^0.3.2 @@ -3163,7 +3163,7 @@ __metadata: ethers: ^5.5.1 starknet: ^5.14.0 starknet_v4.22.0: "npm:starknet@4.22.0" - checksum: 983f842b9dba2bec7cfcd8907f9dd8d420befc61e045b7138f1786f1662ff6c4f3279b8db49193cf2ae39dff0b9173a5475d1b072f918fda72d54d9d05d4bfd5 + checksum: 50a6dbd61c5bbc462e35dcb12099d664e9b324e278ce95ee8dab72320c4acdbb77fedad2f942deaed64449398b437ac025949d9cf58b6ea7ff6ee07f14f02c68 languageName: node linkType: hard @@ -23911,6 +23911,26 @@ __metadata: languageName: node linkType: hard +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 + languageName: node + linkType: hard + +"semver@npm:^7.5.2, semver@npm:^7.5.4": + version: 7.6.0 + resolution: "semver@npm:7.6.0" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 7427f05b70786c696640edc29fdd4bc33b2acf3bbe1740b955029044f80575fc664e1a512e4113c3af21e767154a94b4aa214bf6cd6e42a1f6dba5914e0b208c + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" From 213b2e28d4d913a3d720095c202aee320acbd218 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 10 Apr 2024 18:37:53 +0800 Subject: [PATCH 15/25] chore: update conflict --- packages/starknet-snap/test/constants.test.ts | 2 +- .../starknet-snap/test/src/declareContract.test.ts | 10 +++++----- .../starknet-snap/test/src/extractPrivateKey.test.ts | 4 ++-- .../starknet-snap/test/src/extractPublicKey.test.ts | 4 ++-- .../test/src/signDeclareTransaction.test.ts | 2 +- .../test/src/signDeployAccountTransaction.test.ts | 2 +- .../starknet-snap/test/src/signTransaction.test.ts | 2 +- .../test/src/upgradeAccContract.test.ts | 12 ++++++------ 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/starknet-snap/test/constants.test.ts b/packages/starknet-snap/test/constants.test.ts index e15bcd50..a4c9199b 100644 --- a/packages/starknet-snap/test/constants.test.ts +++ b/packages/starknet-snap/test/constants.test.ts @@ -1,5 +1,5 @@ import { JsonBIP44CoinTypeNode } from '@metamask/key-tree'; -import { num } from 'starknet'; +import { num, constants } from 'starknet'; import { AccContract, Erc20Token, diff --git a/packages/starknet-snap/test/src/declareContract.test.ts b/packages/starknet-snap/test/src/declareContract.test.ts index 95064743..385e55e1 100644 --- a/packages/starknet-snap/test/src/declareContract.test.ts +++ b/packages/starknet-snap/test/src/declareContract.test.ts @@ -31,7 +31,7 @@ describe('Test function: declareContract', function () { }; const requestObject: DeclareContractRequestParams = { - chainId: STARKNET_TESTNET_NETWORK.chainId, + chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, senderAddress: account1.address, contractPayload: { contract: 'TestContract', @@ -63,7 +63,7 @@ describe('Test function: declareContract', function () { const result = await declareContract(apiParams); const { privateKey } = await utils.getKeysFromAddress( apiParams.keyDeriver, - STARKNET_TESTNET_NETWORK, + STARKNET_SEPOLIA_TESTNET_NETWORK, state, account1.address, ); @@ -74,7 +74,7 @@ describe('Test function: declareContract', function () { }); expect(declareContractStub).to.have.been.calledOnce; expect(declareContractStub).to.have.been.calledWith( - STARKNET_TESTNET_NETWORK, + STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address, privateKey, { contract: 'TestContract' }, @@ -86,7 +86,7 @@ describe('Test function: declareContract', function () { const declareContractStub = sandbox.stub(utils, 'declareContract').rejects('error'); const { privateKey } = await utils.getKeysFromAddress( apiParams.keyDeriver, - STARKNET_TESTNET_NETWORK, + STARKNET_SEPOLIA_TESTNET_NETWORK, state, account1.address, ); @@ -99,7 +99,7 @@ describe('Test function: declareContract', function () { expect(result).to.be.an('Error'); expect(declareContractStub).to.have.been.calledOnce; expect(declareContractStub).to.have.been.calledWith( - STARKNET_TESTNET_NETWORK, + STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address, privateKey, { contract: 'TestContract' }, diff --git a/packages/starknet-snap/test/src/extractPrivateKey.test.ts b/packages/starknet-snap/test/src/extractPrivateKey.test.ts index 396ff8d1..b7f0e591 100644 --- a/packages/starknet-snap/test/src/extractPrivateKey.test.ts +++ b/packages/starknet-snap/test/src/extractPrivateKey.test.ts @@ -96,7 +96,7 @@ describe('Test function: extractPrivateKey', function () { } catch (err) { result = err; } finally { - expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address); expect(result).to.be.an('Error'); } }); @@ -115,7 +115,7 @@ describe('Test function: extractPrivateKey', function () { } catch (err) { result = err; } finally { - expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address); 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 b5ea2604..8dd022fd 100644 --- a/packages/starknet-snap/test/src/extractPublicKey.test.ts +++ b/packages/starknet-snap/test/src/extractPublicKey.test.ts @@ -95,7 +95,7 @@ describe('Test function: extractPublicKey', function () { } catch (err) { result = err; } finally { - expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address); expect(result).to.be.an('Error'); } }); @@ -114,7 +114,7 @@ describe('Test function: extractPublicKey', function () { } catch (err) { result = err; } finally { - expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_TESTNET_NETWORK, account1.address); + expect(isUpgradeRequiredStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address); expect(result).to.be.an('Error'); } }); diff --git a/packages/starknet-snap/test/src/signDeclareTransaction.test.ts b/packages/starknet-snap/test/src/signDeclareTransaction.test.ts index eaab125f..e82d6dcf 100644 --- a/packages/starknet-snap/test/src/signDeclareTransaction.test.ts +++ b/packages/starknet-snap/test/src/signDeclareTransaction.test.ts @@ -32,7 +32,7 @@ describe('Test function: signDeclareTransaction', function () { }; const requestObject: SignDeclareTransactionRequestParams = { - chainId: STARKNET_TESTNET_NETWORK.chainId, + chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, signerAddress: account1.address, transaction: { classHash: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', diff --git a/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts b/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts index 36c67e95..2c5e49a1 100644 --- a/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts +++ b/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts @@ -32,7 +32,7 @@ describe('Test function: signDeployAccountTransaction', function () { }; const requestObject: SignDeployAccountTransactionRequestParams = { - chainId: STARKNET_TESTNET_NETWORK.chainId, + chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, signerAddress: account1.address, transaction: { classHash: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', diff --git a/packages/starknet-snap/test/src/signTransaction.test.ts b/packages/starknet-snap/test/src/signTransaction.test.ts index 2943c970..76a7883d 100644 --- a/packages/starknet-snap/test/src/signTransaction.test.ts +++ b/packages/starknet-snap/test/src/signTransaction.test.ts @@ -32,7 +32,7 @@ describe('Test function: signMessage', function () { }; const requestObject: SignTransactionRequestParams = { - chainId: STARKNET_TESTNET_NETWORK.chainId, + chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, signerAddress: account1.address, transactions: [ { diff --git a/packages/starknet-snap/test/src/upgradeAccContract.test.ts b/packages/starknet-snap/test/src/upgradeAccContract.test.ts index ab3d9662..bd3ed17a 100644 --- a/packages/starknet-snap/test/src/upgradeAccContract.test.ts +++ b/packages/starknet-snap/test/src/upgradeAccContract.test.ts @@ -7,7 +7,7 @@ import * as utils from '../../src/utils/starknetUtils'; import * as snapUtils from '../../src/utils/snapUtils'; import { SnapState, VoyagerTransactionType, TransactionStatus } from '../../src/types/snapState'; import { upgradeAccContract } from '../../src/upgradeAccContract'; -import { STARKNET_TESTNET_NETWORK } from '../../src/utils/constants'; +import { STARKNET_SEPOLIA_TESTNET_NETWORK } from '../../src/utils/constants'; import { account1, estimateFeeResp, getBip44EntropyStub, sendTransactionResp } from '../constants.test'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { Mutex } from 'async-mutex'; @@ -28,7 +28,7 @@ describe('Test function: upgradeAccContract', function () { beforeEach(async function () { const requestObject = { contractAddress: account1.address, - chainId: STARKNET_TESTNET_NETWORK.chainId, + chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, }; walletStub = new WalletMock(); @@ -37,7 +37,7 @@ describe('Test function: upgradeAccContract', function () { state = { accContracts: [account1], erc20Tokens: [], - networks: [STARKNET_TESTNET_NETWORK], + networks: [STARKNET_SEPOLIA_TESTNET_NETWORK], transactions: [], }; @@ -154,7 +154,7 @@ describe('Test function: upgradeAccContract', function () { expect(executeTxnStub).to.calledOnce; expect(executeTxnStub).to.calledWith( - STARKNET_TESTNET_NETWORK, + STARKNET_SEPOLIA_TESTNET_NETWORK, address, 'pk', txnInvocation, @@ -188,7 +188,7 @@ describe('Test function: upgradeAccContract', function () { expect(executeTxnStub).to.calledOnce; expect(executeTxnStub).to.calledWith( - STARKNET_TESTNET_NETWORK, + STARKNET_SEPOLIA_TESTNET_NETWORK, address, 'pk', txnInvocation, @@ -265,7 +265,7 @@ describe('Test function: upgradeAccContract', function () { const txn = { txnHash: sendTransactionResp.transaction_hash, txnType: VoyagerTransactionType.INVOKE, - chainId: STARKNET_TESTNET_NETWORK.chainId, + chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, senderAddress: address, contractAddress: address, contractFuncName: 'upgrade', From 63e405a86e0dbc37d72072003b43ff0576d9ee7b Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:40:18 +0800 Subject: [PATCH 16/25] fix: lint issue --- .../test/src/verifySignedMessage.test.ts | 2 +- .../test/utils/starknetUtils.test.ts | 51 +++++++++++++++---- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/packages/starknet-snap/test/src/verifySignedMessage.test.ts b/packages/starknet-snap/test/src/verifySignedMessage.test.ts index b679ed5d..5e1aa2ea 100644 --- a/packages/starknet-snap/test/src/verifySignedMessage.test.ts +++ b/packages/starknet-snap/test/src/verifySignedMessage.test.ts @@ -5,7 +5,7 @@ import { WalletMock } from '../wallet.mock.test'; import { SnapState } from '../../src/types/snapState'; import { verifySignedMessage } from '../../src/verifySignedMessage'; import { STARKNET_SEPOLIA_TESTNET_NETWORK } from '../../src/utils/constants'; -import { account1, getBip44EntropyStub, signature1, signature2, unfoundUserAddress } from '../constants.test'; +import { account1, getBip44EntropyStub, signature1 } from '../constants.test'; import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import * as utils from '../../src/utils/starknetUtils'; import { Mutex } from 'async-mutex'; diff --git a/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index 1340a78b..cd60f228 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -32,7 +32,11 @@ describe('Test function: getAccountInstance', function () { }); it('should return account instance with default cairo version', async function () { - const result = await utils.getAccountInstance(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address, account1.publicKey); + const result = await utils.getAccountInstance( + STARKNET_SEPOLIA_TESTNET_NETWORK, + account1.address, + account1.publicKey, + ); expect(result).to.be.instanceOf(Account); expect(result.cairoVersion).to.equal(CAIRO_VERSION); }); @@ -75,7 +79,13 @@ describe('Test function: findAddressIndex', function () { }); it('should find address index', async function () { - const result = await utils.findAddressIndex(STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, account1.address, 'div', state, 1); + const result = await utils.findAddressIndex( + STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, + account1.address, + 'div', + state, + 1, + ); expect(result).to.be.contains({ index: 0, cairoVersion: 1, @@ -83,7 +93,13 @@ describe('Test function: findAddressIndex', function () { }); it('should find address index address match account legacy', async function () { - const result = await utils.findAddressIndex(STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, account2.address, 'div', state, 1); + const result = await utils.findAddressIndex( + STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, + account2.address, + 'div', + state, + 1, + ); expect(result).to.be.contains({ index: 0, cairoVersion: 0, @@ -93,7 +109,13 @@ describe('Test function: findAddressIndex', function () { it('should throw error if address not found', async function () { let result = null; try { - result = await utils.findAddressIndex(STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, account3.address, 'div', state, 1); + result = await utils.findAddressIndex( + STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, + account3.address, + 'div', + state, + 1, + ); } catch (err) { result = err; } finally { @@ -259,7 +281,11 @@ describe('Test function: getVersion', function () { it('should trigger callContract correct', async function () { const result = await utils.getVersion(account1.address, STARKNET_SEPOLIA_TESTNET_NETWORK); expect(result).to.be.eq(expected); - expect(callContractStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address, 'getVersion'); + expect(callContractStub).to.have.been.calledOnceWith( + STARKNET_SEPOLIA_TESTNET_NETWORK, + account1.address, + 'getVersion', + ); }); }); @@ -278,7 +304,11 @@ describe('Test function: getOwner', function () { it('should trigger callContract correct', async function () { const result = await utils.getOwner(account1.address, STARKNET_SEPOLIA_TESTNET_NETWORK); expect(result).to.be.eq(expected); - expect(callContractStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address, 'get_owner'); + expect(callContractStub).to.have.been.calledOnceWith( + STARKNET_SEPOLIA_TESTNET_NETWORK, + account1.address, + 'get_owner', + ); }); }); @@ -297,9 +327,12 @@ describe('Test function: getBalance', function () { it('should trigger callContract correct', async function () { const result = await utils.getBalance(account1.address, account1.address, STARKNET_SEPOLIA_TESTNET_NETWORK); expect(result).to.be.eq(expected); - expect(callContractStub).to.have.been.calledOnceWith(STARKNET_SEPOLIA_TESTNET_NETWORK, account1.address, 'balanceOf', [ - num.toBigInt(account1.address).toString(10), - ]); + expect(callContractStub).to.have.been.calledOnceWith( + STARKNET_SEPOLIA_TESTNET_NETWORK, + account1.address, + 'balanceOf', + [num.toBigInt(account1.address).toString(10)], + ); }); }); From 29f7a2948c7621a740eb5bbfef49cad55e5e0994 Mon Sep 17 00:00:00 2001 From: Stanley Yuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:04:44 +0800 Subject: [PATCH 17/25] feat: sf 614 rebase (#246) * fix: address field error (#234) * fix: fix address field error * chore: release main (#235) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * feat: bump starknetjs to 6.7.0 (#219) * feat: bump starknetjs to 6.6.0 * chore: remove unuse script * chore: add default config * chore: remove patching * chore: update library version * chore: update getStarkName * feat: sf 612 add alchemy provider (#236) * feat: bump starknetjs to 6.6.0 * chore: remove unuse script * chore: add default config * chore: remove patching * chore: update library version * chore: update getStarkName * feat: add alchemy provider * fix: update mainnet-url * feat: sf 613 add cicd support for alchemy (#237) * feat: bump starknetjs to 6.6.0 * chore: remove unuse script * chore: add default config * chore: remove patching * chore: update library version * chore: update getStarkName * feat: add alchemy provider * fix: add cicd pipeline * feat: add get starknet package (#186) * feat: add get-starknet package * feat: test cicd * feat: test cicd * fix: test ccid * feat: update read me * fix: update lint style * fix: update comment * fix: update lint checking * fix: update readme * fix: export get-starknet type and interface * chore: add webpack bundler to get-starknet * chore: update get-starknet yarn * chore: update output path for get-starknet * fix: incorrect request result * fix: update request method result for get-starkent * chore: update readme * chore: remove unuse files * chore: remove publish script in get-starknet package * chore: remove CICD for get-starknet package * fix: update typo in get-starknet * fix: update wallet default version to wildcard * feat: update package * fix: update root domain * fix: update testing script name * chore: release main (#238) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix: update yarn lock (#240) * fix: get-starknet execute txn not working in firefox (#242) * fix: firefox undefined issue * fix: lint style * feat: add webpack.config.dev.js for local serving of remoteEntry.js * chore: lint and optional env variable for SNAP_ID * fix: update get-starknet/webpack-dev-server dep to address security vulnerability * fix: remoteEntry.js serving done on static file from dist/webpack folder * chore: added .env.sample example file * feat: optional PUBLIC_PATH env variable to produce local remoteEntry.js * chore: updated README.md * fix: methods signature * test: make existing tests pass successfully * fix: rollback check CairoVersion not needed * fix: lint * fix: contractCallData format in massagedTransaction * fix: upgrade function detection in getMassagedTransactions filtering method * refactor: ugrade detection mechanism similar to transfer based on method hex definition * test: adapt constants.test.ts --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Florin Dzeladini --- .github/workflows/deploy.yml | 10 +- .github/workflows/publish-npm-dev.yml | 7 +- .release-please-manifest.json | 2 +- packages/get-starknet/.depcheckrc.json | 3 + packages/get-starknet/.env.sample | 1 + packages/get-starknet/.eslintrc.js | 15 + packages/get-starknet/.gitignore | 5 + packages/get-starknet/.npmignore | 3 + packages/get-starknet/.prettierrc | 11 + packages/get-starknet/LICENSE-APACHE | 203 +++ packages/get-starknet/LICENSE-MIT | 21 + packages/get-starknet/README.md | 29 + packages/get-starknet/package.json | 60 + packages/get-starknet/public/index.html | 1 + packages/get-starknet/src/accounts.ts | 53 + packages/get-starknet/src/index.ts | 5 + packages/get-starknet/src/signer.ts | 57 + packages/get-starknet/src/snap.ts | 358 +++++ packages/get-starknet/src/type.ts | 32 + packages/get-starknet/src/wallet.ts | 150 ++ packages/get-starknet/test-fed.ts | 80 + packages/get-starknet/tsconfig.build.json | 13 + packages/get-starknet/tsconfig.json | 11 + packages/get-starknet/webpack.common.js | 25 + packages/get-starknet/webpack.config.dev.js | 17 + packages/get-starknet/webpack.config.jsonp.js | 26 + packages/get-starknet/webpack.config.prod.js | 22 + packages/get-starknet/webpack.serve.config.js | 36 + packages/starknet-snap/.env.example | 11 + packages/starknet-snap/CHANGELOG.md | 17 + .../openrpc/starknet_snap_api_openrpc.json | 2 +- packages/starknet-snap/package.json | 17 +- packages/starknet-snap/snap.config.js | 29 +- packages/starknet-snap/snap.manifest.json | 4 +- packages/starknet-snap/src/createAccount.ts | 11 +- .../starknet-snap/src/getErc20TokenBalance.ts | 2 +- packages/starknet-snap/src/getValue.ts | 2 +- packages/starknet-snap/src/index.ts | 5 +- packages/starknet-snap/src/signTransaction.ts | 1 - packages/starknet-snap/src/types/snapApi.ts | 1 - packages/starknet-snap/src/utils/constants.ts | 1 + packages/starknet-snap/src/utils/snapUtils.ts | 14 + .../starknet-snap/src/utils/starknetUtils.ts | 112 +- packages/starknet-snap/test/constants.test.ts | 22 +- .../test/src/getErc20TokenBalance.test.ts | 10 +- .../starknet-snap/test/src/getValue.test.ts | 2 +- .../test/src/signDeclareTransaction.test.ts | 20 +- .../src/signDeployAccountTransaction.test.ts | 24 +- .../test/src/switchNetwork.test.ts | 2 +- .../test/utils/snapUtils.test.ts | 19 +- .../test/utils/starknetUtils.test.ts | 8 +- yarn.lock | 1411 ++++++++++++++++- 52 files changed, 2787 insertions(+), 216 deletions(-) create mode 100644 packages/get-starknet/.depcheckrc.json create mode 100644 packages/get-starknet/.env.sample create mode 100644 packages/get-starknet/.eslintrc.js create mode 100644 packages/get-starknet/.gitignore create mode 100644 packages/get-starknet/.npmignore create mode 100644 packages/get-starknet/.prettierrc create mode 100644 packages/get-starknet/LICENSE-APACHE create mode 100644 packages/get-starknet/LICENSE-MIT create mode 100644 packages/get-starknet/README.md create mode 100644 packages/get-starknet/package.json create mode 100644 packages/get-starknet/public/index.html create mode 100644 packages/get-starknet/src/accounts.ts create mode 100644 packages/get-starknet/src/index.ts create mode 100644 packages/get-starknet/src/signer.ts create mode 100644 packages/get-starknet/src/snap.ts create mode 100644 packages/get-starknet/src/type.ts create mode 100644 packages/get-starknet/src/wallet.ts create mode 100644 packages/get-starknet/test-fed.ts create mode 100644 packages/get-starknet/tsconfig.build.json create mode 100644 packages/get-starknet/tsconfig.json create mode 100644 packages/get-starknet/webpack.common.js create mode 100644 packages/get-starknet/webpack.config.dev.js create mode 100644 packages/get-starknet/webpack.config.jsonp.js create mode 100644 packages/get-starknet/webpack.config.prod.js create mode 100644 packages/get-starknet/webpack.serve.config.js create mode 100644 packages/starknet-snap/.env.example diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index faeb1e84..8a24df3f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -48,12 +48,15 @@ jobs: REACT_APP_SNAP_VERSION=${VERSION}-staging yarn workspace wallet-ui build npm --prefix ./packages/starknet-snap version --new-version ${VERSION}-staging --no-git-tag-version - SNAP_ENV=staging VOYAGER_API_KEY=${{ secrets.VOYAGER_API_KEY }} yarn workspace @consensys/starknet-snap build + yarn workspace @consensys/starknet-snap build npm publish ./packages/starknet-snap --tag staging --access public 2>&1 > /dev/null || : aws s3 sync ./packages/wallet-ui/build s3://staging.snaps.consensys.io/starknet env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + SNAP_ENV: staging + VOYAGER_API_KEY: ${{ secrets.VOYAGER_API_KEY }} + ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }} if: inputs.environment == 'staging' - name: Production Deployment @@ -62,12 +65,15 @@ jobs: REACT_APP_SNAP_VERSION=${VERSION} REACT_APP_DEBUG_LEVEL=off yarn workspace wallet-ui build npm --prefix ./packages/starknet-snap version --new-version ${VERSION} --no-git-tag-version --allow-same-version - SNAP_ENV=prod VOYAGER_API_KEY=${{ secrets.VOYAGER_API_KEY }} yarn workspace @consensys/starknet-snap build + yarn workspace @consensys/starknet-snap build npm publish ./packages/starknet-snap --tag latest --access public 2>&1 > /dev/null || : aws s3 sync ./packages/wallet-ui/build s3://snaps.consensys.io/starknet env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + SNAP_ENV: prod + VOYAGER_API_KEY: ${{ secrets.VOYAGER_API_KEY }} + ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }} if: inputs.environment == 'production' - name: invalid aws cloudfront cache diff --git a/.github/workflows/publish-npm-dev.yml b/.github/workflows/publish-npm-dev.yml index 22a72129..dcc99aba 100644 --- a/.github/workflows/publish-npm-dev.yml +++ b/.github/workflows/publish-npm-dev.yml @@ -15,7 +15,6 @@ jobs: publish-dev-to-npm: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - run: | yarn install --no-immutable @@ -24,7 +23,11 @@ jobs: HASH=$(git rev-parse --short HEAD) DATE=$(date +%Y%m%d) npm --prefix ./packages/starknet-snap version --new-version ${VERSION}-dev-${HASH}-${DATE} --no-git-tag-version - SNAP_ENV=dev VOYAGER_API_KEY=${{ secrets.VOYAGER_API_KEY }} yarn workspace @consensys/starknet-snap build + yarn workspace @consensys/starknet-snap build + env: + SNAP_ENV: dev + VOYAGER_API_KEY: ${{ secrets.VOYAGER_API_KEY }} + ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }} - uses: actions/setup-node@v3 with: diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 592409e3..ce5e527a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - "packages/starknet-snap": "2.6.1", + "packages/starknet-snap": "2.7.0", "packages/wallet-ui": "1.21.0" } \ No newline at end of file diff --git a/packages/get-starknet/.depcheckrc.json b/packages/get-starknet/.depcheckrc.json new file mode 100644 index 00000000..6d245428 --- /dev/null +++ b/packages/get-starknet/.depcheckrc.json @@ -0,0 +1,3 @@ +{ + "ignores": ["@types/*", "prettier-plugin-packagejson", "rimraf", "ts-node", "typedoc"] +} diff --git a/packages/get-starknet/.env.sample b/packages/get-starknet/.env.sample new file mode 100644 index 00000000..fe8cd2cf --- /dev/null +++ b/packages/get-starknet/.env.sample @@ -0,0 +1 @@ +SNAPI_ID=local:http://localhost:8081 diff --git a/packages/get-starknet/.eslintrc.js b/packages/get-starknet/.eslintrc.js new file mode 100644 index 00000000..0a8f4696 --- /dev/null +++ b/packages/get-starknet/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: ['plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + rules: { + }, +}; diff --git a/packages/get-starknet/.gitignore b/packages/get-starknet/.gitignore new file mode 100644 index 00000000..95654137 --- /dev/null +++ b/packages/get-starknet/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +js +.nyc_output +/eslint-report.json diff --git a/packages/get-starknet/.npmignore b/packages/get-starknet/.npmignore new file mode 100644 index 00000000..6b5f4d44 --- /dev/null +++ b/packages/get-starknet/.npmignore @@ -0,0 +1,3 @@ +.eslintignore +.eslintrc.js +.prettierrc \ No newline at end of file diff --git a/packages/get-starknet/.prettierrc b/packages/get-starknet/.prettierrc new file mode 100644 index 00000000..36da6fbc --- /dev/null +++ b/packages/get-starknet/.prettierrc @@ -0,0 +1,11 @@ +{ + "bracketSpacing": true, + "jsxBracketSameLine": true, + "printWidth": 120, + "proseWrap": "preserve", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false +} diff --git a/packages/get-starknet/LICENSE-APACHE b/packages/get-starknet/LICENSE-APACHE new file mode 100644 index 00000000..dd2e69f2 --- /dev/null +++ b/packages/get-starknet/LICENSE-APACHE @@ -0,0 +1,203 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Copyright 2022 ConsenSys Software Inc. diff --git a/packages/get-starknet/LICENSE-MIT b/packages/get-starknet/LICENSE-MIT new file mode 100644 index 00000000..6ca719f5 --- /dev/null +++ b/packages/get-starknet/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 ConsenSys Software Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/get-starknet/README.md b/packages/get-starknet/README.md new file mode 100644 index 00000000..c8d4b09a --- /dev/null +++ b/packages/get-starknet/README.md @@ -0,0 +1,29 @@ +# MetaMask Get Starknet + +This repository contains a node module that makes it easy to integrate the Starknet Snap through the interface defined by get-starknet-core. + + +## how to build +Execute the following cmd to build the project in [module federation]([https://webpack.js.org/concepts/module-federation/] ) standard +```bash +yarn build:fed +``` + +## How to use + +```javascript +const walletInstance = new MetaMaskSnapWallet( + provider, snapVersion +); +``` + +`provider` refer to the instance from window.ethereum +`snapVersion` refer to the version of the Starknet Snap that is connecting to, "*" for latest version + +## how to dev +Execute the following cmd to serve a local version of the module federation +```bash +yarn serve:fed +``` + +> The `remoteEntry.js` will be available at `http://localhost:8081/remoteEntry.js` the location can be modified in the package.json if needed, by updating the `PUBLIC_PATH` env variable. \ No newline at end of file diff --git a/packages/get-starknet/package.json b/packages/get-starknet/package.json new file mode 100644 index 00000000..cda997ad --- /dev/null +++ b/packages/get-starknet/package.json @@ -0,0 +1,60 @@ +{ + "name": "@consensys/get-starknet", + "version": "1.0.0", + "license": "(Apache-2.0 OR MIT)", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "/dist" + ], + "repository": { + "type": "git", + "url": "https://github.com/ConsenSys/starknet-snap.git" + }, + "scripts": { + "start": "webpack-cli serve --config webpack.serve.config.js", + "clean": "rimraf dist", + "setup": "yarn install --no-immutable", + "build": "tsc --project tsconfig.build.json", + "build:jsonp": "webpack --config webpack.config.jsonp.js", + "build:fed": "webpack --config webpack.config.prod.js", + "serve:fed": "webpack --config webpack.config.prod.js --env PUBLIC_PATH=http://localhost:8082 && webpack-cli serve --config webpack.config.dev.js", + "prettier": "prettier --write \"src/**/*.ts\"", + "lint": "eslint 'src/*.{js,ts,tsx}' --max-warnings 0 -f json -o eslint-report.json", + "lint:fix": "eslint '**/*.{js,ts,tsx}' --fix", + "test:unit": "" + }, + "keywords": [], + "author": "Consensys", + "devDependencies": { + "@types/chai": "^4.3.1", + "@types/chai-as-promised": "^7.1.5", + "@types/sinon": "^10.0.11", + "@types/sinon-chai": "^3.2.8", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "chai": "^4.3.6", + "chai-as-promised": "^7.1.1", + "depcheck": "^1.4.7", + "eslint": "^8.13.0", + "get-starknet-core": "^3.2.0", + "html-webpack-plugin": "5.6.0", + "prettier": "^2.6.2", + "rimraf": "^3.0.2", + "serve": "14.2.1", + "sinon": "^13.0.2", + "sinon-chai": "^3.7.0", + "ts-loader": "^9.5.1", + "typescript": "^4.6.3", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.0.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "dependencies": { + "starknet": "^5.14.0" + } +} diff --git a/packages/get-starknet/public/index.html b/packages/get-starknet/public/index.html new file mode 100644 index 00000000..6c70bcfe --- /dev/null +++ b/packages/get-starknet/public/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/get-starknet/src/accounts.ts b/packages/get-starknet/src/accounts.ts new file mode 100644 index 00000000..14c4291a --- /dev/null +++ b/packages/get-starknet/src/accounts.ts @@ -0,0 +1,53 @@ +import { MetaMaskSnap } from './snap'; +import { + Abi, + Account, + AllowArray, + CairoVersion, + Call, + DeclareContractPayload, + DeclareContractResponse, + InvocationsDetails, + InvokeFunctionResponse, + ProviderInterface, + ProviderOptions, + Signature, + SignerInterface, + TypedData, +} from 'starknet'; + +export class MetaMaskAccount extends Account { + #snap: MetaMaskSnap; + #address: string; + + constructor( + snap: MetaMaskSnap, + providerOrOptions: ProviderOptions | ProviderInterface, + address: string, + pkOrSigner: Uint8Array | string | SignerInterface, + cairoVersion?: CairoVersion, + ) { + super(providerOrOptions, address, pkOrSigner, cairoVersion); + this.#snap = snap; + this.#address = address; + } + + async execute( + calls: AllowArray, + abis?: Abi[] | undefined, + transactionsDetail?: InvocationsDetails, + ): Promise { + return this.#snap.execute(this.#address, calls, abis, transactionsDetail); + } + + async signMessage(typedData: TypedData): Promise { + return this.#snap.signMessage(typedData, true, this.#address); + } + + async declare( + contractPayload: DeclareContractPayload, + transactionsDetails?: InvocationsDetails, + ): Promise { + return this.#snap.declare(this.#address, contractPayload, transactionsDetails); + } +} diff --git a/packages/get-starknet/src/index.ts b/packages/get-starknet/src/index.ts new file mode 100644 index 00000000..d7314ab2 --- /dev/null +++ b/packages/get-starknet/src/index.ts @@ -0,0 +1,5 @@ +export * from './type'; +export * from './snap'; +export * from './signer'; +export * from './accounts'; +export * from './wallet'; diff --git a/packages/get-starknet/src/signer.ts b/packages/get-starknet/src/signer.ts new file mode 100644 index 00000000..e363528e --- /dev/null +++ b/packages/get-starknet/src/signer.ts @@ -0,0 +1,57 @@ +import { MetaMaskSnap } from './snap'; +import { + Abi, + ArraySignatureType, + Call, + DeclareSignerDetails, + DeployAccountSignerDetails, + InvocationsSignerDetails, + Signature, + SignerInterface, + TypedData, + ec, + num, +} from 'starknet'; + +export class MetaMaskSigner implements SignerInterface { + #snap: MetaMaskSnap; + #address: string; + + constructor(snap: MetaMaskSnap, address: string) { + this.#snap = snap; + this.#address = address; + } + + async getPubKey(): Promise { + return this.#snap.getPubKey(this.#address); + } + + async signMessage(typedData: TypedData, accountAddress: string): Promise { + const result = (await this.#snap.signMessage(typedData, false, accountAddress)) as ArraySignatureType; + return new ec.starkCurve.Signature(num.toBigInt(result[0]), num.toBigInt(result[1])); + } + + async signTransaction( + transactions: Call[], + transactionsDetail: InvocationsSignerDetails, + abis?: Abi[] | undefined, + ): Promise { + const result = (await this.#snap.signTransaction( + this.#address, + transactions, + transactionsDetail, + abis, + )) as ArraySignatureType; + return new ec.starkCurve.Signature(num.toBigInt(result[0]), num.toBigInt(result[1])); + } + + async signDeployAccountTransaction(transaction: DeployAccountSignerDetails): Promise { + const result = (await this.#snap.signDeployAccountTransaction(this.#address, transaction)) as ArraySignatureType; + return new ec.starkCurve.Signature(num.toBigInt(result[0]), num.toBigInt(result[1])); + } + + async signDeclareTransaction(transaction: DeclareSignerDetails): Promise { + const result = (await this.#snap.signDeclareTransaction(this.#address, transaction)) as ArraySignatureType; + return new ec.starkCurve.Signature(num.toBigInt(result[0]), num.toBigInt(result[1])); + } +} diff --git a/packages/get-starknet/src/snap.ts b/packages/get-starknet/src/snap.ts new file mode 100644 index 00000000..2aae13d0 --- /dev/null +++ b/packages/get-starknet/src/snap.ts @@ -0,0 +1,358 @@ +import { AccContract, MetaMaskProvider, Network, RequestSnapResponse } from './type'; +import { + Abi, + AllowArray, + Call, + DeclareContractPayload, + DeclareContractResponse, + DeclareSignerDetails, + DeployAccountSignerDetails, + InvocationsDetails, + InvocationsSignerDetails, + InvokeFunctionResponse, + Signature, + TypedData, +} from 'starknet'; + +export class MetaMaskSnap { + #provider: MetaMaskProvider; + #snapId: string; + #version: string; + + constructor(snapId: string, version: string, provider: MetaMaskProvider) { + this.#provider = provider; + this.#snapId = snapId; + this.#version = version; + } + + async getPubKey(userAddress: string): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_extractPublicKey', + params: { + userAddress, + ...(await this.#getSnapParams()), + }, + }, + }, + })) as string; + } + + async signTransaction( + signerAddress: string, + transactions: Call[], + transactionsDetail: InvocationsSignerDetails, + abis?: Abi[], + ): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_signTransaction', + params: this.removeUndefined({ + signerAddress, + transactions, + transactionsDetail, + abis: abis, + ...(await this.#getSnapParams()), + }), + }, + }, + })) as Signature; + } + + async signDeployAccountTransaction( + signerAddress: string, + transaction: DeployAccountSignerDetails, + ): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_signDeployAccountTransaction', + params: this.removeUndefined({ + signerAddress, + transaction, + ...(await this.#getSnapParams()), + }), + }, + }, + })) as Signature; + } + + async signDeclareTransaction(signerAddress: string, transaction: DeclareSignerDetails): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_signDeclareTransaction', + params: this.removeUndefined({ + signerAddress, + transaction, + ...(await this.#getSnapParams()), + }), + }, + }, + })) as Signature; + } + + async execute( + senderAddress: string, + txnInvocation: AllowArray, + abis?: Abi[], + invocationsDetails?: InvocationsDetails, + ): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_executeTxn', + params: this.removeUndefined({ + senderAddress, + txnInvocation, + invocationsDetails, + abis, + ...(await this.#getSnapParams()), + }), + }, + }, + })) as InvokeFunctionResponse; + } + + async signMessage(typedDataMessage: TypedData, enableAuthorize: boolean, signerAddress: string): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_signMessage', + params: this.removeUndefined({ + signerAddress, + typedDataMessage, + enableAuthorize: enableAuthorize, + ...(await this.#getSnapParams()), + }), + }, + }, + })) as Signature; + } + + async declare( + senderAddress: string, + contractPayload: DeclareContractPayload, + invocationsDetails?: InvocationsDetails, + ): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_declareContract', + params: this.removeUndefined({ + senderAddress, + contractPayload, + invocationsDetails, + ...(await this.#getSnapParams()), + }), + }, + }, + })) as DeclareContractResponse; + } + + async getNetwork(chainId: string): Promise { + const response = (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_getStoredNetworks', + params: {}, + }, + }, + })) as unknown as Network[]; + + const network = response.find((n) => { + return n.chainId === chainId; + }); + + return network; + } + + async recoverDefaultAccount(chainId: string): Promise { + const result = await this.recoverAccounts(chainId, 0, 1, 1); + return result[0]; + } + + async recoverAccounts( + chainId: string, + startScanIndex = 0, + maxScanned = 1, + maxMissed = 1, + ): Promise> { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_recoverAccounts', + params: { + startScanIndex, + maxScanned, + maxMissed, + chainId, + }, + }, + }, + })) as Array; + } + + async switchNetwork(chainId: string): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_switchNetwork', + params: { + chainId, + enableAuthorize: true, + }, + }, + }, + })) as boolean; + } + + async addStarknetChain(chainName: string, chainId: string, rpcUrl: string, explorerUrl: string): Promise { + return (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_addNetwork', + params: this.removeUndefined({ + networkName: chainName, + networkChainId: chainId, + networkNodeUrl: rpcUrl, + networkVoyagerUrl: explorerUrl, + }), + }, + }, + })) as boolean; + } + + async watchAsset(address: string, name: string, symbol: string, decimals: number): Promise { + return this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_addErc20Token', + params: this.removeUndefined({ + tokenAddress: address, + tokenName: name, + tokenSymbol: symbol, + tokenDecimals: decimals, + }), + }, + }, + }) as unknown as boolean; + } + + async getCurrentNetwork(): Promise { + const response = (await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'starkNet_getCurrentNetwork', + params: {}, + }, + }, + })) as unknown as Network; + + return response; + } + + async #getSnapParams() { + const network = await this.getCurrentNetwork(); + return { + chainId: network.chainId, + }; + } + + static async GetProvider(window: { ethereum?: unknown }) { + const { ethereum } = window; + if (!ethereum) { + return null; + } + let providers = [ethereum]; + + //ethereum.detected or ethereum.providers may exist when more than 1 wallet installed + if (ethereum.hasOwnProperty('detected')) { + providers = ethereum['detected']; + } else if (ethereum.hasOwnProperty('providers')) { + providers = ethereum['providers']; + } + + //delect provider by sending request + for (const provider of providers) { + if (provider && (await MetaMaskSnap.IsSupportSnap(provider as MetaMaskProvider))) { + return provider; + } + } + return null; + } + + static async IsSupportSnap(provider: MetaMaskProvider) { + try { + await provider.request({ + method: 'wallet_getSnaps', + }); + return true; + } catch { + return false; + } + } + + async installIfNot(): Promise { + const response = (await this.#provider.request({ + method: 'wallet_requestSnaps', + params: { + [this.#snapId]: { version: this.#version }, + }, + })) as RequestSnapResponse; + if (!response || !response[this.#snapId]?.enabled) { + return false; + } + return true; + } + + async isInstalled() { + try { + await this.#provider.request({ + method: 'wallet_invokeSnap', + params: { + snapId: this.#snapId, + request: { + method: 'ping', + }, + }, + }); + return true; + } catch (err) { + return false; + } + } + + removeUndefined(obj: Record) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined)); + } +} diff --git a/packages/get-starknet/src/type.ts b/packages/get-starknet/src/type.ts new file mode 100644 index 00000000..335e803f --- /dev/null +++ b/packages/get-starknet/src/type.ts @@ -0,0 +1,32 @@ +export interface MetaMaskProvider { + request(args: { method: string; params?: unknown[] | Record }): Promise; +} + +export type AccContract = { + addressSalt: string; + publicKey: string; // in hex + address: string; // in hex + addressIndex: number; + derivationPath: string; + deployTxnHash: string; // in hex + chainId: string; // in hex +}; + +export type Network = { + name: string; + chainId: string; // in hex + baseUrl: string; + nodeUrl: string; + voyagerUrl: string; + accountClassHash: string; // in hex + useOldAccounts?: boolean; +}; + +export type RequestSnapResponse = { + [key in string]: { + enabled: boolean; + version: string; + id: string; + blocked: boolean; + }; +}; diff --git a/packages/get-starknet/src/wallet.ts b/packages/get-starknet/src/wallet.ts new file mode 100644 index 00000000..5e03df76 --- /dev/null +++ b/packages/get-starknet/src/wallet.ts @@ -0,0 +1,150 @@ +import { + type AddStarknetChainParameters, + IStarknetWindowObject, + type RpcMessage, + type SwitchStarknetChainParameter, + type WalletEvents, + type WatchAssetParameters, +} from 'get-starknet-core'; +import { MetaMaskAccount } from './accounts'; +import { MetaMaskSigner } from './signer'; +import { MetaMaskSnap } from './snap'; +import { MetaMaskProvider } from './type'; +import { AccountInterface, Provider, ProviderInterface } from 'starknet'; + +export class MetaMaskSnapWallet implements IStarknetWindowObject { + id: string; + name: string; + version: string; + icon: string; + account?: AccountInterface | undefined; + provider?: ProviderInterface | undefined; + selectedAddress?: string | undefined; + chainId?: string | undefined; + isConnected?: boolean; + + snap: MetaMaskSnap; + metamaskProvider: MetaMaskProvider; + + private static readonly cairoVersion = '0'; + private static readonly SNAPI_ID = process.env.SNAP_ID ?? 'npm:@consensys/starknet-snap'; + + constructor(metamaskProvider: MetaMaskProvider, snapVersion = '*') { + this.id = 'metamask'; + this.name = 'Metamask'; + this.version = 'v1.0.0'; + this.icon = `data:image/svg+xml;utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTIiIGhlaWdodD0iMTg5IiB2aWV3Qm94PSIwIDAgMjEyIDE4OSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cG9seWdvbiBmaWxsPSIjQ0RCREIyIiBwb2ludHM9IjYwLjc1IDE3My4yNSA4OC4zMTMgMTgwLjU2MyA4OC4zMTMgMTcxIDkwLjU2MyAxNjguNzUgMTA2LjMxMyAxNjguNzUgMTA2LjMxMyAxODAgMTA2LjMxMyAxODcuODc1IDg5LjQzOCAxODcuODc1IDY4LjYyNSAxNzguODc1Ii8+PHBvbHlnb24gZmlsbD0iI0NEQkRCMiIgcG9pbnRzPSIxMDUuNzUgMTczLjI1IDEzMi43NSAxODAuNTYzIDEzMi43NSAxNzEgMTM1IDE2OC43NSAxNTAuNzUgMTY4Ljc1IDE1MC43NSAxODAgMTUwLjc1IDE4Ny44NzUgMTMzLjg3NSAxODcuODc1IDExMy4wNjMgMTc4Ljg3NSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjU2LjUgMCkiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjkwLjU2MyAxNTIuNDM4IDg4LjMxMyAxNzEgOTEuMTI1IDE2OC43NSAxMjAuMzc1IDE2OC43NSAxMjMuNzUgMTcxIDEyMS41IDE1Mi40MzggMTE3IDE0OS42MjUgOTQuNSAxNTAuMTg4Ii8+PHBvbHlnb24gZmlsbD0iI0Y4OUMzNSIgcG9pbnRzPSI3NS4zNzUgMjcgODguODc1IDU4LjUgOTUuMDYzIDE1MC4xODggMTE3IDE1MC4xODggMTIzLjc1IDU4LjUgMTM2LjEyNSAyNyIvPjxwb2x5Z29uIGZpbGw9IiNGODlEMzUiIHBvaW50cz0iMTYuMzEzIDk2LjE4OCAuNTYzIDE0MS43NSAzOS45MzggMTM5LjUgNjUuMjUgMTM5LjUgNjUuMjUgMTE5LjgxMyA2NC4xMjUgNzkuMzEzIDU4LjUgODMuODEzIi8+PHBvbHlnb24gZmlsbD0iI0Q4N0MzMCIgcG9pbnRzPSI0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi4zNzUgODcuMTg4IDEyNiA2NS4yNSAxMjAuMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VBOEQzQSIgcG9pbnRzPSI0Ni4xMjUgMTAxLjgxMyA2NS4yNSAxMTkuODEzIDY1LjI1IDEzNy44MTMiLz48cG9seWdvbiBmaWxsPSIjRjg5RDM1IiBwb2ludHM9IjY1LjI1IDEyMC4zNzUgODcuNzUgMTI2IDk1LjA2MyAxNTAuMTg4IDkwIDE1MyA2NS4yNSAxMzguMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSI2NS4yNSAxMzguMzc1IDYwLjc1IDE3My4yNSA5MC41NjMgMTUyLjQzOCIvPjxwb2x5Z29uIGZpbGw9IiNFQThFM0EiIHBvaW50cz0iOTIuMjUgMTAyLjM3NSA5NS4wNjMgMTUwLjE4OCA4Ni42MjUgMTI1LjcxOSIvPjxwb2x5Z29uIGZpbGw9IiNEODdDMzAiIHBvaW50cz0iMzkuMzc1IDEzOC45MzggNjUuMjUgMTM4LjM3NSA2MC43NSAxNzMuMjUiLz48cG9seWdvbiBmaWxsPSIjRUI4RjM1IiBwb2ludHM9IjEyLjkzOCAxODguNDM4IDYwLjc1IDE3My4yNSAzOS4zNzUgMTM4LjkzOCAuNTYzIDE0MS43NSIvPjxwb2x5Z29uIGZpbGw9IiNFODgyMUUiIHBvaW50cz0iODguODc1IDU4LjUgNjQuNjg4IDc4Ljc1IDQ2LjEyNSAxMDEuMjUgOTIuMjUgMTAyLjkzOCIvPjxwb2x5Z29uIGZpbGw9IiNERkNFQzMiIHBvaW50cz0iNjAuNzUgMTczLjI1IDkwLjU2MyAxNTIuNDM4IDg4LjMxMyAxNzAuNDM4IDg4LjMxMyAxODAuNTYzIDY4LjA2MyAxNzYuNjI1Ii8+PHBvbHlnb24gZmlsbD0iI0RGQ0VDMyIgcG9pbnRzPSIxMjEuNSAxNzMuMjUgMTUwLjc1IDE1Mi40MzggMTQ4LjUgMTcwLjQzOCAxNDguNSAxODAuNTYzIDEyOC4yNSAxNzYuNjI1IiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAyNzIuMjUgMCkiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjcwLjMxMyAxMTIuNSA2NC4xMjUgMTI1LjQzOCA4Ni4wNjMgMTE5LjgxMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTUwLjE4OCAwKSIvPjxwb2x5Z29uIGZpbGw9IiNFODhGMzUiIHBvaW50cz0iMTIuMzc1IC41NjMgODguODc1IDU4LjUgNzUuOTM4IDI3Ii8+PHBhdGggZmlsbD0iIzhFNUEzMCIgZD0iTTEyLjM3NTAwMDIsMC41NjI1MDAwMDggTDIuMjUwMDAwMDMsMzEuNTAwMDAwNSBMNy44NzUwMDAxMiw2NS4yNTAwMDEgTDMuOTM3NTAwMDYsNjcuNTAwMDAxIEw5LjU2MjUwMDE0LDcyLjU2MjUgTDUuMDYyNTAwMDgsNzYuNTAwMDAxMSBMMTEuMjUsODIuMTI1MDAxMiBMNy4zMTI1MDAxMSw4NS41MDAwMDEzIEwxNi4zMTI1MDAyLDk2Ljc1MDAwMTQgTDU4LjUwMDAwMDksODMuODEyNTAxMiBDNzkuMTI1MDAxMiw2Ny4zMTI1MDA0IDg5LjI1MDAwMTMsNTguODc1MDAwMyA4OC44NzUwMDEzLDU4LjUwMDAwMDkgQzg4LjUwMDAwMTMsNTguMTI1MDAwOSA2My4wMDAwMDA5LDM4LjgxMjUwMDYgMTIuMzc1MDAwMiwwLjU2MjUwMDAwOCBaIi8+PGcgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjExLjUgMCkiPjxwb2x5Z29uIGZpbGw9IiNGODlEMzUiIHBvaW50cz0iMTYuMzEzIDk2LjE4OCAuNTYzIDE0MS43NSAzOS45MzggMTM5LjUgNjUuMjUgMTM5LjUgNjUuMjUgMTE5LjgxMyA2NC4xMjUgNzkuMzEzIDU4LjUgODMuODEzIi8+PHBvbHlnb24gZmlsbD0iI0Q4N0MzMCIgcG9pbnRzPSI0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi4zNzUgODcuMTg4IDEyNiA2NS4yNSAxMjAuMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VBOEQzQSIgcG9pbnRzPSI0Ni4xMjUgMTAxLjgxMyA2NS4yNSAxMTkuODEzIDY1LjI1IDEzNy44MTMiLz48cG9seWdvbiBmaWxsPSIjRjg5RDM1IiBwb2ludHM9IjY1LjI1IDEyMC4zNzUgODcuNzUgMTI2IDk1LjA2MyAxNTAuMTg4IDkwIDE1MyA2NS4yNSAxMzguMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSI2NS4yNSAxMzguMzc1IDYwLjc1IDE3My4yNSA5MCAxNTMiLz48cG9seWdvbiBmaWxsPSIjRUE4RTNBIiBwb2ludHM9IjkyLjI1IDEwMi4zNzUgOTUuMDYzIDE1MC4xODggODYuNjI1IDEyNS43MTkiLz48cG9seWdvbiBmaWxsPSIjRDg3QzMwIiBwb2ludHM9IjM5LjM3NSAxMzguOTM4IDY1LjI1IDEzOC4zNzUgNjAuNzUgMTczLjI1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSIxMi45MzggMTg4LjQzOCA2MC43NSAxNzMuMjUgMzkuMzc1IDEzOC45MzggLjU2MyAxNDEuNzUiLz48cG9seWdvbiBmaWxsPSIjRTg4MjFFIiBwb2ludHM9Ijg4Ljg3NSA1OC41IDY0LjY4OCA3OC43NSA0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi45MzgiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjcwLjMxMyAxMTIuNSA2NC4xMjUgMTI1LjQzOCA4Ni4wNjMgMTE5LjgxMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTUwLjE4OCAwKSIvPjxwb2x5Z29uIGZpbGw9IiNFODhGMzUiIHBvaW50cz0iMTIuMzc1IC41NjMgODguODc1IDU4LjUgNzUuOTM4IDI3Ii8+PHBhdGggZmlsbD0iIzhFNUEzMCIgZD0iTTEyLjM3NTAwMDIsMC41NjI1MDAwMDggTDIuMjUwMDAwMDMsMzEuNTAwMDAwNSBMNy44NzUwMDAxMiw2NS4yNTAwMDEgTDMuOTM3NTAwMDYsNjcuNTAwMDAxIEw5LjU2MjUwMDE0LDcyLjU2MjUgTDUuMDYyNTAwMDgsNzYuNTAwMDAxMSBMMTEuMjUsODIuMTI1MDAxMiBMNy4zMTI1MDAxMSw4NS41MDAwMDEzIEwxNi4zMTI1MDAyLDk2Ljc1MDAwMTQgTDU4LjUwMDAwMDksODMuODEyNTAxMiBDNzkuMTI1MDAxMiw2Ny4zMTI1MDA0IDg5LjI1MDAwMTMsNTguODc1MDAwMyA4OC44NzUwMDEzLDU4LjUwMDAwMDkgQzg4LjUwMDAwMTMsNTguMTI1MDAwOSA2My4wMDAwMDA5LDM4LjgxMjUwMDYgMTIuMzc1MDAwMiwwLjU2MjUwMDAwOCBaIi8+PC9nPjwvZz48L3N2Zz4=`; + this.metamaskProvider = metamaskProvider; + this.provider = undefined; + this.chainId = undefined; + this.account = undefined; + this.selectedAddress = undefined; + this.isConnected = false; + this.snap = new MetaMaskSnap(MetaMaskSnapWallet.SNAPI_ID, snapVersion, this.metamaskProvider); + } + + async request(call: Omit): Promise { + if (call.type === 'wallet_switchStarknetChain') { + const params = call.params as SwitchStarknetChainParameter; + const result = await this.snap.switchNetwork(params.chainId); + if (result === true) { + await this.enable(); + } + return result as unknown as T['result']; + } + + if (call.type === 'wallet_addStarknetChain') { + const params = call.params as AddStarknetChainParameters; + const currentNetwork = await this.#getNetwork(); + if (currentNetwork?.chainId === params.chainId) { + return true as unknown as T['result']; + } + const result = await this.snap.addStarknetChain( + params.chainName, + params.chainId, + params.rpcUrls ? params.rpcUrls[0] : '', + params.blockExplorerUrls ? params.blockExplorerUrls[0] : '', + ); + return result as unknown as T['result']; + } + + if (call.type === 'wallet_watchAsset') { + const params = call.params as WatchAssetParameters; + const result = + (await this.snap.watchAsset( + params.options.address, + params.options.name, + params.options.symbol, + params.options.decimals, + )) ?? false; + return result as unknown as T['result']; + } + + throw new Error(`Method ${call.type} not implemented`); + } + + async #getNetwork() { + return await this.snap.getCurrentNetwork(); + } + + async #getWalletAddress(chainId: string) { + //address always same regardless network, only single address provided + if (this.selectedAddress) { + return this.selectedAddress; + } + + const accountResponse = await this.snap.recoverDefaultAccount(chainId); + + if (!accountResponse || !accountResponse.address) { + throw new Error('Unable to recover accounts'); + } + + return accountResponse.address; + } + + async #getRPCProvider(network: { chainId: string; nodeUrl: string }) { + return new Provider({ + rpc: { + nodeUrl: network.nodeUrl, + }, + }); + } + + async #getAccountInstance(address: string, provider: ProviderInterface) { + const signer = new MetaMaskSigner(this.snap, address); + + return new MetaMaskAccount(this.snap, provider, address, signer, MetaMaskSnapWallet.cairoVersion); + } + + async enable() { + await this.snap.installIfNot(); + this.isConnected = true; + const network = await this.#getNetwork(); + if (!network) { + throw new Error('Current network not found'); + } + this.chainId = network.chainId; + this.selectedAddress = await this.#getWalletAddress(this.chainId); + if (!this.selectedAddress) { + throw new Error('Address not found'); + } + this.provider = await this.#getRPCProvider(network); + this.account = await this.#getAccountInstance(this.selectedAddress, this.provider); + + return [this.selectedAddress]; + } + + async isPreauthorized() { + return true; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + on() { + throw new Error('Method not supported'); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + off() { + throw new Error('Method not supported'); + } +} diff --git a/packages/get-starknet/test-fed.ts b/packages/get-starknet/test-fed.ts new file mode 100644 index 00000000..d9ad2023 --- /dev/null +++ b/packages/get-starknet/test-fed.ts @@ -0,0 +1,80 @@ +import { MetaMaskSnap } from './src/snap'; +import { MetaMaskSnapWallet } from './src/wallet'; + +const init = async() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const provider = await MetaMaskSnap.GetProvider(window as any) as any; + const wallet = new MetaMaskSnapWallet(provider, '*'); + + const address = await wallet.enable(); + console.log("address", address) + + const wallet_switchStarknetChain = await wallet.request( { + type: 'wallet_switchStarknetChain', + params: { + chainId: '0x534e5f4d41494e', + }, + }); + console.log("wallet_switchStarknetChain", wallet_switchStarknetChain) + + const wallet_addStarknetChain = await wallet.request( { + type: 'wallet_addStarknetChain', + params: { + chainId: '0x534e5f4d41494e', + chainName: 'StarkNet', + rpcUrls: ['https://stg-rpc.starknet.io'], + blockExplorerUrls: ['https://stg-explorer.starknet.io'], + }, + }); + console.log("wallet_addStarknetChain", wallet_addStarknetChain) + + const message = { + types: { + StarkNetDomain: [ + { name: "name", type: "string" }, + { name: "version", type: "felt" }, + { name: "chainId", type: "felt" }, + ], + Airdrop: [ + { name: "address", type: "felt" }, + { name: "amount", type: "felt" } + ], + Validate: [ + { name: "id", type: "felt" }, + { name: "from", type: "felt" }, + { name: "amount", type: "felt" }, + { name: "nameGamer", type: "string" }, + { name: "endDate", type: "felt" }, + { name: "itemsAuthorized", type: "felt*" }, // array of felt + { name: "chkFunction", type: "selector" }, // name of function + { name: "rootList", type: "merkletree", contains: "Airdrop" } // root of a merkle tree + ] + }, + primaryType: "Validate", + domain: { + name: "myDapp", + version: "1", + chainId: "0x534e5f474f45524c49", + }, + message: { + id: "0x0000004f000f", + from: "0x2c94f628d125cd0e86eaefea735ba24c262b9a441728f63e5776661829a4066", + amount: "400", + nameGamer: "Hector26", + endDate: "0x27d32a3033df4277caa9e9396100b7ca8c66a4ef8ea5f6765b91a7c17f0109c", + itemsAuthorized: ["0x01", "0x03", "0x0a", "0x0e"], + chkFunction: "check_authorization", + rootList: [ + { + address: "0x69b49c2cc8b16e80e86bfc5b0614a59aa8c9b601569c7b80dde04d3f3151b79", + amount: "1554785", + } + ] + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any + + const signData = await wallet.account.signMessage(message) + console.log("signData", signData) +} +init() diff --git a/packages/get-starknet/tsconfig.build.json b/packages/get-starknet/tsconfig.build.json new file mode 100644 index 00000000..c6e00d65 --- /dev/null +++ b/packages/get-starknet/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "inlineSources": true, + "noEmit": false, + "outDir": "dist", + "rootDir": "src", + "sourceMap": true + }, + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts"] +} diff --git a/packages/get-starknet/tsconfig.json b/packages/get-starknet/tsconfig.json new file mode 100644 index 00000000..6d4ccf24 --- /dev/null +++ b/packages/get-starknet/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "module": "commonjs", /* Specify what module code is generated. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "skipLibCheck": true , /* Skip type checking all .d.ts files. */ + "resolveJsonModule": true, /* lets us import JSON modules from within TypeScript modules. */ + } +} diff --git a/packages/get-starknet/webpack.common.js b/packages/get-starknet/webpack.common.js new file mode 100644 index 00000000..1e31e2c2 --- /dev/null +++ b/packages/get-starknet/webpack.common.js @@ -0,0 +1,25 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + entry: './src/index.ts', + output: { + path: path.resolve(__dirname, './dist/webpack'), + filename: 'index.js', + library: { + type: 'commonjs2', + }, + }, + resolve: { + extensions: ['.ts', '.tsx', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, +}; \ No newline at end of file diff --git a/packages/get-starknet/webpack.config.dev.js b/packages/get-starknet/webpack.config.dev.js new file mode 100644 index 00000000..f480f87b --- /dev/null +++ b/packages/get-starknet/webpack.config.dev.js @@ -0,0 +1,17 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { merge } = require('webpack-merge'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const common = require('./webpack.common.js'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); +module.exports = merge(common, { + mode: 'development', + output: { + publicPath: 'http://localhost:8082/', // Adjust the development publicPath as needed + }, + devServer: { + static: path.join(__dirname, 'dist/webpack'), + compress: true, + port: 8082, + }, +}); diff --git a/packages/get-starknet/webpack.config.jsonp.js b/packages/get-starknet/webpack.config.jsonp.js new file mode 100644 index 00000000..d6ca14d8 --- /dev/null +++ b/packages/get-starknet/webpack.config.jsonp.js @@ -0,0 +1,26 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + mode: 'production', + entry: './src/index.ts', + output: { + path: path.resolve(__dirname, 'dist/jsonp'), + filename: 'index.jsonp.js', + library: 'MetaMaskStarknetSnapWallet', + libraryTarget: 'jsonp', + libraryExport: 'default', + }, + resolve: { + extensions: ['.ts', '.tsx', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, +}; \ No newline at end of file diff --git a/packages/get-starknet/webpack.config.prod.js b/packages/get-starknet/webpack.config.prod.js new file mode 100644 index 00000000..e69696b3 --- /dev/null +++ b/packages/get-starknet/webpack.config.prod.js @@ -0,0 +1,22 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { merge } = require('webpack-merge'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const common = require('./webpack.common.js'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { ModuleFederationPlugin } = require('webpack').container; + +module.exports = (env) => merge(common, { + mode: 'production', + output: { + publicPath: env.PUBLIC_PATH || 'https://snaps.consensys.io/starknet/get-starknet/v1/', + }, + plugins: [ + new ModuleFederationPlugin({ + name: 'MetaMaskStarknetSnapWallet', + filename: 'remoteEntry.js', + exposes: { + './index': './src/index.ts', + }, + }), + ], +}); \ No newline at end of file diff --git a/packages/get-starknet/webpack.serve.config.js b/packages/get-starknet/webpack.serve.config.js new file mode 100644 index 00000000..0efd44a7 --- /dev/null +++ b/packages/get-starknet/webpack.serve.config.js @@ -0,0 +1,36 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const HtmlWebpackPlugin = require('html-webpack-plugin'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + entry: './test-fed', + mode: 'development', + target: 'web', + devServer: { + static: { + directory: path.join(__dirname, 'dist'), + }, + port: 3001, + }, + output: { + publicPath: 'auto', + }, + resolve: { + extensions: ['.ts', '.tsx', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + template: './public/index.html', + }), + ], +}; \ No newline at end of file diff --git a/packages/starknet-snap/.env.example b/packages/starknet-snap/.env.example new file mode 100644 index 00000000..43147264 --- /dev/null +++ b/packages/starknet-snap/.env.example @@ -0,0 +1,11 @@ +# Description: Environment variables for snap env +# Possible Options: dev | prod | staging +# Default: prod +# Required: false +SNAP_ENV=dev +# Description: Environment variables for API key of VOYAGER +# Required: false +VOYAGER_API_KEY= +# Description: Environment variables for API key of ALCHEMY +# Required: false +ALCHEMY_API_KEY= \ No newline at end of file diff --git a/packages/starknet-snap/CHANGELOG.md b/packages/starknet-snap/CHANGELOG.md index de8ca0e3..7b0fce15 100644 --- a/packages/starknet-snap/CHANGELOG.md +++ b/packages/starknet-snap/CHANGELOG.md @@ -1,6 +1,23 @@ # Changelog +## [2.7.0](https://github.com/Consensys/starknet-snap/compare/starknet-snap-v2.6.2...starknet-snap-v2.7.0) (2024-05-01) + + +### Features + +* add get starknet package ([#186](https://github.com/Consensys/starknet-snap/issues/186)) ([c44c00d](https://github.com/Consensys/starknet-snap/commit/c44c00d3340191d4b276579556c613308c32cc1d)) +* bump starknetjs to 6.7.0 ([#219](https://github.com/Consensys/starknet-snap/issues/219)) ([a07c3f1](https://github.com/Consensys/starknet-snap/commit/a07c3f1b852389d2369320fbcf9258ff73860b69)) +* sf 612 add alchemy provider ([#236](https://github.com/Consensys/starknet-snap/issues/236)) ([499f59d](https://github.com/Consensys/starknet-snap/commit/499f59dc05da5850f9b71d446267dcb62e079748)) +* sf 613 add cicd support for alchemy ([#237](https://github.com/Consensys/starknet-snap/issues/237)) ([033fdc6](https://github.com/Consensys/starknet-snap/commit/033fdc625c8a2c7eee91a17c4e175a6b78626c7a)) + +## [2.6.2](https://github.com/Consensys/starknet-snap/compare/starknet-snap-v2.6.1...starknet-snap-v2.6.2) (2024-04-18) + + +### Bug Fixes + +* address field error ([#234](https://github.com/Consensys/starknet-snap/issues/234)) ([1aa523c](https://github.com/Consensys/starknet-snap/commit/1aa523c8123ccd016953314c9c63520602959651)) + ## [2.6.1](https://github.com/Consensys/starknet-snap/compare/starknet-snap-v2.6.0...starknet-snap-v2.6.1) (2024-04-05) diff --git a/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json b/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json index 331c2e99..6dea641f 100644 --- a/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json +++ b/packages/starknet-snap/openrpc/starknet_snap_api_openrpc.json @@ -1,7 +1,7 @@ { "openrpc": "1.0.0-rc1", "info": { - "version": "2.6.1", + "version": "2.7.0", "title": "Starknet MetaMask Snap API", "license": {} }, diff --git a/packages/starknet-snap/package.json b/packages/starknet-snap/package.json index 13e2642b..4cf0df8b 100644 --- a/packages/starknet-snap/package.json +++ b/packages/starknet-snap/package.json @@ -1,25 +1,30 @@ { "name": "@consensys/starknet-snap", - "version": "2.6.1", + "version": "2.7.0", "license": "(Apache-2.0 OR MIT)", - "main": "src/index.ts", "repository": { "type": "git", "url": "https://github.com/ConsenSys/starknet-snap.git" }, + "main": "./dist/bundle.js", + "files": [ + "dist/", + "images/", + "snap.manifest.json" + ], "scripts": { "setup": "yarn install --immutable && yarn allow-scripts", "build:clean": "yarn clean && yarn build", "build": "mm-snap build", "serve": "mm-snap serve", "clean": "rimraf js && rimraf dist", - "watch": "yarn build && concurrently --kill-others --names SNAP,SERVE -c bgMagenta.bold,bgGreen.bold \"yarn run watch:snap\" \"yarn run serve\"", - "watch:snap": "cross-env SNAP_ENV=prod mm-snap watch", + "watch": "mm-snap watch", "prettier": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "lint": "eslint . --max-warnings 0 -f json -o eslint-report.json", "lint:fix": "eslint '**/*.{js,ts,tsx}' --fix", "test": "yarn run test:unit && yarn run cover:report", "test:unit": "nyc --check-coverage --statements 80 --branches 80 --functions 80 --lines 80 mocha --colors -r ts-node/register \"test/**/*.test.ts\"", + "test:unit:one": "nyc --check-coverage --statements 80 --branches 80 --functions 80 --lines 80 mocha --colors -r ts-node/register", "cover:report": "nyc report --reporter=lcov --reporter=text" }, "keywords": [], @@ -39,7 +44,6 @@ "chai-as-promised": "^7.1.1", "concurrently": "^7.1.0", "cross-env": "^7.0.3", - "envify": "^4.1.0", "eslint": "^8.13.0", "mocha": "^9.2.2", "nyc": "^15.1.0", @@ -54,9 +58,10 @@ "dependencies": { "@metamask/snaps-sdk": "3.0.1", "async-mutex": "^0.3.2", + "dotenv": "^16.4.5", "ethereum-unit-converter": "^0.0.17", "ethers": "^5.5.1", - "starknet": "^5.14.0", + "starknet": "6.7.0", "starknet_v4.22.0": "npm:starknet@4.22.0" }, "publishConfig": { diff --git a/packages/starknet-snap/snap.config.js b/packages/starknet-snap/snap.config.js index 51212828..90d809ef 100644 --- a/packages/starknet-snap/snap.config.js +++ b/packages/starknet-snap/snap.config.js @@ -1,17 +1,16 @@ -import envify from "envify/custom"; +import * as dotenv from "dotenv"; +dotenv.config(); module.exports = { - cliOptions: { - dist: 'dist', - outfileName: 'bundle.js', - src: './src/index.ts', - }, - bundlerCustomizer: (bundler) => { - bundler.transform( - envify({ - SNAP_ENV: process.env.SNAP_ENV, - VOYAGER_API_KEY: process.env.VOYAGER_API_KEY, - }), - ); - }, -}; \ No newline at end of file + bundler: "webpack", + environment: { + SNAP_ENV: process.env.SNAP_ENV ?? "prod", + VOYAGER_API_KEY: process.env.VOYAGER_API_KEY ?? "", + ALCHEMY_API_KEY: process.env.ALCHEMY_API_KEY ?? "", + }, + input: "./src/index.ts", + server: { + port: 8081, + }, + polyfills: true +}; diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index c453b60c..a42295d7 100644 --- a/packages/starknet-snap/snap.manifest.json +++ b/packages/starknet-snap/snap.manifest.json @@ -1,5 +1,5 @@ { - "version": "2.6.1", + "version": "2.7.0", "description": "Manage Starknet accounts and assets with MetaMask.", "proposedName": "Starknet", "repository": { @@ -7,7 +7,7 @@ "url": "https://github.com/ConsenSys/starknet-snap.git" }, "source": { - "shasum": "l+wa5VCGkesCafIYs/fVnQHsiooEg/z+IcCcXNiP0vo=", + "shasum": "oanFWM3QGnXYxoyzPg5doAa629Pekc6NFOzGZmm8d0Q=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/src/createAccount.ts b/packages/starknet-snap/src/createAccount.ts index bcba65ae..2103fa90 100644 --- a/packages/starknet-snap/src/createAccount.ts +++ b/packages/starknet-snap/src/createAccount.ts @@ -100,14 +100,9 @@ export async function createAccount(params: ApiParams, silentMode = false) { } } - const deployResp = await deployAccount( - network, - contractAddress, - contractCallData, - publicKey, - privateKey, - estimateDeployFee?.suggestedMaxFee, - ); + const deployResp = await deployAccount(network, contractAddress, contractCallData, publicKey, privateKey, undefined, { + maxFee: estimateDeployFee?.suggestedMaxFee, + }); if (deployResp.contract_address && deployResp.transaction_hash) { const userAccount: AccContract = { diff --git a/packages/starknet-snap/src/getErc20TokenBalance.ts b/packages/starknet-snap/src/getErc20TokenBalance.ts index aed4a349..5cadb9d0 100644 --- a/packages/starknet-snap/src/getErc20TokenBalance.ts +++ b/packages/starknet-snap/src/getErc20TokenBalance.ts @@ -39,7 +39,7 @@ export async function getErc20TokenBalance(params: ApiParams) { logger.log(`getErc20Balance:\nresp: ${toJson(resp)}`); - return resp.result[0]; + return resp[0]; } catch (err) { logger.error(`Problem found: ${err}`); throw err; diff --git a/packages/starknet-snap/src/getValue.ts b/packages/starknet-snap/src/getValue.ts index a113c018..145f9395 100644 --- a/packages/starknet-snap/src/getValue.ts +++ b/packages/starknet-snap/src/getValue.ts @@ -37,7 +37,7 @@ export async function getValue(params: ApiParams) { logger.log(`getValue:\nresp: ${toJson(resp)}`); - return resp.result; + return resp; } catch (err) { logger.error(`Problem found: ${err}`); throw err; diff --git a/packages/starknet-snap/src/index.ts b/packages/starknet-snap/src/index.ts index 2008ffdf..5d000839 100644 --- a/packages/starknet-snap/src/index.ts +++ b/packages/starknet-snap/src/index.ts @@ -50,7 +50,7 @@ import { logger } from './utils/logger'; import { getStarkName } from './getStarkName'; import type { OnRpcRequestHandler, OnHomePageHandler, OnInstallHandler, OnUpdateHandler } from '@metamask/snaps-sdk'; -import { InternalError, panel, row, address, divider, text } from '@metamask/snaps-sdk'; +import { InternalError, panel, row, divider, text, copyable } from '@metamask/snaps-sdk'; import { ethers } from 'ethers'; declare const snap; @@ -293,7 +293,8 @@ export const onHomePage: OnHomePageHandler = async () => { const userAddress = accContract.address; const chainId = accContract.chainId; const network = getNetworkFromChainId(state, chainId); - panelItems.push(row('Address', address(`${userAddress}`))); + panelItems.push(text('Address')); + panelItems.push(copyable(`${userAddress}`)); panelItems.push(row('Network', text(`${network.name}`))); const ercToken = state.erc20Tokens.find( diff --git a/packages/starknet-snap/src/signTransaction.ts b/packages/starknet-snap/src/signTransaction.ts index 3c2ec4ad..cd8c6434 100644 --- a/packages/starknet-snap/src/signTransaction.ts +++ b/packages/starknet-snap/src/signTransaction.ts @@ -35,7 +35,6 @@ export async function signTransaction(params: ApiParams): Promise { .filter((x) => x.length > 0); }; -export const getProvider = (network: Network, forceSequencer = false): Provider => { +export const getProvider = (network: Network): ProviderInterface => { let providerParam: ProviderOptions = {}; - // same precedence as defined in starknet.js Provider class constructor - if (network.nodeUrl && !forceSequencer) { - providerParam = { - rpc: { - nodeUrl: network.nodeUrl, - }, - }; - } else if (network.baseUrl) { - providerParam = { - sequencer: { - baseUrl: network.baseUrl, - }, - }; - } + providerParam = { + nodeUrl: getRPCUrl(network.chainId), + }; return new Provider(providerParam); }; @@ -115,12 +106,11 @@ export const declareContract = async ( senderAddress: string, privateKey: string | Uint8Array, contractPayload: DeclareContractPayload, - transactionsDetail?: InvocationsDetails, + invocationsDetails?: UniversalDetails, cairoVersion?: CairoVersion, ): Promise => { return getAccountInstance(network, senderAddress, privateKey, cairoVersion).declare( - contractPayload, - transactionsDetail, + contractPayload, { ...invocationsDetails, skipValidate: false, blockIdentifier: 'latest' } ); }; @@ -130,8 +120,11 @@ export const estimateFee = async ( privateKey: string | Uint8Array, txnInvocation: Call | Call[], cairoVersion?: CairoVersion, + invocationsDetails?: UniversalDetails, ): Promise => { return getAccountInstance(network, senderAddress, privateKey, cairoVersion).estimateInvokeFee(txnInvocation, { + ...invocationsDetails, + skipValidate: false, blockIdentifier: 'latest', }); }; @@ -141,12 +134,16 @@ export const estimateFeeBulk = async ( senderAddress: string, privateKey: string | Uint8Array, txnInvocation: Invocations, - invocationsDetails: EstimateFeeDetails = { blockIdentifier: 'latest' }, + invocationsDetails?: UniversalDetails, cairoVersion?: CairoVersion, ): Promise => { return getAccountInstance(network, senderAddress, privateKey, cairoVersion).estimateFeeBulk( txnInvocation, - invocationsDetails, + { + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', + }, ); }; @@ -156,13 +153,17 @@ export const executeTxn = async ( privateKey: string | Uint8Array, txnInvocation: Call | Call[], abis?: Abi[], - invocationsDetails?: InvocationsDetails, + invocationsDetails?: UniversalDetails, cairoVersion?: CairoVersion, ): Promise => { return getAccountInstance(network, senderAddress, privateKey, cairoVersion).execute( txnInvocation, abis, - invocationsDetails, + { + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', + }, ); }; @@ -172,8 +173,8 @@ export const deployAccount = async ( contractCallData: RawCalldata, addressSalt: num.BigNumberish, privateKey: string | Uint8Array, - maxFee: num.BigNumberish, cairoVersion?: CairoVersion, + invocationsDetails?: UniversalDetails, ): Promise => { const deployAccountPayload = { classHash: ACCOUNT_CLASS_HASH, @@ -182,7 +183,9 @@ export const deployAccount = async ( addressSalt, }; return getAccountInstance(network, contractAddress, privateKey, cairoVersion).deployAccount(deployAccountPayload, { - maxFee, + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', }); }; @@ -193,6 +196,7 @@ export const estimateAccountDeployFee = async ( addressSalt: num.BigNumberish, privateKey: string | Uint8Array, cairoVersion?: CairoVersion, + invocationsDetails?: UniversalDetails, ): Promise => { const deployAccountPayload = { classHash: ACCOUNT_CLASS_HASH, @@ -202,22 +206,27 @@ export const estimateAccountDeployFee = async ( }; return getAccountInstance(network, contractAddress, privateKey, cairoVersion).estimateAccountDeployFee( deployAccountPayload, + { + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', + } ); }; export const getSigner = async (userAccAddress: string, network: Network): Promise => { const resp = await callContract(network, userAccAddress, 'getSigner'); - return resp.result[0]; + return resp[0]; }; export const getVersion = async (userAccAddress: string, network: Network): Promise => { const resp = await callContract(network, userAccAddress, 'getVersion'); - return resp.result[0]; + return resp[0]; }; export const getOwner = async (userAccAddress: string, network: Network): Promise => { const resp = await callContract(network, userAccAddress, 'get_owner'); - return resp.result[0]; + return resp[0]; }; export const getContractOwner = async ( @@ -230,7 +239,7 @@ export const getContractOwner = async ( 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]; + return resp[0]; }; export const getTransactionStatus = async (transactionHash: num.BigNumberish, network: Network) => { @@ -347,6 +356,7 @@ export const getMassagedTransactions = async ( ); const bigIntTransferSelectorHex = num.toBigInt(TRANSFER_SELECTOR_HEX); + const bigIntUpgradeSelectorHex = num.toBigInt(UPGRADE_SELECTOR_HEX); let massagedTxns = await Promise.all( txns.map(async (txn) => { logger.log(`getMassagedTransactions: txn:\n${toJson(txn)}`); @@ -366,10 +376,17 @@ export const getMassagedTransactions = async ( txnHash: txnResp.transaction_hash || txn.hash, txnType: txn.type?.toLowerCase(), chainId: network.chainId, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore senderAddress: txnResp.sender_address || txnResp.contract_address || txn.contract_address || '', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore contractAddress: txnResp.calldata?.[1] || txnResp.contract_address || txn.contract_address || '', - contractFuncName: - num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex ? 'transfer' : txn.operations ?? '', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + contractFuncName: num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex ? 'transfer' : num.toBigInt(txnResp.calldata?.[2] || '') === bigIntUpgradeSelectorHex ? 'upgrade' : txn.operations ?? '', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore contractCallData: txnResp.calldata || [], timestamp: txn.timestamp, status: '', //DEPRECATION @@ -426,28 +443,28 @@ export const postData = async (url = '', data = {}) => { return response.json(); // parses JSON response into native JavaScript objects }; -export function getFullPublicKeyPairFromPrivateKey(privateKey: Hex) { +export function getFullPublicKeyPairFromPrivateKey(privateKey: string) { return encode.addHexPrefix(encode.buf2hex(ec.starkCurve.getPublicKey(privateKey, false))); } export const getTypedDataMessageSignature = ( - privateKey: Hex, - typedDataMessage: typedData.TypedData, + privateKey: string, + typedDataMessage: TypedData, signerUserAddress: string, ) => { const msgHash = typedData.getMessageHash(typedDataMessage, signerUserAddress); return ec.starkCurve.sign(msgHash, privateKey); }; -export const getSignatureBySignatureString = (signatureStr: Hex) => { +export const getSignatureBySignatureString = (signatureStr: string) => { return ec.starkCurve.Signature.fromDER(signatureStr); }; export const verifyTypedDataMessageSignature = ( - fullPublicKey: Hex, - typedDataMessage: typedData.TypedData, + fullPublicKey: string, + typedDataMessage: TypedData, signerUserAddress: num.BigNumberish, - signatureStr: Hex, + signatureStr: string, ) => { const signature = getSignatureBySignatureString(signatureStr); const msgHash = typedData.getMessageHash(typedDataMessage, signerUserAddress); @@ -571,7 +588,7 @@ export const isAccountDeployed = async (network: Network, address: string) => { } }; -export const addFeesFromAllTransactions = (fees: EstimateFee[]): EstimateFee => { +export const addFeesFromAllTransactions = (fees: EstimateFee[]): Partial => { let overall_fee_bn = num.toBigInt(0); let suggestedMaxFee_bn = num.toBigInt(0); @@ -735,10 +752,9 @@ export const signTransactions = async ( privateKey: string, transactions: Call[], transactionsDetail: InvocationsSignerDetails, - abis: Abi[], ): Promise => { const signer = new Signer(privateKey); - const signatures = await signer.signTransaction(transactions, transactionsDetail, abis); + const signatures = await signer.signTransaction(transactions, transactionsDetail); return stark.signatureToDecimalArray(signatures); }; @@ -760,11 +776,7 @@ export const signDeclareTransaction = async ( return stark.signatureToDecimalArray(signatures); }; -export const signMessage = async ( - privateKey: Hex, - typedDataMessage: typedData.TypedData, - signerUserAddress: string, -) => { +export const signMessage = async (privateKey: string, typedDataMessage: TypedData, signerUserAddress: string) => { const signer = new Signer(privateKey); const signatures = await signer.signMessage(typedDataMessage, signerUserAddress); return stark.signatureToDecimalArray(signatures); @@ -772,5 +784,5 @@ export const signMessage = async ( export const getStarkNameUtil = async (network: Network, userAddress: string) => { const provider = getProvider(network); - return provider.getStarkName(userAddress); + return Account.getStarkName(provider, userAddress); }; diff --git a/packages/starknet-snap/test/constants.test.ts b/packages/starknet-snap/test/constants.test.ts index a4c9199b..349a9b7b 100644 --- a/packages/starknet-snap/test/constants.test.ts +++ b/packages/starknet-snap/test/constants.test.ts @@ -1,5 +1,5 @@ import { JsonBIP44CoinTypeNode } from '@metamask/key-tree'; -import { num, constants } from 'starknet'; +import { EstimateFee, GetTransactionResponse, constants, num } from 'starknet'; import { AccContract, Erc20Token, @@ -362,45 +362,43 @@ export const mainnetTxn1: Transaction = { eventIds: [], }; -export const getBalanceResp = { - result: ['0x0', '0x0'], -}; +export const getBalanceResp = ['0x0', '0x0']; export const estimateDeployFeeResp = { overall_fee: num.toBigInt('0x0'), gas_consumed: num.toBigInt('0x0'), suggestedMaxFee: num.toBigInt('0x0'), gas_price: num.toBigInt('0x0'), -}; +} as EstimateFee; export const estimateDeployFeeResp2 = { overall_fee: num.toBigInt('0xaff3f0a7'), gas_consumed: num.toBigInt('0x18e1'), suggestedMaxFee: num.toBigInt('0x0107ede8fa'), gas_price: num.toBigInt('0x071287'), -}; +} as EstimateFee; export const estimateDeployFeeResp3 = { overall_fee: num.toBigInt('0x1160f77b2edd'), gas_consumed: num.toBigInt('0x18e1'), suggestedMaxFee: num.toBigInt('0x1a117338c64b'), gas_price: num.toBigInt('0xb2d3297d'), -}; +} as EstimateFee; export const estimateDeployFeeResp4 = { overall_fee: num.toBigInt('0x1160f77b2edd'), suggestedMaxFee: num.toBigInt('0x1a117338c64b'), -}; +} as EstimateFee; export const estimateFeeResp = { overall_fee: num.toBigInt('0x0dc3e44d89e6'), suggestedMaxFee: num.toBigInt('0x14a5d6744ed9'), -}; +} as EstimateFee; export const estimateFeeResp2 = { overall_fee: num.toBigInt('0x0dc3e44d89e6'), suggestedMaxFee: num.toBigInt('0x14a5d6744ed9'), -}; +} as EstimateFee; export const unfoundUserAddress = '0x0404d766fd6db2c23177e5ea289af99e81e5c4a7badae588950ad0f8572c49b9'; export const unfoundUserPrivateKey = '0x38d36fc25592257d913d143d37e12533dba9f6721db6fa954ed513b0dc3d68b'; @@ -675,7 +673,7 @@ export const getTxnFromSequencerResp1 = { ], transaction_hash: '0x1366c2f9f46b1a86ba0c28b5a08fa0aa3750c4d1cbe06e97e72bd46ae2ac1f9', version: '0x0', -}; +} as unknown as GetTransactionResponse; export const getTxnFromSequencerResp2 = { status: 'ACCEPTED_ON_L1', @@ -701,7 +699,7 @@ export const getTxnFromSequencerResp2 = { contract_address: '0x5a98ec74a40383cf99896bfea2ec5e6aad16c7eed50025a5f569d585ebb13a2', transaction_hash: '0x6beceb86579dc78749bdaaf441501edc21e218e020236e2ebea1b6a96d0bac7', version: '0x0', -}; +} as unknown as GetTransactionResponse; export const expectedMassagedTxn4: Transaction = { chainId: '0x534e5f5345504f4c4941', diff --git a/packages/starknet-snap/test/src/getErc20TokenBalance.test.ts b/packages/starknet-snap/test/src/getErc20TokenBalance.test.ts index 36eb5349..1cef6949 100644 --- a/packages/starknet-snap/test/src/getErc20TokenBalance.test.ts +++ b/packages/starknet-snap/test/src/getErc20TokenBalance.test.ts @@ -34,7 +34,7 @@ describe('Test function: getErc20TokenBalance', function () { it('should get the ERC-20 token balance correctly', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['0x64a'] }; //1610 in decimal + return ['0x64a']; //1610 in decimal }); const requestObject: GetErc20TokenBalanceRequestParams = { tokenAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', @@ -65,7 +65,7 @@ describe('Test function: getErc20TokenBalance', function () { it('should throw error if userAddress is empty', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['0x64a'] }; //1610 in decimal + return ['0x64a']; //1610 in decimal }); const requestObject: GetErc20TokenBalanceRequestParams = { tokenAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', @@ -85,7 +85,7 @@ describe('Test function: getErc20TokenBalance', function () { it('should throw error if tokenAddress is empty', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['0x64a'] }; //1610 in decimal + return ['0x64a']; //1610 in decimal }); const requestObject: GetErc20TokenBalanceRequestParams = { tokenAddress: '', @@ -105,7 +105,7 @@ describe('Test function: getErc20TokenBalance', function () { it('should throw error if userAddress is invalid', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['0x64a'] }; //1610 in decimal + return ['0x64a']; //1610 in decimal }); const requestObject: GetErc20TokenBalanceRequestParams = { tokenAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', @@ -125,7 +125,7 @@ describe('Test function: getErc20TokenBalance', function () { it('should throw error if tokenAddress is invalid', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['0x64a'] }; //1610 in decimal + return ['0x64a']; //1610 in decimal }); const requestObject: GetErc20TokenBalanceRequestParams = { tokenAddress: 'wrongAddress', diff --git a/packages/starknet-snap/test/src/getValue.test.ts b/packages/starknet-snap/test/src/getValue.test.ts index e59914ae..6d8c92b8 100644 --- a/packages/starknet-snap/test/src/getValue.test.ts +++ b/packages/starknet-snap/test/src/getValue.test.ts @@ -34,7 +34,7 @@ describe('Test function: getValue', function () { it('should call the contract correctly', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['1'] }; + return ['1']; }); const requestObject: GetValueRequestParams = { contractAddress: '0x07394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10', diff --git a/packages/starknet-snap/test/src/signDeclareTransaction.test.ts b/packages/starknet-snap/test/src/signDeclareTransaction.test.ts index e82d6dcf..3274ae17 100644 --- a/packages/starknet-snap/test/src/signDeclareTransaction.test.ts +++ b/packages/starknet-snap/test/src/signDeclareTransaction.test.ts @@ -9,7 +9,7 @@ import { createAccountProxyTxn, getBip44EntropyStub, account1, signature3 } from import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { Mutex } from 'async-mutex'; import { ApiParams, SignDeclareTransactionRequestParams } from '../../src/types/snapApi'; -import { constants } from 'starknet'; +import { DeclareSignerDetails, constants } from 'starknet'; import * as utils from '../../src/utils/starknetUtils'; chai.use(sinonChai); @@ -31,17 +31,19 @@ describe('Test function: signDeclareTransaction', function () { saveMutex: new Mutex(), }; + const declarePayload = { + classHash: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', + senderAddress: account1.address, + chainId: constants.StarknetChainId.SN_MAIN, + nonce: '0x1', + version: '0x0', + maxFee: 100, + } as unknown as DeclareSignerDetails; + const requestObject: SignDeclareTransactionRequestParams = { chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, signerAddress: account1.address, - transaction: { - classHash: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', - senderAddress: account1.address, - chainId: constants.StarknetChainId.SN_GOERLI, - nonce: '0x1', - version: '0x0', - maxFee: 100, - }, + transaction: declarePayload, enableAuthorize: true, }; diff --git a/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts b/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts index 2c5e49a1..e8de8315 100644 --- a/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts +++ b/packages/starknet-snap/test/src/signDeployAccountTransaction.test.ts @@ -9,7 +9,7 @@ import { createAccountProxyTxn, getBip44EntropyStub, account1, signature3 } from import { getAddressKeyDeriver } from '../../src/utils/keyPair'; import { Mutex } from 'async-mutex'; import { ApiParams, SignDeployAccountTransactionRequestParams } from '../../src/types/snapApi'; -import { constants } from 'starknet'; +import { DeployAccountSignerDetails, constants } from 'starknet'; import * as utils from '../../src/utils/starknetUtils'; chai.use(sinonChai); @@ -31,19 +31,21 @@ describe('Test function: signDeployAccountTransaction', function () { saveMutex: new Mutex(), }; + const declareNDeployPayload = { + classHash: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', + contractAddress: account1.address, + constructorCalldata: [], + addressSalt: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', + chainId: constants.StarknetChainId.SN_MAIN, + nonce: '0x1', + version: '0x0', + maxFee: 100, + } as unknown as DeployAccountSignerDetails; + const requestObject: SignDeployAccountTransactionRequestParams = { chainId: STARKNET_SEPOLIA_TESTNET_NETWORK.chainId, signerAddress: account1.address, - transaction: { - classHash: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', - contractAddress: account1.address, - constructorCalldata: [], - addressSalt: '0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918', - chainId: constants.StarknetChainId.SN_MAIN, - nonce: '0x1', - version: '0x0', - maxFee: 100, - }, + transaction: declareNDeployPayload, enableAuthorize: true, }; diff --git a/packages/starknet-snap/test/src/switchNetwork.test.ts b/packages/starknet-snap/test/src/switchNetwork.test.ts index fded4148..f48129ee 100644 --- a/packages/starknet-snap/test/src/switchNetwork.test.ts +++ b/packages/starknet-snap/test/src/switchNetwork.test.ts @@ -54,7 +54,7 @@ describe('Test function: switchNetwork', function () { expect(state.currentNetwork).to.be.eql(STARKNET_MAINNET_NETWORK); }); - it('should skip autherize when enableAuthorize is false or omit', async function () { + it('should skip authorize when enableAuthorize is false or omit', async function () { const requestObject: SwitchNetworkRequestParams = { chainId: STARKNET_MAINNET_NETWORK.chainId, }; diff --git a/packages/starknet-snap/test/utils/snapUtils.test.ts b/packages/starknet-snap/test/utils/snapUtils.test.ts index c0c6a5c6..e0a9b5a1 100644 --- a/packages/starknet-snap/test/utils/snapUtils.test.ts +++ b/packages/starknet-snap/test/utils/snapUtils.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { Mutex } from 'async-mutex'; +import { constants } from 'starknet'; import { dappUrl, @@ -8,6 +9,7 @@ import { getTransactionFromVoyagerUrl, getTransactionsFromVoyagerUrl, getVoyagerCredentials, + getRPCUrl, } from '../../src/utils/snapUtils'; import { WalletMock } from '../wallet.mock.test'; import { Network, SnapState } from '../../src/types/snapState'; @@ -16,7 +18,6 @@ import { STARKNET_TESTNET_NETWORK, STARKNET_MAINNET_NETWORK, } from '../../src/utils/constants'; -import { constants } from 'starknet'; describe('Snap Utils', () => { it('should return the proper dapp URL based on the environment', () => { @@ -133,3 +134,19 @@ describe('getVoyagerCredentials', () => { expect(getVoyagerCredentials()).to.have.key('X-API-Key'); }); }); + +describe('getRPCUrl', () => { + it('returns Mainnet RPC URL if chain id is Mainnet', () => { + expect(getRPCUrl(constants.StarknetChainId.SN_MAIN)).to.be.equal('https://starknet-mainnet.g.alchemy.com/starknet/version/rpc/v0_7/'); + }); + + it('returns Sepolia RPC URL if chain id is not either Mainnet or Sepolia', () => { + expect(getRPCUrl('0x534e5f474f45524c49')).to.be.equal('https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/'); + }); + + it('returns Sepolia RPC URL if chain id is Sepolia', () => { + expect(getRPCUrl(STARKNET_SEPOLIA_TESTNET_NETWORK.chainId)).to.be.equal( + 'https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/', + ); + }); +}); \ No newline at end of file diff --git a/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index cd60f228..d4b22d21 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -136,7 +136,7 @@ describe('Test function: callContract', function () { it('should get the signer of an user account correctly', async function () { sandbox.stub(utils, 'callContract').callsFake(async () => { - return { result: ['0x795d62a9896b221af17bedd8cceb8d963ac6864857d7476e2f8c03ba0c5df9'] }; + return ['0x795d62a9896b221af17bedd8cceb8d963ac6864857d7476e2f8c03ba0c5df9']; }); const result = await utils.getSigner(userAddress, STARKNET_SEPOLIA_TESTNET_NETWORK); @@ -271,7 +271,7 @@ describe('Test function: getVersion', function () { const expected = '0.3.0'; beforeEach(function () { - callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ({ result: [expected] })); + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ([expected])); }); afterEach(function () { @@ -294,7 +294,7 @@ describe('Test function: getOwner', function () { const expected = 'pk'; beforeEach(function () { - callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ({ result: [expected] })); + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ([expected])); }); afterEach(function () { @@ -317,7 +317,7 @@ describe('Test function: getBalance', function () { const expected = 'pk'; beforeEach(function () { - callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ({ result: [expected] })); + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ([expected])); }); afterEach(function () { diff --git a/yarn.lock b/yarn.lock index b9df91dd..751c880d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -64,6 +64,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.24.1": + version: 7.24.2 + resolution: "@babel/code-frame@npm:7.24.2" + dependencies: + "@babel/highlight": ^7.24.2 + picocolors: ^1.0.0 + checksum: 70e867340cfe09ca5488b2f36372c45cabf43c79a5b6426e6df5ef0611ff5dfa75a57dda841895693de6008f32c21a7c97027a8c7bcabd63a7d17416cbead6f8 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.1, @babel/compat-data@npm:^7.20.5": version: 7.21.0 resolution: "@babel/compat-data@npm:7.21.0" @@ -198,6 +208,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/generator@npm:7.24.1" + dependencies: + "@babel/types": ^7.24.0 + "@jridgewell/gen-mapping": ^0.3.5 + "@jridgewell/trace-mapping": ^0.3.25 + jsesc: ^2.5.1 + checksum: 98c6ce5ec7a1cba2bdf35cdf607273b90cf7cf82bbe75cd0227363fb84d7e1bd8efa74f40247d5900c8c009123f10132ad209a05283757698de918278c3c6700 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.16.0, @babel/helper-annotate-as-pure@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" @@ -776,6 +798,18 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.24.2": + version: 7.24.2 + resolution: "@babel/highlight@npm:7.24.2" + dependencies: + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + picocolors: ^1.0.0 + checksum: 5f17b131cc3ebf3ab285a62cf98a404aef1bd71a6be045e748f8d5bf66d6a6e1aefd62f5972c84369472e8d9f22a614c58a89cd331eb60b7ba965b31b1bbeaf5 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.3": version: 7.21.3 resolution: "@babel/parser@npm:7.21.3" @@ -794,6 +828,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.23.9, @babel/parser@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/parser@npm:7.24.1" + bin: + parser: ./bin/babel-parser.js + checksum: a1068941dddf82ffdf572565b8b7b2cddb963ff9ddf97e6e28f50e843d820b4285e6def8f59170104a94e2a91ae2e3b326489886d77a57ea29d468f6a5e79bf9 + languageName: node + linkType: hard + "@babel/parser@npm:^7.24.0": version: 7.24.0 resolution: "@babel/parser@npm:7.24.0" @@ -2861,6 +2904,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.23.2": + version: 7.24.1 + resolution: "@babel/traverse@npm:7.24.1" + dependencies: + "@babel/code-frame": ^7.24.1 + "@babel/generator": ^7.24.1 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.24.1 + "@babel/types": ^7.24.0 + debug: ^4.3.1 + globals: ^11.1.0 + checksum: 92a5ca906abfba9df17666d2001ab23f18600035f706a687055a0e392a690ae48d6fec67c8bd4ef19ba18699a77a5b7f85727e36b83f7d110141608fe0c24fe9 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.24.0": version: 7.24.0 resolution: "@babel/traverse@npm:7.24.0" @@ -3153,17 +3214,48 @@ __metadata: languageName: node linkType: hard +"@consensys/get-starknet@workspace:packages/get-starknet": + version: 0.0.0-use.local + resolution: "@consensys/get-starknet@workspace:packages/get-starknet" + dependencies: + "@types/chai": ^4.3.1 + "@types/chai-as-promised": ^7.1.5 + "@types/sinon": ^10.0.11 + "@types/sinon-chai": ^3.2.8 + "@typescript-eslint/eslint-plugin": ^5.20.0 + "@typescript-eslint/parser": ^5.20.0 + chai: ^4.3.6 + chai-as-promised: ^7.1.1 + depcheck: ^1.4.7 + eslint: ^8.13.0 + get-starknet-core: ^3.2.0 + html-webpack-plugin: 5.6.0 + prettier: ^2.6.2 + rimraf: ^3.0.2 + serve: 14.2.1 + sinon: ^13.0.2 + sinon-chai: ^3.7.0 + starknet: ^5.14.0 + ts-loader: ^9.5.1 + typescript: ^4.6.3 + webpack: ^5.91.0 + webpack-cli: ^5.1.4 + webpack-dev-server: 4.15.1 + languageName: unknown + linkType: soft + "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": - version: 2.6.1 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=b7e81c&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + version: 2.7.0 + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=660e3f&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: "@metamask/snaps-sdk": 3.0.1 async-mutex: ^0.3.2 + dotenv: ^16.4.5 ethereum-unit-converter: ^0.0.17 ethers: ^5.5.1 - starknet: ^5.14.0 + starknet: 6.7.0 starknet_v4.22.0: "npm:starknet@4.22.0" - checksum: cb88578a94965f39209e923ac4d0d68e55d3a715fc2a8066afd062f2d27af994c2b56b63ff4176f21003a5e0164a465848a51d575f48c6d36fb318be4d4374de + checksum: 983f842b9dba2bec7cfcd8907f9dd8d420befc61e045b7138f1786f1662ff6c4f3279b8db49193cf2ae39dff0b9173a5475d1b072f918fda72d54d9d05d4bfd5 languageName: node linkType: hard @@ -3187,7 +3279,7 @@ __metadata: chai-as-promised: ^7.1.1 concurrently: ^7.1.0 cross-env: ^7.0.3 - envify: ^4.1.0 + dotenv: ^16.4.5 eslint: ^8.13.0 ethereum-unit-converter: ^0.0.17 ethers: ^5.5.1 @@ -3198,7 +3290,7 @@ __metadata: sinon: ^13.0.2 sinon-chai: ^3.7.0 standard-version: ^9.5.0 - starknet: ^5.14.0 + starknet: 6.7.0 starknet_v4.22.0: "npm:starknet@4.22.0" ts-node: ^10.8.1 typescript: ^4.6.3 @@ -3454,7 +3546,7 @@ __metadata: languageName: node linkType: hard -"@discoveryjs/json-ext@npm:^0.5.3": +"@discoveryjs/json-ext@npm:^0.5.0, @discoveryjs/json-ext@npm:^0.5.3": version: 0.5.7 resolution: "@discoveryjs/json-ext@npm:0.5.7" checksum: 2176d301cc258ea5c2324402997cf8134ebb212469c0d397591636cea8d3c02f2b3cf9fd58dcb748c7a0dade77ebdc1b10284fa63e608c033a1db52fddc69918 @@ -4586,6 +4678,17 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" + dependencies: + "@jridgewell/set-array": ^1.2.1 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.24 + checksum: ff7a1764ebd76a5e129c8890aa3e2f46045109dabde62b0b6c6a250152227647178ff2069ea234753a690d8f3c4ac8b5e7b267bbee272bffb7f3b0a370ab6e52 + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" @@ -4607,6 +4710,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 832e513a85a588f8ed4f27d1279420d8547743cc37fcad5a5a76fc74bb895b013dfe614d0eed9cb860048e6546b798f8f2652020b4b2ba0561b05caa8c654b10 + languageName: node + linkType: hard + "@jridgewell/source-map@npm:^0.3.2": version: 0.3.2 resolution: "@jridgewell/source-map@npm:0.3.2" @@ -4634,7 +4744,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.14": +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -4671,6 +4781,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: 9d3c40d225e139987b50c48988f8717a54a8c994d8a948ee42e1412e08988761d0754d7d10b803061cc3aebf35f92a5dbbab493bd0e1a9ef9e89a2130e83ba34 + languageName: node + linkType: hard + "@lavamoat/aa@npm:^3.1.1": version: 3.1.2 resolution: "@lavamoat/aa@npm:3.1.2" @@ -5481,12 +5601,12 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:~1.0.0": - version: 1.0.0 - resolution: "@noble/curves@npm:1.0.0" +"@noble/curves@npm:~1.4.0": + version: 1.4.0 + resolution: "@noble/curves@npm:1.4.0" dependencies: - "@noble/hashes": 1.3.0 - checksum: 6bcef44d626c640dc8961819d68dd67dffb907e3b973b7c27efe0ecdd9a5c6ce62c7b9e3dfc930c66605dced7f1ec0514d191c09a2ce98d6d52b66e3315ffa79 + "@noble/hashes": 1.4.0 + checksum: 0014ff561d16e98da4a57e2310a4015e4bdab3b1e1eafcd18d3f9b955c29c3501452ca5d702fddf8ca92d570bbeadfbe53fe16ebbd81a319c414f739154bb26b languageName: node linkType: hard @@ -5504,20 +5624,20 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.0": - version: 1.3.0 - resolution: "@noble/hashes@npm:1.3.0" - checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e - languageName: node - linkType: hard - -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:~1.3.2": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:~1.3.2, @noble/hashes@npm:~1.3.3": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b languageName: node linkType: hard +"@noble/hashes@npm:1.4.0": + version: 1.4.0 + resolution: "@noble/hashes@npm:1.4.0" + checksum: 8ba816ae26c90764b8c42493eea383716396096c5f7ba6bea559993194f49d80a73c081f315f4c367e51bd2d5891700bcdfa816b421d24ab45b41cb03e4f3342 + languageName: node + linkType: hard + "@noble/hashes@npm:~1.1.1": version: 1.1.5 resolution: "@noble/hashes@npm:1.1.5" @@ -5525,13 +5645,6 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:~1.3.0": - version: 1.3.1 - resolution: "@noble/hashes@npm:1.3.1" - checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 - languageName: node - linkType: hard - "@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:^1.5.5, @noble/secp256k1@npm:^1.7.1, @noble/secp256k1@npm:~1.7.0": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" @@ -5765,6 +5878,48 @@ __metadata: languageName: node linkType: hard +"@rometools/cli-darwin-arm64@npm:12.1.3": + version: 12.1.3 + resolution: "@rometools/cli-darwin-arm64@npm:12.1.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rometools/cli-darwin-x64@npm:12.1.3": + version: 12.1.3 + resolution: "@rometools/cli-darwin-x64@npm:12.1.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rometools/cli-linux-arm64@npm:12.1.3": + version: 12.1.3 + resolution: "@rometools/cli-linux-arm64@npm:12.1.3" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@rometools/cli-linux-x64@npm:12.1.3": + version: 12.1.3 + resolution: "@rometools/cli-linux-x64@npm:12.1.3" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@rometools/cli-win32-arm64@npm:12.1.3": + version: 12.1.3 + resolution: "@rometools/cli-win32-arm64@npm:12.1.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rometools/cli-win32-x64@npm:12.1.3": + version: 12.1.3 + resolution: "@rometools/cli-win32-x64@npm:12.1.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rushstack/eslint-patch@npm:^1.1.0": version: 1.2.0 resolution: "@rushstack/eslint-patch@npm:1.2.0" @@ -5786,6 +5941,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.3": + version: 1.1.6 + resolution: "@scure/base@npm:1.1.6" + checksum: d6deaae91deba99e87939af9e55d80edba302674983f32bba57f942e22b1726a83c62dc50d8f4370a5d5d35a212dda167fb169f4b0d0c297488d8604608fc3d3 + languageName: node + linkType: hard + "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -5828,6 +5990,16 @@ __metadata: languageName: node linkType: hard +"@scure/starknet@npm:~1.0.0": + version: 1.0.0 + resolution: "@scure/starknet@npm:1.0.0" + dependencies: + "@noble/curves": ~1.3.0 + "@noble/hashes": ~1.3.3 + checksum: 2df4234ff7ae025b72c0e7eb5af81e35bb5790d72f9dc7746bd91183527081df3e637579616fa7717d4227abf67e11380cf266bea43144231325503f52644087 + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.24.1": version: 0.24.51 resolution: "@sinclair/typebox@npm:0.24.51" @@ -7973,6 +8145,13 @@ __metadata: languageName: node linkType: hard +"@types/minimatch@npm:^3.0.3": + version: 3.0.5 + resolution: "@types/minimatch@npm:3.0.5" + checksum: c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92 + languageName: node + linkType: hard + "@types/minimist@npm:^1.2.0": version: 1.2.2 resolution: "@types/minimist@npm:1.2.2" @@ -8333,6 +8512,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.5": + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" + dependencies: + "@types/node": "*" + checksum: 3ec416ea2be24042ebd677932a462cf16d2080393d8d7d0b1b3f5d6eaa4a7387aaf0eefb99193c0bfd29444857cf2e0c3ac89899e130550dc6c14ada8a46d25e + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -8506,6 +8694,63 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-core@npm:3.4.21": + version: 3.4.21 + resolution: "@vue/compiler-core@npm:3.4.21" + dependencies: + "@babel/parser": ^7.23.9 + "@vue/shared": 3.4.21 + entities: ^4.5.0 + estree-walker: ^2.0.2 + source-map-js: ^1.0.2 + checksum: 0d6b7732bc5ca5b4561526bbe646f9acd09cd70561b6c822d15856347f21a009ebf30f2f85b1b7500f24f7c0333a2af8ee645c389abe52485c1f4724c982b306 + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:3.4.21": + version: 3.4.21 + resolution: "@vue/compiler-dom@npm:3.4.21" + dependencies: + "@vue/compiler-core": 3.4.21 + "@vue/shared": 3.4.21 + checksum: f53e4f4e0afc954cede91a8cbeb3a4e053531a43a0f5999d1b18da443ca3f1f6fc9344a8741c72c5719a61bb34e18004ac88e16747bcf145ebc8a31188263690 + languageName: node + linkType: hard + +"@vue/compiler-sfc@npm:^3.3.4": + version: 3.4.21 + resolution: "@vue/compiler-sfc@npm:3.4.21" + dependencies: + "@babel/parser": ^7.23.9 + "@vue/compiler-core": 3.4.21 + "@vue/compiler-dom": 3.4.21 + "@vue/compiler-ssr": 3.4.21 + "@vue/shared": 3.4.21 + estree-walker: ^2.0.2 + magic-string: ^0.30.7 + postcss: ^8.4.35 + source-map-js: ^1.0.2 + checksum: 226dc404be96a2811777825918d971feb42650e262159183548d64a463c4153fab97cdc2647224c609c89dbc0d930c6d9dbe6528ef52a1396b4b22163c20569a + languageName: node + linkType: hard + +"@vue/compiler-ssr@npm:3.4.21": + version: 3.4.21 + resolution: "@vue/compiler-ssr@npm:3.4.21" + dependencies: + "@vue/compiler-dom": 3.4.21 + "@vue/shared": 3.4.21 + checksum: c510bee68b1a5b7f8ae3fe771c10ce9c397f876a234ced9df89e4a8353f3874870857e929cbb37e6d785d355b43f2264dc3a7fd5cb6867dc5b39ddca607ea3ed + languageName: node + linkType: hard + +"@vue/shared@npm:3.4.21": + version: 3.4.21 + resolution: "@vue/shared@npm:3.4.21" + checksum: 5f30a408911f339c647baa88c45c3a2f6d58dbdaf2bd404753690f24b612717bdfe9050401d8ffb02613a9a06dd0b43c8307420cd69fda6e92e6d65bf9bc0c6f + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -8526,6 +8771,16 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/ast@npm:1.12.1" + dependencies: + "@webassemblyjs/helper-numbers": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + checksum: 31bcc64147236bd7b1b6d29d1f419c1f5845c785e1e42dc9e3f8ca2e05a029e9393a271b84f3a5bff2a32d35f51ff59e2181a6e5f953fe88576acd6750506202 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/ast@npm:1.9.0" @@ -8593,6 +8848,13 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-buffer@npm:1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/helper-buffer@npm:1.12.1" + checksum: c3ffb723024130308db608e86e2bdccd4868bbb62dffb0a9a1530606496f79c87f8565bd8e02805ce64912b71f1a70ee5fb00307258b0c082c3abf961d097eca + languageName: node + linkType: hard + "@webassemblyjs/helper-buffer@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/helper-buffer@npm:1.9.0" @@ -8692,6 +8954,18 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-wasm-section@npm:1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.12.1" + dependencies: + "@webassemblyjs/ast": 1.12.1 + "@webassemblyjs/helper-buffer": 1.12.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/wasm-gen": 1.12.1 + checksum: c19810cdd2c90ff574139b6d8c0dda254d42d168a9e5b3d353d1bc085f1d7164ccd1b3c05592a45a939c47f7e403dc8d03572bb686642f06a3d02932f6f0bc8f + languageName: node + linkType: hard + "@webassemblyjs/helper-wasm-section@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/helper-wasm-section@npm:1.9.0" @@ -8827,6 +9101,22 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-edit@npm:^1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/wasm-edit@npm:1.12.1" + dependencies: + "@webassemblyjs/ast": 1.12.1 + "@webassemblyjs/helper-buffer": 1.12.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/helper-wasm-section": 1.12.1 + "@webassemblyjs/wasm-gen": 1.12.1 + "@webassemblyjs/wasm-opt": 1.12.1 + "@webassemblyjs/wasm-parser": 1.12.1 + "@webassemblyjs/wast-printer": 1.12.1 + checksum: ae23642303f030af888d30c4ef37b08dfec7eab6851a9575a616e65d1219f880d9223913a39056dd654e49049d76e97555b285d1f7e56935047abf578cce0692 + languageName: node + linkType: hard + "@webassemblyjs/wasm-gen@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" @@ -8853,6 +9143,19 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-gen@npm:1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/wasm-gen@npm:1.12.1" + dependencies: + "@webassemblyjs/ast": 1.12.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/ieee754": 1.11.6 + "@webassemblyjs/leb128": 1.11.6 + "@webassemblyjs/utf8": 1.11.6 + checksum: 5787626bb7f0b033044471ddd00ce0c9fe1ee4584e8b73e232051e3a4c99ba1a102700d75337151c8b6055bae77eefa4548960c610a5e4a504e356bd872138ff + languageName: node + linkType: hard + "@webassemblyjs/wasm-gen@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wasm-gen@npm:1.9.0" @@ -8890,6 +9193,18 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-opt@npm:1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/wasm-opt@npm:1.12.1" + dependencies: + "@webassemblyjs/ast": 1.12.1 + "@webassemblyjs/helper-buffer": 1.12.1 + "@webassemblyjs/wasm-gen": 1.12.1 + "@webassemblyjs/wasm-parser": 1.12.1 + checksum: 0e8fa8a0645304a1e18ff40d3db5a2e9233ebaa169b19fcc651d6fc9fe2cac0ce092ddee927318015ae735d9cd9c5d97c0cafb6a51dcd2932ac73587b62df991 + languageName: node + linkType: hard + "@webassemblyjs/wasm-opt@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wasm-opt@npm:1.9.0" @@ -8930,6 +9245,20 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-parser@npm:1.12.1, @webassemblyjs/wasm-parser@npm:^1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/wasm-parser@npm:1.12.1" + dependencies: + "@webassemblyjs/ast": 1.12.1 + "@webassemblyjs/helper-api-error": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/ieee754": 1.11.6 + "@webassemblyjs/leb128": 1.11.6 + "@webassemblyjs/utf8": 1.11.6 + checksum: 176015de3551ac068cd4505d837414f258d9ade7442bd71efb1232fa26c9f6d7d4e11a5c816caeed389943f409af7ebff6899289a992d7a70343cb47009d21a8 + languageName: node + linkType: hard + "@webassemblyjs/wasm-parser@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wasm-parser@npm:1.9.0" @@ -8978,6 +9307,16 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wast-printer@npm:1.12.1": + version: 1.12.1 + resolution: "@webassemblyjs/wast-printer@npm:1.12.1" + dependencies: + "@webassemblyjs/ast": 1.12.1 + "@xtuc/long": 4.2.2 + checksum: 2974b5dda8d769145ba0efd886ea94a601e61fb37114c14f9a9a7606afc23456799af652ac3052f284909bd42edc3665a76bc9b50f95f0794c053a8a1757b713 + languageName: node + linkType: hard + "@webassemblyjs/wast-printer@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wast-printer@npm:1.9.0" @@ -8989,6 +9328,39 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/configtest@npm:^2.1.1": + version: 2.1.1 + resolution: "@webpack-cli/configtest@npm:2.1.1" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + checksum: 9f9f9145c2d05471fc83d426db1df85cf49f329836b0c4b9f46b6948bed4b013464c00622b136d2a0a26993ce2306976682592245b08ee717500b1db45009a72 + languageName: node + linkType: hard + +"@webpack-cli/info@npm:^2.0.2": + version: 2.0.2 + resolution: "@webpack-cli/info@npm:2.0.2" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + checksum: 8f9a178afca5c82e113aed1efa552d64ee5ae4fdff63fe747c096a981ec74f18a5d07bd6e89bbe6715c3e57d96eea024a410e58977169489fe1df044c10dd94e + languageName: node + linkType: hard + +"@webpack-cli/serve@npm:^2.0.5": + version: 2.0.5 + resolution: "@webpack-cli/serve@npm:2.0.5" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + peerDependenciesMeta: + webpack-dev-server: + optional: true + checksum: 75f0e54681796d567a71ac3e2781d2901a8d8cf1cdfc82f261034dddac59a8343e8c3bc5e32b4bb9d6766759ba49fb29a5cd86ef1701d79c506fe886bb63ac75 + languageName: node + linkType: hard + "@xtuc/ieee754@npm:^1.2.0": version: 1.2.0 resolution: "@xtuc/ieee754@npm:1.2.0" @@ -9003,6 +9375,13 @@ __metadata: languageName: node linkType: hard +"@zeit/schemas@npm:2.29.0": + version: 2.29.0 + resolution: "@zeit/schemas@npm:2.29.0" + checksum: 3cea06bb67d790336aca0cc17580fd492ff3fc66ef4d180dce7053ff7ff54ab81b56bf718ba6f537148c581161d06306a481ec218d540bff922e0e009844ffd1 + languageName: node + linkType: hard + "JSONStream@npm:^1.0.3, JSONStream@npm:^1.0.4": version: 1.3.5 resolution: "JSONStream@npm:1.3.5" @@ -9029,6 +9408,35 @@ __metadata: languageName: node linkType: hard +"abi-wan-kanabi-v1@npm:abi-wan-kanabi@^1.0.3, abi-wan-kanabi@npm:^1.0.1": + version: 1.0.3 + resolution: "abi-wan-kanabi@npm:1.0.3" + dependencies: + abi-wan-kanabi: ^1.0.1 + fs-extra: ^10.0.0 + rome: ^12.1.3 + typescript: ^4.9.5 + yargs: ^17.7.2 + bin: + generate: dist/generate.js + checksum: 03b03c507424f239a7832e310ffbb35568448f0eebbe19e8caf56b03962ad6c047db8c84c3e7487d1ff9c97b0b00733ac9579c3cc640179d2eeacc5558316237 + languageName: node + linkType: hard + +"abi-wan-kanabi-v2@npm:abi-wan-kanabi@^2.1.1, abi-wan-kanabi@npm:^2.2.2": + version: 2.2.2 + resolution: "abi-wan-kanabi@npm:2.2.2" + dependencies: + ansicolors: ^0.3.2 + cardinal: ^2.1.1 + fs-extra: ^10.0.0 + yargs: ^17.7.2 + bin: + generate: dist/generate.js + checksum: 4cf69171887c243a4b5857653f791f3b2042eba93a7326fe03120355cb9f0e29944b91650f96ad739bffad7d8ec9d88c71eeb0debe57df3cd905a1b7c9c2c4a8 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -9275,6 +9683,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:8.11.0": + version: 8.11.0 + resolution: "ajv@npm:8.11.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef + languageName: node + linkType: hard + "ajv@npm:^6.1.0, ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.12.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -9299,7 +9719,7 @@ __metadata: languageName: node linkType: hard -"ansi-align@npm:^3.0.0": +"ansi-align@npm:^3.0.0, ansi-align@npm:^3.0.1": version: 3.0.1 resolution: "ansi-align@npm:3.0.1" dependencies: @@ -9386,6 +9806,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 + languageName: node + linkType: hard + "ansi-to-html@npm:^0.6.11": version: 0.6.15 resolution: "ansi-to-html@npm:0.6.15" @@ -9397,6 +9824,13 @@ __metadata: languageName: node linkType: hard +"ansicolors@npm:^0.3.2, ansicolors@npm:~0.3.2": + version: 0.3.2 + resolution: "ansicolors@npm:0.3.2" + checksum: e84fae7ebc27ac96d9dbb57f35f078cd6dde1b7046b0f03f73dcefc9fbb1f2e82e3685d083466aded8faf038f9fa9ebb408d215282bcd7aaa301d5ac3c486815 + languageName: node + linkType: hard + "anymatch@npm:^2.0.0": version: 2.0.0 resolution: "anymatch@npm:2.0.0" @@ -9454,6 +9888,13 @@ __metadata: languageName: node linkType: hard +"arch@npm:^2.2.0": + version: 2.2.0 + resolution: "arch@npm:2.2.0" + checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f + languageName: node + linkType: hard + "archy@npm:^1.0.0": version: 1.0.0 resolution: "archy@npm:1.0.0" @@ -9491,6 +9932,13 @@ __metadata: languageName: node linkType: hard +"arg@npm:5.0.2, arg@npm:^5.0.2": + version: 5.0.2 + resolution: "arg@npm:5.0.2" + checksum: 6c69ada1a9943d332d9e5382393e897c500908d91d5cb735a01120d5f71daf1b339b7b8980cbeaba8fd1afc68e658a739746179e4315a26e8a28951ff9930078 + languageName: node + linkType: hard + "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -9498,13 +9946,6 @@ __metadata: languageName: node linkType: hard -"arg@npm:^5.0.2": - version: 5.0.2 - resolution: "arg@npm:5.0.2" - checksum: 6c69ada1a9943d332d9e5382393e897c500908d91d5cb735a01120d5f71daf1b339b7b8980cbeaba8fd1afc68e658a739746179e4315a26e8a28951ff9930078 - languageName: node - linkType: hard - "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -9561,6 +10002,13 @@ __metadata: languageName: node linkType: hard +"array-differ@npm:^3.0.0": + version: 3.0.0 + resolution: "array-differ@npm:3.0.0" + checksum: 117edd9df5c1530bd116c6e8eea891d4bd02850fd89b1b36e532b6540e47ca620a373b81feca1c62d1395d9ae601516ba538abe5e8172d41091da2c546b05fb7 + languageName: node + linkType: hard + "array-find-index@npm:^1.0.1": version: 1.0.2 resolution: "array-find-index@npm:1.0.2" @@ -10456,6 +10904,22 @@ __metadata: languageName: node linkType: hard +"boxen@npm:7.0.0": + version: 7.0.0 + resolution: "boxen@npm:7.0.0" + dependencies: + ansi-align: ^3.0.1 + camelcase: ^7.0.0 + chalk: ^5.0.1 + cli-boxes: ^3.0.0 + string-width: ^5.1.2 + type-fest: ^2.13.0 + widest-line: ^4.0.1 + wrap-ansi: ^8.0.1 + checksum: b917cf7a168ef3149635a8c02d5c9717d66182348bd27038d85328ad12655151e3324db0f2815253846c33e5f0ddf28b6cd52d56a12b9f88617b7f8f722b946a + languageName: node + linkType: hard + "boxen@npm:^5.1.2": version: 5.1.2 resolution: "boxen@npm:5.1.2" @@ -11010,6 +11474,13 @@ __metadata: languageName: node linkType: hard +"callsite@npm:^1.0.0": + version: 1.0.0 + resolution: "callsite@npm:1.0.0" + checksum: 569686d622a288a4f0a827466c2f967b6d7a98f2ee1e6ada9dcf5a6802267a5e2a995d40f07113b5f95c7b2b2d5cbff4fdde590195f2a8bed24b829d048688f8 + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -11069,13 +11540,20 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.0.0, camelcase@npm:^6.2.0, camelcase@npm:^6.2.1": +"camelcase@npm:^6.0.0, camelcase@npm:^6.2.0, camelcase@npm:^6.2.1, camelcase@npm:^6.3.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d languageName: node linkType: hard +"camelcase@npm:^7.0.0": + version: 7.0.1 + resolution: "camelcase@npm:7.0.1" + checksum: 86ab8f3ebf08bcdbe605a211a242f00ed30d8bfb77dab4ebb744dd36efbc84432d1c4adb28975ba87a1b8be40a80fbd1e60e2f06565315918fa7350011a26d3d + languageName: node + linkType: hard + "camelize@npm:^1.0.0": version: 1.0.1 resolution: "camelize@npm:1.0.1" @@ -11118,6 +11596,18 @@ __metadata: languageName: node linkType: hard +"cardinal@npm:^2.1.1": + version: 2.1.1 + resolution: "cardinal@npm:2.1.1" + dependencies: + ansicolors: ~0.3.2 + redeyed: ~2.1.0 + bin: + cdl: ./bin/cdl.js + checksum: e8d4ae46439cf8fed481c0efd267711ee91e199aa7821a9143e784ed94a6495accd01a0b36d84d377e8ee2cc9928a6c9c123b03be761c60b805f2c026b8a99ad + languageName: node + linkType: hard + "case-sensitive-paths-webpack-plugin@npm:^2.3.0, case-sensitive-paths-webpack-plugin@npm:^2.4.0": version: 2.4.0 resolution: "case-sensitive-paths-webpack-plugin@npm:2.4.0" @@ -11165,6 +11655,22 @@ __metadata: languageName: node linkType: hard +"chalk-template@npm:0.4.0": + version: 0.4.0 + resolution: "chalk-template@npm:0.4.0" + dependencies: + chalk: ^4.1.2 + checksum: 6c706802a79a7963cbce18f022b046fe86e438a67843151868852f80ea7346e975a6a9749991601e7e5d3b6a6c4852a04c53dc966a9a3d04031bd0e0ed53c819 + languageName: node + linkType: hard + +"chalk@npm:5.0.1": + version: 5.0.1 + resolution: "chalk@npm:5.0.1" + checksum: 7b45300372b908f0471fbf7389ce2f5de8d85bb949026fd51a1b95b10d0ed32c7ed5aab36dd5e9d2bf3191867909b4404cef75c5f4d2d1daeeacd301dd280b76 + languageName: node + linkType: hard + "chalk@npm:^2.0.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -11196,6 +11702,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.0.1": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80 + languageName: node + linkType: hard + "char-regex@npm:^1.0.2": version: 1.0.2 resolution: "char-regex@npm:1.0.2" @@ -11409,6 +11922,13 @@ __metadata: languageName: node linkType: hard +"cli-boxes@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-boxes@npm:3.0.0" + checksum: 637d84419d293a9eac40a1c8c96a2859e7d98b24a1a317788e13c8f441be052fc899480c6acab3acc82eaf1bccda6b7542d7cdcf5c9c3cc39227175dc098d5b2 + languageName: node + linkType: hard + "cli-cursor@npm:^3.1.0": version: 3.1.0 resolution: "cli-cursor@npm:3.1.0" @@ -11445,6 +11965,17 @@ __metadata: languageName: node linkType: hard +"clipboardy@npm:3.0.0": + version: 3.0.0 + resolution: "clipboardy@npm:3.0.0" + dependencies: + arch: ^2.2.0 + execa: ^5.1.1 + is-wsl: ^2.2.0 + checksum: 2c292acb59705494cbe07d7df7c8becff4f01651514d32ebd80f4aec2d20946d8f3824aac67ecdf2d09ef21fdf0eb24b6a7f033c137ccdceedc4661c54455c94 + languageName: node + linkType: hard + "cliui@npm:^6.0.0": version: 6.0.0 resolution: "cliui@npm:6.0.0" @@ -11628,6 +12159,13 @@ __metadata: languageName: node linkType: hard +"colorette@npm:^2.0.14": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d + languageName: node + linkType: hard + "combine-source-map@npm:^0.8.0, combine-source-map@npm:~0.8.0": version: 0.8.0 resolution: "combine-source-map@npm:0.8.0" @@ -11656,6 +12194,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^10.0.1": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 436901d64a818295803c1996cd856621a74f30b9f9e28a588e726b2b1670665bccd7c1a77007ebf328729f0139838a88a19265858a0fa7a8728c4656796db948 + languageName: node + linkType: hard + "commander@npm:^2.19.0, commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -11738,7 +12283,7 @@ __metadata: languageName: node linkType: hard -"compression@npm:^1.7.4": +"compression@npm:1.7.4, compression@npm:^1.7.4": version: 1.7.4 resolution: "compression@npm:1.7.4" dependencies: @@ -12203,7 +12748,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^7.0.0": +"cosmiconfig@npm:^7.0.0, cosmiconfig@npm:^7.1.0": version: 7.1.0 resolution: "cosmiconfig@npm:7.1.0" dependencies: @@ -12912,6 +13457,13 @@ __metadata: languageName: node linkType: hard +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7 + languageName: node + linkType: hard + "deep-freeze-strict@npm:^1.1.1": version: 1.1.1 resolution: "deep-freeze-strict@npm:1.1.1" @@ -13039,6 +13591,39 @@ __metadata: languageName: node linkType: hard +"depcheck@npm:^1.4.7": + version: 1.4.7 + resolution: "depcheck@npm:1.4.7" + dependencies: + "@babel/parser": ^7.23.0 + "@babel/traverse": ^7.23.2 + "@vue/compiler-sfc": ^3.3.4 + callsite: ^1.0.0 + camelcase: ^6.3.0 + cosmiconfig: ^7.1.0 + debug: ^4.3.4 + deps-regex: ^0.2.0 + findup-sync: ^5.0.0 + ignore: ^5.2.4 + is-core-module: ^2.12.0 + js-yaml: ^3.14.1 + json5: ^2.2.3 + lodash: ^4.17.21 + minimatch: ^7.4.6 + multimatch: ^5.0.0 + please-upgrade-node: ^3.2.0 + readdirp: ^3.6.0 + require-package-name: ^2.0.1 + resolve: ^1.22.3 + resolve-from: ^5.0.0 + semver: ^7.5.4 + yargs: ^16.2.0 + bin: + depcheck: bin/depcheck.js + checksum: e648788554ba88bd0dc37ce398f7756f143a78487b4ee3ac01756ad7ed97034476e0709497e9f8e474117bd4258db669a53fd46fafb703f151c9a0394fc8a55a + languageName: node + linkType: hard + "depd@npm:2.0.0, depd@npm:^2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -13053,6 +13638,13 @@ __metadata: languageName: node linkType: hard +"deps-regex@npm:^0.2.0": + version: 0.2.0 + resolution: "deps-regex@npm:0.2.0" + checksum: a782304d481824c21c5aaff3d7fbd2eba9b112688cbadb36537304dde61e106595d4858bd097fad1df8b96fbff3df571dc9bfd73b749cbd24fd088cce3a999d8 + languageName: node + linkType: hard + "deps-sort@npm:^2.0.1": version: 2.0.1 resolution: "deps-sort@npm:2.0.1" @@ -13100,6 +13692,13 @@ __metadata: languageName: node linkType: hard +"detect-file@npm:^1.0.0": + version: 1.0.0 + resolution: "detect-file@npm:1.0.0" + checksum: 1861e4146128622e847abe0e1ed80fef01e78532665858a792267adf89032b7a9c698436137707fcc6f02956c2a6a0052d6a0cef5be3d4b76b1ff0da88e2158a + languageName: node + linkType: hard + "detect-indent@npm:^6.0.0": version: 6.1.0 resolution: "detect-indent@npm:6.1.0" @@ -13442,6 +14041,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.4.5": + version: 16.4.5 + resolution: "dotenv@npm:16.4.5" + checksum: 301a12c3d44fd49888b74eb9ccf9f07a1f5df43f489e7fcb89647a2edcd84c42d6bc349dc8df099cd18f07c35c7b04685c1a4f3e6a6a9e6b30f8d48c15b7f49c + languageName: node + linkType: hard + "dotenv@npm:^8.0.0": version: 8.6.0 resolution: "dotenv@npm:8.6.0" @@ -13487,6 +14093,13 @@ __metadata: languageName: node linkType: hard +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed + languageName: node + linkType: hard + "ecc-jsbn@npm:~0.1.1": version: 0.1.2 resolution: "ecc-jsbn@npm:0.1.2" @@ -13626,6 +14239,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.16.0": + version: 5.16.0 + resolution: "enhanced-resolve@npm:5.16.0" + dependencies: + graceful-fs: ^4.2.4 + tapable: ^2.2.0 + checksum: ccfd01850ecf2aa51e8554d539973319ff7d8a539ef1e0ba3460a0ccad6223c4ef6e19165ee64161b459cd8a48df10f52af4434c60023c65fde6afa32d475f7e + languageName: node + linkType: hard + "enhanced-resolve@npm:^5.10.0": version: 5.12.0 resolution: "enhanced-resolve@npm:5.12.0" @@ -13653,6 +14276,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^4.5.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -13660,15 +14290,12 @@ __metadata: languageName: node linkType: hard -"envify@npm:^4.1.0": - version: 4.1.0 - resolution: "envify@npm:4.1.0" - dependencies: - esprima: ^4.0.0 - through: ~2.3.4 +"envinfo@npm:^7.7.3": + version: 7.13.0 + resolution: "envinfo@npm:7.13.0" bin: - envify: bin/envify - checksum: ee48873a56a117b812fb5e4d50870bf4440f5ba3462db4f4677e041fbdf2d05c70d72baa59af5f584373ab54d751b6543087a9afd4313774e058f020486728b8 + envinfo: dist/cli.js + checksum: 822fc30f53bd0be67f0e25be96eb6a2562b8062f3058846bbd7ec471bd4b7835fca6436ee72c4029c8ae4a3d8f8cddbe2ee725b22291f015232d20a682bee732 languageName: node linkType: hard @@ -14189,7 +14816,7 @@ __metadata: languageName: node linkType: hard -"esprima@npm:^4.0.0, esprima@npm:^4.0.1": +"esprima@npm:^4.0.0, esprima@npm:^4.0.1, esprima@npm:~4.0.0": version: 4.0.1 resolution: "esprima@npm:4.0.1" bin: @@ -14249,6 +14876,13 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 6151e6f9828abe2259e57f5fd3761335bb0d2ebd76dc1a01048ccee22fabcfef3c0859300f6d83ff0d1927849368775ec5a6d265dde2f6de5a1be1721cd94efc + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -14498,6 +15132,15 @@ __metadata: languageName: node linkType: hard +"expand-tilde@npm:^2.0.0, expand-tilde@npm:^2.0.2": + version: 2.0.2 + resolution: "expand-tilde@npm:2.0.2" + dependencies: + homedir-polyfill: ^1.0.1 + checksum: 2efe6ed407d229981b1b6ceb552438fbc9e5c7d6a6751ad6ced3e0aa5cf12f0b299da695e90d6c2ac79191b5c53c613e508f7149e4573abfbb540698ddb7301a + languageName: node + linkType: hard + "expect@npm:^27.5.1": version: 27.5.1 resolution: "expect@npm:27.5.1" @@ -14723,6 +15366,13 @@ __metadata: languageName: node linkType: hard +"fastest-levenshtein@npm:^1.0.12": + version: 1.0.16 + resolution: "fastest-levenshtein@npm:1.0.16" + checksum: a78d44285c9e2ae2c25f3ef0f8a73f332c1247b7ea7fb4a191e6bb51aa6ee1ef0dfb3ed113616dcdc7023e18e35a8db41f61c8d88988e877cf510df8edafbc71 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.15.0 resolution: "fastq@npm:1.15.0" @@ -14750,6 +15400,16 @@ __metadata: languageName: node linkType: hard +"fetch-cookie@npm:^3.0.0": + version: 3.0.1 + resolution: "fetch-cookie@npm:3.0.1" + dependencies: + set-cookie-parser: ^2.4.8 + tough-cookie: ^4.0.0 + checksum: 01657ccdf6305c3bf9b2375a9aedd6930f803acc77583c02e96a0b3d84068cc9406d2ad4d061a8c4d9c106c021de51dd2aa505a0a05a4477a1a859052f2537a0 + languageName: node + linkType: hard + "fetch-retry@npm:^5.0.2": version: 5.0.4 resolution: "fetch-retry@npm:5.0.4" @@ -14940,6 +15600,18 @@ __metadata: languageName: node linkType: hard +"findup-sync@npm:^5.0.0": + version: 5.0.0 + resolution: "findup-sync@npm:5.0.0" + dependencies: + detect-file: ^1.0.0 + is-glob: ^4.0.3 + micromatch: ^4.0.4 + resolve-dir: ^1.0.1 + checksum: 576716c77a0e8330b17ae9cba27d1fda8907c8cda7bf33a47f1999e16e089bfc6df4dd62933e0760f430736183c054348c34aa45dd882d49c8c098f55b89ee1d + languageName: node + linkType: hard + "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -15259,6 +15931,13 @@ __metadata: languageName: node linkType: hard +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 + languageName: node + linkType: hard + "function.prototype.name@npm:^1.1.0, function.prototype.name@npm:^1.1.5": version: 1.1.5 resolution: "function.prototype.name@npm:1.1.5" @@ -15394,6 +16073,18 @@ __metadata: languageName: node linkType: hard +"get-starknet-core@npm:^3.2.0": + version: 3.2.0 + resolution: "get-starknet-core@npm:3.2.0" + peerDependencies: + starknet: ^5.18.0 + peerDependenciesMeta: + starknet: + optional: false + checksum: 9bb3ddea6634a40b951c0f01366444beffe8c6785809a2404ad0ff60ec925420e9a4e846b809ddb40ecb5e9dbb73213359b0a051b8426a6d46974f5cd78ccf93 + languageName: node + linkType: hard + "get-stdin@npm:^4.0.1": version: 4.0.1 resolution: "get-stdin@npm:4.0.1" @@ -15599,6 +16290,17 @@ __metadata: languageName: node linkType: hard +"global-modules@npm:^1.0.0": + version: 1.0.0 + resolution: "global-modules@npm:1.0.0" + dependencies: + global-prefix: ^1.0.1 + is-windows: ^1.0.1 + resolve-dir: ^1.0.0 + checksum: 10be68796c1e1abc1e2ba87ec4ea507f5629873b119ab0cd29c07284ef2b930f1402d10df01beccb7391dedd9cd479611dd6a24311c71be58937beaf18edf85e + languageName: node + linkType: hard + "global-modules@npm:^2.0.0": version: 2.0.0 resolution: "global-modules@npm:2.0.0" @@ -15608,6 +16310,19 @@ __metadata: languageName: node linkType: hard +"global-prefix@npm:^1.0.1": + version: 1.0.2 + resolution: "global-prefix@npm:1.0.2" + dependencies: + expand-tilde: ^2.0.2 + homedir-polyfill: ^1.0.1 + ini: ^1.3.4 + is-windows: ^1.0.1 + which: ^1.2.14 + checksum: 061b43470fe498271bcd514e7746e8a8535032b17ab9570517014ae27d700ff0dca749f76bbde13ba384d185be4310d8ba5712cb0e74f7d54d59390db63dd9a0 + languageName: node + linkType: hard + "global-prefix@npm:^3.0.0": version: 3.0.0 resolution: "global-prefix@npm:3.0.0" @@ -15700,6 +16415,13 @@ __metadata: languageName: node linkType: hard +"graceful-fs@npm:^4.2.11": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 + languageName: node + linkType: hard + "grapheme-splitter@npm:^1.0.4": version: 1.0.4 resolution: "grapheme-splitter@npm:1.0.4" @@ -15927,6 +16649,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.0": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: ^1.1.2 + checksum: e8516f776a15149ca6c6ed2ae3110c417a00b62260e222590e54aa367cbcd6ed99122020b37b7fbdf05748df57b265e70095d7bf35a47660587619b15ffb93db + languageName: node + linkType: hard + "hast-to-hyperscript@npm:^9.0.0": version: 9.0.1 resolution: "hast-to-hyperscript@npm:9.0.1" @@ -16036,6 +16767,15 @@ __metadata: languageName: node linkType: hard +"homedir-polyfill@npm:^1.0.1": + version: 1.0.3 + resolution: "homedir-polyfill@npm:1.0.3" + dependencies: + parse-passwd: ^1.0.0 + checksum: 18dd4db87052c6a2179d1813adea0c4bfcfa4f9996f0e226fefb29eb3d548e564350fa28ec46b0bf1fbc0a1d2d6922ceceb80093115ea45ff8842a4990139250 + languageName: node + linkType: hard + "hoopy@npm:^0.1.4": version: 0.1.4 resolution: "hoopy@npm:0.1.4" @@ -16142,6 +16882,27 @@ __metadata: languageName: node linkType: hard +"html-webpack-plugin@npm:5.6.0": + version: 5.6.0 + resolution: "html-webpack-plugin@npm:5.6.0" + dependencies: + "@types/html-minifier-terser": ^6.0.0 + html-minifier-terser: ^6.0.2 + lodash: ^4.17.21 + pretty-error: ^4.0.0 + tapable: ^2.0.0 + peerDependencies: + "@rspack/core": 0.x || 1.x + webpack: ^5.20.0 + peerDependenciesMeta: + "@rspack/core": + optional: true + webpack: + optional: true + checksum: 32a6e41da538e798fd0be476637d7611a5e8a98a3508f031996e9eb27804dcdc282cb01f847cf5d066f21b49cfb8e21627fcf977ffd0c9bea81cf80e5a65070d + languageName: node + linkType: hard + "html-webpack-plugin@npm:^4.0.0": version: 4.5.2 resolution: "html-webpack-plugin@npm:4.5.2" @@ -16434,6 +17195,13 @@ __metadata: languageName: node linkType: hard +"ignore@npm:^5.2.4": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3 + languageName: node + linkType: hard + "immer@npm:^9.0.16, immer@npm:^9.0.6, immer@npm:^9.0.7": version: 9.0.19 resolution: "immer@npm:9.0.19" @@ -16524,7 +17292,7 @@ __metadata: languageName: node linkType: hard -"ini@npm:^1.3.2, ini@npm:^1.3.4, ini@npm:^1.3.5": +"ini@npm:^1.3.2, ini@npm:^1.3.4, ini@npm:^1.3.5, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: dfd98b0ca3a4fc1e323e38a6c8eb8936e31a97a918d3b377649ea15bdb15d481207a0dda1021efbd86b464cae29a0d33c1d7dcaf6c5672bee17fa849bc50a1b3 @@ -16585,6 +17353,13 @@ __metadata: languageName: node linkType: hard +"interpret@npm:^3.1.1": + version: 3.1.1 + resolution: "interpret@npm:3.1.1" + checksum: 35cebcf48c7351130437596d9ab8c8fe131ce4038da4561e6d665f25640e0034702a031cf7e3a5cea60ac7ac548bf17465e0571ede126f3d3a6933152171ac82 + languageName: node + linkType: hard + "ip@npm:^2.0.0": version: 2.0.1 resolution: "ip@npm:2.0.1" @@ -16754,6 +17529,15 @@ __metadata: languageName: node linkType: hard +"is-core-module@npm:^2.12.0, is-core-module@npm:^2.13.0": + version: 2.13.1 + resolution: "is-core-module@npm:2.13.1" + dependencies: + hasown: ^2.0.0 + checksum: 256559ee8a9488af90e4bad16f5583c6d59e92f0742e9e8bb4331e758521ee86b810b93bae44f390766ffbc518a0488b18d9dab7da9a5ff997d499efc9403f7c + languageName: node + linkType: hard + "is-data-descriptor@npm:^0.1.4": version: 0.1.4 resolution: "is-data-descriptor@npm:0.1.4" @@ -17065,6 +17849,13 @@ __metadata: languageName: node linkType: hard +"is-port-reachable@npm:4.0.0": + version: 4.0.0 + resolution: "is-port-reachable@npm:4.0.0" + checksum: 47b7e10db8edcef27fbf9e50f0de85ad368d35688790ca64a13db67260111ac5f4b98989b11af06199fa93f25d810bd09a5b21b2c2646529668638f7c34d3c04 + languageName: node + linkType: hard + "is-potential-custom-element-name@npm:^1.0.1": version: 1.0.1 resolution: "is-potential-custom-element-name@npm:1.0.1" @@ -17236,7 +18027,7 @@ __metadata: languageName: node linkType: hard -"is-windows@npm:^1.0.2": +"is-windows@npm:^1.0.1, is-windows@npm:^1.0.2": version: 1.0.2 resolution: "is-windows@npm:1.0.2" checksum: 438b7e52656fe3b9b293b180defb4e448088e7023a523ec21a91a80b9ff8cdb3377ddb5b6e60f7c7de4fa8b63ab56e121b6705fe081b3cf1b828b0a380009ad7 @@ -18239,7 +19030,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.13.1": +"js-yaml@npm:^3.13.1, js-yaml@npm:^3.14.1": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" dependencies: @@ -18888,9 +19679,16 @@ __metadata: linkType: hard "lossless-json@npm:^2.0.8": - version: 2.0.9 - resolution: "lossless-json@npm:2.0.9" - checksum: 26aebc8c697b6db20a85b8524a46f9d9b7f428cea1b2b504f833b67c162724d089179e5b0666d9b6a81cd92c8acd4c55ae4d66447e83f8883aafc0c51e5df797 + version: 2.0.11 + resolution: "lossless-json@npm:2.0.11" + checksum: 0282cf40658c844c9c7983dad26b7134538ba30fd5a992c932790228e3c9131b48d2cc89d65b31ced247a4eeedf33676bf187849ba3635d659c7812f106b42a7 + languageName: node + linkType: hard + +"lossless-json@npm:^4.0.1": + version: 4.0.1 + resolution: "lossless-json@npm:4.0.1" + checksum: 41e89f5b7800cf5f863776542c82d8a19a696e5537865699d5ee062726d069561f7b857e838195496f2a43afe64a7dce475be5a2565822881f39feb0eda5d218 languageName: node linkType: hard @@ -18979,6 +19777,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.7": + version: 0.30.8 + resolution: "magic-string@npm:0.30.8" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.15 + checksum: 79922f4500d3932bb587a04440d98d040170decf432edc0f91c0bf8d41db16d364189bf800e334170ac740918feda62cd39dcc170c337dc18050cfcf00a5f232 + languageName: node + linkType: hard + "make-dir@npm:^2.0.0, make-dir@npm:^2.1.0": version: 2.1.0 resolution: "make-dir@npm:2.1.0" @@ -19298,16 +20105,6 @@ __metadata: languageName: node linkType: hard -"micro-starknet@npm:~0.2.1": - version: 0.2.3 - resolution: "micro-starknet@npm:0.2.3" - dependencies: - "@noble/curves": ~1.0.0 - "@noble/hashes": ~1.3.0 - checksum: f1ae152348d1ca0bd0b4d09cb7901aee5cb1e815dcbfb6c3a623d107c169fad00808e154e7ca8abf2b16639a83044bc602c66fc3e0eb0de89a5c81815ff9b2ef - languageName: node - linkType: hard - "microevent.ts@npm:~0.1.1": version: 0.1.1 resolution: "microevent.ts@npm:0.1.1" @@ -19336,7 +20133,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": +"micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": version: 4.0.5 resolution: "micromatch@npm:4.0.5" dependencies: @@ -19490,6 +20287,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^7.4.6": + version: 7.4.6 + resolution: "minimatch@npm:7.4.6" + dependencies: + brace-expansion: ^2.0.1 + checksum: 1a6c8d22618df9d2a88aabeef1de5622eb7b558e9f8010be791cb6b0fa6e102d39b11c28d75b855a1e377b12edc7db8ff12a99c20353441caa6a05e78deb5da9 + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -19761,6 +20567,19 @@ __metadata: languageName: node linkType: hard +"multimatch@npm:^5.0.0": + version: 5.0.0 + resolution: "multimatch@npm:5.0.0" + dependencies: + "@types/minimatch": ^3.0.3 + array-differ: ^3.0.0 + array-union: ^2.1.0 + arrify: ^2.0.1 + minimatch: ^3.0.4 + checksum: 82c8030a53af965cab48da22f1b0f894ef99e16ee680dabdfbd38d2dfacc3c8208c475203d747afd9e26db44118ed0221d5a0d65268c864f06d6efc7ac6df812 + languageName: node + linkType: hard + "nan@npm:^2.12.1": version: 2.17.0 resolution: "nan@npm:2.17.0" @@ -19788,6 +20607,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 + languageName: node + linkType: hard + "nanomatch@npm:^1.2.9": version: 1.2.13 resolution: "nanomatch@npm:1.2.13" @@ -20807,6 +21635,13 @@ __metadata: languageName: node linkType: hard +"parse-passwd@npm:^1.0.0": + version: 1.0.0 + resolution: "parse-passwd@npm:1.0.0" + checksum: 4e55e0231d58f828a41d0f1da2bf2ff7bcef8f4cb6146e69d16ce499190de58b06199e6bd9b17fbf0d4d8aef9052099cdf8c4f13a6294b1a522e8e958073066e + languageName: node + linkType: hard + "parse5@npm:6.0.1, parse5@npm:^6.0.0": version: 6.0.1 resolution: "parse5@npm:6.0.1" @@ -21102,6 +21937,15 @@ __metadata: languageName: node linkType: hard +"please-upgrade-node@npm:^3.2.0": + version: 3.2.0 + resolution: "please-upgrade-node@npm:3.2.0" + dependencies: + semver-compare: ^1.0.0 + checksum: d87c41581a2a022fbe25965a97006238cd9b8cbbf49b39f78d262548149a9d30bd2bdf35fec3d810e0001e630cd46ef13c7e19c389dea8de7e64db271a2381bb + languageName: node + linkType: hard + "pnp-webpack-plugin@npm:1.6.4": version: 1.6.4 resolution: "pnp-webpack-plugin@npm:1.6.4" @@ -22050,6 +22894,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.35": + version: 8.4.38 + resolution: "postcss@npm:8.4.38" + dependencies: + nanoid: ^3.3.7 + picocolors: ^1.0.0 + source-map-js: ^1.2.0 + checksum: 649f9e60a763ca4b5a7bbec446a069edf07f057f6d780a5a0070576b841538d1ecf7dd888f2fbfd1f76200e26c969e405aeeae66332e6927dbdc8bdcb90b9451 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -22521,6 +23376,20 @@ __metadata: languageName: node linkType: hard +"rc@npm:^1.0.1, rc@npm:^1.1.6": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: ^0.6.0 + ini: ~1.3.0 + minimist: ^1.2.0 + strip-json-comments: ~2.0.1 + bin: + rc: ./cli.js + checksum: 2e26e052f8be2abd64e6d1dabfbd7be03f80ec18ccbc49562d31f617d0015fbdbcf0f9eed30346ea6ab789e0fdfe4337f033f8016efdbee0df5354751842080e + languageName: node + linkType: hard + "react-app-polyfill@npm:^3.0.0": version: 3.0.0 resolution: "react-app-polyfill@npm:3.0.0" @@ -23013,7 +23882,7 @@ __metadata: languageName: node linkType: hard -"readdirp@npm:~3.6.0": +"readdirp@npm:^3.6.0, readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" dependencies: @@ -23022,6 +23891,15 @@ __metadata: languageName: node linkType: hard +"rechoir@npm:^0.8.0": + version: 0.8.0 + resolution: "rechoir@npm:0.8.0" + dependencies: + resolve: ^1.20.0 + checksum: ad3caed8afdefbc33fbc30e6d22b86c35b3d51c2005546f4e79bcc03c074df804b3640ad18945e6bef9ed12caedc035655ec1082f64a5e94c849ff939dc0a788 + languageName: node + linkType: hard + "recursive-readdir@npm:^2.2.2": version: 2.2.3 resolution: "recursive-readdir@npm:2.2.3" @@ -23051,6 +23929,15 @@ __metadata: languageName: node linkType: hard +"redeyed@npm:~2.1.0": + version: 2.1.1 + resolution: "redeyed@npm:2.1.1" + dependencies: + esprima: ~4.0.0 + checksum: 39a1426e377727cfb47a0e24e95c1cf78d969fbc388dc1e0fa1e2ef8a8756450cefb8b0c2598f63b85f1a331986fca7604c0db798427a5775a1dbdb9c1291979 + languageName: node + linkType: hard + "redux-persist@npm:^6.0.0": version: 6.0.0 resolution: "redux-persist@npm:6.0.0" @@ -23161,6 +24048,25 @@ __metadata: languageName: node linkType: hard +"registry-auth-token@npm:3.3.2": + version: 3.3.2 + resolution: "registry-auth-token@npm:3.3.2" + dependencies: + rc: ^1.1.6 + safe-buffer: ^5.0.1 + checksum: c9d7ae160a738f1fa825556e3669e6c771d2c0239ce37679f7e8646157a97d0a76464738be075002a1f754ef9bfb913b689f4bbfd5296d28f136fbf98c8c2217 + languageName: node + linkType: hard + +"registry-url@npm:3.1.0": + version: 3.1.0 + resolution: "registry-url@npm:3.1.0" + dependencies: + rc: ^1.0.1 + checksum: 6d223da41b04e1824f5faa63905c6f2e43b216589d72794111573f017352b790aef42cd1f826463062f89d804abb2027e3d9665d2a9a0426a11eedd04d470af3 + languageName: node + linkType: hard + "regjsparser@npm:^0.9.1": version: 0.9.1 resolution: "regjsparser@npm:0.9.1" @@ -23373,6 +24279,13 @@ __metadata: languageName: node linkType: hard +"require-package-name@npm:^2.0.1": + version: 2.0.1 + resolution: "require-package-name@npm:2.0.1" + checksum: 00f4e9e467ebe2bbced2b4198a165de11c83b5ee9f4c20b05a8782659b92bcb544dbd50be9a3eed746d05ecd875453e258c079eb3a79604b50a27cf8ab0798b5 + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -23396,6 +24309,16 @@ __metadata: languageName: node linkType: hard +"resolve-dir@npm:^1.0.0, resolve-dir@npm:^1.0.1": + version: 1.0.1 + resolution: "resolve-dir@npm:1.0.1" + dependencies: + expand-tilde: ^2.0.0 + global-modules: ^1.0.0 + checksum: ef736b8ed60d6645c3b573da17d329bfb50ec4e1d6c5ffd6df49e3497acef9226f9810ea6823b8ece1560e01dcb13f77a9f6180d4f242d00cc9a8f4de909c65c + languageName: node + linkType: hard + "resolve-from@npm:5.0.0, resolve-from@npm:^5.0.0": version: 5.0.0 resolution: "resolve-from@npm:5.0.0" @@ -23467,6 +24390,19 @@ __metadata: languageName: node linkType: hard +"resolve@npm:^1.22.3": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" + dependencies: + is-core-module: ^2.13.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: f8a26958aa572c9b064562750b52131a37c29d072478ea32e129063e2da7f83e31f7f11e7087a18225a8561cfe8d2f0df9dbea7c9d331a897571c0a2527dbb4c + languageName: node + linkType: hard + "resolve@npm:^2.0.0-next.4": version: 2.0.0-next.4 resolution: "resolve@npm:2.0.0-next.4" @@ -23493,6 +24429,19 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@^1.22.3#~builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" + dependencies: + is-core-module: ^2.13.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 5479b7d431cacd5185f8db64bfcb7286ae5e31eb299f4c4f404ad8aa6098b77599563ac4257cb2c37a42f59dfc06a1bec2bcf283bb448f319e37f0feb9a09847 + languageName: node + linkType: hard + "resolve@patch:resolve@^2.0.0-next.4#~builtin": version: 2.0.0-next.4 resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=c3c19d" @@ -23622,6 +24571,35 @@ __metadata: languageName: node linkType: hard +"rome@npm:^12.1.3": + version: 12.1.3 + resolution: "rome@npm:12.1.3" + dependencies: + "@rometools/cli-darwin-arm64": 12.1.3 + "@rometools/cli-darwin-x64": 12.1.3 + "@rometools/cli-linux-arm64": 12.1.3 + "@rometools/cli-linux-x64": 12.1.3 + "@rometools/cli-win32-arm64": 12.1.3 + "@rometools/cli-win32-x64": 12.1.3 + dependenciesMeta: + "@rometools/cli-darwin-arm64": + optional: true + "@rometools/cli-darwin-x64": + optional: true + "@rometools/cli-linux-arm64": + optional: true + "@rometools/cli-linux-x64": + optional: true + "@rometools/cli-win32-arm64": + optional: true + "@rometools/cli-win32-x64": + optional: true + bin: + rome: bin/rome + checksum: 341e5520a23277bdc2571db279e72fe9427a95f4e3025cd215a989d381fe6690f6aa1c0fb9abbd318a8bfd85ff1cec2304e92b732329c8a46c69e259eeb080cc + languageName: node + linkType: hard + "rsvp@npm:^4.8.4": version: 4.8.5 resolution: "rsvp@npm:4.8.5" @@ -23882,6 +24860,13 @@ __metadata: languageName: node linkType: hard +"semver-compare@npm:^1.0.0": + version: 1.0.0 + resolution: "semver-compare@npm:1.0.0" + checksum: dd1d7e2909744cf2cf71864ac718efc990297f9de2913b68e41a214319e70174b1d1793ac16e31183b128c2b9812541300cb324db8168e6cf6b570703b171c68 + languageName: node + linkType: hard + "semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.6.0": version: 5.7.1 resolution: "semver@npm:5.7.1" @@ -24001,7 +24986,7 @@ __metadata: languageName: node linkType: hard -"serve-handler@npm:^6.1.5": +"serve-handler@npm:6.1.5, serve-handler@npm:^6.1.5": version: 6.1.5 resolution: "serve-handler@npm:6.1.5" dependencies: @@ -24044,6 +25029,27 @@ __metadata: languageName: node linkType: hard +"serve@npm:14.2.1": + version: 14.2.1 + resolution: "serve@npm:14.2.1" + dependencies: + "@zeit/schemas": 2.29.0 + ajv: 8.11.0 + arg: 5.0.2 + boxen: 7.0.0 + chalk: 5.0.1 + chalk-template: 0.4.0 + clipboardy: 3.0.0 + compression: 1.7.4 + is-port-reachable: 4.0.0 + serve-handler: 6.1.5 + update-check: 1.5.4 + bin: + serve: build/main.js + checksum: c39a517b5d795a0a5c2f9fb9ff088b7e4962c579e34ace5b85dd62f93e0eacbc8a90359792c153c444a83258ffda392113dff7bfd10d41ced574a2d1886c2994 + languageName: node + linkType: hard + "ses@npm:^0.18.8": version: 0.18.8 resolution: "ses@npm:0.18.8" @@ -24069,6 +25075,13 @@ __metadata: languageName: node linkType: hard +"set-cookie-parser@npm:^2.4.8": + version: 2.6.0 + resolution: "set-cookie-parser@npm:2.6.0" + checksum: bf11ebc594c53d84588f1b4c04f1b8ce14e0498b1c011b3d76b5c6d5aac481bbc3f7c5260ec4ce99bdc1d9aed19f9fc315e73166a36ca74d0f12349a73f6bdc9 + languageName: node + linkType: hard + "set-value@npm:^2.0.0, set-value@npm:^2.0.1": version: 2.0.1 resolution: "set-value@npm:2.0.1" @@ -24344,6 +25357,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.2.0": + version: 1.2.0 + resolution: "source-map-js@npm:1.2.0" + checksum: 791a43306d9223792e84293b00458bf102a8946e7188f3db0e4e22d8d530b5f80a4ce468eb5ec0bf585443ad55ebbd630bf379c98db0b1f317fd902500217f97 + languageName: node + linkType: hard + "source-map-loader@npm:^3.0.0": version: 3.0.2 resolution: "source-map-loader@npm:3.0.2" @@ -24401,7 +25421,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.7.3": +"source-map@npm:^0.7.3, source-map@npm:^0.7.4": version: 0.7.4 resolution: "source-map@npm:0.7.4" checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 @@ -24652,6 +25672,32 @@ __metadata: languageName: unknown linkType: soft +"starknet-types@npm:^0.0.4": + version: 0.0.4 + resolution: "starknet-types@npm:0.0.4" + checksum: a83d306830696ea875b8b07541db47e515a4ddb7bfce0cf5555cce12c8ce0e697b984762720c7f27761dc4114b4e5d3f81006e82fc45e52b052afe8fc86240fa + languageName: node + linkType: hard + +"starknet@npm:6.7.0": + version: 6.7.0 + resolution: "starknet@npm:6.7.0" + dependencies: + "@noble/curves": ~1.4.0 + "@scure/base": ~1.1.3 + "@scure/starknet": ~1.0.0 + abi-wan-kanabi: ^2.2.2 + fetch-cookie: ^3.0.0 + isomorphic-fetch: ^3.0.0 + lossless-json: ^4.0.1 + pako: ^2.0.4 + starknet-types: ^0.0.4 + ts-mixer: ^6.0.3 + url-join: ^4.0.1 + checksum: 3c4abda7bab3008c8f98728c4b23a47a2dc97b876c6a33a824da77907b63611fde2df22298aa4c512e3c81e3fe005dd7e288969f18b00299e6eae450a147769d + languageName: node + linkType: hard + "starknet@npm:^4.22.0, starknet_v4.22.0@npm:starknet@4.22.0": version: 4.22.0 resolution: "starknet@npm:4.22.0" @@ -24672,16 +25718,19 @@ __metadata: linkType: hard "starknet@npm:^5.14.0": - version: 5.16.0 - resolution: "starknet@npm:5.16.0" + version: 5.29.0 + resolution: "starknet@npm:5.29.0" dependencies: - "@noble/curves": ~1.0.0 + "@noble/curves": ~1.3.0 + "@scure/base": ~1.1.3 + "@scure/starknet": ~1.0.0 + abi-wan-kanabi-v1: "npm:abi-wan-kanabi@^1.0.3" + abi-wan-kanabi-v2: "npm:abi-wan-kanabi@^2.1.1" isomorphic-fetch: ^3.0.0 lossless-json: ^2.0.8 - micro-starknet: ~0.2.1 pako: ^2.0.4 url-join: ^4.0.1 - checksum: 745e68d698aacb4836fb638a907b9844c566f0a3a169a80722eef73c7d0c48fe1a31a458f32ed248bc63587e4b3811067ae37e3e573d84bec1a205cd45e678db + checksum: ea44bcc7f7db76ef878a557658e289bd25383b6fbcc9d59153dcf1bd0e014f41484fa5faa95e09ce0bd3c5933833fc867fb5be325f5b55ae1531a2cd29915594 languageName: node linkType: hard @@ -24863,6 +25912,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: ^0.2.0 + emoji-regex: ^9.2.2 + strip-ansi: ^7.0.1 + checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.0 || ^3.0.1, string.prototype.matchall@npm:^4.0.6, string.prototype.matchall@npm:^4.0.8": version: 4.0.8 resolution: "string.prototype.matchall@npm:4.0.8" @@ -25077,6 +26137,13 @@ __metadata: languageName: node linkType: hard +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1 + languageName: node + linkType: hard + "strnum@npm:^1.0.5": version: 1.0.5 resolution: "strnum@npm:1.0.5" @@ -25640,7 +26707,7 @@ __metadata: languageName: node linkType: hard -"through@npm:2, through@npm:>=2.2.7 <3, through@npm:~2.3.4": +"through@npm:2, through@npm:>=2.2.7 <3": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd @@ -25862,6 +26929,29 @@ __metadata: languageName: node linkType: hard +"ts-loader@npm:^9.5.1": + version: 9.5.1 + resolution: "ts-loader@npm:9.5.1" + dependencies: + chalk: ^4.1.0 + enhanced-resolve: ^5.0.0 + micromatch: ^4.0.0 + semver: ^7.3.4 + source-map: ^0.7.4 + peerDependencies: + typescript: "*" + webpack: ^5.0.0 + checksum: 7cf396e656d905388ea2a9b5e82f16d3c955fda8d3df2fbf219f4bee16ff50a3c995c44ae3e584634e9443f056cec70bb3151add3917ffb4588ecd7394bac0ec + languageName: node + linkType: hard + +"ts-mixer@npm:^6.0.3": + version: 6.0.4 + resolution: "ts-mixer@npm:6.0.4" + checksum: 36b1af526befd74345e736e9aa16f5c28876ebcea07784da14d929149fd7e6028cfd2fe9304c8efe8cb91b588443a9cc9e991df58e4c6e602326edbaae2af3ab + languageName: node + linkType: hard + "ts-node@npm:^10.8.1": version: 10.9.1 resolution: "ts-node@npm:10.9.1" @@ -26044,6 +27134,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^2.13.0": + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: a4ef07ece297c9fba78fc1bd6d85dff4472fe043ede98bd4710d2615d15776902b595abf62bd78339ed6278f021235fb28a96361f8be86ed754f778973a0d278 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -26081,7 +27178,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.6.3, typescript@npm:^4.6.4, typescript@npm:^4.7.4": +"typescript@npm:^4.6.3, typescript@npm:^4.6.4, typescript@npm:^4.7.4, typescript@npm:^4.9.5": version: 4.9.5 resolution: "typescript@npm:4.9.5" bin: @@ -26091,7 +27188,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^4.6.3#~builtin, typescript@patch:typescript@^4.6.4#~builtin, typescript@patch:typescript@^4.7.4#~builtin": +"typescript@patch:typescript@^4.6.3#~builtin, typescript@patch:typescript@^4.6.4#~builtin, typescript@patch:typescript@^4.7.4#~builtin, typescript@patch:typescript@^4.9.5#~builtin": version: 4.9.5 resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=23ec76" bin: @@ -26423,6 +27520,16 @@ __metadata: languageName: node linkType: hard +"update-check@npm:1.5.4": + version: 1.5.4 + resolution: "update-check@npm:1.5.4" + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + checksum: 2c9f7de6f030364c5ea02a341e5ae2dfe76da6559b32d40dd3b047b3ac0927408cf92d322c51cd8e009688210a85ccbf1eba449762a65a0d1b14f3cdf1ea5c48 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -26845,6 +27952,16 @@ __metadata: languageName: node linkType: hard +"watchpack@npm:^2.4.1": + version: 2.4.1 + resolution: "watchpack@npm:2.4.1" + dependencies: + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.1.2 + checksum: 5b0179348655dcdf19cac7cb4ff923fdc024d630650c0bf6bec8899cf47c60e19d4f810a88dba692ed0e7f684cf0fcffea86efdbf6c35d81f031e328043b7fab + languageName: node + linkType: hard + "wbuf@npm:^1.1.0, wbuf@npm:^1.7.3": version: 1.7.3 resolution: "wbuf@npm:1.7.3" @@ -26912,6 +28029,38 @@ __metadata: languageName: node linkType: hard +"webpack-cli@npm:^5.1.4": + version: 5.1.4 + resolution: "webpack-cli@npm:5.1.4" + dependencies: + "@discoveryjs/json-ext": ^0.5.0 + "@webpack-cli/configtest": ^2.1.1 + "@webpack-cli/info": ^2.0.2 + "@webpack-cli/serve": ^2.0.5 + colorette: ^2.0.14 + commander: ^10.0.1 + cross-spawn: ^7.0.3 + envinfo: ^7.7.3 + fastest-levenshtein: ^1.0.12 + import-local: ^3.0.2 + interpret: ^3.1.1 + rechoir: ^0.8.0 + webpack-merge: ^5.7.3 + peerDependencies: + webpack: 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 3a4ad0d0342a6815c850ee4633cc2a8a5dae04f918e7847f180bf24ab400803cf8a8943707ffbed03eb20fe6ce647f996f60a2aade87b0b4a9954da3da172ce0 + languageName: node + linkType: hard + "webpack-dev-middleware@npm:^3.7.3": version: 3.7.3 resolution: "webpack-dev-middleware@npm:3.7.3" @@ -26958,6 +28107,53 @@ __metadata: languageName: node linkType: hard +"webpack-dev-server@npm:4.15.1": + version: 4.15.1 + resolution: "webpack-dev-server@npm:4.15.1" + dependencies: + "@types/bonjour": ^3.5.9 + "@types/connect-history-api-fallback": ^1.3.5 + "@types/express": ^4.17.13 + "@types/serve-index": ^1.9.1 + "@types/serve-static": ^1.13.10 + "@types/sockjs": ^0.3.33 + "@types/ws": ^8.5.5 + ansi-html-community: ^0.0.8 + bonjour-service: ^1.0.11 + chokidar: ^3.5.3 + colorette: ^2.0.10 + compression: ^1.7.4 + connect-history-api-fallback: ^2.0.0 + default-gateway: ^6.0.3 + express: ^4.17.3 + graceful-fs: ^4.2.6 + html-entities: ^2.3.2 + http-proxy-middleware: ^2.0.3 + ipaddr.js: ^2.0.1 + launch-editor: ^2.6.0 + open: ^8.0.9 + p-retry: ^4.5.0 + rimraf: ^3.0.2 + schema-utils: ^4.0.0 + selfsigned: ^2.1.1 + serve-index: ^1.9.1 + sockjs: ^0.3.24 + spdy: ^4.0.2 + webpack-dev-middleware: ^5.3.1 + ws: ^8.13.0 + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + bin: + webpack-dev-server: bin/webpack-dev-server.js + checksum: cd0063b068d2b938fd76c412d555374186ac2fa84bbae098265212ed50a5c15d6f03aa12a5a310c544a242943eb58c0bfde4c296d5c36765c182f53799e1bc71 + languageName: node + linkType: hard + "webpack-dev-server@npm:^4.6.0": version: 4.12.0 resolution: "webpack-dev-server@npm:4.12.0" @@ -27045,7 +28241,7 @@ __metadata: languageName: node linkType: hard -"webpack-merge@npm:^5.9.0": +"webpack-merge@npm:^5.7.3, webpack-merge@npm:^5.9.0": version: 5.10.0 resolution: "webpack-merge@npm:5.10.0" dependencies: @@ -27248,6 +28444,43 @@ __metadata: languageName: node linkType: hard +"webpack@npm:^5.91.0": + version: 5.91.0 + resolution: "webpack@npm:5.91.0" + dependencies: + "@types/eslint-scope": ^3.7.3 + "@types/estree": ^1.0.5 + "@webassemblyjs/ast": ^1.12.1 + "@webassemblyjs/wasm-edit": ^1.12.1 + "@webassemblyjs/wasm-parser": ^1.12.1 + acorn: ^8.7.1 + acorn-import-assertions: ^1.9.0 + browserslist: ^4.21.10 + chrome-trace-event: ^1.0.2 + enhanced-resolve: ^5.16.0 + es-module-lexer: ^1.2.1 + eslint-scope: 5.1.1 + events: ^3.2.0 + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.2.11 + json-parse-even-better-errors: ^2.3.1 + loader-runner: ^4.2.0 + mime-types: ^2.1.27 + neo-async: ^2.6.2 + schema-utils: ^3.2.0 + tapable: ^2.1.1 + terser-webpack-plugin: ^5.3.10 + watchpack: ^2.4.1 + webpack-sources: ^3.2.3 + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: f1073715dbb1ed5c070affef293d800a867708bcbc5aba4d8baee87660e0cf53c55966a6f36fab078d1d6c9567cdcd0a9086bdfb607cab87ea68c6449791b9a3 + languageName: node + linkType: hard + "websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": version: 0.7.4 resolution: "websocket-driver@npm:0.7.4" @@ -27378,7 +28611,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^1.2.9, which@npm:^1.3.1": +"which@npm:^1.2.14, which@npm:^1.2.9, which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1" dependencies: @@ -27407,6 +28640,15 @@ __metadata: languageName: node linkType: hard +"widest-line@npm:^4.0.1": + version: 4.0.1 + resolution: "widest-line@npm:4.0.1" + dependencies: + string-width: ^5.0.1 + checksum: 64c48cf27171221be5f86fc54b94dd29879165bdff1a7aa92dde723d9a8c99fb108312768a5d62c8c2b80b701fa27bbd36a1ddc58367585cd45c0db7920a0cba + languageName: node + linkType: hard + "wildcard@npm:^2.0.0": version: 2.0.1 resolution: "wildcard@npm:2.0.1" @@ -27680,6 +28922,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^8.0.1": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: ^6.1.0 + string-width: ^5.0.1 + strip-ansi: ^7.0.1 + checksum: 371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238 + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" @@ -27916,7 +29169,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.7.1": +"yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 2fedd78ae3209372bb41f3fe109e8095630827e8 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:25:50 +0800 Subject: [PATCH 18/25] chore: lint style --- packages/starknet-snap/src/createAccount.ts | 14 ++++- packages/starknet-snap/src/utils/snapUtils.ts | 2 +- .../starknet-snap/src/utils/starknetUtils.ts | 55 +++++++++++-------- .../test/src/sendTransaction.test.ts | 2 +- .../test/utils/snapUtils.test.ts | 8 ++- .../test/utils/starknetUtils.test.ts | 25 +++++++-- yarn.lock | 15 ++++- 7 files changed, 85 insertions(+), 36 deletions(-) diff --git a/packages/starknet-snap/src/createAccount.ts b/packages/starknet-snap/src/createAccount.ts index aa36f729..0ce385e9 100644 --- a/packages/starknet-snap/src/createAccount.ts +++ b/packages/starknet-snap/src/createAccount.ts @@ -108,9 +108,17 @@ export async function createAccount(params: ApiParams, silentMode = false, waitM } } - const deployResp = await deployAccount(network, contractAddress, contractCallData, publicKey, privateKey, undefined, { - maxFee: estimateDeployFee?.suggestedMaxFee, - }); + const deployResp = await deployAccount( + network, + contractAddress, + contractCallData, + publicKey, + privateKey, + undefined, + { + maxFee: estimateDeployFee?.suggestedMaxFee, + }, + ); if (deployResp.contract_address && deployResp.transaction_hash) { const userAccount: AccContract = { diff --git a/packages/starknet-snap/src/utils/snapUtils.ts b/packages/starknet-snap/src/utils/snapUtils.ts index 02dc23c6..f0c63ae5 100644 --- a/packages/starknet-snap/src/utils/snapUtils.ts +++ b/packages/starknet-snap/src/utils/snapUtils.ts @@ -538,7 +538,7 @@ export function getRPCUrl(chainId: string) { } export function getRPCCredentials(): string { - return process.env.ALCHEMY_API_KEY ?? '' + return process.env.ALCHEMY_API_KEY ?? ''; } export function getVoyagerUrl(chainId: string) { diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 4b726421..654691e3 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -34,6 +34,7 @@ import { CairoVersion, InvocationsSignerDetails, ProviderInterface, + GetTransactionReceiptResponse, } from 'starknet'; import { Network, SnapState, Transaction, TransactionType } from '../types/snapState'; import { @@ -101,6 +102,16 @@ export const callContract = async ( ); }; +export const waitForTransaction = async ( + network: Network, + senderAddress: string, + privateKey: string | Uint8Array, + txnHash: num.BigNumberish, + cairoVersion?: CairoVersion, +): Promise => { + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).waitForTransaction(txnHash); +}; + export const declareContract = async ( network: Network, senderAddress: string, @@ -109,9 +120,11 @@ export const declareContract = async ( invocationsDetails?: UniversalDetails, cairoVersion?: CairoVersion, ): Promise => { - return getAccountInstance(network, senderAddress, privateKey, cairoVersion).declare( - contractPayload, { ...invocationsDetails, skipValidate: false, blockIdentifier: 'latest' } - ); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).declare(contractPayload, { + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', + }); }; export const estimateFee = async ( @@ -137,14 +150,11 @@ export const estimateFeeBulk = async ( invocationsDetails?: UniversalDetails, cairoVersion?: CairoVersion, ): Promise => { - return getAccountInstance(network, senderAddress, privateKey, cairoVersion).estimateFeeBulk( - txnInvocation, - { - ...invocationsDetails, - skipValidate: false, - blockIdentifier: 'latest', - }, - ); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).estimateFeeBulk(txnInvocation, { + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', + }); }; export const executeTxn = async ( @@ -156,15 +166,11 @@ export const executeTxn = async ( invocationsDetails?: UniversalDetails, cairoVersion?: CairoVersion, ): Promise => { - return getAccountInstance(network, senderAddress, privateKey, cairoVersion).execute( - txnInvocation, - abis, - { - ...invocationsDetails, - skipValidate: false, - blockIdentifier: 'latest', - }, - ); + return getAccountInstance(network, senderAddress, privateKey, cairoVersion).execute(txnInvocation, abis, { + ...invocationsDetails, + skipValidate: false, + blockIdentifier: 'latest', + }); }; export const deployAccount = async ( @@ -210,7 +216,7 @@ export const estimateAccountDeployFee = async ( ...invocationsDetails, skipValidate: false, blockIdentifier: 'latest', - } + }, ); }; @@ -384,7 +390,12 @@ export const getMassagedTransactions = async ( contractAddress: txnResp.calldata?.[1] || txnResp.contract_address || txn.contract_address || '', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - contractFuncName: num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex ? 'transfer' : num.toBigInt(txnResp.calldata?.[2] || '') === bigIntUpgradeSelectorHex ? 'upgrade' : txn.operations ?? '', + contractFuncName: + num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex + ? 'transfer' + : num.toBigInt(txnResp.calldata?.[2] || '') === bigIntUpgradeSelectorHex + ? 'upgrade' + : txn.operations ?? '', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore contractCallData: txnResp.calldata || [], diff --git a/packages/starknet-snap/test/src/sendTransaction.test.ts b/packages/starknet-snap/test/src/sendTransaction.test.ts index 31e0a282..9d684ce2 100644 --- a/packages/starknet-snap/test/src/sendTransaction.test.ts +++ b/packages/starknet-snap/test/src/sendTransaction.test.ts @@ -59,7 +59,6 @@ describe('Test function: sendTransaction', function () { beforeEach(async function () { walletStub.rpcStubs.snap_getBip44Entropy.callsFake(getBip44EntropyStub); apiParams.keyDeriver = await getAddressKeyDeriver(walletStub); - //sandbox.stub(utils, 'waitForTransaction').resolves({} as unknown as GetTransactionReceiptResponse); }); afterEach(function () { @@ -229,6 +228,7 @@ describe('Test function: sendTransaction', function () { executeTxnStub = sandbox.stub(utils, 'executeTxn').resolves(executeTxnResp); walletStub.rpcStubs.snap_manageState.resolves(state); walletStub.rpcStubs.snap_dialog.resolves(true); + sandbox.stub(utils, 'waitForTransaction').resolves({} as unknown as GetTransactionReceiptResponse); }); describe('when account is deployed', function () { diff --git a/packages/starknet-snap/test/utils/snapUtils.test.ts b/packages/starknet-snap/test/utils/snapUtils.test.ts index 90bd6e93..a88f1eb7 100644 --- a/packages/starknet-snap/test/utils/snapUtils.test.ts +++ b/packages/starknet-snap/test/utils/snapUtils.test.ts @@ -137,11 +137,15 @@ describe('getVoyagerCredentials', () => { describe('getRPCUrl', () => { it('returns Mainnet RPC URL if chain id is Mainnet', () => { - expect(getRPCUrl(constants.StarknetChainId.SN_MAIN)).to.be.equal('https://starknet-mainnet.g.alchemy.com/starknet/version/rpc/v0_7/'); + expect(getRPCUrl(constants.StarknetChainId.SN_MAIN)).to.be.equal( + 'https://starknet-mainnet.g.alchemy.com/starknet/version/rpc/v0_7/', + ); }); it('returns Sepolia RPC URL if chain id is not either Mainnet or Sepolia', () => { - expect(getRPCUrl('0x534e5f474f45524c49')).to.be.equal('https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/'); + expect(getRPCUrl('0x534e5f474f45524c49')).to.be.equal( + 'https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/', + ); }); it('returns Sepolia RPC URL if chain id is Sepolia', () => { diff --git a/packages/starknet-snap/test/utils/starknetUtils.test.ts b/packages/starknet-snap/test/utils/starknetUtils.test.ts index d4b22d21..7ef654f2 100644 --- a/packages/starknet-snap/test/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/test/utils/starknetUtils.test.ts @@ -14,7 +14,7 @@ import { account3, } from '../constants.test'; import { SnapState } from '../../src/types/snapState'; -import { Calldata, num, Account, Provider } from 'starknet'; +import { Calldata, num, Account, Provider, GetTransactionReceiptResponse } from 'starknet'; import { hexToString } from '../../src/utils/formatterUtils'; chai.use(sinonChai); @@ -271,7 +271,7 @@ describe('Test function: getVersion', function () { const expected = '0.3.0'; beforeEach(function () { - callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ([expected])); + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => [expected]); }); afterEach(function () { @@ -294,7 +294,7 @@ describe('Test function: getOwner', function () { const expected = 'pk'; beforeEach(function () { - callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ([expected])); + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => [expected]); }); afterEach(function () { @@ -317,7 +317,7 @@ describe('Test function: getBalance', function () { const expected = 'pk'; beforeEach(function () { - callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => ([expected])); + callContractStub = sandbox.stub(utils, 'callContract').callsFake(async () => [expected]); }); afterEach(function () { @@ -579,3 +579,20 @@ describe('Test function: getCorrectContractAddress', function () { }); }); }); + +describe('Test function: waitForTransaction', function () { + const walletStub = new WalletMock(); + const userAddress = '0x27f204588cadd08a7914f6a9808b34de0cbfc4cb53aa053663e7fd3a34dbc26'; + + afterEach(function () { + walletStub.reset(); + sandbox.restore(); + }); + + it('pass parameter to waitForTransaction correctly', async function () { + const stub = sandbox.stub(utils, 'waitForTransaction'); + stub.resolves({} as unknown as GetTransactionReceiptResponse); + await utils.waitForTransaction(STARKNET_SEPOLIA_TESTNET_NETWORK, userAddress, 'pk', 'txHash'); + expect(stub).to.have.been.calledWith(STARKNET_SEPOLIA_TESTNET_NETWORK, userAddress, 'pk', 'txHash'); + }); +}); diff --git a/yarn.lock b/yarn.lock index 27a2cd54..c86b0207 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3248,7 +3248,7 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.7.0 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=099522&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=2d7b8f&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: "@metamask/snaps-sdk": 3.0.1 async-mutex: ^0.3.2 @@ -3257,7 +3257,7 @@ __metadata: ethers: ^5.5.1 starknet: 6.7.0 starknet_v4.22.0: "npm:starknet@4.22.0" - checksum: a53e3b5b9b53448473b4b362ebdda3d1ecb97321baa7db52393c0b43e9454ccb81d641bd2a86956896a591c23f237566d4c131034806c2609de79f9f1a12ba41 + checksum: b5bada5353623f8523c7c0ab3493b8da94446f0320b2195be81f442e4822f9c2b265da93e90a464c12c68491bb12d08438da446d263f4363fb820df1fbf7c0f1 languageName: node linkType: hard @@ -25348,6 +25348,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.2": + version: 7.6.2 + resolution: "semver@npm:7.6.2" + bin: + semver: bin/semver.js + checksum: 40f6a95101e8d854357a644da1b8dd9d93ce786d5c6a77227bc69dbb17bea83d0d1d1d7c4cd5920a6df909f48e8bd8a5909869535007f90278289f2451d0292d + languageName: node + linkType: hard + "semver@npm:^7.5.4": version: 7.6.0 resolution: "semver@npm:7.6.0" @@ -28383,7 +28392,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 b5cd8c61d0f2f625a9b819dcf7e155762548a239 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:37:49 +0800 Subject: [PATCH 19/25] fix: lint issue --- packages/starknet-snap/snap.manifest.json | 2 +- packages/starknet-snap/src/utils/starknetUtils.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index f5285026..08623dde 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": "vVFyA2r3PuADhGuwrnvREDPCLnSidbQzT3lxf9uzvOA=", + "shasum": "KYC9Ksio3trenccTZNjeQYWnIzEUHKrcsC0v8GWSE2M=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 654691e3..c1d2abf9 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -388,12 +388,15 @@ export const getMassagedTransactions = async ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore contractAddress: txnResp.calldata?.[1] || txnResp.contract_address || txn.contract_address || '', - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + contractFuncName: + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore num.toBigInt(txnResp.calldata?.[2] || '') === bigIntTransferSelectorHex ? 'transfer' - : num.toBigInt(txnResp.calldata?.[2] || '') === bigIntUpgradeSelectorHex + : // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + num.toBigInt(txnResp.calldata?.[2] || '') === bigIntUpgradeSelectorHex ? 'upgrade' : txn.operations ?? '', // eslint-disable-next-line @typescript-eslint/ban-ts-comment From 17f59757d24e124cc46641f76b6db7b8f1033635 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:42:23 +0800 Subject: [PATCH 20/25] fix: yarn lock --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index c86b0207..61f44eec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3248,7 +3248,7 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.7.0 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=2d7b8f&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=9bd857&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: "@metamask/snaps-sdk": 3.0.1 async-mutex: ^0.3.2 @@ -3257,7 +3257,7 @@ __metadata: ethers: ^5.5.1 starknet: 6.7.0 starknet_v4.22.0: "npm:starknet@4.22.0" - checksum: b5bada5353623f8523c7c0ab3493b8da94446f0320b2195be81f442e4822f9c2b265da93e90a464c12c68491bb12d08438da446d263f4363fb820df1fbf7c0f1 + checksum: 67dd2a879a94ef48d63cb715bf3a194e3c5a7c8edb08501c578a70e38aaba58e0441fa076f8516c7288055ace5129855a5f7af5fc534ff6fa13e0476d5093583 languageName: node linkType: hard From 3209fe38d221cd20433aa5b77ed42f022a00b338 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Thu, 13 Jun 2024 11:32:02 +0800 Subject: [PATCH 21/25] chore: add update text --- packages/starknet-snap/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/starknet-snap/src/index.ts b/packages/starknet-snap/src/index.ts index 5d000839..0561c0be 100644 --- a/packages/starknet-snap/src/index.ts +++ b/packages/starknet-snap/src/index.ts @@ -253,7 +253,7 @@ export const onInstall: OnInstallHandler = async () => { export const onUpdate: OnUpdateHandler = async () => { const component = panel([ text('Features released with this update:'), - text('Deprecation of the Starknet Goerli Testnet'), + text('Cairo contract upgrade support.'), ]); await snap.request({ From d695ead64889503e86bdca55b2bb435fe4e016df Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Thu, 13 Jun 2024 11:41:24 +0800 Subject: [PATCH 22/25] chore: update snap file --- packages/starknet-snap/snap.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index 08623dde..e8666d3f 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": "KYC9Ksio3trenccTZNjeQYWnIzEUHKrcsC0v8GWSE2M=", + "shasum": "iWfHvJyvoqFsf3POJ0pkO+gCh6MjL4HZLUI36IPWD7E=", "location": { "npm": { "filePath": "dist/bundle.js", From 47ffbb51d43db6572bbc18c8c83110c1c1be8bc7 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Thu, 13 Jun 2024 12:25:38 +0800 Subject: [PATCH 23/25] chore: update yarn.lock --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 61f44eec..dd37e5b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3248,7 +3248,7 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.7.0 - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=9bd857&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=6d8d1a&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: "@metamask/snaps-sdk": 3.0.1 async-mutex: ^0.3.2 @@ -3257,7 +3257,7 @@ __metadata: ethers: ^5.5.1 starknet: 6.7.0 starknet_v4.22.0: "npm:starknet@4.22.0" - checksum: 67dd2a879a94ef48d63cb715bf3a194e3c5a7c8edb08501c578a70e38aaba58e0441fa076f8516c7288055ace5129855a5f7af5fc534ff6fa13e0476d5093583 + checksum: 31b742bf003c4ad0eb99801d5264e58ea178cd0e550aeb35896d88af5271e57acf9c989b75c1c9f160da3a038bc82a23f112f5ffa845602a118db9da0b228669 languageName: node linkType: hard From 20f4fac9caf30c1b3c265db8bb60132fb5c89614 Mon Sep 17 00:00:00 2001 From: stanleyyuen <102275989+stanleyyconsensys@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:00:02 +0800 Subject: [PATCH 24/25] chore: update snap sdk --- yarn.lock | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/yarn.lock b/yarn.lock index a370f854..8606aacc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3248,11 +3248,7 @@ __metadata: "@consensys/starknet-snap@file:../starknet-snap::locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui": version: 2.7.0 -<<<<<<< HEAD - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=6d8d1a&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" -======= - resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=da052f&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" ->>>>>>> main + resolution: "@consensys/starknet-snap@file:../starknet-snap#../starknet-snap::hash=64cb92&locator=wallet-ui%40workspace%3Apackages%2Fwallet-ui" dependencies: "@metamask/snaps-sdk": 3.0.1 async-mutex: ^0.3.2 @@ -3261,11 +3257,7 @@ __metadata: ethers: ^5.5.1 starknet: 6.7.0 starknet_v4.22.0: "npm:starknet@4.22.0" -<<<<<<< HEAD - checksum: 31b742bf003c4ad0eb99801d5264e58ea178cd0e550aeb35896d88af5271e57acf9c989b75c1c9f160da3a038bc82a23f112f5ffa845602a118db9da0b228669 -======= - checksum: d9b1b74d87bd0062457119a8a47fde7ca7914e5e235d2bda51b6f99f74f6a621d6134845518e0c4c969cc84eeaca24bc0cf4ee91245a69c98dc4e4a0a955caab ->>>>>>> main + checksum: 12c71045fd6bd5d137d51a9fb9a92b3aae206b7b1b1a036bd8fe4ddbf953d94967be56ecb170c4e871a30fa065ba64571dca714180522b89b5a9bf4cc6476b41 languageName: node linkType: hard From f77eb06b03aeb6337362ddf9b824bbea3e6b8292 Mon Sep 17 00:00:00 2001 From: Florin Dzeladini Date: Wed, 19 Jun 2024 10:22:49 +0200 Subject: [PATCH 25/25] fix: building and unit tests --- packages/starknet-snap/snap.manifest.json | 6 +----- packages/starknet-snap/test/src/executeTxn.test.ts | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/starknet-snap/snap.manifest.json b/packages/starknet-snap/snap.manifest.json index e5c905ab..170837ee 100644 --- a/packages/starknet-snap/snap.manifest.json +++ b/packages/starknet-snap/snap.manifest.json @@ -7,11 +7,7 @@ "url": "https://github.com/ConsenSys/starknet-snap.git" }, "source": { -<<<<<<< HEAD - "shasum": "iWfHvJyvoqFsf3POJ0pkO+gCh6MjL4HZLUI36IPWD7E=", -======= - "shasum": "n98JYVZFBDx4LYHqusYAftu/diScJU/ePb8FTYHZXsU=", ->>>>>>> main + "shasum": "kl5iM2GRyXtFYwezb2vvrh2iTNC4Gw4K1I3PVOttXJ4=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/starknet-snap/test/src/executeTxn.test.ts b/packages/starknet-snap/test/src/executeTxn.test.ts index 9c579be2..667c0eab 100644 --- a/packages/starknet-snap/test/src/executeTxn.test.ts +++ b/packages/starknet-snap/test/src/executeTxn.test.ts @@ -294,6 +294,7 @@ describe('Test function: executeTxn', function () { }); it('should return false if user rejected to sign the transaction', async function () { + sandbox.stub(utils, 'isAccountDeployed').resolves(true); walletStub.rpcStubs.snap_dialog.resolves(false); const stub = sandbox.stub(utils, 'executeTxn').resolves({ transaction_hash: 'transaction_hash',