From 30d554442ed3173bf4fa1d80dd3acaf2461b78aa Mon Sep 17 00:00:00 2001 From: Dhruv Kelawala Date: Fri, 7 Oct 2022 15:43:19 +0530 Subject: [PATCH] feat: add declare flow with estimate fee --- src/account/default.ts | 83 ++++++++++++++++++++++++++++++++++++-- src/account/interface.ts | 26 +++++++++++- src/contract/default.ts | 4 +- src/provider/default.ts | 27 ++++++++++--- src/provider/interface.ts | 55 ++++++++++++++++++------- src/provider/rpc.ts | 49 +++++++++++++++++----- src/provider/sequencer.ts | 44 ++++++++++++++------ src/signer/default.ts | 27 ++++++++++++- src/signer/interface.ts | 16 +++++++- src/types/api/openrpc.ts | 10 ++++- src/types/api/sequencer.ts | 19 +++++---- src/types/lib.ts | 20 +++++++-- src/types/provider.ts | 9 +---- src/types/signer.ts | 11 +++++ src/utils/hash.ts | 32 +++++++++++++++ 15 files changed, 360 insertions(+), 72 deletions(-) diff --git a/src/account/default.ts b/src/account/default.ts index e82646fd4..a07181a01 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -6,6 +6,7 @@ import { Signer, SignerInterface } from '../signer'; import { Abi, Call, + DeclareContractResponse, InvocationsDetails, InvocationsSignerDetails, InvokeFunctionResponse, @@ -13,8 +14,10 @@ import { Signature, } from '../types'; import { EstimateFee, EstimateFeeDetails } from '../types/account'; +import { DeclareContractPayload } from '../types/lib'; import { transactionVersion } from '../utils/hash'; import { BigNumberish, toBN } from '../utils/number'; +import { parseContract } from '../utils/provider'; import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark'; import { fromCallsToExecuteCalldata } from '../utils/transaction'; import { TypedData, getMessageHash } from '../utils/typedData'; @@ -40,7 +43,7 @@ export class Account extends Provider implements AccountInterface { return super.getNonce(this.address, blockIdentifier); } - public async estimateFee( + public async estimateInvokeFee( calls: Call | Call[], { nonce: providedNonce, blockIdentifier }: EstimateFeeDetails = {} ): Promise { @@ -60,7 +63,7 @@ export class Account extends Provider implements AccountInterface { const signature = await this.signer.signTransaction(transactions, signerDetails); const calldata = fromCallsToExecuteCalldata(transactions); - const response = await super.getEstimateFee( + const response = await super.getInvokeEstimateFee( { contractAddress: this.address, calldata, signature }, { version, nonce }, blockIdentifier @@ -74,6 +77,37 @@ export class Account extends Provider implements AccountInterface { }; } + public async estimateDeclareFee( + { classHash, contract }: DeclareContractPayload, + { blockIdentifier, nonce: providedNonce }: EstimateFeeDetails = {} + ): Promise { + const nonce = toBN(providedNonce ?? (await this.getNonce())); + const version = toBN(transactionVersion); + const chainId = await this.getChainId(); + const contractDefinition = parseContract(contract); + + const signature = await this.signer.signDeclareTransaction({ + classHash, + senderAddress: this.address, + chainId, + maxFee: ZERO, + version, + nonce, + }); + + const response = await super.getDeclareEstimateFee( + { senderAddress: this.address, signature, contractDefinition }, + { version, nonce }, + blockIdentifier + ); + const suggestedMaxFee = estimatedFeeToMaxFee(response.overall_fee); + + return { + ...response, + suggestedMaxFee, + }; + } + /** * Invoke execute function in account contract * @@ -95,7 +129,7 @@ export class Account extends Provider implements AccountInterface { if (transactionsDetail.maxFee || transactionsDetail.maxFee === 0) { maxFee = transactionsDetail.maxFee; } else { - const { suggestedMaxFee } = await this.estimateFee(transactions, { nonce }); + const { suggestedMaxFee } = await this.estimateInvokeFee(transactions, { nonce }); maxFee = suggestedMaxFee.toString(); } @@ -124,6 +158,49 @@ export class Account extends Provider implements AccountInterface { ); } + public async declare( + { classHash, contract }: DeclareContractPayload, + transactionsDetail: InvocationsDetails = {} + ): Promise { + const nonce = toBN(transactionsDetail.nonce ?? (await this.getNonce())); + let maxFee: BigNumberish = '0'; + + if (transactionsDetail.maxFee || transactionsDetail.maxFee === 0) { + maxFee = transactionsDetail.maxFee; + } else { + const { suggestedMaxFee } = await this.estimateDeclareFee( + { contract, classHash }, + { + nonce, + } + ); + maxFee = suggestedMaxFee.toString(); + } + + const version = toBN(transactionVersion); + const chainId = await this.getChainId(); + + const signature = await this.signer.signDeclareTransaction({ + classHash, + senderAddress: this.address, + chainId, + maxFee, + version, + nonce, + }); + + const contractDefinition = parseContract(contract); + + return this.declareContract( + { contractDefinition, senderAddress: this.address, signature }, + { + nonce, + maxFee, + version, + } + ); + } + /** * Sign an JSON object with the starknet private key and return the signature * diff --git a/src/account/interface.ts b/src/account/interface.ts index 6bb8738a7..91c04b4a6 100644 --- a/src/account/interface.ts +++ b/src/account/interface.ts @@ -4,12 +4,14 @@ import { SignerInterface } from '../signer'; import { Abi, Call, + DeclareContractResponse, EstimateFeeDetails, EstimateFeeResponse, InvocationsDetails, InvokeFunctionResponse, Signature, } from '../types'; +import { DeclareContractPayload } from '../types/lib'; import { BigNumberish } from '../utils/number'; import { TypedData } from '../utils/typedData/types'; @@ -29,11 +31,16 @@ export abstract class AccountInterface extends ProviderInterface { * * @returns response from addTransaction */ - public abstract estimateFee( + public abstract estimateInvokeFee( calls: Call | Call[], estimateFeeDetails?: EstimateFeeDetails ): Promise; + public abstract estimateDeclareFee( + contractPayload: DeclareContractPayload, + transactionsDetail?: EstimateFeeDetails + ): Promise; + /** * Invoke execute function in account contract * @@ -52,6 +59,23 @@ export abstract class AccountInterface extends ProviderInterface { transactionsDetail?: InvocationsDetails ): Promise; + /** + * Declares a given compiled contract (json) to starknet + * @param contractPayload transaction payload to be deployed containing: + - contract: compiled contract code + - classHash: computed class hash of compiled contract + - signature + * @param transactionsDetail Invocation Details containing: + - optional nonce + - optional version + - optional maxFee + * @returns a confirmation of sending a transaction on the starknet contract + */ + public abstract declare( + contractPayload: DeclareContractPayload, + transactionsDetail?: InvocationsDetails + ): Promise; + /** * Sign an JSON object for off-chain usage with the starknet private key and return the signature * This adds a message prefix so it cant be interchanged with transactions diff --git a/src/contract/default.ts b/src/contract/default.ts index 244154f88..6b88d5c11 100644 --- a/src/contract/default.ts +++ b/src/contract/default.ts @@ -633,8 +633,8 @@ export class Contract implements ContractInterface { // validate method and args this.validateMethodAndArgs('INVOKE', method, args); const invocation = this.populateTransaction[method](...args); - if ('estimateFee' in this.providerOrAccount) { - return this.providerOrAccount.estimateFee(invocation); + if ('estimateInvokeFee' in this.providerOrAccount) { + return this.providerOrAccount.estimateInvokeFee(invocation); } throw Error('Contract must be connected to the account contract to estimate'); } diff --git a/src/provider/default.ts b/src/provider/default.ts index b5d51554c..f6c7f0f5a 100644 --- a/src/provider/default.ts +++ b/src/provider/default.ts @@ -3,7 +3,6 @@ import { Call, CallContractResponse, ContractClass, - DeclareContractPayload, DeclareContractResponse, DeployContractPayload, DeployContractResponse, @@ -16,6 +15,7 @@ import { InvocationsDetailsWithNonce, InvokeFunctionResponse, } from '../types'; +import { DeclareContractTransaction } from '../types/lib'; import { BigNumberish } from '../utils/number'; import { ProviderInterface } from './interface'; import { RpcProvider, RpcProviderOptions } from './rpc'; @@ -61,12 +61,16 @@ export class Provider implements ProviderInterface { return this.provider.getClassAt(contractAddress, blockIdentifier); } - public async getEstimateFee( - invocation: Invocation, + public async getInvokeEstimateFee( + invocationWithTxType: Invocation, invocationDetails: InvocationsDetailsWithNonce, blockIdentifier: BlockIdentifier = 'pending' ): Promise { - return this.provider.getEstimateFee(invocation, invocationDetails, blockIdentifier); + return this.provider.getInvokeEstimateFee( + invocationWithTxType, + invocationDetails, + blockIdentifier + ); } public async getNonce( @@ -110,8 +114,19 @@ export class Provider implements ProviderInterface { return this.provider.deployContract(payload); } - public async declareContract(payload: DeclareContractPayload): Promise { - return this.provider.declareContract(payload); + public async declareContract( + transaction: DeclareContractTransaction, + details: InvocationsDetailsWithNonce + ): Promise { + return this.provider.declareContract(transaction, details); + } + + public async getDeclareEstimateFee( + transaction: DeclareContractTransaction, + details: InvocationsDetailsWithNonce, + blockIdentifier: BlockIdentifier = 'pending' + ): Promise { + return this.provider.getDeclareEstimateFee(transaction, details, blockIdentifier); } public async getCode( diff --git a/src/provider/interface.ts b/src/provider/interface.ts index 86278b3e3..078e7f847 100644 --- a/src/provider/interface.ts +++ b/src/provider/interface.ts @@ -3,7 +3,6 @@ import type { Call, CallContractResponse, ContractClass, - DeclareContractPayload, DeclareContractResponse, DeployContractPayload, DeployContractResponse, @@ -16,6 +15,7 @@ import type { InvocationsDetailsWithNonce, InvokeFunctionResponse, } from '../types'; +import { DeclareContractTransaction } from '../types/lib'; import type { BigNumberish } from '../utils/number'; import { BlockIdentifier } from './utils'; @@ -123,18 +123,6 @@ export abstract class ProviderInterface { */ public abstract deployContract(payload: DeployContractPayload): Promise; - /** - * Declares a given compiled contract (json) to starknet - * - * @param payload payload to be deployed containing: - * - compiled contract code - * - optional version - * @returns a confirmation of sending a transaction on the starknet contract - */ - public abstract declareContract( - payload: DeclareContractPayload - ): Promise; - /** * Invokes a function on starknet * @deprecated This method wont be supported as soon as fees are mandatory @@ -156,7 +144,24 @@ export abstract class ProviderInterface { ): Promise; /** - * Estimates the fee for a given transaction + * Declares a given compiled contract (json) to starknet + * @param transaction transaction payload to be deployed containing: + * - compiled contract code + * - sender address + * - signature + * @param details Invocation Details containing: + * - nonce + * - optional version + * - optional maxFee + * @returns a confirmation of sending a transaction on the starknet contract + */ + public abstract declareContract( + transaction: DeclareContractTransaction, + details: InvocationsDetailsWithNonce + ): Promise; + + /** + * Estimates the fee for a given INVOKE transaction * * @param invocation the invocation object containing: * - contractAddress - the address of the contract @@ -169,12 +174,32 @@ export abstract class ProviderInterface { * - version - optional version * @returns the estimated fee */ - public abstract getEstimateFee( + public abstract getInvokeEstimateFee( invocation: Invocation, details: InvocationsDetailsWithNonce, blockIdentifier: BlockIdentifier ): Promise; + /** + * Estimates the fee for a given DECLARE transaction + * + * @param transaction transaction payload to be deployed containing: + * - compiled contract code + * - sender address + * - signature - (defaults to []) the signature + * @param details - optional details containing: + * - nonce + * - version - optional version + * - optional maxFee + * @param blockIdentifier - block identifier + * @returns the estimated fee + */ + public abstract getDeclareEstimateFee( + transaction: DeclareContractTransaction, + details: InvocationsDetailsWithNonce, + blockIdentifier: BlockIdentifier + ): Promise; + /** * Wait for the transaction to be accepted * @param txHash - transaction hash diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 66a13705b..be881b9b6 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -2,7 +2,6 @@ import { StarknetChainId } from '../constants'; import { Call, CallContractResponse, - DeclareContractPayload, DeclareContractResponse, DeployContractPayload, DeployContractResponse, @@ -16,6 +15,7 @@ import { InvokeFunctionResponse, } from '../types'; import { RPC } from '../types/api'; +import { DeclareContractTransaction } from '../types/lib'; import fetch from '../utils/fetchPonyfill'; import { getSelectorFromName } from '../utils/hash'; import { stringify } from '../utils/json'; @@ -210,7 +210,7 @@ export class RpcProvider implements ProviderInterface { throw new Error('RPC 0.1.0 does not implement getCode function'); } - public async getEstimateFee( + public async getInvokeEstimateFee( invocation: Invocation, invocationDetails: InvocationsDetailsWithNonce, blockIdentifier: BlockIdentifier = 'pending' @@ -218,29 +218,60 @@ export class RpcProvider implements ProviderInterface { const block_id = new Block(blockIdentifier).identifier; return this.fetchEndpoint('starknet_estimateFee', { request: { + type: 'INVOKE', contract_address: invocation.contractAddress, calldata: parseCalldata(invocation.calldata), signature: bigNumberishArrayToHexadecimalStringArray(invocation.signature || []), version: toHex(toBN(invocationDetails?.version || 0)), + nonce: toHex(toBN(invocationDetails.nonce)), max_fee: toHex(toBN(invocationDetails?.maxFee || 0)), }, block_id, }).then(this.responseParser.parseFeeEstimateResponse); } - public async declareContract({ - contract, - version, - }: DeclareContractPayload): Promise { - const contractDefinition = parseContract(contract); + // TODO: Revisit after Pathfinder release with JSON-RPC v0.2.1 RPC Spec + public async getDeclareEstimateFee( + { senderAddress, contractDefinition, signature }: DeclareContractTransaction, + details: InvocationsDetailsWithNonce, + blockIdentifier: BlockIdentifier = 'pending' + ): Promise { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_estimateFee', { + request: { + type: 'DECLARE', + contract_class: { + program: contractDefinition.program, + entry_points_by_type: contractDefinition.entry_points_by_type, + abi: contractDefinition.abi, // rpc 2.0 + }, + sender_address: senderAddress, + signature: bigNumberishArrayToHexadecimalStringArray(signature || []), + version: toHex(toBN(details?.version || 0)), + nonce: toHex(toBN(details.nonce)), + max_fee: toHex(toBN(details?.maxFee || 0)), + }, + block_id, + }).then(this.responseParser.parseFeeEstimateResponse); + } + + // TODO: Revisit after Pathfinder release with JSON-RPC v0.2.1 RPC Spec + public async declareContract( + { contractDefinition, signature, senderAddress }: DeclareContractTransaction, + details: InvocationsDetailsWithNonce + ): Promise { return this.fetchEndpoint('starknet_addDeclareTransaction', { contract_class: { program: contractDefinition.program, entry_points_by_type: contractDefinition.entry_points_by_type, abi: contractDefinition.abi, // rpc 2.0 }, - version: toHex(toBN(version || 0)), + version: toHex(toBN(details.version || 0)), + max_fee: toHex(toBN(details.maxFee || 0)), + signature: bigNumberishArrayToHexadecimalStringArray(signature || []), + sender_address: senderAddress, + nonce: toHex(toBN(details.nonce)), }); } @@ -286,7 +317,7 @@ export class RpcProvider implements ProviderInterface { const result = await this.fetchEndpoint('starknet_call', { request: { contract_address: call.contractAddress, - entry_point_selector: getSelectorFromName(call.entrypoint), + entry_point_selector: getSelectorFromName(call.entrypoint.toString()), calldata: parseCalldata(call.calldata), }, block_id, diff --git a/src/provider/sequencer.ts b/src/provider/sequencer.ts index 576d63f35..1b64a41d2 100644 --- a/src/provider/sequencer.ts +++ b/src/provider/sequencer.ts @@ -1,11 +1,10 @@ import urljoin from 'url-join'; -import { ONE, StarknetChainId, ZERO } from '../constants'; +import { StarknetChainId } from '../constants'; import { Call, CallContractResponse, ContractClass, - DeclareContractPayload, DeclareContractResponse, DeployContractPayload, DeployContractResponse, @@ -23,6 +22,7 @@ import { GetTransactionTraceResponse, Sequencer, } from '../types/api'; +import { DeclareContractTransaction } from '../types/lib'; import fetch from '../utils/fetchPonyfill'; import { getSelectorFromName } from '../utils/hash'; import { parse, parseAlwaysAsBig, stringify } from '../utils/json'; @@ -222,7 +222,7 @@ export class SequencerProvider implements ProviderInterface { { signature: [], contract_address: contractAddress, - entry_point_selector: getSelectorFromName(entryPointSelector), + entry_point_selector: getSelectorFromName(entryPointSelector.toString()), calldata, } ).then(this.responseParser.parseCallContractResponse); @@ -307,21 +307,22 @@ export class SequencerProvider implements ProviderInterface { }).then(this.responseParser.parseDeployContractResponse); } - public async declareContract({ - contract, - }: DeclareContractPayload): Promise { - const contractDefinition = parseContract(contract); - + public async declareContract( + { senderAddress, contractDefinition, signature }: DeclareContractTransaction, + details: InvocationsDetailsWithNonce + ): Promise { return this.fetchEndpoint('add_transaction', undefined, { type: 'DECLARE', contract_class: contractDefinition, - nonce: toHex(ZERO), - signature: [], - sender_address: toHex(ONE), + nonce: toHex(toBN(details.nonce)), + signature: bigNumberishArrayToDecimalStringArray(signature || []), + sender_address: senderAddress, + max_fee: toHex(toBN(details.maxFee || 0)), + version: toHex(toBN(details.version || 1)), }).then(this.responseParser.parseDeclareContractResponse); } - public async getEstimateFee( + public async getInvokeEstimateFee( invocation: Invocation, invocationDetails: InvocationsDetailsWithNonce, blockIdentifier: BlockIdentifier = 'pending' @@ -340,6 +341,25 @@ export class SequencerProvider implements ProviderInterface { ).then(this.responseParser.parseFeeEstimateResponse); } + public async getDeclareEstimateFee( + { senderAddress, contractDefinition, signature }: DeclareContractTransaction, + details: InvocationsDetailsWithNonce, + blockIdentifier: BlockIdentifier = 'pending' + ): Promise { + return this.fetchEndpoint( + 'estimate_fee', + { blockIdentifier }, + { + type: 'DECLARE', + sender_address: senderAddress, + contract_class: contractDefinition, + signature: bigNumberishArrayToDecimalStringArray(signature || []), + version: toHex(toBN(details?.version || 1)), + nonce: toHex(toBN(details.nonce)), + } + ).then(this.responseParser.parseFeeEstimateResponse); + } + public async getCode( contractAddress: string, blockIdentifier: BlockIdentifier = 'pending' diff --git a/src/signer/default.ts b/src/signer/default.ts index 0bf08a408..68a0f6718 100644 --- a/src/signer/default.ts +++ b/src/signer/default.ts @@ -1,6 +1,13 @@ -import { Abi, Call, InvocationsSignerDetails, KeyPair, Signature } from '../types'; +import { + Abi, + Call, + DeclareSignerDetails, + InvocationsSignerDetails, + KeyPair, + Signature, +} from '../types'; import { genKeyPair, getStarkKey, sign } from '../utils/ellipticCurve'; -import { calculateTransactionHash } from '../utils/hash'; +import { calculateDeclareTransactionHash, calculateTransactionHash } from '../utils/hash'; import { fromCallsToExecuteCalldata } from '../utils/transaction'; import { TypedData, getMessageHash } from '../utils/typedData'; import { SignerInterface } from './interface'; @@ -40,6 +47,22 @@ export class Signer implements SignerInterface { return sign(this.keyPair, msgHash); } + public async signDeclareTransaction( + // contractClass: ContractClass, // Should be used once class hash is present in ContractClass + { classHash, senderAddress, chainId, maxFee, version, nonce }: DeclareSignerDetails + ) { + const msgHash = calculateDeclareTransactionHash( + classHash, + senderAddress, + version, + maxFee, + chainId, + nonce + ); + + return sign(this.keyPair, msgHash); + } + public async signMessage(typedData: TypedData, accountAddress: string): Promise { const msgHash = getMessageHash(typedData, accountAddress); return sign(this.keyPair, msgHash); diff --git a/src/signer/interface.ts b/src/signer/interface.ts index d9c9ee208..008d06501 100644 --- a/src/signer/interface.ts +++ b/src/signer/interface.ts @@ -1,4 +1,4 @@ -import { Abi, Call, InvocationsSignerDetails, Signature } from '../types'; +import { Abi, Call, DeclareSignerDetails, InvocationsSignerDetails, Signature } from '../types'; import { TypedData } from '../utils/typedData'; export abstract class SignerInterface { @@ -27,7 +27,6 @@ export abstract class SignerInterface { * - contractAddress - the address of the contract * - entrypoint - the entrypoint of the contract * - calldata - (defaults to []) the calldata - * - signature - (defaults to []) the signature * @param abi (optional) the abi of the contract for better displaying * * @returns signature @@ -37,4 +36,17 @@ export abstract class SignerInterface { transactionsDetail: InvocationsSignerDetails, abis?: Abi[] ): Promise; + + /** + * Signs a DECLARE transaction with the starknet private key and returns the signature + * @param transaction + * - classHash - computed class hash. Will be replaced by ContractClass in future once class hash is present in CompiledContract + * - senderAddress - the address of the sender + * - chainId - the chainId to declare contract on + * - maxFee - maxFee for the declare transaction + * - version - transaction version + * - nonce - Nonce of the declare transaction + * @returns signature + */ + public abstract signDeclareTransaction(transaction: DeclareSignerDetails): Promise; } diff --git a/src/types/api/openrpc.ts b/src/types/api/openrpc.ts index 27f17a419..ad222f8d6 100644 --- a/src/types/api/openrpc.ts +++ b/src/types/api/openrpc.ts @@ -104,6 +104,10 @@ type DECLARE_TXN = COMMON_TXN_PROPERTIES & { class_hash: FELT; sender_address: ADDRESS; }; +type DECLARE_TXN_REQUEST = COMMON_TXN_PROPERTIES & { + contract_class: CONTRACT_CLASS; + sender_address: ADDRESS; +}; type DEPLOY_TXN = { transaction_hash: TXN_HASH; class_hash: FELT; @@ -328,7 +332,7 @@ export namespace OPENRPC { | Errors.INVALID_BLOCK_ID; }; starknet_estimateFee: { - params: { request: INVOKE_TXN; block_id: BLOCK_ID }; + params: { request: INVOKE_TXN | DECLARE_TXN_REQUEST; block_id: BLOCK_ID }; result: FEE_ESTIMATE; errors: | Errors.CONTRACT_NOT_FOUND @@ -384,7 +388,11 @@ export namespace OPENRPC { starknet_addDeclareTransaction: { params: { contract_class: CONTRACT_CLASS; + sender_address: ADDRESS; + signature: SIGNATURE; + max_fee: NUM_AS_HEX; version: NUM_AS_HEX; + nonce: FELT; }; result: DeclaredTransaction; errors: Errors.INVALID_CONTRACT_CLASS; diff --git a/src/types/api/sequencer.ts b/src/types/api/sequencer.ts index ab951db9f..575eb3d75 100644 --- a/src/types/api/sequencer.ts +++ b/src/types/api/sequencer.ts @@ -5,13 +5,13 @@ import { BigNumberish } from '../../utils/number'; import { Abi, BlockNumber, + ContractClass, EntryPointType, RawCalldata, Signature, Status, TransactionStatus, } from '../lib'; -import { ContractClass } from '../provider'; export type GetTransactionStatusResponse = { tx_status: Status; @@ -76,10 +76,12 @@ export type RawArgs = { export namespace Sequencer { export type DeclareTransaction = { type: 'DECLARE'; + sender_address: string; contract_class: ContractClass; + signature?: Signature; nonce: BigNumberish; - sender_address: BigNumberish; - signature: Signature; + max_fee?: BigNumberish; + version?: BigNumberish; }; export type DeployTransaction = { @@ -208,10 +210,11 @@ export namespace Sequencer { result: string[]; }; - export type EstimateFeeTransaction = Omit< - InvokeFunctionTransaction, - 'max_fee' | 'entry_point_type' - >; + export type InvokeEstimateFee = Omit; + export type DeclareEstimateFee = Omit; + export type DeployEstimateFee = DeployTransaction; + + export type EstimateFeeRequest = InvokeEstimateFee | DeclareEstimateFee | DeployEstimateFee; // Support 0.9.1 changes in a backward-compatible way export type EstimateFeeResponse = @@ -307,7 +310,7 @@ export namespace Sequencer { QUERY: { blockIdentifier: BlockIdentifier; }; - REQUEST: EstimateFeeTransaction; + REQUEST: EstimateFeeRequest; RESPONSE: EstimateFeeResponse; }; get_class_by_hash: { diff --git a/src/types/lib.ts b/src/types/lib.ts index 1da51b530..64d300610 100644 --- a/src/types/lib.ts +++ b/src/types/lib.ts @@ -1,11 +1,18 @@ import type { ec as EC } from 'elliptic'; import type { BigNumberish } from '../utils/number'; +import { RPC } from './api/rpc'; export type KeyPair = EC.KeyPair; export type Signature = string[]; export type RawCalldata = BigNumberish[]; +export interface ContractClass { + program: CompressedProgram; + entry_points_by_type: RPC.ContractClass['entry_points_by_type']; + abi?: Abi; +} + export type DeployContractPayload = { contract: CompiledContract | string; constructorCalldata?: RawCalldata; @@ -13,8 +20,15 @@ export type DeployContractPayload = { }; export type DeclareContractPayload = { + // The line is commented until we have Class Hash in the compiled contract contract: CompiledContract | string; - version?: BigNumberish; + classHash: string; +}; + +export type DeclareContractTransaction = { + contractDefinition: ContractClass; + senderAddress: string; + signature?: Signature; }; export type CallDetails = { @@ -24,7 +38,7 @@ export type CallDetails = { export type Invocation = CallDetails & { signature?: Signature }; -export type Call = CallDetails & { entrypoint: string }; +export type Call = CallDetails & { entrypoint: BigNumberish }; export type InvocationsDetails = { nonce?: BigNumberish; @@ -42,7 +56,7 @@ export type Status = | 'ACCEPTED_ON_L1' | 'REJECTED'; export type TransactionStatus = 'TRANSACTION_RECEIVED'; -export type Type = 'DECLARE' | 'DEPLOY' | 'INVOKE_FUNCTION'; +export type TransactionType = 'DECLARE' | 'DEPLOY' | 'INVOKE_FUNCTION'; export type EntryPointType = 'EXTERNAL'; export type CompressedProgram = string; diff --git a/src/types/provider.ts b/src/types/provider.ts index 9722338f7..1092569ef 100644 --- a/src/types/provider.ts +++ b/src/types/provider.ts @@ -4,8 +4,7 @@ */ import BN from 'bn.js'; -import { RPC } from './api/rpc'; -import { Abi, CompressedProgram, RawCalldata, Signature, Status } from './lib'; +import { RawCalldata, Signature, Status } from './lib'; export interface GetBlockResponse { timestamp: number; @@ -43,12 +42,6 @@ export interface ContractEntryPoint { selector: string; } -export interface ContractClass { - program: CompressedProgram; - entry_points_by_type: RPC.ContractClass['entry_points_by_type']; - abi?: Abi; -} - export interface DeclareTransactionResponse extends CommonTransactionResponse { contract_class?: any; sender_address?: string; diff --git a/src/types/signer.ts b/src/types/signer.ts index cab43ce86..bece1d71a 100644 --- a/src/types/signer.ts +++ b/src/types/signer.ts @@ -1,7 +1,18 @@ import { StarknetChainId } from '../constants'; +import { BigNumberish } from '../utils/number'; import { InvocationsDetails } from './lib'; export interface InvocationsSignerDetails extends Required { walletAddress: string; chainId: StarknetChainId; } + +export interface DeclareSignerDetails { + // contractClass: ContractClass, // Should be used once class hash is present in ContractClass + classHash: string; + senderAddress: BigNumberish; + chainId: StarknetChainId; + maxFee: BigNumberish; + version: BigNumberish; + nonce: BigNumberish; +} diff --git a/src/utils/hash.ts b/src/utils/hash.ts index 3c295c67b..54475987a 100644 --- a/src/utils/hash.ts +++ b/src/utils/hash.ts @@ -124,6 +124,38 @@ export function calculateDeployTransactionHash( ); } +export function calculateDeclareTransactionHash( + // contractClass: ContractClass, // Should be used once class hash is present in ContractClass + classHash: string, + senderAddress: BigNumberish, + version: BigNumberish, + maxFee: BigNumberish, + chainId: StarknetChainId, + nonce: BigNumberish +): string { + let calldata: BigNumberish[] = []; + let additionalData: BigNumberish[] = []; + + if (version !== ZERO) { + calldata = [classHash]; + additionalData = [nonce]; + } else { + calldata = []; + additionalData = [classHash]; + } + + return calculateTransactionHashCommon( + TransactionHashPrefix.DECLARE, + version, + senderAddress, + 0, + calldata, + maxFee, + chainId, + additionalData + ); +} + export function calculateTransactionHash( contractAddress: BigNumberish, version: BigNumberish,