diff --git a/.changeset/grumpy-flies-sleep.md b/.changeset/grumpy-flies-sleep.md new file mode 100644 index 00000000000..96b53fc4aee --- /dev/null +++ b/.changeset/grumpy-flies-sleep.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/contract": patch +--- + +fix: loader contract being called via proxy diff --git a/packages/contract/src/loader/loader-script.test.ts b/packages/contract/src/loader/loader-script.test.ts index 329b5369e0b..31511569f21 100644 --- a/packages/contract/src/loader/loader-script.test.ts +++ b/packages/contract/src/loader/loader-script.test.ts @@ -13,7 +13,7 @@ describe('Loader Script', () => { const actual = getLoaderInstructions(blobIds); const expected = new Uint8Array([ - 26, 64, 192, 0, 80, 65, 0, 48, 26, 88, 80, 0, 114, 76, 0, 3, 186, 69, 0, 0, 50, 64, 4, 65, 80, + 26, 64, 48, 0, 80, 65, 0, 48, 26, 88, 80, 0, 114, 76, 0, 3, 186, 69, 0, 0, 50, 64, 4, 65, 80, 65, 0, 32, 89, 77, 48, 1, 119, 76, 0, 3, 32, 89, 99, 0, 82, 89, 96, 4, 74, 88, 0, 0, 1, 2, 3, ]); expect(actual).toStrictEqual(expected); diff --git a/packages/contract/src/loader/loader-script.ts b/packages/contract/src/loader/loader-script.ts index 4a889d7ace1..6d9c64e1254 100644 --- a/packages/contract/src/loader/loader-script.ts +++ b/packages/contract/src/loader/loader-script.ts @@ -13,7 +13,7 @@ export const getLoaderInstructions = (blobIds: string[]): Uint8Array => { // Bytes for the Blob Ids const blobIdBytes = concat(blobIds.map((b) => arrayify(b))); - // Reference: https://github.com/FuelLabs/fuels-ts/issues/2741#issuecomment-2260364179 + // Reference: https://github.com/FuelLabs/fuels-rs/blob/master/packages/fuels-programs/src/contract/loader.rs // There are 2 main steps: // 1. Load the blob contents into memory // 2. Jump to the beginning of the memory where the blobs were loaded @@ -22,7 +22,7 @@ export const getLoaderInstructions = (blobIds: string[]): Uint8Array => { const instructionBytes = new InstructionSet( // 1. load the blob contents into memory // find the start of the hardcoded blob ids, which are located after the code ends - asm.move_(0x10, RegId.is().to_u8()), + asm.move_(0x10, RegId.pc().to_u8()), // 0x10 to hold the address of the current blob id asm.addi(0x10, 0x10, numberOfInstructions * Instruction.size()), // The contract is going to be loaded from the current value of SP onwards, save diff --git a/packages/fuel-gauge/src/contract-factory.test.ts b/packages/fuel-gauge/src/contract-factory.test.ts index 128f46a2265..6f6b35b63f9 100644 --- a/packages/fuel-gauge/src/contract-factory.test.ts +++ b/packages/fuel-gauge/src/contract-factory.test.ts @@ -11,6 +11,7 @@ import { ConfigurableContractFactory, LargeContract, ConfigurableContract, + ProxyContractFactory, } from '../test/typegen'; import { launchTestContract } from './utils'; @@ -553,4 +554,37 @@ describe('Contract Factory', () => { const { value: secondValue } = await secondCall.waitForResult(); expect(secondValue.toNumber()).toBe(1001); }, 25000); + + it('deploys large contract and calls via a proxy', async () => { + using launched = await launchTestNode(); + + const { + wallets: [wallet], + } = launched; + + const largeContractDeploy = await LargeContractFactory.deploy(wallet); + const { contract: largeContract } = await largeContractDeploy.waitForResult(); + expect(largeContract.id).toBeDefined(); + + const largeContractCall = await largeContract.functions.something().call(); + const { value: largeContractValue } = await largeContractCall.waitForResult(); + expect(largeContractValue.toNumber()).toBe(1001); + + const proxyContractDeploy = await ProxyContractFactory.deploy(wallet); + const { contract: proxyContract } = await proxyContractDeploy.waitForResult(); + expect(proxyContract.id).toBeDefined(); + + const setTargetCall = await proxyContract.functions + .set_target_contract({ bits: largeContract.id.toB256() }) + .call(); + const setTargetResult = await setTargetCall.waitForResult(); + expect(setTargetResult.transactionResult.isStatusSuccess).toBe(true); + + const proxyCall = await proxyContract.functions + .something() + .addContracts([largeContract]) + .call(); + const { value: proxyValue } = await proxyCall.waitForResult(); + expect(proxyValue.toNumber()).toBe(1001); + }, 25000); }); diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml index b7fa116ba4c..5b7cc0762b3 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml +++ b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml @@ -39,6 +39,7 @@ members = [ "predicate-validate-transfer", "predicate-vector-types", "predicate-with-configurable", + "proxy-contract", "raw-slice-contract", "reentrant-bar", "reentrant-foo", diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/proxy-contract/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/proxy-contract/Forc.toml new file mode 100644 index 00000000000..fef4ebc1a3e --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/proxy-contract/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "proxy-contract" + +[dependencies] diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/proxy-contract/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/proxy-contract/src/main.sw new file mode 100644 index 00000000000..2f3e4058366 --- /dev/null +++ b/packages/fuel-gauge/test/fixtures/forc-projects/proxy-contract/src/main.sw @@ -0,0 +1,29 @@ +contract; + +use std::execution::run_external; + +abi Proxy { + #[storage(write)] + fn set_target_contract(id: ContractId); + + // this targets the method of the `large_contract project + #[storage(read)] + fn something() -> u64; +} + +storage { + target_contract: Option = None, +} + +impl Proxy for Contract { + #[storage(write)] + fn set_target_contract(id: ContractId) { + storage.target_contract.write(Some(id)); + } + + #[storage(read)] + fn something() -> u64 { + let target = storage.target_contract.read().unwrap(); + run_external(target) + } +}