diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs index e5c01bf3c49..2051b52c065 100644 --- a/avm-transpiler/src/opcodes.rs +++ b/avm-transpiler/src/opcodes.rs @@ -61,6 +61,7 @@ pub enum AvmOpcode { HEADERMEMBER, EMITUNENCRYPTEDLOG, SENDL2TOL1MSG, + CONTRACTINSTANCECOPY, // External calls CALL, STATICCALL, @@ -148,6 +149,7 @@ impl AvmOpcode { // Accrued Substate AvmOpcode::EMITUNENCRYPTEDLOG => "EMITUNENCRYPTEDLOG", AvmOpcode::SENDL2TOL1MSG => "SENDL2TOL1MSG", + AvmOpcode::CONTRACTINSTANCECOPY => "CONTRACTINSTANCECOPY", // Control Flow - Contract Calls AvmOpcode::CALL => "CALL", diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 6883265c401..fedc2afe880 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -287,6 +287,7 @@ fn handle_foreign_call( "avmOpcodePoseidon" => { handle_single_field_hash_instruction(avm_instrs, function, destinations, inputs) } + "getContractInstance" => handle_get_contract_instance(avm_instrs, destinations, inputs), "storageRead" => handle_storage_read(avm_instrs, destinations, inputs), "storageWrite" => handle_storage_write(avm_instrs, destinations, inputs), // Getters. @@ -955,6 +956,42 @@ fn handle_storage_write( }) } +/// Emit a CONTRACTINSTANCECOPY opcode +fn handle_get_contract_instance( + avm_instrs: &mut Vec, + destinations: &Vec, + inputs: &Vec, +) { + assert!(inputs.len() == 1); + assert!(destinations.len() == 1); + + let address_offset_maybe = inputs[0]; + let address_offset = match address_offset_maybe { + ValueOrArray::MemoryAddress(slot_offset) => slot_offset.0, + _ => panic!("CONTRACTINSTANCECOPY address should be a single value"), + }; + + let dest_offset_maybe = destinations[0]; + let dest_offset = match dest_offset_maybe { + ValueOrArray::HeapArray(HeapArray { pointer, .. }) => pointer.0, + _ => panic!("CONTRACTINSTANCECOPY destination should be an array"), + }; + + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::CONTRACTINSTANCECOPY, + indirect: Some(FIRST_OPERAND_INDIRECT), + operands: vec![ + AvmOperand::U32 { + value: address_offset as u32, + }, + AvmOperand::U32 { + value: dest_offset as u32, + }, + ], + ..Default::default() + }) +} + /// Emit a storage read opcode /// The current implementation reads an array of values from storage ( contiguous slots in memory ) fn handle_storage_read( diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp index 02296aa73e9..a7565ecd76f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.hpp @@ -87,6 +87,7 @@ enum class OpCode : uint8_t { // Accrued Substate EMITUNENCRYPTEDLOG, SENDL2TOL1MSG, // Messages + CONTRACTINSTANCECOPY, // Control Flow - Contract Calls CALL, diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index f09e441aa99..8c398a6302e 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -26,7 +26,11 @@ contract AvmTest { // Libs use dep::aztec::prelude::Map; use dep::aztec::state_vars::{PublicImmutable, PublicMutable}; - use dep::aztec::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH}; + use dep::aztec::protocol_types::{ + address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH, + contract_instance::ContractInstance + }; + use dep::aztec::oracle::get_contract_instance::{get_contract_instance, get_contract_instance_internal}; use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; use dep::aztec::protocol_types::traits::ToField; use dep::aztec::protocol_types::constants::RETURN_VALUES_LENGTH; @@ -181,6 +185,26 @@ contract AvmTest { dep::std::hash::pedersen_hash_with_separator(data, 20) } + /************************************************************************ + * Contract instance + ************************************************************************/ + #[aztec(public-vm)] + fn test_get_contract_instance_raw() { + let fields = get_contract_instance_internal(context.this_address()); + assert(fields.len() == 6); + assert(fields[0] == 0x123); + assert(fields[1] == 0x456); + assert(fields[2] == 0x789); + assert(fields[3] == 0x101112); + assert(fields[4] == 0x131415); + assert(fields[5] == 0x161718); + } + + #[aztec(public-vm)] + fn test_get_contract_instance() { + get_contract_instance(context.this_address()); + } + /************************************************************************ * AvmContext functions ************************************************************************/ diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 99032a48228..77fe6cf806f 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -1,4 +1,4 @@ -import { AztecAddress, Wallet } from '@aztec/aztec.js'; +import { AztecAddress, TxStatus, Wallet } from '@aztec/aztec.js'; import { AvmTestContract } from '@aztec/noir-contracts.js'; import { jest } from '@jest/globals'; @@ -43,10 +43,17 @@ describe('e2e_avm_simulator', () => { }); }); + describe('Contract instance', () => { + it('Works', async () => { + const tx = await avmContact.methods.test_get_contract_instance().send().wait(); + expect(tx.status).toEqual(TxStatus.MINED); + }); + }); + describe('Nullifiers', () => { it('Emit and check', async () => { - await avmContact.methods.emit_nullifier_and_check(123456).send().wait(); - // TODO: check NOT reverted + const tx = await avmContact.methods.emit_nullifier_and_check(123456).send().wait(); + expect(tx.status).toEqual(TxStatus.MINED); }); }); }); diff --git a/yarn-project/simulator/src/avm/avm_gas_cost.ts b/yarn-project/simulator/src/avm/avm_gas_cost.ts index 69f80e2dfe7..63cb9a0c31f 100644 --- a/yarn-project/simulator/src/avm/avm_gas_cost.ts +++ b/yarn-project/simulator/src/avm/avm_gas_cost.ts @@ -89,6 +89,7 @@ export const GasCosts = { [Opcode.HEADERMEMBER]: TemporaryDefaultGasCost, [Opcode.EMITUNENCRYPTEDLOG]: TemporaryDefaultGasCost, [Opcode.SENDL2TOL1MSG]: TemporaryDefaultGasCost, + [Opcode.CONTRACTINSTANCECOPY]: TemporaryDefaultGasCost, // External calls [Opcode.CALL]: TemporaryDefaultGasCost, [Opcode.STATICCALL]: TemporaryDefaultGasCost, diff --git a/yarn-project/simulator/src/avm/avm_memory_types.ts b/yarn-project/simulator/src/avm/avm_memory_types.ts index 7f4b07462a0..82b9002f61e 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -221,6 +221,9 @@ export class TaggedMemory { assert(offset < TaggedMemory.MAX_MEMORY_SIZE); const word = this._mem[offset]; TaggedMemory.log(`get(${offset}) = ${word}`); + if (word === undefined) { + TaggedMemory.log.warn(`Memory at offset ${offset} is undefined! This might be OK if it's stack dumping.`); + } return word as T; } @@ -229,6 +232,7 @@ export class TaggedMemory { assert(offset + size < TaggedMemory.MAX_MEMORY_SIZE); const value = this._mem.slice(offset, offset + size); TaggedMemory.log(`getSlice(${offset}, ${size}) = ${value}`); + assert(!value.some(e => e === undefined), 'Memory slice contains undefined values.'); assert(value.length === size, `Expected slice of size ${size}, got ${value.length}.`); return value; } diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 21402c71d4f..bf66b78d9ba 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -747,6 +747,30 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect([...storageTrace.values()]).toEqual([[value]]); }); }); + + describe('Contract', () => { + it(`CONTRACTINSTANCECOPY deserializes correctly`, async () => { + const context = initContext(); + const contractInstance = { + address: AztecAddress.random(), + version: 1 as const, + salt: new Fr(0x123), + deployer: AztecAddress.fromBigInt(0x456n), + contractClassId: new Fr(0x789), + initializationHash: new Fr(0x101112), + portalContractAddress: EthAddress.fromField(new Fr(0x131415)), + publicKeysHash: new Fr(0x161718), + }; + + jest + .spyOn(context.persistableState.hostStorage.contractsDb, 'getContractInstance') + .mockReturnValue(Promise.resolve(contractInstance)); + const bytecode = getAvmTestContractBytecode('test_get_contract_instance_raw'); + const results = await new AvmSimulator(context).executeBytecode(bytecode); + + expect(results.reverted).toBe(false); + }); + }); }); function getAvmTestContractBytecode(functionName: string): Buffer { diff --git a/yarn-project/simulator/src/avm/opcodes/contract.test.ts b/yarn-project/simulator/src/avm/opcodes/contract.test.ts new file mode 100644 index 00000000000..06735836141 --- /dev/null +++ b/yarn-project/simulator/src/avm/opcodes/contract.test.ts @@ -0,0 +1,70 @@ +import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; + +import { DeepMockProxy, mockDeep } from 'jest-mock-extended'; + +import { AvmContext } from '../avm_context.js'; +import { Field } from '../avm_memory_types.js'; +import { initContext } from '../fixtures/index.js'; +import { AvmPersistableStateManager } from '../journal/journal.js'; +import { ContractInstanceCopy } from './contract.js'; + +describe('Contract opcodes', () => { + let context: AvmContext; + let journal: DeepMockProxy; + const address = AztecAddress.random(); + + beforeEach(async () => { + journal = mockDeep(); + context = initContext({ + persistableState: journal, + }); + }); + + describe('CONTRACTINSTANCECOPY', () => { + it('Should (de)serialize correctly', () => { + const buf = Buffer.from([ + ContractInstanceCopy.opcode, // opcode + 0x01, // indirect + ...Buffer.from('12345678', 'hex'), // addressOffset + ...Buffer.from('a2345678', 'hex'), // dstOffset + ]); + const inst = new ContractInstanceCopy( + /*indirect=*/ 0x01, + /*addressOffset=*/ 0x12345678, + /*dstOffset=*/ 0xa2345678, + ); + + expect(ContractInstanceCopy.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); + }); + + it('should copy contract instance to memory', async () => { + context.machineState.memory.set(0, new Field(address.toField())); + + const contractInstance = { + address: address, + version: 1 as const, + salt: new Fr(20), + contractClassId: new Fr(30), + initializationHash: new Fr(40), + portalContractAddress: EthAddress.random(), + publicKeysHash: new Fr(50), + deployer: AztecAddress.random(), + }; + + journal.hostStorage.contractsDb.getContractInstance.mockReturnValue(Promise.resolve(contractInstance)); + + await new ContractInstanceCopy(/*indirect=*/ 0, /*addressOffset=*/ 0, /*dstOffset=*/ 1).execute(context); + + const actual = context.machineState.memory.getSlice(1, 6); + expect(actual).toEqual([ + new Field(contractInstance.salt), + new Field(contractInstance.deployer), + new Field(contractInstance.contractClassId), + new Field(contractInstance.initializationHash), + new Field(contractInstance.portalContractAddress.toField()), + new Field(contractInstance.publicKeysHash), + ]); + }); + }); +}); diff --git a/yarn-project/simulator/src/avm/opcodes/contract.ts b/yarn-project/simulator/src/avm/opcodes/contract.ts new file mode 100644 index 00000000000..717e6ae32c3 --- /dev/null +++ b/yarn-project/simulator/src/avm/opcodes/contract.ts @@ -0,0 +1,50 @@ +import { AztecAddress } from '@aztec/circuits.js'; + +import type { AvmContext } from '../avm_context.js'; +import { Field } from '../avm_memory_types.js'; +import { InstructionExecutionError } from '../errors.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; +import { Addressing } from './addressing_mode.js'; +import { Instruction } from './instruction.js'; + +export class ContractInstanceCopy extends Instruction { + static readonly type: string = 'CONTRACTINSTANCECOPY'; + static readonly opcode: Opcode = Opcode.CONTRACTINSTANCECOPY; + // Informs (de)serialization. See Instruction.deserialize. + static readonly wireFormat: OperandType[] = [ + OperandType.UINT8, + OperandType.UINT8, + OperandType.UINT32, + OperandType.UINT32, + ]; + + constructor(private indirect: number, private addressOffset: number, private dstOffset: number) { + super(); + } + + async execute(context: AvmContext): Promise { + const [addressOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve( + [this.addressOffset, this.dstOffset], + context.machineState.memory, + ); + + const address = AztecAddress.fromField(context.machineState.memory.get(addressOffset).toFr()); + const instance = await context.persistableState.hostStorage.contractsDb.getContractInstance(address); + + if (!instance) { + throw new InstructionExecutionError(`Contract instance not found: ${address}`); + } + const data = [ + instance.salt, + instance.deployer.toField(), + instance.contractClassId, + instance.initializationHash, + instance.portalContractAddress.toField(), + instance.publicKeysHash, + ].map(f => new Field(f)); + + context.machineState.memory.setSlice(dstOffset, data); + + context.machineState.incrementPc(); + } +} diff --git a/yarn-project/simulator/src/avm/opcodes/index.ts b/yarn-project/simulator/src/avm/opcodes/index.ts index d8dc52a8cf5..e3263fdfd1c 100644 --- a/yarn-project/simulator/src/avm/opcodes/index.ts +++ b/yarn-project/simulator/src/avm/opcodes/index.ts @@ -1,6 +1,7 @@ export * from './arithmetic.js'; export * from './bitwise.js'; export * from './control_flow.js'; +export * from './contract.js'; export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; diff --git a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts index fe26496ea28..1d25c62d82d 100644 --- a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts @@ -9,6 +9,7 @@ import { CalldataCopy, Cast, ChainId, + ContractInstanceCopy, Div, EmitNoteHash, EmitNullifier, @@ -126,6 +127,7 @@ const INSTRUCTION_SET = () => // Accrued Substate [EmitUnencryptedLog.opcode, EmitUnencryptedLog], [SendL2ToL1Message.opcode, SendL2ToL1Message], + [ContractInstanceCopy.opcode, ContractInstanceCopy], // Control Flow - Contract Calls [Call.opcode, Call], diff --git a/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts index 1a6ae62c82f..84d137ba7c7 100644 --- a/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts @@ -66,6 +66,7 @@ export enum Opcode { HEADERMEMBER, EMITUNENCRYPTEDLOG, SENDL2TOL1MSG, + CONTRACTINSTANCECOPY, // External calls CALL, STATICCALL, diff --git a/yellow-paper/docs/public-vm/gen/_instruction-set.mdx b/yellow-paper/docs/public-vm/gen/_instruction-set.mdx index 73902c179ef..11394c26a2f 100644 --- a/yellow-paper/docs/public-vm/gen/_instruction-set.mdx +++ b/yellow-paper/docs/public-vm/gen/_instruction-set.mdx @@ -418,7 +418,21 @@ if exists: - 0x35 [`CALL`](#isa-section-call) + 0x35 [`CONTRACTINSTANCECOPY`](#isa-section-contractInstanceCopy) + Copies contract instance data to memory + +{`M[dstOffset:dstOffset+CONTRACT_INSTANCE_SIZE] = [ + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.portalContractAddress.toField(), + instance.publicKeysHash, +]`} + + + + 0x36 [`CALL`](#isa-section-call) Call into another contract {`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } @@ -433,7 +447,7 @@ updateContextAfterNestedCall(context, instr.args, nestedContext)`} - 0x36 [`STATICCALL`](#isa-section-staticcall) + 0x37 [`STATICCALL`](#isa-section-staticcall) Call into another contract, disallowing World State and Accrued Substate modifications {`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } @@ -448,7 +462,7 @@ updateContextAfterNestedCall(context, instr.args, nestedContext)`} - 0x37 [`DELEGATECALL`](#isa-section-delegatecall) + 0x38 [`DELEGATECALL`](#isa-section-delegatecall) Call into another contract, but keep the caller's `sender` and `storageAddress` {`// instr.args are { gasOffset, addrOffset, argsOffset, retOffset, retSize } @@ -463,7 +477,7 @@ updateContextAfterNestedCall(context, instr.args, nestedContext)`} - 0x38 [`RETURN`](#isa-section-return) + 0x39 [`RETURN`](#isa-section-return) Halt execution within this context (without revert), optionally returning some data {`context.contractCallResults.output = M[retOffset:retOffset+retSize] @@ -471,7 +485,7 @@ halt`} - 0x39 [`REVERT`](#isa-section-revert) + 0x3a [`REVERT`](#isa-section-revert) Halt execution within this context as `reverted`, optionally returning some data {`context.contractCallResults.output = M[retOffset:retOffset+retSize] @@ -499,6 +513,7 @@ Addition (a + b) - **bOffset**: memory offset of the operation's right input - **dstOffset**: memory offset specifying where to store operation's result - **Expression**: `M[dstOffset] = M[aOffset] + M[bOffset] mod 2^k` +- **Details**: Wraps on overflow - **Tag checks**: `T[aOffset] == T[bOffset] == inTag` - **Tag updates**: `T[dstOffset] = inTag` - **Bit-size**: 128 @@ -520,6 +535,7 @@ Subtraction (a - b) - **bOffset**: memory offset of the operation's right input - **dstOffset**: memory offset specifying where to store operation's result - **Expression**: `M[dstOffset] = M[aOffset] - M[bOffset] mod 2^k` +- **Details**: Wraps on undeflow - **Tag checks**: `T[aOffset] == T[bOffset] == inTag` - **Tag updates**: `T[dstOffset] = inTag` - **Bit-size**: 128 @@ -541,6 +557,7 @@ Multiplication (a * b) - **bOffset**: memory offset of the operation's right input - **dstOffset**: memory offset specifying where to store operation's result - **Expression**: `M[dstOffset] = M[aOffset] * M[bOffset] mod 2^k` +- **Details**: Wraps on overflow - **Tag checks**: `T[aOffset] == T[bOffset] == inTag` - **Tag updates**: `T[dstOffset] = inTag` - **Bit-size**: 128 @@ -1637,12 +1654,38 @@ Send an L2-to-L1 message [![](./images/bit-formats/SENDL2TOL1MSG.png)](./images/bit-formats/SENDL2TOL1MSG.png) +### `CONTRACTINSTANCECOPY` +Copies contract instance data to memory + +[See in table.](#isa-table-contractInstanceCopy) + +- **Opcode**: 0x35 +- **Category**: Other +- **Flags**: + - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. +- **Args**: + - **addressOffset**: memory offset of the contract instance address + - **dstOffset**: location to write the contract instance information to +- **Expression**: + +{`M[dstOffset:dstOffset+CONTRACT_INSTANCE_SIZE] = [ + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.portalContractAddress.toField(), + instance.publicKeysHash, +]`} + +- **Bit-size**: 88 + + ### `CALL` Call into another contract [See in table.](#isa-table-call) -- **Opcode**: 0x35 +- **Opcode**: 0x36 - **Category**: Control Flow - Contract Calls - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. @@ -1690,7 +1733,7 @@ Call into another contract, disallowing World State and Accrued Substate modific [See in table.](#isa-table-staticcall) -- **Opcode**: 0x36 +- **Opcode**: 0x37 - **Category**: Control Flow - Contract Calls - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. @@ -1735,7 +1778,7 @@ Call into another contract, but keep the caller's `sender` and `storageAddress` [See in table.](#isa-table-delegatecall) -- **Opcode**: 0x37 +- **Opcode**: 0x38 - **Category**: Control Flow - Contract Calls - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. @@ -1780,7 +1823,7 @@ Halt execution within this context (without revert), optionally returning some d [See in table.](#isa-table-return) -- **Opcode**: 0x38 +- **Opcode**: 0x39 - **Category**: Control Flow - Contract Calls - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. @@ -1802,7 +1845,7 @@ Halt execution within this context as `reverted`, optionally returning some data [See in table.](#isa-table-revert) -- **Opcode**: 0x39 +- **Opcode**: 0x3a - **Category**: Control Flow - Contract Calls - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. diff --git a/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js b/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js index 9eafdfcc205..437c9591f11 100644 --- a/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js +++ b/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js @@ -1087,6 +1087,31 @@ context.accruedSubstate.sentL2ToL1Messages.append( "Tag checks": "", "Tag updates": "", }, + { + "id": "contractInstanceCopy", + "Name": "`CONTRACTINSTANCECOPY`", + "Category": "Other", + "Flags": [ + {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, + ], + "Args": [ + {"name": "addressOffset", "description": "memory offset of the contract instance address"}, + {"name": "dstOffset", "description": "location to write the contract instance information to"}, + ], + "Expression": ` +M[dstOffset:dstOffset+CONTRACT_INSTANCE_SIZE] = [ + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.portalContractAddress.toField(), + instance.publicKeysHash, +] +`, + "Summary": "Copies contract instance data to memory", + "Tag checks": "", + "Tag updates": "", + }, { "id": "call", "Name": "`CALL`",