f-o-a-m / purescript-web3

a purescript library for the web3 api
Apache License 2.0
127 stars 24 forks source link

Generate types from open-rpc spec #158

Closed srghma closed 1 year ago

srghma commented 2 years ago

just an idea for future

as per https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/eth1.0-apis/assembled-spec/openrpc.json

the Block from eth_getBlockByHash should be not

newtype Block
  = Block
  { difficulty :: BigNumber
  , extraData :: HexString
  , gasLimit :: BigNumber
  , gasUsed :: BigNumber
  , hash :: Maybe HexString
  , logsBloom :: Maybe HexString
  , miner :: HexString
  , nonce :: Maybe HexString
  , number :: Maybe BigNumber
  , parentHash :: HexString
  , receiptsRoot :: HexString
  , sha3Uncles :: HexString
  , size :: BigNumber
  , stateRoot :: HexString
  , timestamp :: BigNumber
  , totalDifficulty :: BigNumber
  , transactions :: Array HexString
  , transactionsRoot :: HexString
  , uncles :: Array HexString
  }

but

-- only lowercase
newtype BigNumberFromLowercaseString = ...

-- only lowercase, first number is 1 OR 0x0
newtype BigNumberFromLowercaseWithLeading1 = ...

newtype Block transactionType
  = Block
  { baseFeePerGas :: Maybe BigNumberFromLowercaseString
  , difficulty :: Maybe BigNumberFromLowercaseString
  , extraData :: HexStringOnlyLowercaseAnyLenght
  , gasLimit :: BigNumberFromLowercaseWithLeading1 
  , gasUsed :: BigNumberFromLowercaseWithLeading1
  , mixHash :: HexStringOnlyLowercaseWithLenght 64
  , logsBloom :: Maybe (HexStringOnlyLowercaseWithLenght 64)
  , miner :: HexStringAnyCaseWithLenght 40
  , nonce :: HexString -- FromOnlyLowercaseString
  , number :: BigNumberFromLowercaseWithLeading1
  , parentHash :: HexStringOnlyLowercaseWithLenght 64
  , receiptsRoot :: HexStringOnlyLowercaseWithLenght 64
  , sha3Uncles :: HexStringOnlyLowercaseWithLenght 64
  , size :: BigNumberFromLowercaseWithLeading1
  , stateRoot :: HexStringOnlyLowercaseWithLenght 64
  , timestamp :: BigNumberFromLowercaseWithLeading1
  , totalDifficulty :: BigNumberFromLowercaseWithLeading1
  , transactions :: Array transactionType
  , transactionsRoot :: HexStringOnlyLowercaseWithLenght 64
  , uncles :: Array (HexStringOnlyLowercaseWithLenght 64)
  }

