diff --git a/__tests__/account.test.ts b/__tests__/account.test.ts index 95a06255a..273447638 100644 --- a/__tests__/account.test.ts +++ b/__tests__/account.test.ts @@ -10,7 +10,6 @@ import { compiledErc20, compiledTestDapp, erc20ClassHash, - getERC20DeployPayload, getTestAccount, getTestProvider, } from './fixtures'; @@ -25,15 +24,38 @@ describe('deploy and test Wallet', () => { beforeAll(async () => { expect(account).toBeInstanceOf(Account); - const erc20DeployPayload = getERC20DeployPayload(account.address); - - const erc20Response = await provider.deployContract(erc20DeployPayload); + /* // Declare + const declareTx = await account.declare({ + contract: compiledErc20, + classHash: '0x54328a1075b8820eb43caf0caa233923148c983742402dcfc38541dd843d01a', + }); + expect(declareTx.transaction_hash).toBeDefined(); + await provider.waitForTransaction(declareTx.transaction_hash); + + // Deploy Contract + const erc20Response = await account.deployContract2({ + classHash: declareTx.class_hash, + constructorCalldata: [ + encodeShortString('Token'), + encodeShortString('ERC20'), + account.address, + ], + }); + expect(erc20Response.contract_address).toBeDefined(); */ + + const declareDeploy = await account.declareDeploy({ + contract: compiledErc20, + classHash: '0x54328a1075b8820eb43caf0caa233923148c983742402dcfc38541dd843d01a', + constructorCalldata: [ + encodeShortString('Token'), + encodeShortString('ERC20'), + account.address, + ], + }); - erc20Address = erc20Response.contract_address; + erc20Address = declareDeploy.deploy.contract_address; erc20 = new Contract(compiledErc20.abi, erc20Address, provider); - await provider.waitForTransaction(erc20Response.transaction_hash); - const x = await erc20.balanceOf(account.address); expect(number.toBN(x[0].low).toString()).toStrictEqual(number.toBN(1000).toString()); @@ -176,6 +198,27 @@ describe('deploy and test Wallet', () => { expect(declareTx.class_hash).toEqual(erc20ClassHash); }); + test('UDC DeployContract', async () => { + const deployResponse = await account.deployContract2({ + classHash: erc20ClassHash, + constructorCalldata: [ + encodeShortString('Token'), + encodeShortString('ERC20'), + account.address, + ], + }); + + expect(deployResponse.contract_address).toBeDefined(); + expect(deployResponse.transaction_hash).toBeDefined(); + expect(deployResponse.address).toBeDefined(); + expect(deployResponse.deployer).toBeDefined(); + expect(deployResponse.unique).toBeDefined(); + expect(deployResponse.classHash).toBeDefined(); + expect(deployResponse.calldata_len).toBeDefined(); + expect(deployResponse.calldata).toBeDefined(); + expect(deployResponse.salt).toBeDefined(); + }); + test('UDC Deploy', async () => { const salt = randomAddress(); // use random salt diff --git a/__tests__/fixtures.ts b/__tests__/fixtures.ts index 0c0c520fe..2aebc7d83 100644 --- a/__tests__/fixtures.ts +++ b/__tests__/fixtures.ts @@ -1,8 +1,7 @@ import fs from 'fs'; import { Account, ProviderInterface, RpcProvider, SequencerProvider, ec, json } from '../src'; -import { CompiledContract, DeployContractPayload } from '../src/types'; -import { encodeShortString } from '../src/utils/shortString'; +import { CompiledContract } from '../src/types'; const readContract = (name: string): CompiledContract => json.parse(fs.readFileSync(`./__mocks__/${name}.json`).toString('ascii')); @@ -74,10 +73,3 @@ export const describeIfRpc = describeIf(IS_RPC); export const describeIfNotDevnet = describeIf(!IS_DEVNET); export const erc20ClassHash = '0x54328a1075b8820eb43caf0caa233923148c983742402dcfc38541dd843d01a'; - -export const getERC20DeployPayload = (recipient: string): DeployContractPayload => { - return { - contract: compiledErc20, - constructorCalldata: [encodeShortString('Token'), encodeShortString('ERC20'), recipient], - }; -}; diff --git a/src/account/default.ts b/src/account/default.ts index 7fe5996dd..9d7230f6d 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -22,6 +22,7 @@ import { DeployAccountContractPayload, UniversalDeployerContractPayload, } from '../types/lib'; +import { AccountDeployContractResponse, InvokeTransactionReceiptResponse } from '../types/provider'; import { calculateContractAddressFromHash, feeTransactionVersion, @@ -29,7 +30,7 @@ import { } from '../utils/hash'; import { BigNumberish, toBN, toCairoBool } from '../utils/number'; import { parseContract } from '../utils/provider'; -import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark'; +import { compileCalldata, estimatedFeeToMaxFee, randomAddress } from '../utils/stark'; import { fromCallsToExecuteCalldata } from '../utils/transaction'; import { TypedData, getMessageHash } from '../utils/typedData'; import { AccountInterface } from './interface'; @@ -169,7 +170,7 @@ export class Account extends Provider implements AccountInterface { public async estimateDeployFee( { classHash, - salt, + salt = '', unique = true, constructorCalldata = [], additionalCalls = [], @@ -272,7 +273,7 @@ export class Account extends Provider implements AccountInterface { public async deploy( { classHash, - salt, + salt = '', unique = true, constructorCalldata = [], additionalCalls = [], @@ -280,8 +281,8 @@ export class Account extends Provider implements AccountInterface { transactionsDetail: InvocationsDetails = {} ): Promise { const compiledConstructorCallData = compileCalldata(constructorCalldata); - const callsArray = Array.isArray(additionalCalls) ? additionalCalls : [additionalCalls]; + const randomSalt = salt ?? randomAddress(); return this.execute( [ @@ -290,7 +291,7 @@ export class Account extends Provider implements AccountInterface { entrypoint: UDC.ENTRYPOINT, calldata: [ classHash, - salt, + randomSalt, toCairoBool(unique), compiledConstructorCallData.length, ...compiledConstructorCallData, @@ -303,6 +304,70 @@ export class Account extends Provider implements AccountInterface { ); } + /** + * Simplify deploy simulating old DeployContract with same response + UDC specific response + * + * @param payload UniversalDeployerContractPayload + * @param detials InvocationsDetails + * @returns Promise + */ + public async deployContract2( + { + classHash, + salt, + unique = true, + constructorCalldata = [], + additionalCalls = [], + }: UniversalDeployerContractPayload, + transactionsDetail: InvocationsDetails = {} + ): Promise { + const deployTx = await this.deploy( + { + classHash, + salt, + unique, + constructorCalldata, + additionalCalls, + }, + transactionsDetail + ); + const txReceipt = await this.waitForTransaction(deployTx.transaction_hash); + + return this.getUDCResponse(txReceipt); + } + + /** + * Parse Transaction Receipt Event from UDC invoke transaction and + * create DeployContractResponse compatibile response with adition of UDC Event data + * + * @param txReceipt + * @returns DeployContractResponse | UDC Event Response data + */ + public getUDCResponse(txReceipt: InvokeTransactionReceiptResponse | void) { + if (txReceipt && txReceipt.events) { + const event = txReceipt.events.find((it) => it.from_address === UDC.ADDRESS) || { data: [] }; + return { + transaction_hash: txReceipt.transaction_hash, + contract_address: event.data[0], + address: event.data[0], + deployer: event.data[1], + unique: event.data[2], + classHash: event.data[3], + calldata_len: event.data[4], + calldata: event.data.slice(5, 5 + parseInt(event.data[4], 16)), + salt: event.data[event.data.length - 1], + }; + } + return new Error("UDC didn't emmit event"); + } + + public async declareDeploy({ classHash, contract, constructorCalldata }: any) { + const { transaction_hash } = await this.declare({ contract, classHash }); + const declare = await this.waitForTransaction(transaction_hash); + const deploy = await this.deployContract2({ classHash, constructorCalldata }); + return { declare: { ...declare, class_hash: classHash }, deploy }; + } + public async deployAccount( { classHash, diff --git a/src/provider/default.ts b/src/provider/default.ts index a22fa4adb..0fd18e9cb 100644 --- a/src/provider/default.ts +++ b/src/provider/default.ts @@ -35,13 +35,20 @@ export class Provider implements ProviderInterface { private provider!: ProviderInterface; constructor(providerOrOptions?: ProviderOptions | ProviderInterface) { - if (providerOrOptions && 'chainId' in providerOrOptions) { - this.provider = providerOrOptions; - } else if (providerOrOptions?.rpc) { - this.provider = new RpcProvider(providerOrOptions.rpc); - } else if (providerOrOptions?.sequencer) { - this.provider = new SequencerProvider(providerOrOptions.sequencer); + if ( + providerOrOptions instanceof RpcProvider || + providerOrOptions instanceof SequencerProvider + ) { + // providerOrOptions is provider + this.provider = providerOrOptions; + } else if (providerOrOptions && 'rpc' in providerOrOptions) { + // providerOrOptions is rpc option + this.provider = new RpcProvider(providerOrOptions.rpc); + } else if (providerOrOptions && 'sequencer' in providerOrOptions) { + // providerOrOptions is sequencer option + this.provider = new SequencerProvider(providerOrOptions.sequencer); } else { + // providerOrOptions is none, create SequencerProvider as default this.provider = new SequencerProvider(); } } @@ -180,7 +187,7 @@ export class Provider implements ProviderInterface { return this.provider.getCode(contractAddress, blockIdentifier); } - public async waitForTransaction(txHash: BigNumberish, retryInterval?: number): Promise { + public async waitForTransaction(txHash: BigNumberish, retryInterval?: number): Promise { return this.provider.waitForTransaction(txHash, retryInterval); } } diff --git a/src/provider/interface.ts b/src/provider/interface.ts index 5d7d888f5..950e40aca 100644 --- a/src/provider/interface.ts +++ b/src/provider/interface.ts @@ -291,5 +291,5 @@ export abstract class ProviderInterface { * @param txHash - transaction hash * @param retryInterval - retry interval */ - public abstract waitForTransaction(txHash: BigNumberish, retryInterval?: number): Promise; + public abstract waitForTransaction(txHash: BigNumberish, retryInterval?: number): Promise; } diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 5c7f6a0ba..92c410244 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -407,6 +407,7 @@ export class RpcProvider implements ProviderInterface { public async waitForTransaction(txHash: string, retryInterval: number = 8000) { let { retries } = this; let onchain = false; + let res; while (!onchain) { const successStates = ['ACCEPTED_ON_L1', 'ACCEPTED_ON_L2', 'PENDING']; @@ -416,7 +417,7 @@ export class RpcProvider implements ProviderInterface { await wait(retryInterval); try { // eslint-disable-next-line no-await-in-loop - const res = await this.getTransactionReceipt(txHash); + res = await this.getTransactionReceipt(txHash); if (!('status' in res)) { const error = new Error('pending transaction'); @@ -445,6 +446,7 @@ export class RpcProvider implements ProviderInterface { } await wait(retryInterval); + return res; } /** diff --git a/src/provider/sequencer.ts b/src/provider/sequencer.ts index d1072d0f4..9fc77c0a6 100644 --- a/src/provider/sequencer.ts +++ b/src/provider/sequencer.ts @@ -450,12 +450,13 @@ export class SequencerProvider implements ProviderInterface { public async waitForTransaction(txHash: BigNumberish, retryInterval: number = 8000) { let onchain = false; + let res; while (!onchain) { // eslint-disable-next-line no-await-in-loop await wait(retryInterval); // eslint-disable-next-line no-await-in-loop - const res = await this.getTransactionStatus(txHash); + res = await this.getTransactionStatus(txHash); const successStates = ['ACCEPTED_ON_L1', 'ACCEPTED_ON_L2', 'PENDING']; const errorStates = ['REJECTED', 'NOT_RECEIVED']; @@ -471,6 +472,7 @@ export class SequencerProvider implements ProviderInterface { throw error; } } + return res; } /** diff --git a/src/types/lib.ts b/src/types/lib.ts index 3d8d4e236..d699f2e58 100644 --- a/src/types/lib.ts +++ b/src/types/lib.ts @@ -21,8 +21,8 @@ export interface ContractClass { export type UniversalDeployerContractPayload = { classHash: BigNumberish; - salt: string; - unique: boolean; + salt?: string; + unique?: boolean; constructorCalldata?: RawArgs; additionalCalls?: AllowArray; // support multicall }; diff --git a/src/types/provider.ts b/src/types/provider.ts index 9da3ecf84..0017818fb 100644 --- a/src/types/provider.ts +++ b/src/types/provider.ts @@ -111,6 +111,16 @@ export interface DeployContractResponse { transaction_hash: string; } +export interface AccountDeployContractResponse extends DeployContractResponse { + address: string; + deployer: string; + unique: string; + classHash: string; + calldata_len: string; + calldata: Array; + salt: string; +} + export interface DeclareContractResponse { transaction_hash: string; class_hash: string;