ethers-io / ethers.js

Complete Ethereum library and wallet implementation in JavaScript.
https://ethers.org/
MIT License
7.89k stars 1.82k forks source link

Unable to deploy vyper contract with decimal type #458

Open desimira opened 5 years ago

desimira commented 5 years ago

Hi Richard! I am trying to deploy a vyper contract with ethersjs. Unfortunately it throws an error when contract contains some types that are not supported in Solidity, e.g. decimal. Here is the error:

{ Error: invalid type (arg="type", value="fixed168x10", version=4.0.23)
    at Object.throwError (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/errors.js:76:17)
    at getParamCoder (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/utils/abi-coder.js:906:12)
    at formatParamType (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/utils/abi-coder.js:292:12)
    at /Users/desimiramitkova/Projects/etherlime/node_modules/ethers/utils/abi-coder.js:297:76
    at Array.map (<anonymous>)
    at Object.formatSignature (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/utils/abi-coder.js:297:50)
    at Interface.addMethod (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/utils/interface.js:244:41)
    at Array.forEach (<anonymous>)
    at new Interface (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/utils/interface.js:329:14)
    at new ContractFactory (/Users/desimiramitkova/Projects/etherlime/node_modules/ethers/contract.js:633:60)
    at EtherlimeGanacheDeployer._prepareDeployTransaction (/Users/desimiramitkova/Projects/etherlime/deployer/deployer.js:117:17)
    at EtherlimeGanacheDeployer.deploy (/Users/desimiramitkova/Projects/etherlime/deployer/deployer.js:71:38)
  reason: 'invalid type',
  code: 'INVALID_ARGUMENT',
  arg: 'type',
  value: 'fixed168x10' }

Here is the contract's ABI:

"abi": [
        {
            "name": "sqrt_decimal",
            "outputs": [
                {
                    "type": "uint256",
                    "name": "out"
                }
            ],
            "inputs": [
                {
                    "type": "fixed168x10",
                    "name": "num"
                },
                {
                    "type": "int128",
                    "name": "fractionLength"
                }
            ],
            "constant": true,
            "payable": false,
            "type": "function",
            "gas": 994592
        },
        {
            "name": "sqrt_uint256",
            "outputs": [
                {
                    "type": "uint256",
                    "name": "out"
                }
            ],
            "inputs": [
                {
                    "type": "uint256",
                    "name": "num"
                },
                {
                    "type": "int128",
                    "name": "fractionLength"
                }
            ],
            "constant": true,
            "payable": false,
            "type": "function",
            "gas": 994666
        }
    ]

And here is the contract itself:

@private
@constant
def recalculate_root(root: decimal) -> decimal:

    lastRootDigit: decimal = root % 10.0
    rootWithoutLastDigit: uint256 = convert(root / 10.0, uint256)

    return convert(rootWithoutLastDigit, decimal) * 100.0 + lastRootDigit

@private
@constant
def normalize(num: decimal) -> (decimal, int128):

    normalizationSteps: int128 = 0
    rangedNumber: decimal = num

    for i in range(256):
        if rangedNumber <= 100.0:
            normalizationSteps = i
            break

        rangedNumber /= 100.0

    return rangedNumber, normalizationSteps

# High fraction precision sqrt
# Advantages:
#   Vyper decimals are rounded at 10 fraction symbol, and this is a problem when working with Tokens calculations
#   This algorithm provides you a fraction containing as many symbols as you want
#   It is useful for Tokens Calculations

@private
@constant
def sqrt_hfp(num: decimal, fractionLength: int128) -> uint256:

    # normalizedNumber => 0 < normalizedNumber < 100
    normalizedNumber: decimal = 0.0
    normalizationSteps: int128 = 0

    (normalizedNumber, normalizationSteps) = self.normalize(num)

    normalizationSteps += fractionLength + 1

    root: decimal = 5.0
    rootCalculation: decimal = normalizedNumber * root

    fractionLengthCounter: int128 = 0

    for i in range(256):

        if fractionLengthCounter == normalizationSteps:
            break

        if rootCalculation >= root:
            rootCalculation = rootCalculation - root
            root += 10.0
        else:
            rootCalculation *= 100.0
            root = self.recalculate_root(root)
            fractionLengthCounter += 1

    return convert(root / 100.0, uint256)

# This function could be used only in Vyper contracts because of decimal input
@public
@constant
def sqrt_decimal(num: decimal, fractionLength: int128) -> uint256:
    return self.sqrt_hfp(num, fractionLength)

# This function could be used from Vyper and Solidity contracts
@public
@constant
def sqrt_uint256(num: uint256, fractionLength: int128) -> uint256:
    return self.sqrt_hfp(convert(num, decimal), fractionLength)

Is there a chance ethersjs to not throw error when ABI includes untypical types or to add them as a valid type?

ricmoo commented 5 years ago

Oh! So, v4 does not support Fixed-Point math, but I have added in in v5, but haven't had any test cases, since Solidity does not support them. Are they fully supported in the Vyper? If so, I can add the test cases and enable Fixed fully in v5, which will be going into a public beta soon.

desimira commented 5 years ago

“Yes they are supported in Vyper. Is there any way to enable them in v4?”

ricmoo commented 5 years ago

No, not in v4. The fixed and ufixed type are not supported at all. In v5 they will be, and I will use vyper to add test cases. It should be available soon for public beta.

Thanks! :)

desimira commented 5 years ago

Great! Thanks! :)

vykintasmak commented 2 years ago

It doesn't seem to work in v5 as well, at least i get this:

index.js?ffb2:185 Uncaught (in promise) Error: invalid type (argument="type", value="fixed168x10", code=INVALID_ARGUMENT, version=abi/5.5.0)
    at Logger.makeError (index.js?ffb2:185)
    at Logger.throwError (index.js?ffb2:194)
    at Logger.throwArgumentError (index.js?ffb2:197)
    at AbiCoder._getCoder (abi-coder.js?5791:63)
    at eval (abi-coder.js?5791:84)
    at Array.map (<anonymous>)
    at AbiCoder.encode (abi-coder.js?5791:84)
    at Interface._encodeParams (interface.js?a807:254)
    at Interface.encodeFunctionData (interface.js?a807:296)
    at eval (index.js?f179:124)
    at Generator.next (<anonymous>)
    at fulfilled (index.js?f179:5)

Is it expected to be in some future release or is it on hold?