type BlockWithoutHydratedTransactions = Block (HexStringOnlyLowercaseWithLenght 64)
type BlockWithHydratedTransactions = Block Transaction
        {
            "name": "eth_getBlockByHash",
            "summary": "Returns information about a block by hash.",
            "params": [
                {
                    "name": "Block hash",
                    "required": true,
                    "schema": {
                        "title": "32 byte hex value",
                        "type": "string",
                        "pattern": "^0x[0-9a-f]{64}$"
                    }
                },
                {
                    "name": "Hydrated transactions",
                    "required": true,
                    "schema": {
                        "title": "hydrated",
                        "type": "boolean"
                    }
                }
            ],
            "result": {
                "name": "Block information",
                "schema": {
                    "title": "Block object",
                    "type": "object",
                    "required": [
                        "parentHash",
                        "sha3Uncles",
                        "miner",
                        "stateRoot",
                        "transactionsRoot",
                        "receiptsRoot",
                        "logsBloom",
                        "totalDifficulty",
                        "number",
                        "gasLimit",
                        "gasUsed",
                        "timestamp",
                        "extraData",
                        "mixHash",
                        "nonce",
                        "size",
                        "transactions",
                        "uncles"
                    ],
                    "properties": {
                        "parentHash": {
                            "title": "Parent block hash",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{64}$"
                        },
                        "sha3Uncles": {
                            "title": "Ommers hash",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{64}$"
                        },
                        "miner": {
                            "title": "Coinbase",
                            "type": "string",
                            "pattern": "^0x[0-9,a-f,A-F]{40}$"
                        },
                        "stateRoot": {
                            "title": "State root",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{64}$"
                        },
                        "transactionsRoot": {
                            "title": "Transactions root",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{64}$"
                        },
                        "receiptsRoot": {
                            "title": "Receipts root",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{64}$"
                        },
                        "logsBloom": {
                            "title": "Bloom filter",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{512}$"
                        },
                        "difficulty": {
                            "title": "Difficulty",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]*$"
                        },
                        "number": {
                            "title": "Number",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "gasLimit": {
                            "title": "Gas limit",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "gasUsed": {
                            "title": "Gas used",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "timestamp": {
                            "title": "Timestamp",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "extraData": {
                            "title": "Extra data",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]*$"
                        },
                        "mixHash": {
                            "title": "Mix hash",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]{64}$"
                        },
                        "nonce": {
                            "title": "Nonce",
                            "type": "string",
                            "pattern": "^0x[0-9a-f]*$"
                        },
                        "totalDifficulty": {
                            "title": "Total difficult",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "baseFeePerGas": {
                            "title": "Base fee per gas",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "size": {
                            "title": "Block size",
                            "type": "string",
                            "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                        },
                        "transactions": {
                            "oneOf": [
                                {
                                    "title": "Transaction hashes",
                                    "type": "array",
                                    "items": {
                                        "title": "32 byte hex value",
                                        "type": "string",
                                        "pattern": "^0x[0-9a-f]{64}$"
                                    }
                                },
                                {
                                    "title": "Full transactions",
                                    "type": "array",
                                    "items": {
                                        "oneOf": [
                                            {
                                                "title": "Signed 1559 Transaction",
                                                "type": "object",
                                                "required": [
                                                    "accessList",
                                                    "chainId",
                                                    "gas",
                                                    "input",
                                                    "maxFeePerGas",
                                                    "maxPriorityFeePerGas",
                                                    "nonce",
                                                    "r",
                                                    "s",
                                                    "type",
                                                    "value",
                                                    "yParity"
                                                ],
                                                "properties": {
                                                    "type": {
                                                        "title": "type",
                                                        "type": "string",
                                                        "pattern": "^0x([0-9,a-f,A-F]?){1,2}$"
                                                    },
                                                    "nonce": {
                                                        "title": "nonce",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "to": {
                                                        "title": "to address",
                                                        "type": "string",
                                                        "pattern": "^0x[0-9,a-f,A-F]{40}$"
                                                    },
                                                    "gas": {
                                                        "title": "gas limit",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "value": {
                                                        "title": "value",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "input": {
                                                        "title": "input data",
                                                        "type": "string",
                                                        "pattern": "^0x[0-9a-f]*$"
                                                    },
                                                    "maxPriorityFeePerGas": {
                                                        "title": "max priority fee per gas",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "Maximum fee per gas the sender is willing to pay to miners in wei"
                                                    },
                                                    "maxFeePerGas": {
                                                        "title": "max fee per gas",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei"
                                                    },
                                                    "accessList": {
                                                        "title": "accessList",
                                                        "type": "array",
                                                        "description": "EIP-2930 access list",
                                                        "items": {
                                                            "title": "Access list entry",
                                                            "type": "object",
                                                            "properties": {
                                                                "address": {
                                                                    "title": "hex encoded address",
                                                                    "type": "string",
                                                                    "pattern": "^0x[0-9,a-f,A-F]{40}$"
                                                                },
                                                                "storageKeys": {
                                                                    "type": "array",
                                                                    "items": {
                                                                        "title": "32 byte hex value",
                                                                        "type": "string",
                                                                        "pattern": "^0x[0-9a-f]{64}$"
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    },
                                                    "chainId": {
                                                        "title": "chainId",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "Chain ID that this transaction is valid on."
                                                    },
                                                    "yParity": {
                                                        "title": "yParity",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
                                                    },
                                                    "r": {
                                                        "title": "r",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "s": {
                                                        "title": "s",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    }
                                                }
                                            },
                                            {
                                                "title": "Signed 2930 Transaction",
                                                "type": "object",
                                                "required": [
                                                    "accessList",
                                                    "chainId",
                                                    "gas",
                                                    "gasPrice",
                                                    "input",
                                                    "nonce",
                                                    "r",
                                                    "s",
                                                    "type",
                                                    "value",
                                                    "yParity"
                                                ],
                                                "properties": {
                                                    "type": {
                                                        "title": "type",
                                                        "type": "string",
                                                        "pattern": "^0x([0-9,a-f,A-F]?){1,2}$"
                                                    },
                                                    "nonce": {
                                                        "title": "nonce",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "to": {
                                                        "title": "to address",
                                                        "type": "string",
                                                        "pattern": "^0x[0-9,a-f,A-F]{40}$"
                                                    },
                                                    "gas": {
                                                        "title": "gas limit",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "value": {
                                                        "title": "value",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "input": {
                                                        "title": "input data",
                                                        "type": "string",
                                                        "pattern": "^0x[0-9a-f]*$"
                                                    },
                                                    "gasPrice": {
                                                        "title": "gas price",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "The gas price willing to be paid by the sender in wei"
                                                    },
                                                    "accessList": {
                                                        "title": "accessList",
                                                        "type": "array",
                                                        "description": "EIP-2930 access list",
                                                        "items": {
                                                            "title": "Access list entry",
                                                            "type": "object",
                                                            "properties": {
                                                                "address": {
                                                                    "title": "hex encoded address",
                                                                    "type": "string",
                                                                    "pattern": "^0x[0-9,a-f,A-F]{40}$"
                                                                },
                                                                "storageKeys": {
                                                                    "type": "array",
                                                                    "items": {
                                                                        "title": "32 byte hex value",
                                                                        "type": "string",
                                                                        "pattern": "^0x[0-9a-f]{64}$"
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    },
                                                    "chainId": {
                                                        "title": "chainId",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "Chain ID that this transaction is valid on."
                                                    },
                                                    "yParity": {
                                                        "title": "yParity",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature."
                                                    },
                                                    "r": {
                                                        "title": "r",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "s": {
                                                        "title": "s",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    }
                                                }
                                            },
                                            {
                                                "title": "Signed Legacy Transaction",
                                                "type": "object",
                                                "required": [
                                                    "gas",
                                                    "gasPrice",
                                                    "input",
                                                    "nonce",
                                                    "r",
                                                    "s",
                                                    "type",
                                                    "v",
                                                    "value"
                                                ],
                                                "properties": {
                                                    "type": {
                                                        "title": "type",
                                                        "type": "string",
                                                        "pattern": "^0x([0-9,a-f,A-F]?){1,2}$"
                                                    },
                                                    "nonce": {
                                                        "title": "nonce",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "to": {
                                                        "title": "to address",
                                                        "type": "string",
                                                        "pattern": "^0x[0-9,a-f,A-F]{40}$"
                                                    },
                                                    "gas": {
                                                        "title": "gas limit",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "value": {
                                                        "title": "value",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "input": {
                                                        "title": "input data",
                                                        "type": "string",
                                                        "pattern": "^0x[0-9a-f]*$"
                                                    },
                                                    "gasPrice": {
                                                        "title": "gas price",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "The gas price willing to be paid by the sender in wei"
                                                    },
                                                    "chainId": {
                                                        "title": "chainId",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$",
                                                        "description": "Chain ID that this transaction is valid on."
                                                    },
                                                    "v": {
                                                        "title": "v",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "r": {
                                                        "title": "r",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    },
                                                    "s": {
                                                        "title": "s",
                                                        "type": "string",
                                                        "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$"
                                                    }
                                                }
                                            }
                                        ]
                                    }
                                }
                            ]
                        },
                        "uncles": {
                            "title": "Uncles",
                            "type": "array",
                            "items": {
                                "title": "32 byte hex value",
                                "type": "string",
                                "pattern": "^0x[0-9a-f]{64}$"
                            }
                        }
                    }
                }
            }
srghma commented 2 years ago

https://github.com/ethereum/execution-apis/blob/main/src/schemas/base-types.json

martyall commented 2 years ago

My opinion is that the types as they stand now are way simpler to maintain than something like this — types like BigNumberFromLowercaseString are unnecessarily specific, BigNumber is fine as it is. What is the real advantage to introducing this other than potentially debugging what is already there, which we currently have no evidence is wrong?

martyall commented 2 years ago

Also how would you actually generate valid purescript types/modules

srghma commented 2 years ago

Also how would you actually generate valid purescript types/modules

here is a better proposal

from https://github.com/ethereum/execution-apis/blob/596ad145d728f2db900e5d730c55f4b3c9df4dc2/src/schemas/base-types.json#L2-L6

module EthereumExecutionApi.Address where

-- hex encoded address
newtype Address = Address String

addressRegex = unsafeRegex "^0x[0-9,a-f,A-F]{40}$" noFlags

mkAddress :: String -> Maybe Address
mkAddress string =
  if test addressRegex string
  then Just $ Address string
  else Nothing

-- or to use https://github.com/garyb/purescript-codec-argonaut
-- which is actually better, though one have to write codecs by hand (no deriving)
instance DecodeJson Address
  ...

-- or
codecAddress :: JsonCodec Address
codecAddress = ...

from https://github.com/ethereum/execution-apis/blob/main/src/schemas/block.json#L2-L33

module EthereumExecutionApi.Block where

-- Block object

-- NOTE: "oneOf" becomes a parameter to Block
type Block transactions =
  { ....
  , transactions :: transactions
  , miner :: Address
  , uncles :: Array Hash32
  }

blockCodec :: forall . JsonCodec transactions -> JsonCodec (Block transactions)
blockCodec transactionsCodec = ...

What is the real advantage to introducing this other than potentially debugging what is already there

just an idea