Skip to content

Commit

Permalink
feat: add declare flow with estimate fee
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvkelawala committed Oct 7, 2022
1 parent 48ff4eb commit 30d5544
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 72 deletions.
83 changes: 80 additions & 3 deletions src/account/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { Signer, SignerInterface } from '../signer';
import {
Abi,
Call,
DeclareContractResponse,
InvocationsDetails,
InvocationsSignerDetails,
InvokeFunctionResponse,
KeyPair,
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';
Expand All @@ -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<EstimateFee> {
Expand All @@ -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
Expand All @@ -74,6 +77,37 @@ export class Account extends Provider implements AccountInterface {
};
}

public async estimateDeclareFee(
{ classHash, contract }: DeclareContractPayload,
{ blockIdentifier, nonce: providedNonce }: EstimateFeeDetails = {}
): Promise<EstimateFee> {
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
*
Expand All @@ -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();
}

Expand Down Expand Up @@ -124,6 +158,49 @@ export class Account extends Provider implements AccountInterface {
);
}

public async declare(
{ classHash, contract }: DeclareContractPayload,
transactionsDetail: InvocationsDetails = {}
): Promise<DeclareContractResponse> {
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
*
Expand Down
26 changes: 25 additions & 1 deletion src/account/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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<EstimateFeeResponse>;

public abstract estimateDeclareFee(
contractPayload: DeclareContractPayload,
transactionsDetail?: EstimateFeeDetails
): Promise<EstimateFeeResponse>;

/**
* Invoke execute function in account contract
*
Expand All @@ -52,6 +59,23 @@ export abstract class AccountInterface extends ProviderInterface {
transactionsDetail?: InvocationsDetails
): Promise<InvokeFunctionResponse>;

/**
* 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<DeclareContractResponse>;

/**
* 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
Expand Down
4 changes: 2 additions & 2 deletions src/contract/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand Down
27 changes: 21 additions & 6 deletions src/provider/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Call,
CallContractResponse,
ContractClass,
DeclareContractPayload,
DeclareContractResponse,
DeployContractPayload,
DeployContractResponse,
Expand All @@ -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';
Expand Down Expand Up @@ -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<EstimateFeeResponse> {
return this.provider.getEstimateFee(invocation, invocationDetails, blockIdentifier);
return this.provider.getInvokeEstimateFee(
invocationWithTxType,
invocationDetails,
blockIdentifier
);
}

public async getNonce(
Expand Down Expand Up @@ -110,8 +114,19 @@ export class Provider implements ProviderInterface {
return this.provider.deployContract(payload);
}

public async declareContract(payload: DeclareContractPayload): Promise<DeclareContractResponse> {
return this.provider.declareContract(payload);
public async declareContract(
transaction: DeclareContractTransaction,
details: InvocationsDetailsWithNonce
): Promise<DeclareContractResponse> {
return this.provider.declareContract(transaction, details);
}

public async getDeclareEstimateFee(
transaction: DeclareContractTransaction,
details: InvocationsDetailsWithNonce,
blockIdentifier: BlockIdentifier = 'pending'
): Promise<EstimateFeeResponse> {
return this.provider.getDeclareEstimateFee(transaction, details, blockIdentifier);
}

public async getCode(
Expand Down
55 changes: 40 additions & 15 deletions src/provider/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type {
Call,
CallContractResponse,
ContractClass,
DeclareContractPayload,
DeclareContractResponse,
DeployContractPayload,
DeployContractResponse,
Expand All @@ -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';

Expand Down Expand Up @@ -123,18 +123,6 @@ export abstract class ProviderInterface {
*/
public abstract deployContract(payload: DeployContractPayload): Promise<DeployContractResponse>;

/**
* 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<DeclareContractResponse>;

/**
* Invokes a function on starknet
* @deprecated This method wont be supported as soon as fees are mandatory
Expand All @@ -156,7 +144,24 @@ export abstract class ProviderInterface {
): Promise<InvokeFunctionResponse>;

/**
* 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<DeclareContractResponse>;

/**
* Estimates the fee for a given INVOKE transaction
*
* @param invocation the invocation object containing:
* - contractAddress - the address of the contract
Expand All @@ -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<EstimateFeeResponse>;

/**
* 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<EstimateFeeResponse>;

/**
* Wait for the transaction to be accepted
* @param txHash - transaction hash
Expand Down
Loading

0 comments on commit 30d5544

Please sign in to comment.