xf00f / web3x

Ethereum TypeScript Client Library - for perfect types and tiny builds.
211 stars 27 forks source link

Expose Contract options #79

Open ondratra opened 4 years ago

ondratra commented 4 years ago

Contracts in web3x have their abi, eth (provider), and default options declared as private, so there is no way to get to these parameters used to construct the Contract. Web3js exposes these parameters as options property. Could we get the same in web3x? https://web3js.readthedocs.io/en/v1.2.0/web3-eth-contract.html#new-contract

Real-world use case

I have a contract with ERC721 ABI "loaded in it". Since some older contracts follow the initial draft of ERC721 they miss getApproved and isApprovedForAll methods. One such example is CryptoKitties contract, which fortunately implement getApproved functionality via it's own kittyIndexToApproved.

At some point in code I want to do this:

const regularERC721Contract = new Contract<void>(eth, erc721ABI, contractAddress)

... more code here ...

// then clone contract and to append ABI for CK function `kittyIndexToApproved`
const extraAbi: ContractEntryDefinition[] = [{
    constant: true,
    inputs: [
        {
            'name': '',
            'type': 'uint256'
        }
    ],
    name: 'kittyIndexToApproved',
    outputs: [
        {
            'name': '',
            'type': 'address'
        }
    ],
    payable: false,
    stateMutability: 'view' as 'view',
    type: 'function' as 'function'
}]

const oldAbi = []
    .concat((contract as any).contractAbi.functions)
    .concat((contract as any).contractAbi.events)
    .concat((contract as any).contractAbi.ctor)
    .map(item => item.entry)
const newAbi: ContractAbi = new ContractAbi([
    ...oldAbi,
    ...abiToAppend,
])

const ckContract = new Contract<void>((contract as any).eth, newAbi, contract.address, (contract as any).defaultOptions)

Because of missing options property, I have to hack my way to Contract's private properties (via casting contract to any) that may be changed in the future.

With options feature code will be reduced to:

const regularERC721Contract = new Contract<void>(eth, erc721ABI, contractAddress)
const extraAbi: ContractEntryDefinition[] = [{
    ...
}]
const newAbi: ContractAbi = new ContractAbi([
    ...regularERC721Contract.options.jsonInterface,
    ...extraAbi
])
ondratra commented 4 years ago

Hello @xf00f, I've noticed that you haven't been active on GitHub for some time. Can you accept this PR when you got a moment, please?

xf00f commented 4 years ago

Hello. I'm not sure about this solution. If the contract definition has changed, and the ABI being provided to the code generator is out of date, should the solution not be to update the ABI being fed to the generator?

I'm not sure what your ABI source is, but you can provide files as inputs rather than etherscan. So you could modify the actual ABI json which you store in your repo to have the new function in it...

I'm unsure as to why there isn't an up-to-date ABI out there with this kittyIndexToApproved defined however?

ondratra commented 4 years ago

I'm unsure as to why there isn't an up-to-date ABI out there with this kittyIndexToApproved defined however?

CryptoKitties is very old contract (one of the ERC721 pioneers) that cannot be updated. Original ERC721 draft doesn't standardize getApproved() function (https://github.com/ethereum/EIPs/issues/721). That's why I need to create exceptions in code that usually works with generic ERC721. Btw: there can be other contracts than CryptoKitties following the original draft.

I'm not sure what your ABI source is, but you can provide files as inputs rather than etherscan. So you could modify the actual ABI json which you store in your repo to have the new function in it...

I don't think CryptoKitties has open source repository on GitHub -> that's why I send link to Etherscan where anybody can see getApproved() is missing in the contract.

The problem I am trying to solve is this: at some point in code you have access only to particular Contract instance and Contract factory (for example (abi: any, address: Address): Contract).

should the solution not be to update the ABI being fed to the generator?

That creates a need to have access to original ABI Contract was created with but it is redudant since the Contract have all the information already (but private now).

With proposed changes, you can take the existing Contract and add features to it. Modular code where each module can discover some supported irregularity in the standardized smart contract will benefit from it. I think that's why web3js project have it too.