-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
contract-factory.ts
95 lines (80 loc) · 2.92 KB
/
contract-factory.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import type { BytesLike } from '@ethersproject/bytes';
import { Logger } from '@ethersproject/logger';
import { Interface } from '@fuel-ts/abi-coder';
import type { JsonAbi } from '@fuel-ts/abi-coder';
import { randomBytes } from '@fuel-ts/keystore';
import type { CreateTransactionRequestLike } from '@fuel-ts/providers';
import { Provider, CreateTransactionRequest } from '@fuel-ts/providers';
import type { StorageSlot } from '@fuel-ts/transactions';
import { MAX_GAS_PER_TX } from '@fuel-ts/transactions';
import { Wallet } from '@fuel-ts/wallet';
import { getContractId, getContractStorageRoot, includeHexPrefix } from '../util';
import Contract from './contract';
const logger = new Logger(process.env.BUILD_VERSION || '~');
type DeployContractOptions = {
salt?: BytesLike;
storageSlots?: StorageSlot[];
stateRoot?: BytesLike;
} & CreateTransactionRequestLike;
export default class ContractFactory {
bytecode: BytesLike;
interface: Interface;
provider!: Provider | null;
wallet!: Wallet | null;
constructor(
bytecode: BytesLike,
abi: JsonAbi | Interface,
walletOrProvider: Wallet | Provider | null = null
) {
this.bytecode = bytecode;
if (abi instanceof Interface) {
this.interface = abi;
} else {
this.interface = new Interface(abi);
}
if (walletOrProvider instanceof Wallet) {
this.provider = walletOrProvider.provider;
this.wallet = walletOrProvider;
} else if (walletOrProvider instanceof Provider) {
this.provider = walletOrProvider;
this.wallet = null;
} else {
this.provider = null;
this.wallet = null;
}
}
connect(provider: Provider | null) {
return new ContractFactory(this.bytecode, this.interface, provider);
}
async deployContract(deployContractOptions?: DeployContractOptions) {
if (!this.wallet) {
return logger.throwArgumentError('Cannot deploy without wallet', 'wallet', this.wallet);
}
const storageSlots = deployContractOptions?.storageSlots
?.map(({ key, value }) => ({
key: includeHexPrefix(key),
value: includeHexPrefix(value),
}))
.sort(({ key: keyA }, { key: keyB }) => keyA.localeCompare(keyB));
const options = {
salt: randomBytes(32),
...deployContractOptions,
storageSlots: storageSlots || [],
};
const stateRoot = options.stateRoot || getContractStorageRoot(options.storageSlots);
const contractId = getContractId(this.bytecode, options.salt, stateRoot);
const request = new CreateTransactionRequest({
gasPrice: 0,
gasLimit: MAX_GAS_PER_TX,
bytePrice: 0,
bytecodeWitnessIndex: 0,
witnesses: [this.bytecode],
...options,
});
request.addContractCreatedOutput(contractId, stateRoot);
await this.wallet.fund(request);
const response = await this.wallet.sendTransaction(request);
await response.wait();
return new Contract(contractId, this.interface, this.wallet);
}
}