Skip to content

Commit

Permalink
fix: loader contract being called via proxy (#3122)
Browse files Browse the repository at this point in the history
* fix: resolve loader contract being called via a proxy

* chore: changeset

* chore: lint and add timeout

* chore: update code ref
  • Loading branch information
danielbate authored Sep 6, 2024
1 parent 8faeaa4 commit e9ad8d0
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-flies-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/contract": patch
---

fix: loader contract being called via proxy
2 changes: 1 addition & 1 deletion packages/contract/src/loader/loader-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions packages/contract/src/loader/loader-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
34 changes: 34 additions & 0 deletions packages/fuel-gauge/src/contract-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ConfigurableContractFactory,
LargeContract,
ConfigurableContract,
ProxyContractFactory,
} from '../test/typegen';

import { launchTestContract } from './utils';
Expand Down Expand Up @@ -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);
});
1 change: 1 addition & 0 deletions packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ members = [
"predicate-validate-transfer",
"predicate-vector-types",
"predicate-with-configurable",
"proxy-contract",
"raw-slice-contract",
"reentrant-bar",
"reentrant-foo",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "proxy-contract"

[dependencies]
Original file line number Diff line number Diff line change
@@ -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<ContractId> = 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)
}
}

0 comments on commit e9ad8d0

Please sign in to comment.