diff --git a/__tests__/account.test.ts b/__tests__/account.test.ts index 29a76bab4..90a99222a 100644 --- a/__tests__/account.test.ts +++ b/__tests__/account.test.ts @@ -1,5 +1,3 @@ -import { Signature } from 'micro-starknet'; - import typedDataExample from '../__mocks__/typedDataExample.json'; import { Account, @@ -8,17 +6,16 @@ import { Provider, TransactionStatus, TransactionType, + cairo, + contractClassResponseToLegacyCompiledContract, ec, + extractContractHashes, hash, + num, + parseUDCEvent, + shortString, stark, } from '../src'; -import { uint256 } from '../src/utils/calldata/cairo'; -import { extractContractHashes } from '../src/utils/contract'; -import { parseUDCEvent } from '../src/utils/events'; -import { calculateContractAddressFromHash, feeTransactionVersion } from '../src/utils/hash'; -import { cleanHex, hexToDecimalString, toBigInt, toHex } from '../src/utils/num'; -import { encodeShortString } from '../src/utils/shortString'; -import { randomAddress } from '../src/utils/stark'; import { compiledErc20, compiledHelloSierra, @@ -34,18 +31,25 @@ import { } from './fixtures'; import { initializeMatcher } from './schema'; +const { cleanHex, hexToDecimalString, toBigInt, toHex } = num; +const { encodeShortString } = shortString; +const { randomAddress } = stark; +const { uint256 } = cairo; +const { Signature } = ec.starkCurve; + describe('deploy and test Wallet', () => { const provider = new Provider(getTestProvider()); const account = getTestAccount(provider); let erc20: Contract; let erc20Address: string; let dapp: Contract; + let dd: DeclareDeployUDCResponse; beforeAll(async () => { initializeMatcher(expect); expect(account).toBeInstanceOf(Account); - const declareDeploy = await account.declareAndDeploy({ + dd = await account.declareAndDeploy({ contract: compiledErc20, constructorCalldata: [ encodeShortString('Token'), @@ -54,7 +58,7 @@ describe('deploy and test Wallet', () => { ], }); - erc20Address = declareDeploy.deploy.contract_address; + erc20Address = dd.deploy.contract_address; erc20 = new Contract(compiledErc20.abi, erc20Address, provider); const { balance } = await erc20.balanceOf(account.address); @@ -68,6 +72,19 @@ describe('deploy and test Wallet', () => { dapp = new Contract(compiledTestDapp.abi, dappResponse.deploy.contract_address!, provider); }); + xtest('validate TS for redeclare - skip testing', async () => { + const cc0 = await account.getClassAt(dd.deploy.address); + const cc0_1 = await account.getClassByHash(toHex(dd.declare.class_hash)); + + await account.declare({ + contract: contractClassResponseToLegacyCompiledContract(cc0), + }); + + await account.declare({ + contract: contractClassResponseToLegacyCompiledContract(cc0_1), + }); + }); + test('estimateInvokeFee Cairo 0', async () => { const innerInvokeEstFeeSpy = jest.spyOn(account.signer, 'signTransaction'); const result = await account.estimateInvokeFee({ @@ -77,7 +94,7 @@ describe('deploy and test Wallet', () => { }); expect(result).toMatchSchemaRef('EstimateFee'); - expect(innerInvokeEstFeeSpy.mock.calls[0][1].version).toBe(feeTransactionVersion); + expect(innerInvokeEstFeeSpy.mock.calls[0][1].version).toBe(hash.feeTransactionVersion); innerInvokeEstFeeSpy.mockClear(); }); @@ -287,7 +304,7 @@ describe('deploy and test Wallet', () => { await provider.waitForTransaction(declareAccount.transaction_hash); const privateKey = stark.randomAddress(); const starkKeyPub = ec.starkCurve.getStarkKey(privateKey); - const precalculatedAddress = calculateContractAddressFromHash( + const precalculatedAddress = hash.calculateContractAddressFromHash( starkKeyPub, accountClassHash, { publicKey: starkKeyPub }, @@ -595,7 +612,7 @@ describe('deploy and test Wallet', () => { const privateKey = stark.randomAddress(); starkKeyPub = ec.starkCurve.getStarkKey(privateKey); - precalculatedAddress = calculateContractAddressFromHash( + precalculatedAddress = hash.calculateContractAddressFromHash( starkKeyPub, accountClassHash, { publicKey: starkKeyPub }, diff --git a/__tests__/cairo1.test.ts b/__tests__/cairo1.test.ts index 40e249932..78206b2c2 100644 --- a/__tests__/cairo1.test.ts +++ b/__tests__/cairo1.test.ts @@ -4,6 +4,7 @@ import { BigNumberish, CallData, Calldata, + CompiledSierra, Contract, DeclareDeployUDCResponse, RawArgsArray, @@ -13,11 +14,10 @@ import { ec, hash, num, + selector, shortString, stark, } from '../src'; -import { isCairo1Abi } from '../src/utils/calldata/cairo'; -import { starknetKeccak } from '../src/utils/selector'; import { compiledC1Account, compiledC1AccountCasm, @@ -32,6 +32,10 @@ import { } from './fixtures'; import { initializeMatcher } from './schema'; +const { uint256, tuple, isCairo1Abi } = cairo; +const { toHex } = num; +const { starknetKeccak } = selector; + describeIfDevnet('Cairo 1 Devnet', () => { describe('API & Contract interactions', () => { const provider = getTestProvider(); @@ -55,6 +59,21 @@ describeIfDevnet('Cairo 1 Devnet', () => { expect(cairo1Contract).toBeInstanceOf(Contract); }); + xtest('validate TS for redeclare - skip testing', async () => { + const cc0 = await account.getClassAt(dd.deploy.address); + const cc0_1 = await account.getClassByHash(toHex(dd.declare.class_hash)); + + await account.declare({ + contract: cc0 as CompiledSierra, + casm: compiledHelloSierraCasm, + }); + + await account.declare({ + contract: cc0_1 as CompiledSierra, + casm: compiledHelloSierraCasm, + }); + }); + test('deployContract Cairo1', async () => { const deploy = await account.deployContract({ classHash: dd.deploy.classHash, @@ -123,7 +142,7 @@ describeIfDevnet('Cairo 1 Devnet', () => { expect(result).toBe(2n ** 256n - 1n); // defined as struct - const result1 = await cairo1Contract.test_u256(cairo.uint256(2n ** 256n - 2n)); + const result1 = await cairo1Contract.test_u256(uint256(2n ** 256n - 2n)); expect(result1).toBe(2n ** 256n - 1n); }); @@ -200,7 +219,7 @@ describeIfDevnet('Cairo 1 Devnet', () => { }); test('Cairo 1 Contract Interaction - echo flat un-named un-nested tuple', async () => { - const status = await cairo1Contract.echo_un_tuple(cairo.tuple(77, 123)); + const status = await cairo1Contract.echo_un_tuple(tuple(77, 123)); expect(Object.values(status)).toEqual([77n, 123n]); }); @@ -214,10 +233,10 @@ describeIfDevnet('Cairo 1 Devnet', () => { // uint256 defined as struct const status11 = await cairo1Contract.echo_array_u256([ - cairo.uint256(123), - cairo.uint256(55), - cairo.uint256(77), - cairo.uint256(255), + uint256(123), + uint256(55), + uint256(77), + uint256(255), ]); expect(status11).toEqual([123n, 55n, 77n, 255n]); @@ -305,7 +324,7 @@ describeIfDevnet('Cairo 1 Devnet', () => { 1: true, }); - const res1 = await cairo1Contract.tuple_echo(cairo.tuple([1, 2, 3], [4, 5, 6])); + const res1 = await cairo1Contract.tuple_echo(tuple([1, 2, 3], [4, 5, 6])); expect(res1).toEqual({ 0: [1n, 2n, 3n], 1: [4n, 5n, 6n], @@ -331,7 +350,7 @@ describeIfDevnet('Cairo 1 Devnet', () => { initial_supply: myFalseUint256, recipient: '0x7e00d496e324876bbc8531f2d9a82bf154d1a04a50218ee74cdd372f75a551a', decimals: 18, - tupoftup: cairo.tuple(cairo.tuple(34, '0x5e'), myFalseUint256), + tupoftup: tuple(tuple(34, '0x5e'), myFalseUint256), card: myOrder2bis, longText: 'Bug is back, for ever, here and everywhere', array1: [100, 101, 102], @@ -342,9 +361,9 @@ describeIfDevnet('Cairo 1 Devnet', () => { ], array3: [myOrder2bis, myOrder2bis], array4: [myFalseUint256, myFalseUint256], - tuple1: cairo.tuple(40000n, myOrder2bis, [54, 55n, '0xae'], 'texte'), + tuple1: tuple(40000n, myOrder2bis, [54, 55n, '0xae'], 'texte'), name: 'niceToken', - array5: [cairo.tuple(251, 40000n), cairo.tuple(252, 40001n)], + array5: [tuple(251, 40000n), tuple(252, 40001n)], }; const myRawArgsArray: RawArgsArray = [ 'niceToken', @@ -503,7 +522,7 @@ describeIfDevnet('Cairo 1 Devnet', () => { entrypoint: 'transfer', calldata: { recipient: toBeAccountAddress, - amount: cairo.uint256(1_000_000_000_000_000), + amount: uint256(1_000_000_000_000_000), }, }); await account.waitForTransaction(transaction_hash); @@ -575,7 +594,7 @@ describeIfSequencerTestnet2('Cairo1 Testnet2', () => { }); test('Cairo 1 - uint256 struct', async () => { - const myUint256 = cairo.uint256(2n ** 256n - 2n); + const myUint256 = uint256(2n ** 256n - 2n); const result = await cairo1Contract.test_u256(myUint256); expect(result).toBe(2n ** 256n - 1n); }); diff --git a/src/index.ts b/src/index.ts index c5713c280..b1ededfba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,9 +26,13 @@ export * as shortString from './utils/shortString'; export * as typedData from './utils/typedData'; export * as ec from './utils/ec'; export * as starknetId from './utils/starknetId'; +export * as provider from './utils/provider'; +export * as selector from './utils/selector'; export * from './utils/address'; export * from './utils/url'; export * from './utils/calldata'; +export * from './utils/contract'; +export * from './utils/events'; /** * Deprecated diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 1b9a9a614..1b8a7104b 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -337,14 +337,13 @@ export class RpcProvider implements ProviderInterface { details: InvocationsDetailsWithNonce ): Promise { if (!isSierra(contract)) { - const legacyContract = contract as LegacyContractClass; return this.fetchEndpoint('starknet_addDeclareTransaction', { declare_transaction: { type: RPC.TransactionType.DECLARE, contract_class: { - program: legacyContract.program, - entry_points_by_type: legacyContract.entry_points_by_type, - abi: legacyContract.abi, + program: contract.program, + entry_points_by_type: contract.entry_points_by_type, + abi: contract.abi, }, version: toHex(transactionVersion), max_fee: toHex(details.maxFee || 0), @@ -354,15 +353,14 @@ export class RpcProvider implements ProviderInterface { }, }); } - const sierraContract = contract as SierraContractClass; return this.fetchEndpoint('starknet_addDeclareTransaction', { declare_transaction: { type: RPC.TransactionType.DECLARE, contract_class: { - sierra_program: decompressProgram(sierraContract.sierra_program), - contract_class_version: sierraContract.contract_class_version, - entry_points_by_type: sierraContract.entry_points_by_type, - abi: sierraContract.abi, + sierra_program: decompressProgram(contract.sierra_program), + contract_class_version: contract.contract_class_version, + entry_points_by_type: contract.entry_points_by_type, + abi: contract.abi, }, compiled_class_hash: compiledClassHash || '', version: toHex(transactionVersion_2), diff --git a/src/types/lib/contract/index.ts b/src/types/lib/contract/index.ts index dc603e5c7..810d65505 100644 --- a/src/types/lib/contract/index.ts +++ b/src/types/lib/contract/index.ts @@ -7,10 +7,15 @@ import { CompiledSierra, SierraContractClass } from './sierra'; * CompressedCompiledContract */ export type ContractClass = LegacyContractClass | SierraContractClass; + /** * format produced after compile .cairo to .json */ export type CompiledContract = LegacyCompiledContract | CompiledSierra; + +/** + * Compressed or decompressed Cairo0 or Cairo1 Contract + */ export type CairoContract = ContractClass | CompiledContract; // Basic elements diff --git a/src/types/lib/contract/sierra.ts b/src/types/lib/contract/sierra.ts index f44dc3399..2973f9a83 100644 --- a/src/types/lib/contract/sierra.ts +++ b/src/types/lib/contract/sierra.ts @@ -18,7 +18,7 @@ export type CairoAssembly = { */ export type CompiledSierra = { sierra_program: ByteCode; - sierra_program_debug_info: SierraProgramDebugInfo; + sierra_program_debug_info?: SierraProgramDebugInfo; contract_class_version: string; entry_points_by_type: SierraEntryPointsByType; abi: Abi; diff --git a/src/types/provider/response.ts b/src/types/provider/response.ts index d9102d674..f41cb3009 100644 --- a/src/types/provider/response.ts +++ b/src/types/provider/response.ts @@ -6,13 +6,13 @@ import { RPC } from '../api/rpc'; import { Sequencer } from '../api/sequencer'; import { - Abi, AllowArray, ByteCode, Call, - ContractClass, + CompiledSierra, DeclareContractPayload, DeployAccountContractPayload, + LegacyContractClass, RawCalldata, Signature, Status, @@ -172,4 +172,12 @@ export interface StateUpdateResponse { }; } -export type ContractClassResponse = Omit & { abi?: Abi }; +/** + * Standardized type + * Cairo0 program compressed and Cairo1 sierra_program decompressed + * abi Abi + * CompiledSierra without '.sierra_program_debug_info' + */ +export type ContractClassResponse = + | LegacyContractClass + | Omit; diff --git a/src/utils/contract.ts b/src/utils/contract.ts index 21ef23917..cfb4fdb52 100644 --- a/src/utils/contract.ts +++ b/src/utils/contract.ts @@ -1,9 +1,19 @@ -import { CairoContract } from '../types/lib/contract/index'; +import { ContractClassResponse } from '../types'; +import { + CairoContract, + CompiledSierra, + LegacyCompiledContract, + LegacyContractClass, + SierraContractClass, +} from '../types/lib/contract/index'; import { CompleteDeclareContractPayload, DeclareContractPayload } from '../types/lib/index'; import { computeCompiledClassHash, computeContractClassHash } from './hash'; import { parse } from './json'; +import { decompressProgram } from './stark'; -export function isSierra(contract: CairoContract | string) { +export function isSierra( + contract: CairoContract | string +): contract is SierraContractClass | CompiledSierra { const compiledContract = typeof contract === 'string' ? parse(contract) : contract; return 'sierra_program' in compiledContract; } @@ -29,3 +39,16 @@ export function extractContractHashes( return response; } + +/** + * Helper to redeclare response Cairo0 contract + * @param ccr ContractClassResponse + * @returns LegacyCompiledContract + */ +export function contractClassResponseToLegacyCompiledContract(ccr: ContractClassResponse) { + if (isSierra(ccr)) { + throw Error('ContractClassResponse need to be LegacyContractClass (cairo0 response class)'); + } + const contract = ccr as LegacyContractClass; + return { ...contract, program: decompressProgram(contract.program) } as LegacyCompiledContract; +} diff --git a/src/utils/provider.ts b/src/utils/provider.ts index b1fc74749..2da5e3f3c 100644 --- a/src/utils/provider.ts +++ b/src/utils/provider.ts @@ -33,7 +33,6 @@ export function parseContract(contract: CompiledContract | string): ContractClas if (!isSierra(contract)) { return { ...parsedContract, - // TODO: Why do we gzip program object? ...('program' in parsedContract && { program: compressProgram(parsedContract.program) }), } as LegacyContractClass; }