Skip to content

Commit

Permalink
fix: proxy deploy with storage (#3235)
Browse files Browse the repository at this point in the history
* test: add assertions for the storage methods

* feat: pass target slots to proxy contract

* chore: nits

* chore: cleanup test

* chore: lint

* chore: changeset
  • Loading branch information
danielbate authored Oct 2, 2024
1 parent 3015555 commit 83b0ea9
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 62 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-bulldogs-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fuels": patch
---

fix: proxy deploy with storage
7 changes: 6 additions & 1 deletion packages/fuels/src/cli/commands/deploy/deployContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ export async function deployContract(

const targetBytecode = readFileSync(binaryPath);
const targetAbi = JSON.parse(readFileSync(abiPath, 'utf-8'));
const targetStorageSlots = deployConfig.storageSlots ?? [];

const proxyBytecode = Src14OwnedProxyFactory.bytecode;
const proxyAbi = Src14OwnedProxy.abi;
const proxyFactory = new Src14OwnedProxyFactory(wallet);
const proxyStorageSlots = Src14OwnedProxy.storageSlots ?? [];

const isProxyEnabled = tomlContents?.proxy?.enabled;
const proxyAddress = tomlContents?.proxy?.address;
Expand Down Expand Up @@ -82,15 +84,18 @@ export async function deployContract(
// b. Deploy the SR-C14 Compliant / Proxy Contract
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { storageSlots, stateRoot, ...commonDeployConfig } = deployConfig;
const mergedStorageSlots = targetStorageSlots.concat(proxyStorageSlots);

const proxyDeployConfig: DeployContractOptions = {
...commonDeployConfig,
storageSlots: mergedStorageSlots,
configurableConstants: {
INITIAL_TARGET: { bits: targetContract.id.toB256() },
INITIAL_OWNER: { Initialized: { Address: { bits: wallet.address.toB256() } } },
},
};

const proxyFactory = new ContractFactory(proxyBytecode, proxyAbi, wallet);
const { waitForResult: waitForProxy } = await proxyFactory.deploy(proxyDeployConfig);
const { contract: proxyContract } = await waitForProxy();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/consistent-type-imports */

/*
Fuels version: 0.94.7
Fuels version: 0.94.8
*/

import { Contract, Interface } from "../../../../..";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/consistent-type-imports */

/*
Fuels version: 0.94.7
Fuels version: 0.94.8
*/

import { Contract, ContractFactory, decompressBytecode } from "../../../../..";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/consistent-type-imports */

/*
Fuels version: 0.94.7
Fuels version: 0.94.8
*/

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/consistent-type-imports */

/*
Fuels version: 0.94.7
Fuels version: 0.94.8
*/

export { Src14OwnedProxy } from './Src14OwnedProxy';
Expand Down
117 changes: 60 additions & 57 deletions packages/fuels/test/features/deploy.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { JsonAbi } from '@fuel-ts/abi-coder';
import type { Account } from '@fuel-ts/account';
import { Contract } from '@fuel-ts/program';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
Expand Down Expand Up @@ -58,6 +60,20 @@ describe('deploy', { timeout: 180000 }, () => {
expect(firstFuelsContents.fooBar).toMatch(/0x/);
});

/**
* Executes the target contract and returns the values of the functions for proxy deploys.
*/
async function executeTargetContract(contractId: string, abi: JsonAbi, wallet: Account) {
const targetContract = new Contract(contractId, abi, wallet);

const { value: getCountValue } = await targetContract.functions.get_value().get();

const res = await targetContract.functions.test_function().call();
const { value: testFunctionValue } = await res.waitForResult();

return { getCountValue, testFunctionValue };
}

it('should run `deploy` command [using proxy + re-deploy]', async () => {
using launched = await launchTestNode({
nodeOptions: {
Expand Down Expand Up @@ -92,34 +108,26 @@ describe('deploy', { timeout: 180000 }, () => {
expect(firstFuelsContents.fooBar).toMatch(/0x/);
expect(firstFuelsContents.upgradable).toMatch(/0x/);

/**
* a) Add helper
* For interacting with deployed contract
*/
async function executeTargetContract() {
const upgradableContractId = firstFuelsContents.upgradable;
const upgradableAbi = JSON.parse(
readFileSync(
join(paths.upgradableContractPath, 'out', 'debug', 'upgradable-abi.json'),
'utf-8'
)
);

const targetContract = new Contract(upgradableContractId, upgradableAbi, wallet);
const res = await targetContract.functions.test_function().call();
const { value } = await res.waitForResult();

return value;
}
const contractId = firstFuelsContents.upgradable;
const abi = JSON.parse(
readFileSync(
join(paths.upgradableContractPath, 'out', 'debug', 'upgradable-abi.json'),
'utf-8'
)
);

/**
* b) Interact with target contract
* Calling `test_function` should return `true` for the first execution.
* a) Interacting with the target contract
* Calling `test_function` should return `true` and `get_value`
* should return `10` for the first execution.
*/
expect(await executeTargetContract()).toBe(true); // TRUE
expect(await executeTargetContract(contractId, abi, wallet)).toStrictEqual({
getCountValue: 10,
testFunctionValue: true,
});

/**
* c) Modify `main.sw` method before second deploy
* b) Modify `main.sw` method before second deploy
* This will make the method return `false` instead of `true`.
*/
const mainPath = join(paths.upgradableContractPath, 'src', 'main.sw');
Expand All @@ -140,10 +148,14 @@ describe('deploy', { timeout: 180000 }, () => {
expect(firstFuelsContents.upgradable).toEqual(secondFuelsContents.upgradable);

/**
* d) Interact with target contract
* Now, calling `test_function` should return `false` instead.
* c) Interact with target contract
* Now, calling `test_function` should return `false` instead,
* but `get_value` should still return `10`.
*/
expect(await executeTargetContract()).toBe(false); // FALSE
expect(await executeTargetContract(contractId, abi, wallet)).toStrictEqual({
getCountValue: 10,
testFunctionValue: false,
});
});

it('should run `deploy` command [using proxy and chunking + re-deploy]', async () => {
Expand Down Expand Up @@ -180,39 +192,26 @@ describe('deploy', { timeout: 180000 }, () => {
expect(firstFuelsContents.fooBar).toMatch(/0x/);
expect(firstFuelsContents.upgradable).toMatch(/0x/);

/**
* a) Add helper
* For interacting with deployed contract
*/
async function executeTargetContract() {
const upgradableChunkedContractId = firstFuelsContents.upgradableChunked;
const upgradableChunkedAbi = JSON.parse(
readFileSync(
join(paths.upgradableChunkedContractPath, 'out', 'debug', 'upgradable-chunked-abi.json'),
'utf-8'
)
);

const targetContract = new Contract(
upgradableChunkedContractId,
upgradableChunkedAbi,
wallet
);

const res = await targetContract.functions.test_function().call();
const { value } = await res.waitForResult();

return value;
}
const contractId = firstFuelsContents.upgradableChunked;
const abi = JSON.parse(
readFileSync(
join(paths.upgradableChunkedContractPath, 'out', 'debug', 'upgradable-chunked-abi.json'),
'utf-8'
)
);

/**
* b) Interact with target contract
* Calling `test_function` should return `true` for the first execution.
* a) Interacting with the target contract
* Calling `test_function` should return `true` and `get_value`
* should return `10` for the first execution.
*/
expect(await executeTargetContract()).toBe(true); // TRUE
expect(await executeTargetContract(contractId, abi, wallet)).toStrictEqual({
getCountValue: 10,
testFunctionValue: true,
});

/**
* c) Modify `main.sw` method before second deploy
* b) Modify `main.sw` method before second deploy
* This will make the method return `false` instead of `true`.
*/
const mainPath = join(paths.upgradableChunkedContractPath, 'src', 'main.sw');
Expand All @@ -233,9 +232,13 @@ describe('deploy', { timeout: 180000 }, () => {
expect(firstFuelsContents.upgradableChunked).toEqual(secondFuelsContents.upgradableChunked);

/**
* d) Interact with target contract
* Now, calling `test_function` should return `false` instead.
* c) Interact with target contract
* Now, calling `test_function` should return `false` instead,
* but `get_value` should still return `10`.
*/
expect(await executeTargetContract()).toBe(false); // FALSE
expect(await executeTargetContract(contractId, abi, wallet)).toStrictEqual({
getCountValue: 10,
testFunctionValue: false,
});
});
});

0 comments on commit 83b0ea9

Please sign in to comment.