diff --git a/yarn-project/accounts/src/ecdsa/ecdsa_k/account_contract.ts b/yarn-project/accounts/src/ecdsa/ecdsa_k/account_contract.ts index 1c5f7d6934fb..2375fed1ca23 100644 --- a/yarn-project/accounts/src/ecdsa/ecdsa_k/account_contract.ts +++ b/yarn-project/accounts/src/ecdsa/ecdsa_k/account_contract.ts @@ -21,7 +21,7 @@ export abstract class EcdsaKBaseAccountContract extends DefaultAccountContract { const signingPublicKey = await new Ecdsa().computePublicKey(this.signingPrivateKey); return { constructorName: 'constructor', - constructorArgs: [signingPublicKey.subarray(0, 32), signingPublicKey.subarray(32, 64)], + constructorArgs: [[...signingPublicKey.subarray(0, 32)], [...signingPublicKey.subarray(32, 64)]], }; } diff --git a/yarn-project/accounts/src/ecdsa/ecdsa_r/account_contract.ts b/yarn-project/accounts/src/ecdsa/ecdsa_r/account_contract.ts index 4d8d92b37332..f467f6e48fad 100644 --- a/yarn-project/accounts/src/ecdsa/ecdsa_r/account_contract.ts +++ b/yarn-project/accounts/src/ecdsa/ecdsa_r/account_contract.ts @@ -21,7 +21,7 @@ export abstract class EcdsaRBaseAccountContract extends DefaultAccountContract { const signingPublicKey = await new Ecdsa('secp256r1').computePublicKey(this.signingPrivateKey); return { constructorName: 'constructor', - constructorArgs: [signingPublicKey.subarray(0, 32), signingPublicKey.subarray(32, 64)], + constructorArgs: [[...signingPublicKey.subarray(0, 32)], [...signingPublicKey.subarray(32, 64)]], }; } diff --git a/yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/account_contract.ts b/yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/account_contract.ts index 916382cbc8be..64c048b2139b 100644 --- a/yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/account_contract.ts +++ b/yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/account_contract.ts @@ -25,7 +25,7 @@ export abstract class EcdsaRSSHBaseAccountContract extends DefaultAccountContrac getInitializationFunctionAndArgs() { return Promise.resolve({ constructorName: 'constructor', - constructorArgs: [this.signingPublicKey.subarray(0, 32), this.signingPublicKey.subarray(32, 64)], + constructorArgs: [[...this.signingPublicKey.subarray(0, 32)], [...this.signingPublicKey.subarray(32, 64)]], }); } diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_proven_gadgets.test.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_proven_gadgets.test.ts index 4041a3971cd1..2cfb0ed61061 100644 --- a/yarn-project/bb-prover/src/avm_proving_tests/avm_proven_gadgets.test.ts +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_proven_gadgets.test.ts @@ -63,7 +63,7 @@ describe.skip('AVM proven gadgets test', () => { { address: avmGadgetsTestContract.address, fnName: 'keccak_hash_1400', - args: [/*input=*/ Array.from({ length: 2400 }, () => randomInt(2 ** 8))], + args: [/*input=*/ Array.from({ length: 1400 }, () => randomInt(2 ** 8))], }, ], ); diff --git a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts index b781544eafee..d3e55a0a597b 100644 --- a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts +++ b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts @@ -277,10 +277,8 @@ describe('HA Full Setup', () => { // Deploy a contract to trigger block building const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const sender = ownerAddress; - - logger.info(`Deploying contract from ${sender}`); - const { receipt } = await deployer.deploy(ownerAddress, sender, 1).send({ + logger.info(`Deploying contract from ${ownerAddress}`); + const { receipt } = await deployer.deploy(ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), wait: { returnReceipt: true }, @@ -399,7 +397,7 @@ describe('HA Full Setup', () => { // Send a transaction to trigger block building which will also trigger voting logger.info('Sending transaction to trigger block building...'); const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const { receipt } = await deployer.deploy(ownerAddress, ownerAddress, 42).send({ + const { receipt } = await deployer.deploy(ownerAddress, 42).send({ from: ownerAddress, contractAddressSalt: Fr.random(), wait: { returnReceipt: true }, @@ -515,7 +513,7 @@ describe('HA Full Setup', () => { logger.info(`Active nodes: ${haNodeServices.length - killedNodes.length}/${NODE_COUNT}`); const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const { receipt } = await deployer.deploy(ownerAddress, ownerAddress, i + 100).send({ + const { receipt } = await deployer.deploy(ownerAddress, i + 100).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(i + 100)), wait: { returnReceipt: true }, diff --git a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts index c7b9a3be05c4..1d2b1fceebc6 100644 --- a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts +++ b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts @@ -324,9 +324,9 @@ describe('e2e_multi_validator_node', () => { await rmdir(keyStoreDirectory, { recursive: true }); }); - const sendTx = (sender: AztecAddress, contractAddressSalt: Fr) => { + const sendTx = (contractAddressSalt: Fr) => { const deployer = new ContractDeployer(artifact, wallet); - return deployer.deploy(ownerAddress, sender, 1).send({ + return deployer.deploy(ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt, skipClassPublication: true, @@ -398,7 +398,7 @@ describe('e2e_multi_validator_node', () => { // Then we check the results captured above const sentTransactionPromises = Array.from({ length: BLOCK_COUNT }, (_, i) => { const contractAddressSalt = new Fr(i + 1); - return sendTx(ownerAddress, contractAddressSalt); + return sendTx(contractAddressSalt); }); const settledTransactions = await Promise.all( diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts index 3e704d342355..579a1cc42132 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts @@ -2,7 +2,7 @@ import { computeSecretHash } from '@aztec/aztec.js/crypto'; import { Fr } from '@aztec/aztec.js/fields'; import type { TxHash } from '@aztec/aztec.js/tx'; -import { BITSIZE_TOO_BIG_ERROR, U128_OVERFLOW_ERROR } from '../fixtures/index.js'; +import { U128_OVERFLOW_ERROR } from '../fixtures/index.js'; import { BlacklistTokenContractTest } from './blacklist_token_contract_test.js'; describe('e2e_blacklist_token_contract mint', () => { @@ -44,14 +44,6 @@ describe('e2e_blacklist_token_contract mint', () => { ); }); - // TODO(#12221): re-enable this test once we have proper unsigned integer overflow checks - it.skip('mint >u128 tokens to overflow', async () => { - const amount = 2n ** 128n; // u128::max() + 1; - await expect(asset.methods.mint_public(adminAddress, amount).simulate({ from: adminAddress })).rejects.toThrow( - BITSIZE_TOO_BIG_ERROR, - ); - }); - it('mint u128', async () => { const amount = 2n ** 128n - tokenSim.balanceOfPublic(adminAddress); await expect(asset.methods.mint_public(adminAddress, amount).simulate({ from: adminAddress })).rejects.toThrow( @@ -124,14 +116,6 @@ describe('e2e_blacklist_token_contract mint', () => { ); }); - // TODO(#12221): re-enable this test once we have proper unsigned integer overflow checks - it.skip('mint >u128 tokens to overflow', async () => { - const amount = 2n ** 128n; // u128::max() + 1; - await expect(asset.methods.mint_private(amount, secretHash).simulate({ from: adminAddress })).rejects.toThrow( - BITSIZE_TOO_BIG_ERROR, - ); - }); - it('mint u128', async () => { const amount = 2n ** 128n - tokenSim.balanceOfPrivate(adminAddress); expect(amount).toBeLessThan(2n ** 128n); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index 37da34dd7f3c..8eec708e9e32 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -245,8 +245,7 @@ describe('e2e_deploy_contract contract class registration', () => { describe('error scenarios in deployment', () => { it('app logic call to an undeployed contract reverts, but can be included', async () => { const whom = defaultAccountAddress; - const sender = whom; - const instance = await t.registerContract(wallet, StatefulTestContract, { initArgs: [whom, sender, 42] }); + const instance = await t.registerContract(wallet, StatefulTestContract, { initArgs: [whom, 42] }); // Confirm that the tx reverts with the expected message await expect( instance.methods.increment_public_value_no_init_check(whom, 10).simulate({ from: defaultAccountAddress }), diff --git a/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts b/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts index 53060168826d..8e04f856b206 100644 --- a/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts @@ -112,9 +112,8 @@ describe('e2e_multi_validator_node', () => { it('should build blocks & attest with multiple validator keys', async () => { const deployer = new ContractDeployer(artifact, wallet); - const sender = ownerAddress; - logger.info(`Deploying contract from ${sender}`); - const { receipt: tx } = await deployer.deploy(ownerAddress, sender, 1).send({ + logger.info(`Deploying contract from ${ownerAddress}`); + const { receipt: tx } = await deployer.deploy(ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), wait: { returnReceipt: true }, @@ -171,11 +170,9 @@ describe('e2e_multi_validator_node', () => { expect(committee?.length).toBe(COMMITTEE_SIZE); // new aztec transaction - const sender = ownerAddress; - - logger.info(`Deploying contract from ${sender}`); + logger.info(`Deploying contract from ${ownerAddress}`); const deployer = new ContractDeployer(artifact, wallet); - const { receipt: tx } = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: tx } = await deployer.deploy(ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), wait: { returnReceipt: true }, diff --git a/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts index 221c3086d924..4702f3077144 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts @@ -130,7 +130,7 @@ describe('e2e_reload_keystore', () => { // Send a tx and verify the block uses the initial coinbase const deployer = new ContractDeployer(artifact, wallet); - const { txHash: sentTx1 } = await deployer.deploy(ownerAddress, ownerAddress, 1).send({ + const { txHash: sentTx1 } = await deployer.deploy(ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(1), wait: NO_WAIT, @@ -195,7 +195,7 @@ describe('e2e_reload_keystore', () => { // Whichever validator is the proposer, its coinbase must be from the reloaded keystore. const allNewCoinbasesLower = newCoinbases.map(c => c.toString().toLowerCase()); - const { txHash: sentTx2 } = await deployer.deploy(ownerAddress, ownerAddress, 2).send({ + const { txHash: sentTx2 } = await deployer.deploy(ownerAddress, 2).send({ from: ownerAddress, contractAddressSalt: new Fr(2), wait: NO_WAIT, diff --git a/yarn-project/end-to-end/src/e2e_simple.test.ts b/yarn-project/end-to-end/src/e2e_simple.test.ts index e38df666d579..df4ad6be3a9c 100644 --- a/yarn-project/end-to-end/src/e2e_simple.test.ts +++ b/yarn-project/end-to-end/src/e2e_simple.test.ts @@ -71,8 +71,7 @@ describe('e2e_simple', () => { it('deploys a contract', async () => { const deployer = new ContractDeployer(artifact, wallet); - const sender = ownerAddress; - const { receipt: txReceipt } = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: txReceipt } = await deployer.deploy(ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), wait: { returnReceipt: true }, diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index 1e2cc3117f1b..6bab6c4cbdf0 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -195,10 +195,7 @@ describe('e2e_static_calls', () => { it('fails when performing non-static enqueue calls to poorly written public static functions', async () => { await expect( parentContract.methods - .enqueue_call(childContract.address, await childContract.methods.pub_illegal_inc_value.selector(), [ - 42n, - owner, - ]) + .enqueue_call(childContract.address, await childContract.methods.pub_illegal_inc_value.selector(), [42n]) .simulate({ from: owner }), ).rejects.toThrow(STATIC_CONTEXT_ASSERTION_ERROR); }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts index e7479801a3a6..46908a688eab 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts @@ -79,12 +79,13 @@ describe('e2e_token_contract minting', () => { ).rejects.toThrow('Assertion failed: caller is not minter'); }); + // This test is expected to fail at the ABI encoder rather than during contract logic. + // We keep the test to be defensive as it is the only e2e test with overflowed inputs. it('mint >u128 tokens to overflow', async () => { const overflowAmount = 2n ** 128n; - await expect( asset.methods.mint_to_private(adminAddress, overflowAmount).simulate({ from: adminAddress }), - ).rejects.toThrow('Cannot satisfy constraint'); + ).rejects.toThrow('does not fit in u128'); }); it('mint u128', async () => { diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts index 95da879d711c..769ee795f601 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts @@ -562,7 +562,7 @@ describe('Private Execution test suite', () => { }); it('should have a constructor with arguments that inserts notes', async () => { - const initArgs = [owner, owner, 140]; + const initArgs = [owner, 140]; const instance = await getContractInstanceFromInstantiationParams(StatefulTestContractArtifact, { constructorArgs: initArgs, salt: Fr.random(), @@ -594,7 +594,7 @@ describe('Private Execution test suite', () => { it('should run the create_note function', async () => { const { entrypoint: result } = await runSimulator({ - args: [owner, owner, 140], + args: [owner, 140], artifact: StatefulTestContractArtifact, anchorBlockHeader, functionName: 'create_note_no_init_check', diff --git a/yarn-project/simulator/src/public/fixtures/bulk_test.ts b/yarn-project/simulator/src/public/fixtures/bulk_test.ts index 8dc87fa59d3a..0e600ad8be42 100644 --- a/yarn-project/simulator/src/public/fixtures/bulk_test.ts +++ b/yarn-project/simulator/src/public/fixtures/bulk_test.ts @@ -30,9 +30,7 @@ export async function bulkTest( // for it to use as "expected" values when testing contract instance retrieval. const expectContractInstance = avmTestContract; const argsField = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); - const argsU8 = [1, 2, 3, 4, 5, 6, 7, 8].map(x => new Fr(x)); - argsU8.push(new Fr(2n ** 128n + 9n)); // Trigger truncation from large (> 128 bits) value (canonical decomposition event) - argsU8.push(new Fr(2n ** 125n + 10n)); // Trigger truncation from small (< 128 bits) value (no canonical decomposition event) + const argsU8 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => new Fr(x)); const args = [ argsField, argsU8, @@ -62,7 +60,7 @@ export async function bulkTest( { address: avmTestContract.address, fnName: 'assert_calldata_copy', - args: [argsField.slice(3), /* with_selector: */ true], + args: [argsField.slice(0, 3), /* with_selector: */ true], }, { address: avmTestContract.address, diff --git a/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_gadgets.test.ts b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_gadgets.test.ts index bb24b2e559cd..f1700ecd60c8 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_gadgets.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_gadgets.test.ts @@ -83,7 +83,7 @@ describe('Public TX simulator apps tests: gadgets', () => { { address: avmGadgetsTestContract.address, fnName: 'keccak_hash_1400', - args: [/*input=*/ Array.from({ length: 2400 }, () => randomInt(2 ** 8))], + args: [/*input=*/ Array.from({ length: 1400 }, () => randomInt(2 ** 8))], }, ], ); diff --git a/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/bench.test.ts b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/bench.test.ts index 6ad2712f8f03..50ffe0f8cebe 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/bench.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/bench.test.ts @@ -252,7 +252,7 @@ describe('Public TX simulator apps tests: benchmarks', () => { { address: avmGadgetsTestContract.address, fnName: 'keccak_hash_1400', - args: [/*input=*/ Array.from({ length: 2400 }, () => randomInt(2 ** 8))], + args: [/*input=*/ Array.from({ length: 1400 }, () => randomInt(2 ** 8))], }, ], ); diff --git a/yarn-project/stdlib/src/abi/encoder.test.ts b/yarn-project/stdlib/src/abi/encoder.test.ts index 0bf1c64ab57e..ec8d15075ff4 100644 --- a/yarn-project/stdlib/src/abi/encoder.test.ts +++ b/yarn-project/stdlib/src/abi/encoder.test.ts @@ -236,6 +236,170 @@ describe('abi/encoder', () => { }); }); + it('throws when array is larger than declared fixed size', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { + name: 'values', + type: { kind: 'array', length: 2, type: { kind: 'field' } }, + visibility: 'private', + }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, [[Fr.random(), Fr.random(), Fr.random()]])).toThrow( + "Expected array of length 2 for 'values' but received length 3", + ); + }); + + it('throws when array is smaller than declared fixed size', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { + name: 'values', + type: { kind: 'array', length: 3, type: { kind: 'field' } }, + visibility: 'private', + }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, [[Fr.random()]])).toThrow( + "Expected array of length 3 for 'values' but received length 1", + ); + }); + + it('throws when too many arguments are provided', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { + name: 'value', + type: { kind: 'field' }, + visibility: 'private', + }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, [Fr.random(), Fr.random()])).toThrow( + "Function 'test' expects 1 argument(s) but received 2", + ); + }); + + it('throws when too few arguments are provided', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { name: 'a', type: { kind: 'field' }, visibility: 'private' }, + { name: 'b', type: { kind: 'field' }, visibility: 'private' }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, [Fr.random()])).toThrow("Function 'test' expects 2 argument(s) but received 1"); + }); + + it('throws when string is longer than declared length', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { + name: 'label', + type: { kind: 'string', length: 3 }, + visibility: 'private', + }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, ['abcdef'])).toThrow( + "Expected string of max length 3 for 'label' but received length 6", + ); + }); + + it('throws when unsigned integer overflows declared width', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { + name: 'count', + type: { kind: 'integer', sign: 'unsigned', width: 8 }, + visibility: 'private', + }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, [256])).toThrow( + "Value 256 does not fit in u8 for 'count' (valid range: 0 to 255)", + ); + expect(() => encodeArguments(abi, [-1])).toThrow("Value -1 does not fit in u8 for 'count' (valid range: 0 to 255)"); + expect(encodeArguments(abi, [255])).toEqual([new Fr(255n)]); + expect(encodeArguments(abi, [0])).toEqual([new Fr(0n)]); + }); + + it('throws when signed integer overflows declared width', () => { + const abi: FunctionAbi = { + name: 'test', + isInitializer: false, + functionType: FunctionType.PRIVATE, + isOnlySelf: false, + isStatic: false, + parameters: [ + { + name: 'value', + type: { kind: 'integer', sign: 'signed', width: 8 }, + visibility: 'private', + }, + ], + returnTypes: [], + errorTypes: {}, + }; + + expect(() => encodeArguments(abi, [128])).toThrow( + "Value 128 does not fit in i8 for 'value' (valid range: -128 to 127)", + ); + expect(() => encodeArguments(abi, [-129])).toThrow( + "Value -129 does not fit in i8 for 'value' (valid range: -128 to 127)", + ); + expect(encodeArguments(abi, [127])).toEqual([new Fr(127n)]); + expect(encodeArguments(abi, [-128])).toEqual([new Fr(128n)]); + }); + it('throws when passing string argument as field', () => { const testFunctionAbi: FunctionAbi = { name: 'constructor', diff --git a/yarn-project/stdlib/src/abi/encoder.ts b/yarn-project/stdlib/src/abi/encoder.ts index ecd0c127819b..184a89f2e0a5 100644 --- a/yarn-project/stdlib/src/abi/encoder.ts +++ b/yarn-project/stdlib/src/abi/encoder.ts @@ -106,13 +106,28 @@ class ArgumentEncoder { this.flattened.push(new Fr(arg ? 1n : 0n)); break; case 'array': + if (!Array.isArray(arg)) { + throw new Error(`Expected array for '${name ?? 'unnamed'}' but received ${typeof arg}`); + } + if (arg.length !== abiType.length) { + throw new Error( + `Expected array of length ${abiType.length} for '${name ?? 'unnamed'}' but received length ${arg.length}`, + ); + } for (let i = 0; i < abiType.length; i += 1) { this.encodeArgument(abiType.type, arg[i], `${name}[${i}]`); } break; case 'string': + if (typeof arg !== 'string') { + throw new Error(`Expected string for '${name ?? 'unnamed'}' but received ${typeof arg}`); + } + if (arg.length > abiType.length) { + throw new Error( + `Expected string of max length ${abiType.length} for '${name ?? 'unnamed'}' but received length ${arg.length}`, + ); + } for (let i = 0; i < abiType.length; i += 1) { - // If the string is shorter than the defined length, pad it with 0s. const toInsert = i < arg.length ? BigInt((arg as string).charCodeAt(i)) : 0n; this.flattened.push(new Fr(toInsert)); } @@ -157,12 +172,28 @@ class ArgumentEncoder { } case 'integer': { const value = BigInt(arg); - if (abiType.sign === 'signed' && value < 0n) { - // Convert negative values to two's complement representation - const twosComplement = value + (1n << BigInt(abiType.width)); - this.flattened.push(new Fr(twosComplement)); - } else { + if (abiType.sign === 'unsigned') { + const maxValue = (1n << BigInt(abiType.width)) - 1n; + if (value < 0n || value > maxValue) { + throw new Error( + `Value ${value} does not fit in u${abiType.width} for '${name ?? 'unnamed'}' (valid range: 0 to ${maxValue})`, + ); + } this.flattened.push(new Fr(value)); + } else { + const minValue = -(1n << BigInt(abiType.width - 1)); + const maxValue = (1n << BigInt(abiType.width - 1)) - 1n; + if (value < minValue || value > maxValue) { + throw new Error( + `Value ${value} does not fit in i${abiType.width} for '${name ?? 'unnamed'}' (valid range: ${minValue} to ${maxValue})`, + ); + } + if (value < 0n) { + const twosComplement = value + (1n << BigInt(abiType.width)); + this.flattened.push(new Fr(twosComplement)); + } else { + this.flattened.push(new Fr(value)); + } } break; } @@ -176,6 +207,11 @@ class ArgumentEncoder { * @returns The encoded arguments. */ public encode() { + if (this.args.length !== this.abi.parameters.length) { + throw new Error( + `Function '${this.abi.name}' expects ${this.abi.parameters.length} argument(s) but received ${this.args.length}`, + ); + } for (let i = 0; i < this.abi.parameters.length; i += 1) { const parameterAbi = this.abi.parameters[i]; this.encodeArgument(parameterAbi.type, this.args[i], parameterAbi.name);