/
contract.ts
126 lines (109 loc) · 3.78 KB
/
contract.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type { FunctionFragment, JsonAbi } from '@fuel-ts/abi-coder';
import { Interface } from '@fuel-ts/abi-coder';
import type { Account, Provider } from '@fuel-ts/account';
import { Address } from '@fuel-ts/address';
import type { AbstractAddress, AbstractContract, BytesLike } from '@fuel-ts/interfaces';
import { FunctionInvocationScope } from './functions/invocation-scope';
import { MultiCallInvocationScope } from './functions/multicall-scope';
import type { InvokeFunction, InvokeFunctions } from './types';
/**
* `Contract` provides a way to interact with the contract program type.
*/
export default class Contract implements AbstractContract {
/**
* The unique contract identifier.
*/
id!: AbstractAddress;
/**
* The provider for interacting with the contract.
*/
provider!: Provider;
/**
* The contract's ABI interface.
*/
interface!: Interface;
/**
* The account associated with the contract, if available.
*/
account!: Account | null;
/**
* A collection of functions available on the contract.
*/
functions: InvokeFunctions = {};
/**
* Creates an instance of the Contract class.
*
* @param id - The contract's address.
* @param abi - The contract's ABI (JSON ABI or Interface instance).
* @param accountOrProvider - The account or provider for interaction.
*/
constructor(
id: string | AbstractAddress,
abi: JsonAbi | Interface,
accountOrProvider: Account | Provider
) {
this.interface = abi instanceof Interface ? abi : new Interface(abi);
this.id = Address.fromAddressOrString(id);
/**
Instead of using `instanceof` to compare classes, we instead check
if `accountOrProvider` have a `provider` property inside. If yes,
than we assume it's a Wallet.
This approach is safer than using `instanceof` because it
there might be different versions and bundles of the library.
The same is done at:
- ./contract-factory.ts
@see ContractFactory
*/
if (accountOrProvider && 'provider' in accountOrProvider) {
this.provider = accountOrProvider.provider;
this.account = accountOrProvider;
} else {
this.provider = accountOrProvider;
this.account = null;
}
Object.keys(this.interface.functions).forEach((name) => {
const fragment = this.interface.getFunction(name);
Object.defineProperty(this.functions, fragment.name, {
value: this.buildFunction(fragment),
writable: false,
});
});
}
/**
* Build a function invocation scope for the provided function fragment.
*
* @param func - The function fragment to build a scope for.
* @returns A function that creates a FunctionInvocationScope.
*/
buildFunction(func: FunctionFragment) {
return (() => {
const funcInvocationScopeCreator = (...args: Array<unknown>) =>
new FunctionInvocationScope(this, func, args);
Object.defineProperty(funcInvocationScopeCreator, 'isReadOnly', {
value: () => func.isReadOnly(),
writable: false,
});
return funcInvocationScopeCreator;
})() as InvokeFunction;
}
/**
* Create a multi-call invocation scope for the provided function invocation scopes.
*
* @param calls - An array of FunctionInvocationScopes to execute in a batch.
* @returns A MultiCallInvocationScope instance.
*/
multiCall(calls: Array<FunctionInvocationScope>) {
return new MultiCallInvocationScope(this, calls);
}
/**
* Get the balance for a given asset ID for this contract.
*
* @param assetId - The specified asset ID.
* @returns The balance of the contract for the specified asset.
*/
// #region contract-balance-1
getBalance(assetId: BytesLike) {
return this.provider.getContractBalance(this.id, assetId);
}
// #endregion contract-balance-1
}