diff --git a/biome.json b/biome.json index 8cd13c3c..59141c0b 100644 --- a/biome.json +++ b/biome.json @@ -12,6 +12,7 @@ }, "linter": { "enabled": true, + "ignore": ["scripts/**"], "rules": { "recommended": true, "correctness": { @@ -49,7 +50,6 @@ "ignore": [ ".vscode", ".idea", - "scripts/**", "node_modules/**", ".turbo/**", "build/**", diff --git a/docker/fuel-core/Dockerfile b/docker/fuel-core/Dockerfile index e077efc5..e6cae516 100644 --- a/docker/fuel-core/Dockerfile +++ b/docker/fuel-core/Dockerfile @@ -6,7 +6,7 @@ # We should be supporting always the same fuel-core version as the fuels (ts-sdk) # https://github.com/FuelLabs/fuels-ts/blob/master/internal/fuel-core/VERSION -FROM ghcr.io/fuellabs/fuel-core:v0.40.0 +FROM ghcr.io/fuellabs/fuel-core:v0.44.0 # dependencies ENV DEBIAN_FRONTEND=noninteractive diff --git a/packages/contracts/package.json b/packages/contracts/package.json index aad1811b..a290f7b2 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -17,7 +17,8 @@ "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand --silent", "build": "tsup", "build:sway": "pnpm fuels build", - "contracts:start": "ts-node scripts/construct-contracts.ts" + "contracts:start": "dotenv -e .env -- ts-node scripts/construct-contracts.ts", + "contracts:deploy": "dotenv -e .env -- ts-node scripts/deploy.ts" }, "keywords": [], "author": "", diff --git a/packages/contracts/scripts/construct-contracts.ts b/packages/contracts/scripts/construct-contracts.ts index 7eeea15a..f85276ab 100644 --- a/packages/contracts/scripts/construct-contracts.ts +++ b/packages/contracts/scripts/construct-contracts.ts @@ -1,28 +1,19 @@ -import { getContractId, Manager, Nft, Registry, Resolver } from '../src'; +import { Manager, Nft, Registry, Resolver, getContractId } from '../src'; import { logger, setup } from './utils'; -const main = async () => { - const { provider, wallet } = await setup(); - - const managerId = getContractId(provider.url, 'manager'); - const resolverId = getContractId(provider.url, 'resolver'); - const registryId = getContractId(provider.url, 'registry'); - const nftId = getContractId(provider.url, 'nft'); - - const manager = new Manager(managerId, wallet); - const resolver = new Resolver(resolverId, wallet); - const registry = new Registry(registryId, wallet); +export const constructNft = async (wallet: any, registryId: string) => { + const nftId = getContractId(wallet.provider.url, 'nft'); const nft = new Nft(nftId, wallet); try { - const { value: nftOwner } = await manager.functions.owner().get(); + const { value: nftOwner } = await nft.functions.owner().get(); // @ts-ignore if (nftOwner === 'Uninitialized') { const nftConstruct = await nft.functions .constructor( { Address: { bits: wallet.address.toB256() } }, - { ContractId: { bits: registryId } }, + { ContractId: { bits: registryId } } ) .call(); await nftConstruct.waitForResult(); @@ -44,6 +35,13 @@ const main = async () => { } } + return nftId; +}; + +export const constructManager = async (wallet: any, registryId: string) => { + const managerId = getContractId(wallet.provider.url, 'manager'); + const manager = new Manager(managerId, wallet); + try { const { value: managerOwner } = await manager.functions.owner().get(); @@ -52,7 +50,7 @@ const main = async () => { const managerConstruct = await manager.functions .constructor( { Address: { bits: wallet.address.toB256() } }, - { ContractId: { bits: registryId } }, + { ContractId: { bits: registryId } } ) .call(); await managerConstruct.waitForResult(); @@ -74,6 +72,13 @@ const main = async () => { } } + return managerId; +}; + +export const constructResolver = async (wallet: any, managerId: string) => { + const resolverId = getContractId(wallet.provider.url, 'resolver'); + const resolver = new Resolver(resolverId, wallet); + try { const resolverConstruct = await resolver.functions .constructor({ bits: managerId }) @@ -88,12 +93,23 @@ const main = async () => { } } + return resolverId; +}; + +export const constructRegistry = async ( + wallet: any, + managerId: string, + nftId: string +) => { + const registryId = getContractId(wallet.provider.url, 'registry'); + const registry = new Registry(registryId, wallet); + try { const registryConstruct = await registry.functions .constructor( { bits: wallet.address.toB256() }, { bits: managerId }, - { bits: nftId }, + { bits: nftId } ) .call(); await registryConstruct.waitForResult(); @@ -105,6 +121,19 @@ const main = async () => { logger.error('Registry construct failed', e); } } + + return registryId; +}; + +const main = async () => { + const { provider, wallet } = await setup(); + + const registryId = getContractId(provider.url, 'registry'); + + const nftId = await constructNft(wallet, registryId); + const managerId = await constructManager(wallet, registryId); + await constructResolver(wallet, managerId); + await constructRegistry(wallet, managerId, nftId); }; main() diff --git a/packages/contracts/scripts/deploy.ts b/packages/contracts/scripts/deploy.ts new file mode 100644 index 00000000..33a3b3e7 --- /dev/null +++ b/packages/contracts/scripts/deploy.ts @@ -0,0 +1,143 @@ +import { + type Account, + type ContractFactory, + type Provider, + Src14OwnedProxy, + Src14OwnedProxyFactory, + ZeroBytes32, +} from 'fuels'; +import { + type Contracts, + ManagerFactory, + NftFactory, + RegistryFactory, + ResolverFactory, + getContractId, +} from '../src'; +import { logger, setContractId, setup } from './utils'; + +type DeployConfig = { + contract: { + name: Contracts; + factory: ContractFactory; + configurableConstants?: Record; + }; + provider: Provider; + wallet: Account; +}; + +const deployContractWithProxy = async (config: DeployConfig) => { + const { provider, wallet, contract } = config; + + logger.info(`[${contract.name}] Deploying new instance...`); + const { contractId, waitForResult: waitForDeploy } = + await contract.factory.deploy({ + configurableConstants: contract.configurableConstants, + }); + await waitForDeploy(); + + logger.info(`[${contract.name}] Checking has proxy instance...`); + const proxyAddress = getContractId(provider.url, contract.name); + const addressType = await provider.getAddressType( + proxyAddress ?? ZeroBytes32 + ); + const isDeployed = addressType === 'Contract'; + + if (!isDeployed) { + logger.info(`[${contract.name}] Deploying proxy...`); + const proxyDeployment = await Src14OwnedProxyFactory.deploy(wallet, { + configurableConstants: { + INITIAL_TARGET: { bits: contractId }, + INITIAL_OWNER: { + Initialized: { Address: { bits: wallet.address.toB256() } }, + }, + }, + }); + + const { contract: proxy } = await proxyDeployment.waitForResult(); + const { waitForResult } = await proxy.functions.initialize_proxy().call(); + await waitForResult(); + + logger.success( + `Proxy for ${contract.name} deployed! Id: ${proxy.id.toB256()}` + ); + const proxyAddress = proxy.id.toB256(); + setContractId(provider.url, contract.name, proxyAddress); + return proxy.id.toString(); + } + + logger.info(`[${contract.name}] Has proxy instance! Setting target...`); + const proxy = new Src14OwnedProxy(proxyAddress, wallet); + const { waitForResult } = await proxy.functions + .set_proxy_target({ bits: contractId }) + .call(); + await waitForResult(); + logger.success( + `[${contract.name}] Target set! Proxy: ${proxy.id.toB256()} Target: ${contractId}` + ); + return proxy.id.toString(); +}; + +const deployContract = async (config: DeployConfig) => { + const { provider, contract } = config; + + logger.info(`Deploying ${contract.name}...`); + const { contractId, waitForResult: waitForDeploy } = + await contract.factory.deploy({ + configurableConstants: contract.configurableConstants, + }); + await waitForDeploy(); + + logger.success(`${contract.name} deployed! Id: ${contractId}`); + setContractId(provider.url, contract.name, contractId); +}; + +const main = async () => { + const { wallet, provider } = await setup(); + + await deployContractWithProxy({ + wallet, + provider, + contract: { + name: 'registry', + factory: new RegistryFactory(wallet), + }, + }); + + await deployContractWithProxy({ + wallet, + provider, + contract: { + name: 'manager', + factory: new ManagerFactory(wallet), + }, + }); + + await deployContract({ + wallet, + provider, + contract: { + name: 'resolver', + factory: new ResolverFactory(wallet), + }, + }); + + await deployContract({ + wallet, + provider, + contract: { + name: 'nft', + factory: new NftFactory(wallet), + }, + }); +}; + +main() + .then(() => { + logger.success('Done!'); + process.exit(0); + }) + .catch((error) => { + logger.error('Construct failed!', error); + process.exit(1); + }); diff --git a/packages/contracts/scripts/utils.ts b/packages/contracts/scripts/utils.ts index 836716cb..849d4529 100644 --- a/packages/contracts/scripts/utils.ts +++ b/packages/contracts/scripts/utils.ts @@ -1,5 +1,9 @@ +import fs from 'node:fs'; +import path from 'node:path'; import dotenv from 'dotenv'; import { Provider, Wallet } from 'fuels'; +import { type Contracts, type NetworkKeys, resolveNetwork } from '../src'; +import contracts from '../src/artifacts/contracts-fuel.json'; dotenv.config({ path: '../.env', @@ -12,6 +16,28 @@ export const logger = { warn: (...data: any) => console.log(`❌ `, ...data), }; +export const setContractId = ( + provider: string, + contract: Contracts, + contractId: string +) => { + const network = resolveNetwork(provider) as N; + if (!contracts[network]) { + contracts[network] = { + manager: '', + registry: '', + resolver: '', + nft: '', + }; + } + + contracts[network]![contract] = contractId; + fs.writeFileSync( + path.join(__dirname, '..', 'src', './artifacts/contracts-fuel.json'), + JSON.stringify(contracts, null, 2) + ); +}; + export const requireEnv = (name: string) => { const value = process.env[name]; if (!value) { @@ -32,7 +58,7 @@ export const setup = async () => { `Setup Provider: ${providerUrl} Wallet: ${wallet.address.toB256()} - Balance: ${balance.format()} ETH`, + Balance: ${balance.format()} ETH` ); return { provider, wallet }; diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 2f6fe87d..2fc01054 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -1,7 +1,7 @@ import contracts from './artifacts/contracts-fuel.json'; export type NetworkKeys = keyof typeof contracts; -type ContractKeys = keyof (typeof contracts)[N]; +export type Contracts = 'manager' | 'registry' | 'resolver' | 'nft'; const DEFAULT_NETWORK: NetworkKeys = 'testnet'; @@ -23,7 +23,7 @@ export const resolveNetwork = (provider: string) => { export const getContractId = ( provider: string, - contract: ContractKeys + contract: Contracts ) => { const network = resolveNetwork(provider) as N; return contracts[network]?.[contract];