Cherry pick library linking code from #2456

This commit is contained in:
Lawrence Forman
2020-01-31 14:38:42 -05:00
parent 19589aec57
commit e7b3246dd0
10 changed files with 729 additions and 42 deletions

View File

@@ -1,5 +1,5 @@
export { artifacts } from './artifacts';
export { DevUtilsContract } from './wrappers';
export { DevUtilsContract, LibAssetDataContract, LibTransactionDecoderContract } from './wrappers';
export {
ContractArtifact,
ContractChains,

View File

@@ -1,4 +1,13 @@
[
{
"version": "5.2.0",
"changes": [
{
"note": "Support deploying contracts with unliked libraries through `deployWithLibrariesFrom0xArtifactAsync()`",
"pr": 2463
}
]
},
{
"timestamp": 1580988106,
"version": "5.1.2",

View File

@@ -10,6 +10,7 @@ import {
{{#if events~}}SubscriptionManager,{{/if~}}
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
@@ -27,7 +28,7 @@ import {
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
@@ -97,6 +98,47 @@ export class {{contractName}}Contract extends BaseContract {
}
return {{contractName}}Contract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly, {{> params inputs=ctor.inputs}});
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: (ContractArtifact | SimpleContractArtifact) },
{{> typed_params inputs=ctor.inputs}}
): Promise<{{contractName}}Contract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await {{contractName}}Contract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults
);
const bytecode = linkLibrariesInBytecode(
artifact.compilerOutput.evm.bytecode,
libraryAddresses,
);
if (!hexUtils.isHex(bytecode)) {
throw new Error(`Bytecode for "${artifact.contractName}" was not fully linked.`);
}
return {{contractName}}Contract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly, {{> params inputs=ctor.inputs}});
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
@@ -138,7 +180,6 @@ export class {{contractName}}Contract extends BaseContract {
return contractInstance;
}
/**
* @returns The contract ABI
*/
@@ -168,12 +209,64 @@ export class {{contractName}}Contract extends BaseContract {
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await {{contractName}}Contract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(
libraryArtifact.compilerOutput.evm.bytecode,
libraryAddresses,
);
if (!hexUtils.isHex(linkedLibraryBytecode)) {
throw new Error(`Bytecode for library "${libraryArtifact.contractName}" was not fully linked.`);
}
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = {{contractName}}Contract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as {{contractName}}Contract;
@@ -181,6 +274,7 @@ export class {{contractName}}Contract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as {{contractName}}Contract;
@@ -188,6 +282,7 @@ export class {{contractName}}Contract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as {{contractName}}Contract;
@@ -254,6 +349,7 @@ export class {{contractName}}Contract extends BaseContract {
);
return subscriptionToken;
}
/**
* Cancel a subscription
* @param subscriptionToken Subscription token returned by `subscribe()`
@@ -261,12 +357,14 @@ export class {{contractName}}Contract extends BaseContract {
public unsubscribe(subscriptionToken: string): void {
this._subscriptionManager.unsubscribe(subscriptionToken);
}
/**
* Cancels all existing subscriptions
*/
public unsubscribeAll(): void {
this._subscriptionManager.unsubscribeAll();
}
/**
* Gets historical logs without creating a subscription
* @param eventName The {{contractName}} contract event you would like to subscribe to.
@@ -292,6 +390,7 @@ export class {{contractName}}Contract extends BaseContract {
);
return logs;
}{{/if}}
constructor(
address: string,
supportedProvider: SupportedProvider,

View File

@@ -3,8 +3,13 @@ async callAsync(
defaultBlock?: BlockParam,
): Promise<{{> return_type outputs=outputs}}> {
BaseContract._assertCallParams(callData, defaultBlock);
{{#ifEquals this.stateMutability "pure"}}
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
{{#ifEquals this.stateMutability "pure" }}
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync({ ...callData, data: this.getABIEncodedTransactionData() }, defaultBlock);
}
{{else}}
const rawCallResult = await self._performCallAsync({ ...callData, data: this.getABIEncodedTransactionData() }, defaultBlock);
{{/ifEquals}}

View File

@@ -10,6 +10,7 @@ import {
SubscriptionManager,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
@@ -27,7 +28,7 @@ import {
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
@@ -88,6 +89,43 @@ export class AbiGenDummyContract extends BaseContract {
}
return AbiGenDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<AbiGenDummyContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await AbiGenDummyContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact.compilerOutput.evm.bytecode, libraryAddresses);
if (!hexUtils.isHex(bytecode)) {
throw new Error(`Bytecode for "${artifact.contractName}" was not fully linked.`);
}
return AbiGenDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
@@ -869,12 +907,64 @@ export class AbiGenDummyContract extends BaseContract {
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await AbiGenDummyContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(
libraryArtifact.compilerOutput.evm.bytecode,
libraryAddresses,
);
if (!hexUtils.isHex(linkedLibraryBytecode)) {
throw new Error(`Bytecode for library "${libraryArtifact.contractName}" was not fully linked.`);
}
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = AbiGenDummyContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as AbiGenDummyContract;
@@ -882,6 +972,7 @@ export class AbiGenDummyContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as AbiGenDummyContract;
@@ -889,6 +980,7 @@ export class AbiGenDummyContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as AbiGenDummyContract;
@@ -908,7 +1000,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -926,7 +1026,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -964,7 +1072,15 @@ export class AbiGenDummyContract extends BaseContract {
dolor: string;
}> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<{
@@ -1001,7 +1117,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
@@ -1072,8 +1196,17 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
},
getABIEncodedTransactionData(): string {
@@ -1096,8 +1229,17 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
},
getABIEncodedTransactionData(): string {
@@ -1119,7 +1261,15 @@ export class AbiGenDummyContract extends BaseContract {
Array<{ someBytes: string; anInteger: number; aDynamicArrayOfBytes: string[]; aString: string }>
> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<
@@ -1138,7 +1288,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<[BigNumber, string]> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<[BigNumber, string]>(rawCallResult);
@@ -1160,7 +1318,15 @@ export class AbiGenDummyContract extends BaseContract {
defaultBlock?: BlockParam,
): Promise<{ innerStruct: { aField: BigNumber } }> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<{ innerStruct: { aField: BigNumber } }>(rawCallResult);
@@ -1190,7 +1356,15 @@ export class AbiGenDummyContract extends BaseContract {
defaultBlock?: BlockParam,
): Promise<[string, string, string]> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<[string, string, string]>(rawCallResult);
@@ -1216,7 +1390,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1242,7 +1424,15 @@ export class AbiGenDummyContract extends BaseContract {
description: string;
}> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<{
@@ -1270,7 +1460,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1290,7 +1488,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
@@ -1400,7 +1606,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1418,7 +1632,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1435,7 +1657,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
@@ -1452,7 +1682,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1469,7 +1707,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1490,7 +1736,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1511,7 +1765,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
@@ -1528,7 +1790,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
@@ -1546,7 +1816,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
@@ -1563,7 +1841,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1580,7 +1866,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1603,7 +1897,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
@@ -1632,7 +1934,15 @@ export class AbiGenDummyContract extends BaseContract {
defaultBlock?: BlockParam,
): Promise<{ someBytes: string; anInteger: number; aDynamicArrayOfBytes: string[]; aString: string }> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<{
@@ -1665,7 +1975,15 @@ export class AbiGenDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
@@ -1753,6 +2071,7 @@ export class AbiGenDummyContract extends BaseContract {
);
return subscriptionToken;
}
/**
* Cancel a subscription
* @param subscriptionToken Subscription token returned by `subscribe()`
@@ -1760,12 +2079,14 @@ export class AbiGenDummyContract extends BaseContract {
public unsubscribe(subscriptionToken: string): void {
this._subscriptionManager.unsubscribe(subscriptionToken);
}
/**
* Cancels all existing subscriptions
*/
public unsubscribeAll(): void {
this._subscriptionManager.unsubscribeAll();
}
/**
* Gets historical logs without creating a subscription
* @param eventName The AbiGenDummy contract event you would like to subscribe to.
@@ -1791,6 +2112,7 @@ export class AbiGenDummyContract extends BaseContract {
);
return logs;
}
constructor(
address: string,
supportedProvider: SupportedProvider,

View File

@@ -9,6 +9,7 @@ import {
BaseContract,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
@@ -25,7 +26,7 @@ import {
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
@@ -67,6 +68,43 @@ export class LibDummyContract extends BaseContract {
}
return LibDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<LibDummyContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await LibDummyContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact.compilerOutput.evm.bytecode, libraryAddresses);
if (!hexUtils.isHex(bytecode)) {
throw new Error(`Bytecode for "${artifact.contractName}" was not fully linked.`);
}
return LibDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
@@ -116,12 +154,64 @@ export class LibDummyContract extends BaseContract {
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await LibDummyContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(
libraryArtifact.compilerOutput.evm.bytecode,
libraryAddresses,
);
if (!hexUtils.isHex(linkedLibraryBytecode)) {
throw new Error(`Bytecode for library "${libraryArtifact.contractName}" was not fully linked.`);
}
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = LibDummyContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as LibDummyContract;
@@ -129,6 +219,7 @@ export class LibDummyContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as LibDummyContract;
@@ -136,6 +227,7 @@ export class LibDummyContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as LibDummyContract;

View File

@@ -9,6 +9,7 @@ import {
BaseContract,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
@@ -25,7 +26,7 @@ import {
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
@@ -68,6 +69,43 @@ export class TestLibDummyContract extends BaseContract {
}
return TestLibDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<TestLibDummyContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await TestLibDummyContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact.compilerOutput.evm.bytecode, libraryAddresses);
if (!hexUtils.isHex(bytecode)) {
throw new Error(`Bytecode for "${artifact.contractName}" was not fully linked.`);
}
return TestLibDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
@@ -156,12 +194,64 @@ export class TestLibDummyContract extends BaseContract {
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await TestLibDummyContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(
libraryArtifact.compilerOutput.evm.bytecode,
libraryAddresses,
);
if (!hexUtils.isHex(linkedLibraryBytecode)) {
throw new Error(`Bytecode for library "${libraryArtifact.contractName}" was not fully linked.`);
}
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = TestLibDummyContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as TestLibDummyContract;
@@ -169,6 +259,7 @@ export class TestLibDummyContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as TestLibDummyContract;
@@ -176,6 +267,7 @@ export class TestLibDummyContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as TestLibDummyContract;
@@ -191,7 +283,15 @@ export class TestLibDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
@@ -209,7 +309,15 @@ export class TestLibDummyContract extends BaseContract {
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
let rawCallResult;
if (self._deployedBytecodeIfExists) {
rawCallResult = await self._evmExecAsync(this.getABIEncodedTransactionData());
} else {
rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
}
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);

View File

@@ -1,4 +1,17 @@
[
{
"version": "6.2.0",
"changes": [
{
"note": "Ignore bytecode with unlinked library references in constructor",
"pr": 2463
},
{
"note": "Add exported function `linkLibrariesInBytecode()`",
"pr": 2463
}
]
},
{
"timestamp": 1580988106,
"version": "6.1.2",

View File

@@ -30,7 +30,7 @@ import * as util from 'ethereumjs-util';
import { default as VM } from 'ethereumjs-vm';
import PStateManager from 'ethereumjs-vm/dist/state/promisified';
export { methodAbiToFunctionSignature } from './utils';
export { linkLibrariesInBytecode, methodAbiToFunctionSignature } from './utils';
import { AwaitTransactionSuccessOpts } from './types';
import { formatABIDataItem } from './utils';
@@ -77,6 +77,9 @@ export class PromiseWithTransactionHash<T> implements Promise<T> {
public catch<TResult>(onRejected?: (reason: any) => Promise<TResult>): Promise<TResult | T> {
return this._promise.catch(onRejected);
}
public finally(onFinally?: (() => void) | null): Promise<T> {
return this._promise.finally(onFinally);
}
// tslint:enable:promise-function-async
// tslint:enable:async-suffix
get [Symbol.toStringTag](): 'Promise' {
@@ -359,8 +362,17 @@ export class BaseContract {
assert.isString('contractName', contractName);
assert.isETHAddressHex('address', address);
if (deployedBytecode !== undefined && deployedBytecode !== '') {
assert.isHexString('deployedBytecode', deployedBytecode);
this._deployedBytecodeIfExists = Buffer.from(deployedBytecode.substr(2), 'hex');
// `deployedBytecode` might contain references to
// unlinked libraries and, hence, would not be a hex string. We'll just
// leave `_deployedBytecodeIfExists` empty if this is the case.
// TODO(dorothy-zbornak): We should link the `deployedBytecode`
// beforehand in the generated wrappers.
try {
assert.isHexString('deployedBytecode', deployedBytecode);
this._deployedBytecodeIfExists = Buffer.from(deployedBytecode.substr(2), 'hex');
} catch (err) {
// Do nothing.
}
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
if (callAndTxnDefaults !== undefined) {

View File

@@ -1,5 +1,5 @@
import { AbiEncoder } from '@0x/utils';
import { DataItem, MethodAbi } from 'ethereum-types';
import { DataItem, EvmBytecodeOutput, MethodAbi } from 'ethereum-types';
// tslint:disable-next-line:completed-docs
export function formatABIDataItem(abi: DataItem, value: any, formatter: (type: string, value: any) => any): any {
@@ -38,3 +38,30 @@ export function methodAbiToFunctionSignature(methodAbi: MethodAbi): string {
const method = AbiEncoder.createMethod(methodAbi.name, methodAbi.inputs);
return method.getSignature();
}
/**
* Replaces unliked library references in bytecode with real addresses
* and returns the bytecode.
*/
export function linkLibrariesInBytecode(
bytecodeArtifact: EvmBytecodeOutput,
libraryAddresses: { [libraryName: string]: string },
): string {
let bytecode = bytecodeArtifact.object.substr(2);
for (const link of Object.values(bytecodeArtifact.linkReferences)) {
for (const [libraryName, libraryRefs] of Object.entries(link)) {
const libraryAddress = libraryAddresses[libraryName];
if (!libraryAddress) {
continue;
}
for (const ref of libraryRefs) {
bytecode = [
bytecode.substring(0, ref.start * 2),
libraryAddress.toLowerCase().substr(2),
bytecode.substring((ref.start + ref.length) * 2),
].join('');
}
}
}
return `0x${bytecode}`;
}