From b5ac97c06f561b3f8f66ad863054cc5719eb820c Mon Sep 17 00:00:00 2001 From: dmarzzz Date: Tue, 3 Oct 2023 18:14:34 +0200 Subject: [PATCH] initial formatter and rigil chain config --- src/chains/definitions/suaveRigil.ts | 36 +++++++++ src/chains/index.ts | 1 + src/chains/suave/formatters.test.ts | 104 ++++++++++++++++++++++++ src/chains/suave/formatters.ts | 115 +++++++++++++++++++++++++++ src/chains/suave/parsers.test.ts | 96 ++++++++++++++++++++++ src/chains/suave/parsers.ts | 95 ++++++++++++++++++++++ src/chains/suave/serializers.ts | 63 +++++++++++++++ src/chains/suave/types.ts | 110 +++++++++++++++++++++++++ 8 files changed, 620 insertions(+) create mode 100644 src/chains/definitions/suaveRigil.ts create mode 100644 src/chains/suave/formatters.test.ts create mode 100644 src/chains/suave/formatters.ts create mode 100644 src/chains/suave/parsers.test.ts create mode 100644 src/chains/suave/parsers.ts create mode 100644 src/chains/suave/serializers.ts create mode 100644 src/chains/suave/types.ts diff --git a/src/chains/definitions/suaveRigil.ts b/src/chains/definitions/suaveRigil.ts new file mode 100644 index 00000000..85fc4f14 --- /dev/null +++ b/src/chains/definitions/suaveRigil.ts @@ -0,0 +1,36 @@ +import { defineChain } from '../../utils/chain.js' +import { formattersSuave } from '../suave/formatters.js' + +export const suaveRigil = /*#__PURE__*/ defineChain( + { + id: 424242, + name: 'Suave Rigil Testnet', + network: 'rigil-testnet', + nativeCurrency: { + decimals: 18, + name: 'Suave Goerli', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://testnet.rpc.flashbots.net'], + webSocket: ['wss://testnet.rpc.flashbots.net'], + }, + public: { + http: ['https://testnet.rpc.flashbots.net'], + webSocket: ['wss://testnet.rpc.flashbots.net'], + }, + }, + blockExplorers: { + default: { + name: 'Explorer', + url: 'https://testnet.explorer.flashbots.net', + }, + }, + contracts: {}, + testnet: true, + }, + { + formatters: formattersSuave, + }, +) diff --git a/src/chains/index.ts b/src/chains/index.ts index a81828c2..4570e384 100644 --- a/src/chains/index.ts +++ b/src/chains/index.ts @@ -124,6 +124,7 @@ export { skaleTitanTestnet } from './definitions/skale/titanTestnet.js' export { songbird } from './definitions/songbird.js' export { songbirdTestnet } from './definitions/songbirdTestnet.js' export { shardeumSphinx } from './definitions/shardeumSphinx.js' +export { suaveRigil } from './definitions/suaveRigil.js' export { syscoin } from './definitions/syscoin.js' export { syscoinTestnet } from './definitions/syscoinTestnet.js' export { taraxa } from './definitions/taraxa.js' diff --git a/src/chains/suave/formatters.test.ts b/src/chains/suave/formatters.test.ts new file mode 100644 index 00000000..5ce6c902 --- /dev/null +++ b/src/chains/suave/formatters.test.ts @@ -0,0 +1,104 @@ +// import { describe, expect, test } from 'vitest' + +// // Assuming you have similar actions for the Suave chain like the Celo ones provided. +// import { getBlock } from '../../actions/public/getBlock.js' +// import { getTransaction } from '../../actions/public/getTransaction.js' +// import { getTransactionReceipt } from '../../actions/public/getTransactionReceipt.js' + +// import { suaveRigil } from '../index.js' + +// describe('block', () => { +// test('formatter', () => { +// const { block } = suaveRigil.formatters! + +// const formattedBlock = block.format({ +// randomness: 'sampleRandomValue', +// transactions: [ +// { +// ExecutionNode: 'sampleExecutionNode', +// ConfidentialComputeRequest: 'sampleRequest', +// ConfidentialComputeResult: 'sampleResult', +// // ... other RpcTransaction fields if present +// }, +// ], +// }) + +// expect(formattedBlock).toMatchInlineSnapshot(` +// { +// "randomness": "sampleRandomValue", +// "transactions": [ +// { +// "ExecutionNode": "sampleExecutionNode", +// "ConfidentialComputeRequest": "sampleRequest", +// "ConfidentialComputeResult": "sampleResult", +// // ... Other expected fields here +// } +// ] +// } +// `) +// }) +// }) + +// describe('transaction', () => { +// test('formatter', () => { +// const { transaction } = suaveRigil.formatters! + +// const inputTransaction = { +// ExecutionNode: 'sampleExecutionNode', +// ConfidentialComputeRequest: 'sampleRequest', +// ConfidentialComputeResult: 'sampleResult', +// // ... other fields if present +// } + +// const formattedTransaction = transaction.format(inputTransaction) + +// expect(formattedTransaction).toMatchInlineSnapshot(` +// { +// "ExecutionNode": "sampleExecutionNode", +// "ConfidentialComputeRequest": "sampleRequest", +// "ConfidentialComputeResult": "sampleResult", +// // ... Other expected fields here +// } +// `) +// }) +// }) + +// describe('transactionReceipt', () => { +// test('formatter', () => { +// const { transactionReceipt } = suaveRigil.formatters! + +// const inputReceipt = { +// // ... input fields based on SuaveRpcTransactionReceiptOverrides +// } + +// const formattedReceipt = transactionReceipt.format(inputReceipt) + +// expect(formattedReceipt).toMatchInlineSnapshot(` +// { +// // ... Expected fields here based on the SuaveRpcTransactionReceiptOverrides format +// } +// `) +// }) +// }) + +// describe('transactionRequest', () => { +// test('formatter', () => { +// const { transactionRequest } = suaveRigil.formatters! + +// const inputRequest = { +// ExecutionNode: 'sampleExecutionNode', +// ConfidentialComputeRequest: 'sampleRequest', +// // ... other fields if present +// } + +// const formattedRequest = transactionRequest.format(inputRequest) + +// expect(formattedRequest).toMatchInlineSnapshot(` +// { +// "ExecutionNode": "sampleExecutionNode", +// "ConfidentialComputeRequest": "sampleRequest", +// // ... Other expected fields here +// } +// `) +// }) +// }) diff --git a/src/chains/suave/formatters.ts b/src/chains/suave/formatters.ts new file mode 100644 index 00000000..01dd69ed --- /dev/null +++ b/src/chains/suave/formatters.ts @@ -0,0 +1,115 @@ +import { type ChainFormatters } from '../../types/chain.js' +import type { Hash } from '../../types/misc.js' +import type { RpcTransaction } from '../../types/rpc.js' +import { defineBlock } from '../../utils/formatters/block.js' +import { + defineTransaction, + formatTransaction, +} from '../../utils/formatters/transaction.js' +import { defineTransactionReceipt } from '../../utils/formatters/transactionReceipt.js' +import { defineTransactionRequest } from '../../utils/formatters/transactionRequest.js' + +// Introduce the new types +export type ConfidentialComputeRequest = { + ExecutionNode: string // Assuming address is a string type + Wrapped: RpcTransaction // This might need to be adjusted to the actual Ethereum Transaction type +} + +export type SuaveTransaction = { + ExecutionNode: string + ConfidentialComputeRequest: ConfidentialComputeRequest + ConfidentialComputeResult: string // Assuming bytes are represented as hexadecimal strings + // TODO: signature fields +} + +import type { + SuaveBlockOverrides, + SuaveRpcTransaction, + SuaveRpcTransactionRequest, + SuaveTransactionReceipt, + SuaveTransactionReceiptOverrides, + SuaveTransactionRequest, +} from './types.js' + +export const formattersSuave = { + block: /*#__PURE__*/ defineBlock({ + exclude: ['difficulty', 'gasLimit', 'mixHash', 'nonce', 'uncles'], + format( + args: SuaveBlockOverrides & { + transactions: Hash[] | SuaveRpcTransaction[] + }, + ): SuaveBlockOverrides & { + transactions: Hash[] | SuaveTransaction[] + } { + const transactions = args.transactions?.map((transaction) => { + if (typeof transaction === 'string') return transaction + return { + ...formatTransaction(transaction as RpcTransaction), + ExecutionNode: transaction.ExecutionNode, + ConfidentialComputeRequest: { + ExecutionNode: transaction.ExecutionNode, + Wrapped: transaction as RpcTransaction, + }, + ConfidentialComputeResult: transaction.ConfidentialComputeResult, + // TODO : Signature fields + } + }) as Hash[] | SuaveTransaction[] + return { + transactions, + } + }, + }), + transaction: /*#__PURE__*/ defineTransaction({ + format(args: SuaveRpcTransaction): SuaveTransaction { + if (args.IsConfidential) { + return { + ExecutionNode: args.ExecutionNode, + ConfidentialComputeRequest: { + ExecutionNode: args.ExecutionNode, + Wrapped: args.ConfidentialComputeRequest, // This assumes that args.ConfidentialComputeRequest is of type Transaction + }, + ConfidentialComputeResult: args.ConfidentialComputeResult, + // TODO : Signature fields + } as SuaveTransaction + } else { + return args as any // TODO : Handle as regular Ethereum transaction + } + }, + }), + transactionReceipt: /*#__PURE__*/ defineTransactionReceipt({ + format(args: SuaveTransactionReceiptOverrides): SuaveTransactionReceipt { + const { + ExecutionNode, + ConfidentialComputeRequest, + ConfidentialComputeResult, + ...baseProps + } = args + + return { + ...baseProps, + ExecutionNode, + ConfidentialComputeRequest: { + ...ConfidentialComputeRequest, + }, + ConfidentialComputeResult, + // signature fields + } as SuaveTransactionReceipt + }, + }), + + transactionRequest: /*#__PURE__*/ defineTransactionRequest({ + format(args: SuaveTransactionRequest): SuaveRpcTransactionRequest { + if (args.IsConfidential) { + const { ExecutionNode, IsConfidential } = args + return { + ...args, // Include other properties from args + ExecutionNode: ExecutionNode, + IsConfidential: IsConfidential, + // We omit the ConfidentialComputeRequest here + } as SuaveRpcTransactionRequest + } else { + return args as any // TODO : Handle as regular Ethereum transaction + } + }, + }), +} as const satisfies ChainFormatters diff --git a/src/chains/suave/parsers.test.ts b/src/chains/suave/parsers.test.ts new file mode 100644 index 00000000..aca3cea4 --- /dev/null +++ b/src/chains/suave/parsers.test.ts @@ -0,0 +1,96 @@ +// import { expect, test } from 'vitest' + +// import { accounts } from '~test/src/constants.js' +// import { +// parseEther, +// parseTransaction as parseTransaction_, +// serializeTransaction, +// toRlp, +// } from '../../index.js' +// import { parseTransactionSuave } from './parsers.js' +// import { serializeTransactionSuave } from './serializers.js' +// import type { TransactionSerializableSuave } from './types.js' + +// test('should be able to parse a standard Suave transaction', () => { +// const signedTransaction = /* Sample Suave signed transaction */; + +// expect(parseTransactionSuave(signedTransaction)).toMatchInlineSnapshot(` +// { +// "chainId": /* Some chain ID */, +// "gas": /* Some gas amount */, +// "to": /* Some address */, +// "value": /* Some value */, +// "ExecutionNode": /* Execution Node value */, +// "ConfidentialComputeRequest": /* Compute Request value */, +// "ConfidentialComputeResult": /* Compute Result value */ +// } +// `) +// }) + +// test('should parse a Suave transaction with data', () => { +// const transactionWithData = { +// ...transaction, +// data: '0x1234', // Example data for this test +// } + +// const serialized = serializeTransactionSuave(transactionWithData) + +// expect(parseTransactionSuave(serialized)).toMatchInlineSnapshot(` +// { +// ...otherTransactionDetails, +// "data": "0x1234" +// } +// `) +// }) + +// test('should parse a Suave transaction with Execution Node', () => { +// const transactionWithNode = { +// ...transaction, +// ExecutionNode: accounts[1].address, // Example address +// } + +// const serialized = serializeTransactionSuave(transactionWithNode) + +// expect(parseTransactionSuave(serialized)).toMatchInlineSnapshot(` +// { +// ...otherTransactionDetails, +// "ExecutionNode": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8" +// } +// `) +// }) + +// test('invalid transaction (all missing)', () => { +// expect(() => +// parseTransactionSuave(`0xYourPrefix${toRlp([]).slice(2)}`), +// ).toThrowErrorMatchingInlineSnapshot(` +// "Invalid serialized transaction of type \\"suave\\" was provided. + +// Serialized Transaction: \\"YourSerializedTransaction\\" +// Missing Attributes: /* List of missing attributes */ + +// Version: viem@YourVersion" +// `) +// }) + +// test('invalid transaction (missing ConfidentialComputeRequest)', () => { +// const transactionMissingCompute = { +// ...transaction, +// ConfidentialComputeRequest: undefined, +// } + +// const serialized = serializeTransactionSuave(transactionMissingCompute) + +// expect(() => +// parseTransactionSuave(serialized), +// ).toThrowErrorMatchingInlineSnapshot(` +// "Invalid serialized transaction of type \\"suave\\" was provided. + +// Serialized Transaction: \\"YourSerializedTransaction\\" +// Missing Attributes: ConfidentialComputeRequest + +// Version: viem@YourVersion" +// `) +// }) + +// // ... Additional tests specific to your needs ... + diff --git a/src/chains/suave/parsers.ts b/src/chains/suave/parsers.ts new file mode 100644 index 00000000..25b9c03d --- /dev/null +++ b/src/chains/suave/parsers.ts @@ -0,0 +1,95 @@ +// import { InvalidSerializedTransactionError } from '../../errors/transaction.js' +// import type { Hex } from '../../types/misc.js' +// import { isHex } from '../../utils/data/isHex.js' +// import { sliceHex } from '../../utils/data/slice.js' +// import { hexToBigInt, hexToNumber } from '../../utils/encoding/fromHex.js' +// import type { RecursiveArray } from '../../utils/encoding/toRlp.js' +// import type { GetSerializedTransactionType } from '../../utils/transaction/getSerializedTransactionType.js' +// import { +// type ParseTransactionReturnType, +// parseAccessList, +// parseTransaction, +// toTransactionArray, +// } from '../../utils/transaction/parseTransaction.js' +// import { assertTransactionSuave } from './serializers.js' +// import type { +// SuaveTransactionSerialized, +// SuaveTransactionType, +// TransactionSerializableSuave, +// } from './types.js' + +// export type ParseTransactionSuaveReturnType< +// TSerialized extends SuaveTransactionSerialized = SuaveTransactionSerialized, +// TType extends SuaveTransactionType = GetSerializedTransactionType, +// > = ParseTransactionReturnType + +// export function parseTransactionSuave< +// TSerialized extends SuaveTransactionSerialized, +// >( +// serializedTransaction: TSerialized, +// ): ParseTransactionSuaveReturnType { +// return parseTransaction( +// serializedTransaction, +// ) as ParseTransactionSuaveReturnType +// } + +// function parseSuaveTransaction( +// serializedTransaction: SuaveTransactionSerialized, +// ): TransactionSerializableSuave { +// const transactionArray = toTransactionArray(serializedTransaction) + +// const [ +// chainId, +// nonce, +// gas, +// to, +// value, +// data, +// ExecutionNode, +// ConfidentialComputeRequest, +// ConfidentialComputeResult, +// v, +// r, +// s, +// ] = transactionArray + +// if (transactionArray.length !== 12) { +// throw new InvalidSerializedTransactionError({ +// attributes: { +// chainId, +// nonce, +// gas, +// to, +// value, +// data, +// ExecutionNode, +// ConfidentialComputeRequest, +// ConfidentialComputeResult, +// v, +// r, +// s, +// }, +// serializedTransaction, +// type: 'suave', +// }) +// } + +// const transaction: Partial = { +// chainId: hexToNumber(chainId as Hex), +// } + +// if (isHex(to) && to !== '0x') transaction.to = to +// if (isHex(gas) && gas !== '0x') transaction.gas = hexToBigInt(gas) +// if (isHex(data) && data !== '0x') transaction.data = data +// if (isHex(nonce) && nonce !== '0x') transaction.nonce = hexToNumber(nonce) +// if (isHex(value) && value !== '0x') transaction.value = hexToBigInt(value) +// if (isHex(ExecutionNode)) transaction.ExecutionNode = ExecutionNode +// if (isHex(ConfidentialComputeRequest)) +// transaction.ConfidentialComputeRequest = ConfidentialComputeRequest +// if (isHex(ConfidentialComputeResult)) +// transaction.ConfidentialComputeResult = ConfidentialComputeResult + +// assertTransactionSuave(transaction as TransactionSerializableSuave) + +// return transaction as TransactionSerializableSuave +// } diff --git a/src/chains/suave/serializers.ts b/src/chains/suave/serializers.ts new file mode 100644 index 00000000..185ae07e --- /dev/null +++ b/src/chains/suave/serializers.ts @@ -0,0 +1,63 @@ +// // Import necessary utilities and types +// import { InvalidAddressError } from '../../errors/address.js' +// import { InvalidChainIdError } from '../../errors/chain.js' +// import type { ChainSerializers } from '../../types/chain.js' +// import type { Signature } from '../../types/misc.js' +// import { isAddress } from '../../utils/address/isAddress.js' +// import { toHex } from '../../utils/encoding/toHex.js' +// import { toRlp } from '../../utils/encoding/toRlp.js' +// import type { +// SuaveTransactionSerializable, +// TransactionSerializedSuave, +// } from './types.js' // Adjust the import path + +// // Define a type for the serialized Suave transaction +// export type TransactionSerializedSuave = string // Adjust the type definition as necessary + +// // Define a function to serialize Suave transactions +// export const serializeTransactionSuave = ( +// transaction: SuaveTransactionSerializable, +// signature?: Signature, +// ): TransactionSerializedSuave => { +// // Extract fields from the transaction +// const { +// chainId, +// nonce, +// gas, +// to, +// value, +// data, +// ExecutionNode, +// ConfidentialComputeRequest, +// ConfidentialComputeResult, +// } = transaction + +// // Serialize the transaction fields into an array +// const serializedTransaction = [ +// toHex(chainId), +// nonce ? toHex(nonce) : '0x', +// gas ? toHex(gas) : '0x', +// to ?? '0x', +// value ? toHex(value) : '0x', +// data ?? '0x', +// ExecutionNode ?? '0x', +// // ... serialize ConfidentialComputeRequest and ConfidentialComputeResult +// ] + +// // Append the signature to the serialized transaction if provided +// if (signature) { +// serializedTransaction.push( +// signature.v === 27n ? '0x' : toHex(1), // yParity +// toHex(signature.r), +// toHex(signature.s), +// ) +// } + +// // Concatenate the serialized transaction array into a single string using RLP encoding +// return toRlp(serializedTransaction) +// } + +// // Define the Suave serializers object +// export const serializersSuave = { +// transaction: serializeTransactionSuave, +// } as const satisfies ChainSerializers diff --git a/src/chains/suave/types.ts b/src/chains/suave/types.ts new file mode 100644 index 00000000..3ae224a6 --- /dev/null +++ b/src/chains/suave/types.ts @@ -0,0 +1,110 @@ +import type { Address } from 'abitype' +import type { Block, BlockTag } from '../../types/block.js' +import type { Hex } from '../../types/misc.js' +import type { + Index, + Quantity, + RpcBlock, + RpcTransaction as RpcTransaction_, + RpcTransactionReceipt, + RpcTransactionRequest as RpcTransactionRequest_, +} from '../../types/rpc.js' +import type { + Transaction as Transaction_, + TransactionBase, + TransactionReceipt, + TransactionRequest as TransactionRequest_, +} from '../../types/transaction.js' + +export type SuaveBlockOverrides = {} // Add any specific block overrides if necessary for Suave + +export type SuaveBlock< + TIncludeTransactions extends boolean = boolean, + TBlockTag extends BlockTag = BlockTag, +> = Block< + bigint, + TIncludeTransactions, + TBlockTag, + SuaveTransaction +> & + SuaveBlockOverrides + +export type SuaveRpcBlock< + TBlockTag extends BlockTag = BlockTag, + TIncludeTransactions extends boolean = boolean, +> = RpcBlock & SuaveBlockOverrides + +export type SuaveRpcTransaction = + | (RpcTransaction_ & { + ExecutionNode: Address + ConfidentialComputeRequest: RpcTransaction_ + ConfidentialComputeResult: Hex + IsConfidential?: boolean // Add this line + }) + | RpcTransactionSuave + +export type SuaveRpcTransactionRequest = RpcTransactionRequest_ & { + ExecutionNode?: Address + IsConfidential?: boolean +} + +export type SuaveTransaction = Transaction_< + bigint, + number, + TPending +> & { + ExecutionNode: Address + ConfidentialComputeRequest: Transaction_ + ConfidentialComputeResult: Hex +} + +export type SuaveTransactionReceiptOverrides = { + ExecutionNode: Address | null + ConfidentialComputeRequest: Transaction_ | null // TODO : modify to regular transaction + ConfidentialComputeResult: Hex | null +} + +export type SuaveTransactionReceipt = TransactionReceipt & + SuaveTransactionReceiptOverrides + +export type SuaveTransactionRequest = TransactionRequest_ & { + ExecutionNode?: Address + IsConfidential?: boolean +} + +type RpcTransactionSuave = TransactionBase< + Quantity, + Index, + TPending +> & { + ExecutionNode: Address + ConfidentialComputeRequest: RpcTransaction_ + ConfidentialComputeResult: Hex + IsConfidential?: boolean +} + +export type SuaveRpcTransactionReceipt = RpcTransactionReceipt & { + ExecutionNode: Address + ConfidentialComputeRequest: RpcTransaction_ + ConfidentialComputeResult: Hex +} + +// Define a type for serializable Suave transactions +export type SuaveTransactionSerializable = { + chainId: bigint + nonce: bigint + gas: bigint + to: Address + value: bigint + data: Hex + ExecutionNode: Address + ConfidentialComputeRequest: { + ExecutionNode: Address + Wrapped: { toHex: () => Hex } // Adjust this type as necessary + // ... other fields + } + ConfidentialComputeResult: Hex +} + +// Define a type for serialized Suave transactions +export type TransactionSerializedSuave = string