diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e4280..028b4fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.8.0 + +- Update dependencies. + ## 3.7.0 - Update dependencies. diff --git a/example/lib/example/solana/token_program/create_mint_example.dart b/example/lib/example/solana/token_program/create_mint_example.dart index 5501d5b..6dc805f 100644 --- a/example/lib/example/solana/token_program/create_mint_example.dart +++ b/example/lib/example/solana/token_program/create_mint_example.dart @@ -6,6 +6,7 @@ void main() async { final freezeAuthority = QuickWalletForTest(index: 1112); final mintAccount = QuickWalletForTest(index: 1111); final mintAccSpace = SolanaMintAccount.size; + final rent = await QuickWalletForTest.rpc .request(SolanaRPCGetMinimumBalanceForRentExemption(size: mintAccSpace)); final createAccount = SystemProgram.createAccount( diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index f76cc54..9fa0fb3 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -1 +1,20 @@ -void main() async {} +void main() async { +// /// WebSocket RPC Service +// final websocketRpc = await RPCWebSocketService.connect( +// "wss://go.getblock.io/b9c91d92aaeb4e5ba2d4cca664ab708c", onEvents: (p0) { +// print("on event $p0"); +// }, onClose: (p0) {}); +// // Establish a WebSocket RPC connection to the specified endpoint for real-time updates. + +// /// Ethereum RPC +// final rpc = EVMRPC(websocketRpc); + +// final getblock = await rpc.request(RPCETHSubscribe()); +// print("block $getblock"); +// Timer.periodic(const Duration(seconds: 5), (s) async { +// // final re = await rpc.request(RPCETHSubscribe()); +// // print("re $re"); +// }); +// print("get block $getblock"); +// final changed = await Future.delayed(const Duration(seconds: 60)); +} diff --git a/lib/ada/src/models/certificate/types/pool/pool_params.dart b/lib/ada/src/models/certificate/types/pool/pool_params.dart index 844ff51..4c96f0a 100644 --- a/lib/ada/src/models/certificate/types/pool/pool_params.dart +++ b/lib/ada/src/models/certificate/types/pool/pool_params.dart @@ -72,7 +72,8 @@ class PoolParams with ADASerialization { .toList(), poolMetadata: cbor .getIndex(8) - ?.to((e) => PoolMetadata.deserialize(e)), + ?.castTo( + (e) => PoolMetadata.deserialize(e)), ); } PoolParams copyWith({ diff --git a/lib/ada/src/models/certificate/types/pool/relay/relays/single_host_address.dart b/lib/ada/src/models/certificate/types/pool/relay/relays/single_host_address.dart index 2571756..d473597 100644 --- a/lib/ada/src/models/certificate/types/pool/relay/relays/single_host_address.dart +++ b/lib/ada/src/models/certificate/types/pool/relay/relays/single_host_address.dart @@ -25,10 +25,10 @@ class SingleHostAddr extends Relay { port: cbor.getIndex(1)?.value, ipv4: cbor .getIndex(2) - ?.to((e) => Ipv4.deserialize(e)), + ?.castTo((e) => Ipv4.deserialize(e)), ipv6: cbor .getIndex(3) - ?.to((e) => Ipv6.deserialize(e)), + ?.castTo((e) => Ipv6.deserialize(e)), ); } diff --git a/lib/ada/src/models/header/header/header_body.dart b/lib/ada/src/models/header/header/header_body.dart index dcd4b0f..21096da 100644 --- a/lib/ada/src/models/header/header/header_body.dart +++ b/lib/ada/src/models/header/header/header_body.dart @@ -51,7 +51,8 @@ class HeaderBody with ADASerialization { cbor.sublist(operationIndex, operationIndex + 4)), prevHash: cbor .getIndex(2) - ?.to((e) => BlockHash.deserialize(e)), + ?.castTo( + (e) => BlockHash.deserialize(e)), protocolVersion: ProtocolVersion.deserialize(cbor.sublist(protocolVersionIndex)), vrfvKey: VRFVKey.deserialize(cbor.getIndex(4))); diff --git a/lib/ada/src/models/transaction/output/models/transaction_output.dart b/lib/ada/src/models/transaction/output/models/transaction_output.dart index 0998188..2a8618b 100644 --- a/lib/ada/src/models/transaction/output/models/transaction_output.dart +++ b/lib/ada/src/models/transaction/output/models/transaction_output.dart @@ -49,10 +49,12 @@ class TransactionOutput with ADASerialization { amount: Value.deserialize(cbor.getIndex(1)), plutusData: cbor .getIndex(2) - ?.to((e) => DataOption.deserialize(e)), + ?.castTo( + (e) => DataOption.deserialize(e)), scriptRef: cbor .getIndex(3) - ?.to((e) => ScriptRef.deserialize(e))); + ?.castTo( + (e) => ScriptRef.deserialize(e))); } final CborMapValue cborMap = cbor.cast(); final address = @@ -62,10 +64,10 @@ class TransactionOutput with ADASerialization { amount: Value.deserialize(cborMap.getValueFromIntKey(1)), plutusData: cborMap .getValueFromIntKey(2) - ?.to((e) => DataOption.deserialize(e)), + ?.castTo((e) => DataOption.deserialize(e)), scriptRef: cborMap .getValueFromIntKey(3) - ?.to((e) => ScriptRef.deserialize(e))); + ?.castTo((e) => ScriptRef.deserialize(e))); } TransactionOutput copyWith({ ADAAddress? address, diff --git a/lib/ada/src/models/transaction/transaction/auxiliary_data.dart b/lib/ada/src/models/transaction/transaction/auxiliary_data.dart index c8de31e..e2a46ff 100644 --- a/lib/ada/src/models/transaction/transaction/auxiliary_data.dart +++ b/lib/ada/src/models/transaction/transaction/auxiliary_data.dart @@ -37,11 +37,11 @@ class AuxiliaryData with ADASerialization { return AuxiliaryData( metadata: cbor .getIndex(0) - ?.to( + ?.castTo( (e) => GeneralTransactionMetadata.deserialize(e)), nativeScripts: cbor .getIndex(1) - ?.to, CborListValue>((e) => + ?.castTo, CborListValue>((e) => e.value.map((i) => NativeScript.deserialize(i)).toList()), ); } else { @@ -51,19 +51,19 @@ class AuxiliaryData with ADASerialization { preferAlonzoFormat: true, metadata: cobrList .getValueFromIntKey(0) - ?.to( + ?.castTo( (e) => GeneralTransactionMetadata.deserialize(e)), nativeScripts: cobrList .getValueFromIntKey(1) - ?.to, CborListValue>((e) => + ?.castTo, CborListValue>((e) => e.value.map((i) => NativeScript.deserialize(i)).toList()), plutusScripts: cobrList .getValueFromIntKey(2) - ?.to, CborListValue>((e) { + ?.castTo, CborListValue>((e) { final v1 = e.value.map((i) => PlutusScript.deserialize(i)).toList(); final v2 = cobrList .getValueFromIntKey(3) - ?.to, CborListValue>((e) { + ?.castTo, CborListValue>((e) { return e.value .map((i) => PlutusScript.deserialize(i, language: Language.plutusV2)) diff --git a/lib/ada/src/models/transaction/transaction/body.dart b/lib/ada/src/models/transaction/transaction/body.dart index 3451e95..69f6ee7 100644 --- a/lib/ada/src/models/transaction/transaction/body.dart +++ b/lib/ada/src/models/transaction/transaction/body.dart @@ -71,29 +71,28 @@ class TransactionBody with ADASerialization { ttl: cbor.getValueFromIntKey(3)?.getInteger(), withdrawals: cbor .getValueFromIntKey(5) - ?.to((e) => Withdrawals.deserialize(e)), + ?.castTo( + (e) => Withdrawals.deserialize(e)), auxiliaryDataHash: cbor .getValueFromIntKey(7) - ?.to( + ?.castTo( (CborBytesValue e) => AuxiliaryDataHash.deserialize(e)), certs: cbor .getValueFromIntKey(4) - ?.to, CborListValue>( - (CborListValue e) => e.value - .map((e) => Certificate.deserialize(e.cast())) - .toList()), + ?.castTo, CborListValue>((CborListValue e) => + e.value.map((e) => Certificate.deserialize(e.cast())).toList()), update: cbor .getValueFromIntKey(6) - ?.to((e) => Update.deserialize(e)), + ?.castTo((e) => Update.deserialize(e)), validityStartInterval: cbor.getValueFromIntKey(8)?.getInteger(), - mint: cbor.getValueFromIntKey(9)?.to((e) => Mint.deserialize(e.cast())), - scriptDataHash: cbor.getValueFromIntKey(11)?.to((e) => ScriptDataHash.deserialize(e)), - collateral: cbor.getValueFromIntKey(13)?.to, CborListValue>((e) => e.value.map((e) => TransactionInput.deserialize(e.cast())).toList()), - requiredSigners: cbor.getValueFromIntKey(14)?.to, CborListValue>((e) => e.value.map((e) => Ed25519KeyHash.deserialize(e.cast())).toList()), - network: cbor.getValueFromIntKey(15)?.to((e) => ADANetwork.fromTag(e.value)), - collateralReturn: cbor.getValueFromIntKey(16)?.to((e) => TransactionOutput.deserialize(e)), + mint: cbor.getValueFromIntKey(9)?.castTo((e) => Mint.deserialize(e.cast())), + scriptDataHash: cbor.getValueFromIntKey(11)?.castTo((e) => ScriptDataHash.deserialize(e)), + collateral: cbor.getValueFromIntKey(13)?.castTo, CborListValue>((e) => e.value.map((e) => TransactionInput.deserialize(e.cast())).toList()), + requiredSigners: cbor.getValueFromIntKey(14)?.castTo, CborListValue>((e) => e.value.map((e) => Ed25519KeyHash.deserialize(e.cast())).toList()), + network: cbor.getValueFromIntKey(15)?.castTo((e) => ADANetwork.fromTag(e.value)), + collateralReturn: cbor.getValueFromIntKey(16)?.castTo((e) => TransactionOutput.deserialize(e)), totalCollateral: cbor.getValueFromIntKey(17)?.getInteger(), - referenceInputs: cbor.getValueFromIntKey(18)?.to, CborListValue>((e) => e.value.map((e) => TransactionInput.deserialize(e.cast())).toList())); + referenceInputs: cbor.getValueFromIntKey(18)?.castTo, CborListValue>((e) => e.value.map((e) => TransactionInput.deserialize(e.cast())).toList())); } TransactionBody copyWith({ List? inputs, diff --git a/lib/ada/src/models/transaction/transaction/transaction.dart b/lib/ada/src/models/transaction/transaction/transaction.dart index cae2701..63d10b6 100644 --- a/lib/ada/src/models/transaction/transaction/transaction.dart +++ b/lib/ada/src/models/transaction/transaction/transaction.dart @@ -24,7 +24,7 @@ class ADATransaction with ADASerialization { body: TransactionBody.deserialize(cbor.getIndex(0)), witnessSet: TransactionWitnessSet.deserialize(cbor.getIndex(1)), isValid: cbor.getIndex(2), - data: cbor.getIndex(3)?.to( + data: cbor.getIndex(3)?.castTo( (e) => AuxiliaryData.deserialize(e))); } factory ADATransaction.fromJson(Map json) { diff --git a/lib/ada/src/models/transaction/witnesses/transaction_witness_set.dart b/lib/ada/src/models/transaction/witnesses/transaction_witness_set.dart index 438b02d..aa18e32 100644 --- a/lib/ada/src/models/transaction/witnesses/transaction_witness_set.dart +++ b/lib/ada/src/models/transaction/witnesses/transaction_witness_set.dart @@ -38,12 +38,12 @@ class TransactionWitnessSet with ADASerialization { factory TransactionWitnessSet.deserialize(CborMapValue cbor) { final v1 = cbor .getValueFromIntKey(3) - ?.to, CborListValue>((e) { + ?.castTo, CborListValue>((e) { return e.value.map((i) => PlutusScript.deserialize(i.cast())).toList(); }); final v2 = cbor .getValueFromIntKey(6) - ?.to, CborListValue>((e) => e.value + ?.castTo, CborListValue>((e) => e.value .map((i) => PlutusScript.deserialize(i.cast(), language: Language.plutusV2)) .toList()); @@ -53,23 +53,21 @@ class TransactionWitnessSet with ADASerialization { ?.value .map((e) => Vkeywitness.deserialize(e)) .toList(), - nativeScripts: cbor - .getValueFromIntKey(1) - ?.to, CborListValue>((e) => e.value - .map((e) => NativeScript.deserialize(e.cast())) - .toList()), + nativeScripts: cbor.getValueFromIntKey(1)?.castTo, CborListValue>((e) => + e.value.map((e) => NativeScript.deserialize(e.cast())).toList()), bootstraps: cbor .getValueFromIntKey(2) - ?.to, CborListValue>((e) => e.value + ?.castTo, CborListValue>((e) => e.value .map((e) => BootstrapWitness.deserialize(e.cast())) .toList()), plutusScripts: v1 == null && v2 == null ? null : [...v1 ?? [], ...v2 ?? []], - plutusData: cbor - .getValueFromIntKey(4) - ?.to((e) => PlutusList.deserialize(e)), - redeemers: cbor.getValueFromIntKey(5)?.to, CborListValue>( - (e) => e.value.map((i) => Redeemer.deserialize(i.cast())).toList())); + plutusData: cbor.getValueFromIntKey(4)?.castTo( + (e) => PlutusList.deserialize(e)), + redeemers: cbor + .getValueFromIntKey(5) + ?.castTo, CborListValue>( + (e) => e.value.map((i) => Redeemer.deserialize(i.cast())).toList())); } TransactionWitnessSet copyWith({ List? vKeys, diff --git a/lib/ada/src/models/update/models/protocol_param_update.dart b/lib/ada/src/models/update/models/protocol_param_update.dart index 59284ed..fe5ece0 100644 --- a/lib/ada/src/models/update/models/protocol_param_update.dart +++ b/lib/ada/src/models/update/models/protocol_param_update.dart @@ -77,28 +77,28 @@ class ProtocolParamUpdate with ADASerialization { nOpt: cbor.getValueFromIntKey(8)?.value, poolPledgeInfluence: cbor .getValueFromIntKey(9) - ?.to( + ?.castTo( (e) => UnitInterval.deserialize(e)), - expansionRate: cbor.getValueFromIntKey(10)?.to( + expansionRate: cbor.getValueFromIntKey(10)?.castTo( (e) => UnitInterval.deserialize(e)), treasuryGrowthRate: cbor .getValueFromIntKey(11) - ?.to( + ?.castTo( (e) => UnitInterval.deserialize(e)), - d: cbor.getValueFromIntKey(12)?.to( + d: cbor.getValueFromIntKey(12)?.castTo( (e) => UnitInterval.deserialize(e)), extraEntropy: cbor .getValueFromIntKey(13) - ?.to((e) => Nonce.deserialize(e)), + ?.castTo((e) => Nonce.deserialize(e)), protocolVersion: cbor .getValueFromIntKey(14) - ?.to((e) => ProtocolVersion.deserialize(e)), + ?.castTo((e) => ProtocolVersion.deserialize(e)), minPoolCost: cbor.getValueFromIntKey(16)?.getInteger(), adaPerUtxoByte: cbor.getValueFromIntKey(17)?.getInteger(), - costModel: cbor.getValueFromIntKey(18)?.to((e) => Costmdls.deserialize(e)), - executionCosts: cbor.getValueFromIntKey(19)?.to((e) => ExUnitPrices.deserialize(e)), - maxTxExUnits: cbor.getValueFromIntKey(20)?.to((e) => ExUnits.deserialize(e)), - maxBlockExUnits: cbor.getValueFromIntKey(21)?.to((e) => ExUnits.deserialize(e)), + costModel: cbor.getValueFromIntKey(18)?.castTo((e) => Costmdls.deserialize(e)), + executionCosts: cbor.getValueFromIntKey(19)?.castTo((e) => ExUnitPrices.deserialize(e)), + maxTxExUnits: cbor.getValueFromIntKey(20)?.castTo((e) => ExUnits.deserialize(e)), + maxBlockExUnits: cbor.getValueFromIntKey(21)?.castTo((e) => ExUnits.deserialize(e)), maxValueSize: cbor.getValueFromIntKey(22)?.value, collateralPercentage: cbor.getValueFromIntKey(23)?.value, maxCollateralInputs: cbor.getValueFromIntKey(24)?.value); diff --git a/lib/ada/src/provider/blockfrost/methods/ledger/blockchain_genesis.dart b/lib/ada/src/provider/blockfrost/methods/ledger/blockchain_genesis.dart index 4da15bc..5a27489 100644 --- a/lib/ada/src/provider/blockfrost/methods/ledger/blockchain_genesis.dart +++ b/lib/ada/src/provider/blockfrost/methods/ledger/blockchain_genesis.dart @@ -17,7 +17,6 @@ class BlockfrostRequestBlockchainGenesis extends BlockforestRequestParam< @override ADAGenesisParametersResponse onResonse(Map result) { - print("result $result"); return ADAGenesisParametersResponse.fromJson(result); } } diff --git a/lib/ada/src/provider/exception/blockfrost_api_error.dart b/lib/ada/src/provider/exception/blockfrost_api_error.dart index 42d95fb..1e4be72 100644 --- a/lib/ada/src/provider/exception/blockfrost_api_error.dart +++ b/lib/ada/src/provider/exception/blockfrost_api_error.dart @@ -1,5 +1,15 @@ import 'package:blockchain_utils/exception/exceptions.dart'; +/// https://blockfrost.dev/api/blockfrost-io-api-documentation +class BlockfrostStatusCode { + static const int invalidRequest = 400; + static const int exceedRequestLimit = 402; + static const int inValidAuthenticated = 403; + static const int resourceDoesNotExist = 404; + static const int rateLimit = 429; + static const int bannedToMuchRequest = 418; +} + class BlockfrostError implements BlockchainUtilsException { @override final String message; @@ -18,3 +28,5 @@ class BlockfrostError implements BlockchainUtilsException { return "Error: $error, Message: $message, StatusCode: $statusCode"; } } +// HTTP 418 return code is used when the user has been auto-banned for flooding too much after previously receiving error code 402 or 429. +// HTTP 500 return code is used when our endpoints are having a problem. diff --git a/lib/ada/src/serialization/cbor/cbor_serialization.dart b/lib/ada/src/serialization/cbor/cbor_serialization.dart index 87ed275..4b659ea 100644 --- a/lib/ada/src/serialization/cbor/cbor_serialization.dart +++ b/lib/ada/src/serialization/cbor/cbor_serialization.dart @@ -81,7 +81,7 @@ extension QuickCborObject on CborObject { /// Converts the value of the [CborObject] to the specified type [E] using the provided function [toe]. /// /// Throws a [MessageException] if the value cannot be converted to type [T]. - E to(E Function(T e) toe) { + E castTo(E Function(T e) toe) { if (this is T) { return toe(this as T); } diff --git a/lib/ethereum/src/address/evm_address.dart b/lib/ethereum/src/address/evm_address.dart index ff2f6b2..0d1f30d 100644 --- a/lib/ethereum/src/address/evm_address.dart +++ b/lib/ethereum/src/address/evm_address.dart @@ -2,14 +2,14 @@ import 'package:on_chain/solidity/address/core.dart'; import 'package:blockchain_utils/bip/address/eth_addr.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -/// Class representing an Ethereum address, implementing the [BaseHexAddress] interface. +/// Class representing an Ethereum address, implementing the [SolidityAddress] interface. class ETHAddress implements SolidityAddress { - /// Private constructor for creating an instance of [ETHAddress] with a given Ethereum address - const ETHAddress._(this.address); - /// The Ethereum address string. final String address; + /// Private constructor for creating an instance of [ETHAddress] with a given Ethereum address + const ETHAddress._(this.address); + /// Creates an [ETHAddress] instance from a public key represented as a bytes. factory ETHAddress.fromPublicKey(List keyBytes) { try { @@ -45,11 +45,6 @@ class ETHAddress implements SolidityAddress { return BytesUtils.fromHexString(address); } - @override - String toString() { - return address; - } - /// Constant representing the length of the ETH address in bytes static const int lengthInBytes = 20; @@ -57,4 +52,18 @@ class ETHAddress implements SolidityAddress { String toHex() { return address; } + + @override + String toString() { + return address; + } + + @override + operator ==(other) { + if (other is! ETHAddress) return false; + return address == other.address; + } + + @override + int get hashCode => address.hashCode; } diff --git a/lib/ethereum/src/rpc/core/core.dart b/lib/ethereum/src/rpc/core/core.dart index ce18f0f..9809225 100644 --- a/lib/ethereum/src/rpc/core/core.dart +++ b/lib/ethereum/src/rpc/core/core.dart @@ -14,11 +14,8 @@ abstract class ETHRequestParams { /// Represents the details of an Ethereum JSON-RPC request. class ETHRequestDetails { - const ETHRequestDetails({ - required this.id, - required this.method, - required this.params, - }); + const ETHRequestDetails( + {required this.id, required this.method, required this.params}); /// The unique identifier for the JSON-RPC request. final int id; diff --git a/lib/ethereum/src/rpc/core/methods.dart b/lib/ethereum/src/rpc/core/methods.dart index 29897ba..6d32754 100644 --- a/lib/ethereum/src/rpc/core/methods.dart +++ b/lib/ethereum/src/rpc/core/methods.dart @@ -3,94 +3,169 @@ class EthereumMethods { final String value; /// The string value of the method. - const EthereumMethods._(this.value); + const EthereumMethods(this.value); static const EthereumMethods getProtocolVersion = - EthereumMethods._('eth_protocolVersion'); - static const EthereumMethods subscribe = EthereumMethods._('eth_subscribe'); - static const EthereumMethods getSyncing = EthereumMethods._('eth_syncing'); - static const EthereumMethods getCoinbase = EthereumMethods._('eth_coinbase'); - static const EthereumMethods getMining = EthereumMethods._('eth_mining'); - static const EthereumMethods getHashRate = EthereumMethods._('eth_hashrate'); - static const EthereumMethods getGasPrice = EthereumMethods._('eth_gasPrice'); - static const EthereumMethods getAccounts = EthereumMethods._('eth_accounts'); + EthereumMethods('eth_protocolVersion'); + static const EthereumMethods subscribe = EthereumMethods('eth_subscribe'); + static const EthereumMethods getSyncing = EthereumMethods('eth_syncing'); + static const EthereumMethods getCoinbase = EthereumMethods('eth_coinbase'); + static const EthereumMethods getMining = EthereumMethods('eth_mining'); + static const EthereumMethods getHashRate = EthereumMethods('eth_hashrate'); + static const EthereumMethods getGasPrice = EthereumMethods('eth_gasPrice'); + static const EthereumMethods getAccounts = EthereumMethods('eth_accounts'); static const EthereumMethods getBlockNumber = - EthereumMethods._('eth_blockNumber'); - static const EthereumMethods getBalance = EthereumMethods._('eth_getBalance'); + EthereumMethods('eth_blockNumber'); + static const EthereumMethods getBalance = EthereumMethods('eth_getBalance'); static const EthereumMethods getStorageAt = - EthereumMethods._('eth_getStorageAt'); + EthereumMethods('eth_getStorageAt'); static const EthereumMethods getTransactionCount = - EthereumMethods._('eth_getTransactionCount'); + EthereumMethods('eth_getTransactionCount'); static const EthereumMethods getBlockTransactionCountByHash = - EthereumMethods._('eth_getBlockTransactionCountByHash'); + EthereumMethods('eth_getBlockTransactionCountByHash'); static const EthereumMethods getBlockTransactionCountByNumber = - EthereumMethods._('eth_getBlockTransactionCountByNumber'); + EthereumMethods('eth_getBlockTransactionCountByNumber'); static const EthereumMethods getUncleCountByBlockHash = - EthereumMethods._('eth_getUncleCountByBlockHash'); + EthereumMethods('eth_getUncleCountByBlockHash'); static const EthereumMethods getUncleCountByBlockNumber = - EthereumMethods._('eth_getUncleCountByBlockNumber'); - static const EthereumMethods getCode = EthereumMethods._('eth_getCode'); - static const EthereumMethods sign = EthereumMethods._('eth_sign'); + EthereumMethods('eth_getUncleCountByBlockNumber'); + static const EthereumMethods getCode = EthereumMethods('eth_getCode'); + static const EthereumMethods sign = EthereumMethods('eth_sign'); static const EthereumMethods signTransaction = - EthereumMethods._('eth_signTransaction'); + EthereumMethods('eth_signTransaction'); + static const EthereumMethods ethSubscribe = EthereumMethods('eth_subscribe'); + static const EthereumMethods ethUnsubscribe = + EthereumMethods('eth_unsubscribe'); static const EthereumMethods sendTransaction = - EthereumMethods._('eth_sendTransaction'); + EthereumMethods('eth_sendTransaction'); static const EthereumMethods sendRawTransaction = - EthereumMethods._('eth_sendRawTransaction'); - static const EthereumMethods call = EthereumMethods._('eth_call'); - static const EthereumMethods estimateGas = - EthereumMethods._('eth_estimateGas'); + EthereumMethods('eth_sendRawTransaction'); + static const EthereumMethods call = EthereumMethods('eth_call'); + static const EthereumMethods estimateGas = EthereumMethods('eth_estimateGas'); static const EthereumMethods getBlockByHash = - EthereumMethods._('eth_getBlockByHash'); + EthereumMethods('eth_getBlockByHash'); + static const EthereumMethods getBlockByNumber = - EthereumMethods._('eth_getBlockByNumber'); + EthereumMethods('eth_getBlockByNumber'); static const EthereumMethods getTransactionByHash = - EthereumMethods._('eth_getTransactionByHash'); + EthereumMethods('eth_getTransactionByHash'); static const EthereumMethods getTransactionByBlockHashAndIndex = - EthereumMethods._('eth_getTransactionByBlockHashAndIndex'); + EthereumMethods('eth_getTransactionByBlockHashAndIndex'); + static const EthereumMethods getTransactionByBlockNumberAndIndex = - EthereumMethods._('eth_getTransactionByBlockNumberAndIndex'); + EthereumMethods('eth_getTransactionByBlockNumberAndIndex'); static const EthereumMethods getTransactionReceipt = - EthereumMethods._('eth_getTransactionReceipt'); + EthereumMethods('eth_getTransactionReceipt'); + static const EthereumMethods getUncleByBlockHashAndIndex = - EthereumMethods._('eth_getUncleByBlockHashAndIndex'); + EthereumMethods('eth_getUncleByBlockHashAndIndex'); static const EthereumMethods getUncleByBlockNumberAndIndex = - EthereumMethods._('eth_getUncleByBlockNumberAndIndex'); + EthereumMethods('eth_getUncleByBlockNumberAndIndex'); static const EthereumMethods getCompilers = - EthereumMethods._('eth_getCompilers'); + EthereumMethods('eth_getCompilers'); + static const EthereumMethods compileSolidity = - EthereumMethods._('eth_compileSolidity'); - static const EthereumMethods compileLLL = EthereumMethods._('eth_compileLLL'); + EthereumMethods('eth_compileSolidity'); + static const EthereumMethods compileLLL = EthereumMethods('eth_compileLLL'); static const EthereumMethods compileSerpent = - EthereumMethods._('eth_compileSerpent'); - static const EthereumMethods newFilter = EthereumMethods._('eth_newFilter'); + EthereumMethods('eth_compileSerpent'); + static const EthereumMethods newFilter = EthereumMethods('eth_newFilter'); + static const EthereumMethods newBlockFilter = - EthereumMethods._('eth_newBlockFilter'); + EthereumMethods('eth_newBlockFilter'); static const EthereumMethods newPendingTransactionFilter = - EthereumMethods._('eth_newPendingTransactionFilter'); + EthereumMethods('eth_newPendingTransactionFilter'); static const EthereumMethods uninstallFilter = - EthereumMethods._('eth_uninstallFilter'); + EthereumMethods('eth_uninstallFilter'); static const EthereumMethods getFilterChanges = - EthereumMethods._('eth_getFilterChanges'); + EthereumMethods('eth_getFilterChanges'); + static const EthereumMethods getFilterLogs = - EthereumMethods._('eth_getFilterLogs'); - static const EthereumMethods getLogs = EthereumMethods._('eth_getLogs'); - static const EthereumMethods getWork = EthereumMethods._('eth_getWork'); - static const EthereumMethods submitWork = EthereumMethods._('eth_submitWork'); + EthereumMethods('eth_getFilterLogs'); + static const EthereumMethods getLogs = EthereumMethods('eth_getLogs'); + static const EthereumMethods getWork = EthereumMethods('eth_getWork'); + static const EthereumMethods submitWork = EthereumMethods('eth_submitWork'); static const EthereumMethods submitHashrate = - EthereumMethods._('eth_submitHashrate'); + EthereumMethods('eth_submitHashrate'); static const EthereumMethods getFeeHistory = - EthereumMethods._('eth_feeHistory'); + EthereumMethods('eth_feeHistory'); static const EthereumMethods getPendingTransactions = - EthereumMethods._('eth_pendingTransactions'); + EthereumMethods('eth_pendingTransactions'); static const EthereumMethods requestAccounts = - EthereumMethods._('eth_requestAccounts'); - static const EthereumMethods getChainId = EthereumMethods._('eth_chainId'); - static const EthereumMethods getProof = EthereumMethods._('eth_getProof'); + EthereumMethods('eth_requestAccounts'); + static const EthereumMethods getChainId = EthereumMethods('eth_chainId'); + static const EthereumMethods getProof = EthereumMethods('eth_getProof'); static const EthereumMethods getNodeInfo = - EthereumMethods._('web3_clientVersion'); + EthereumMethods('web3_clientVersion'); static const EthereumMethods createAccessList = - EthereumMethods._('eth_createAccessList'); + EthereumMethods('eth_createAccessList'); static const EthereumMethods signTypedData = - EthereumMethods._('eth_signTypedData'); + EthereumMethods('eth_signTypedData'); + static const EthereumMethods netVersion = EthereumMethods('net_version'); + + static const List values = [ + netVersion, + signTypedData, + createAccessList, + getNodeInfo, + getProtocolVersion, + subscribe, + getSyncing, + getCoinbase, + getMining, + getHashRate, + getGasPrice, + getAccounts, + getBlockNumber, + getBalance, + getStorageAt, + getTransactionCount, + getBlockTransactionCountByHash, + getBlockTransactionCountByNumber, + getUncleCountByBlockHash, + getUncleCountByBlockNumber, + getCode, + sign, + signTransaction, + sendTransaction, + sendRawTransaction, + call, + estimateGas, + getBlockByHash, + getBlockByNumber, + getTransactionByHash, + getTransactionByBlockHashAndIndex, + getTransactionByBlockNumberAndIndex, + getTransactionReceipt, + getUncleByBlockHashAndIndex, + getUncleByBlockNumberAndIndex, + getCompilers, + compileSolidity, + compileLLL, + compileSerpent, + newFilter, + newBlockFilter, + newPendingTransactionFilter, + uninstallFilter, + getFilterChanges, + getFilterLogs, + getLogs, + getWork, + submitWork, + submitHashrate, + getFeeHistory, + getPendingTransactions, + requestAccounts, + getChainId, + getProof, + getNodeInfo + ]; + + static EthereumMethods? fromName(String? name) { + try { + return values.firstWhere((e) => e.value == name); + } on StateError { + return null; + } + } } diff --git a/lib/ethereum/src/rpc/methds/get_code.dart b/lib/ethereum/src/rpc/methds/get_code.dart index e9bb2b2..0f940d9 100644 --- a/lib/ethereum/src/rpc/methds/get_code.dart +++ b/lib/ethereum/src/rpc/methds/get_code.dart @@ -7,7 +7,7 @@ import 'package:on_chain/ethereum/src/rpc/core/methods.dart'; class RPCGetCode extends ETHRPCRequest { RPCGetCode({ required this.address, - BlockTagOrNumber? tag = BlockTagOrNumber.finalized, + BlockTagOrNumber? tag = BlockTagOrNumber.pending, }) : super(blockNumber: tag); /// eth_getCode diff --git a/lib/ethereum/src/rpc/methds/methods.dart b/lib/ethereum/src/rpc/methds/methods.dart index 0a47d5e..90031f1 100644 --- a/lib/ethereum/src/rpc/methds/methods.dart +++ b/lib/ethereum/src/rpc/methds/methods.dart @@ -48,3 +48,4 @@ export "sign_transaction.dart"; export "submit_hash_rate.dart"; export "submit_work.dart"; export "uninstall_filter.dart"; +export 'subscribes/subscribe.dart'; diff --git a/lib/ethereum/src/rpc/methds/subscribes/const/constant.dart b/lib/ethereum/src/rpc/methds/subscribes/const/constant.dart new file mode 100644 index 0000000..3a7f377 --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/const/constant.dart @@ -0,0 +1,6 @@ +class RPCETHSubscribeConst { + static const String logs = "logs"; + static const String newHeads = "newHeads"; + static const String newPendingTransactions = "newPendingTransactions"; + static const String syncing = "syncing"; +} diff --git a/lib/ethereum/src/rpc/methds/subscribes/methods/logs.dart b/lib/ethereum/src/rpc/methds/subscribes/methods/logs.dart new file mode 100644 index 0000000..b402892 --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/methods/logs.dart @@ -0,0 +1,27 @@ +import 'package:on_chain/ethereum/src/address/evm_address.dart'; +import 'package:on_chain/ethereum/src/rpc/core/core.dart'; +import 'package:on_chain/ethereum/src/rpc/core/methods.dart'; +import 'package:on_chain/ethereum/src/rpc/methds/subscribes/const/constant.dart'; + +/// https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub +class RPCETHSubscribeLogs extends ETHRPCRequest { + RPCETHSubscribeLogs({this.filter}); + final SubscribeLogsFilter? filter; + + @override + EthereumMethods get method => EthereumMethods.ethSubscribe; + + @override + List toJson() { + return [RPCETHSubscribeConst.logs, if (filter != null) filter!.toJson()]; + } +} + +class SubscribeLogsFilter { + final ETHAddress address; + final List topics; + const SubscribeLogsFilter({required this.address, this.topics = const []}); + Map toJson() { + return {"address": address.address, "topics": topics}; + } +} diff --git a/lib/ethereum/src/rpc/methds/subscribes/methods/new_heads.dart b/lib/ethereum/src/rpc/methds/subscribes/methods/new_heads.dart new file mode 100644 index 0000000..27f36e0 --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/methods/new_heads.dart @@ -0,0 +1,16 @@ +import 'package:on_chain/ethereum/src/rpc/core/core.dart'; +import 'package:on_chain/ethereum/src/rpc/core/methods.dart'; +import 'package:on_chain/ethereum/src/rpc/methds/subscribes/const/constant.dart'; + +/// https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub +class RPCETHSubscribeNewHeads extends ETHRPCRequest { + RPCETHSubscribeNewHeads(); + + @override + EthereumMethods get method => EthereumMethods.ethSubscribe; + + @override + List toJson() { + return [RPCETHSubscribeConst.newHeads]; + } +} diff --git a/lib/ethereum/src/rpc/methds/subscribes/methods/pending_transactions.dart b/lib/ethereum/src/rpc/methds/subscribes/methods/pending_transactions.dart new file mode 100644 index 0000000..1fc64ea --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/methods/pending_transactions.dart @@ -0,0 +1,16 @@ +import 'package:on_chain/ethereum/src/rpc/core/core.dart'; +import 'package:on_chain/ethereum/src/rpc/core/methods.dart'; +import 'package:on_chain/ethereum/src/rpc/methds/subscribes/const/constant.dart'; + +/// https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub +class RPCETHSubscribeNewPendingTransactions extends ETHRPCRequest { + RPCETHSubscribeNewPendingTransactions(); + + @override + EthereumMethods get method => EthereumMethods.ethSubscribe; + + @override + List toJson() { + return [RPCETHSubscribeConst.newPendingTransactions]; + } +} diff --git a/lib/ethereum/src/rpc/methds/subscribes/methods/syncing.dart b/lib/ethereum/src/rpc/methds/subscribes/methods/syncing.dart new file mode 100644 index 0000000..803119c --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/methods/syncing.dart @@ -0,0 +1,16 @@ +import 'package:on_chain/ethereum/src/rpc/core/core.dart'; +import 'package:on_chain/ethereum/src/rpc/core/methods.dart'; +import 'package:on_chain/ethereum/src/rpc/methds/subscribes/const/constant.dart'; + +/// https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub +class RPCETHSubscribeSyncing extends ETHRPCRequest { + RPCETHSubscribeSyncing(); + + @override + EthereumMethods get method => EthereumMethods.ethSubscribe; + + @override + List toJson() { + return [RPCETHSubscribeConst.syncing]; + } +} diff --git a/lib/ethereum/src/rpc/methds/subscribes/methods/unsubscribe.dart b/lib/ethereum/src/rpc/methds/subscribes/methods/unsubscribe.dart new file mode 100644 index 0000000..c44cd91 --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/methods/unsubscribe.dart @@ -0,0 +1,15 @@ +import 'package:on_chain/ethereum/src/rpc/core/core.dart'; +import 'package:on_chain/ethereum/src/rpc/core/methods.dart'; + +/// https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub +class RPCETHUnsubscribe extends ETHRPCRequest { + final String subscriptionId; + RPCETHUnsubscribe(this.subscriptionId); + @override + EthereumMethods get method => EthereumMethods.ethUnsubscribe; + + @override + List toJson() { + return [subscriptionId]; + } +} diff --git a/lib/ethereum/src/rpc/methds/subscribes/subscribe.dart b/lib/ethereum/src/rpc/methds/subscribes/subscribe.dart new file mode 100644 index 0000000..6943f0e --- /dev/null +++ b/lib/ethereum/src/rpc/methds/subscribes/subscribe.dart @@ -0,0 +1,5 @@ +export 'methods/logs.dart'; +export 'methods/new_heads.dart'; +export 'methods/pending_transactions.dart'; +export 'methods/syncing.dart'; +export 'methods/unsubscribe.dart'; diff --git a/lib/ethereum/src/rpc/provider/provider.dart b/lib/ethereum/src/rpc/provider/provider.dart index b67e229..adb634d 100644 --- a/lib/ethereum/src/rpc/provider/provider.dart +++ b/lib/ethereum/src/rpc/provider/provider.dart @@ -43,4 +43,19 @@ class EVMRPC { final data = await rpc.call(params, timeout); return request.onResonse(_findResult(data, params)); } + + Future requestDynamic(String method, dynamic params, + [Duration? timeout]) async { + final id = ++_id; + final requestParams = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": id + }; + final ETHRequestDetails request = ETHRequestDetails( + id: id, method: method, params: StringUtils.fromJson(requestParams)); + final data = await rpc.call(request, timeout); + return _findResult(data, request); + } } diff --git a/lib/on_chain.dart b/lib/on_chain.dart index 44fb941..0eb9d6f 100644 --- a/lib/on_chain.dart +++ b/lib/on_chain.dart @@ -4,3 +4,4 @@ export 'solidity/solidity.dart'; export 'ethereum/ethereum.dart'; export 'tron/tron.dart'; export 'ada/ada.dart'; +export 'solana/solana.dart'; diff --git a/lib/solana/src/instructions/stake/types/types/delegation.dart b/lib/solana/src/instructions/stake/types/types/delegation.dart index 7d69845..5997fad 100644 --- a/lib/solana/src/instructions/stake/types/types/delegation.dart +++ b/lib/solana/src/instructions/stake/types/types/delegation.dart @@ -3,21 +3,21 @@ import 'package:blockchain_utils/layout/layout.dart'; import 'package:on_chain/solana/src/borsh_serialization/program_layout.dart'; import 'package:on_chain/solana/src/utils/layouts.dart'; -class StakeDelegation extends LayoutSerializable { +class SolanaStakeDelegation extends LayoutSerializable { final SolAddress voterPubkey; final BigInt stake; final BigInt activationEpoch; final BigInt deactivationEpoch; final double warmupCooldownRate; - const StakeDelegation( + const SolanaStakeDelegation( {required this.voterPubkey, required this.stake, required this.activationEpoch, required this.deactivationEpoch, required this.warmupCooldownRate}); - factory StakeDelegation.fromJson(Map json) { - return StakeDelegation( + factory SolanaStakeDelegation.fromJson(Map json) { + return SolanaStakeDelegation( voterPubkey: json["voterPubkey"], stake: json["stake"], activationEpoch: json["activationEpoch"], @@ -49,6 +49,6 @@ class StakeDelegation extends LayoutSerializable { @override String toString() { - return "StakeDelegation${serialize()}"; + return "SolanaStakeDelegation${serialize()}"; } } diff --git a/lib/solana/src/instructions/stake/types/types/stake.dart b/lib/solana/src/instructions/stake/types/types/stake.dart index 3a9a473..dd6a222 100644 --- a/lib/solana/src/instructions/stake/types/types/stake.dart +++ b/lib/solana/src/instructions/stake/types/types/stake.dart @@ -3,18 +3,18 @@ import 'package:blockchain_utils/layout/layout.dart'; import 'package:on_chain/solana/src/borsh_serialization/program_layout.dart'; class StakeStake extends LayoutSerializable { - final StakeDelegation delegation; + final SolanaStakeDelegation delegation; final BigInt creditsObserved; const StakeStake({required this.delegation, required this.creditsObserved}); factory StakeStake.fromJson(Map json) { return StakeStake( - delegation: StakeDelegation.fromJson(json["delegation"]), + delegation: SolanaStakeDelegation.fromJson(json["delegation"]), creditsObserved: json["creditsObserved"]); } static final StructLayout staticLayout = LayoutConst.struct([ - StakeDelegation.staticLayout, + SolanaStakeDelegation.staticLayout, LayoutConst.u64(property: "creditsObserved"), ], property: "stake"); diff --git a/lib/solidity/abi/abi.dart b/lib/solidity/abi/abi.dart index b3a49fa..6df0d08 100644 --- a/lib/solidity/abi/abi.dart +++ b/lib/solidity/abi/abi.dart @@ -32,6 +32,7 @@ import 'package:on_chain/tron/src/address/tron_address.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; part 'utils/utils.dart'; +part 'exception/abi_exception.dart'; part 'types/address.dart'; part 'types/array.dart'; part 'types/boolean.dart'; diff --git a/lib/solidity/abi/core/abi.dart b/lib/solidity/abi/core/abi.dart index 1541de8..460495f 100644 --- a/lib/solidity/abi/core/abi.dart +++ b/lib/solidity/abi/core/abi.dart @@ -37,7 +37,8 @@ abstract class ABICoder { // Use the corrected type or the original type if not modified correctType ??= type; if (!_types.containsKey(correctType)) { - throw MessageException("Unsuported ABI type", details: {"type": type}); + throw SolidityAbiException("Unsuported ABI type. codec not found", + details: {"type": type}); } // Return the corresponding ABICoder instance return _types[correctType]! as ABICoder; @@ -58,7 +59,7 @@ class AbiParameter { static const AbiParameter uint32 = AbiParameter(name: "", type: "uint32"); /// The name of the parameter. - final String name; + final String? name; /// The type of the parameter. final String type; @@ -105,7 +106,8 @@ class AbiParameter { type: type ?? this.type, baseType: baseType ?? this.baseType, indexed: indexed ?? this.indexed, - components: components ?? List.from(this.components), + components: components ?? + List.unmodifiable(components ?? this.components), internalType: internalType ?? this.internalType, ); } @@ -113,14 +115,15 @@ class AbiParameter { /// Factory method to create an AbiParameter instance from a JSON representation. factory AbiParameter.fromJson(Map json, bool tronTypes) { final List inputs = json["components"] ?? []; + final String name = json["name"] ?? ""; return AbiParameter( - name: json["name"] ?? "", + name: name.isEmpty ? null : name, type: json["type"], internalType: json["internalType"], indexed: json["indexed"] ?? false, tronTypes: tronTypes, - components: - inputs.map((e) => AbiParameter.fromJson(e, tronTypes)).toList(), + components: List.unmodifiable( + inputs.map((e) => AbiParameter.fromJson(e, tronTypes)).toList()), ); } @@ -184,8 +187,11 @@ class EncoderResult { /// The encoded data as a bytes. final List encoded; + final String? name; + /// Constructor for EncoderResult. - const EncoderResult({required this.isDynamic, required this.encoded}); + const EncoderResult( + {required this.isDynamic, required this.encoded, required this.name}); } /// Represents the result of decoding data using ABI decoding. @@ -196,8 +202,11 @@ class DecoderResult { /// The number of bytes consumed during decoding. final int consumed; + final String? name; + /// Constructor for DecoderResult. - const DecoderResult({required this.result, required this.consumed}); + const DecoderResult( + {required this.result, required this.consumed, required this.name}); /// Overrides the default toString method to provide a readable representation of DecoderResult. @override diff --git a/lib/solidity/abi/eip712/eip712.dart b/lib/solidity/abi/eip712/eip712.dart index e606d03..51157ab 100644 --- a/lib/solidity/abi/eip712/eip712.dart +++ b/lib/solidity/abi/eip712/eip712.dart @@ -19,15 +19,44 @@ class EIP712Version { /// EIP712 version 4. static const EIP712Version v4 = EIP712Version._("V4", 4); + + static const List values = [v1, v3, v4]; + + static EIP712Version fromVersion(int? version) { + return values.firstWhere((e) => e.version == version, + orElse: () => throw SolidityAbiException( + "Invalid EIP712Version version.", + details: { + "version": version, + "excepted": values.map((e) => e.version).join(", ") + })); + } } /// Abstract base class for encoding data according to the Ethereum Improvement Proposal (EIP) 712 specification. abstract class EIP712Base { /// Encodes the data into a list of integers according to EIP-712. - List encode(); + /// hashing encoded type bytes using [QuickCrypto.keccack256Hash] + List encode({bool hash = true}); + + /// Encodes the data into a hex according to EIP-712. + /// hashing encoded type bytes using [QuickCrypto.keccack256Hash] + String encodeHex({bool hash = true}); /// Represents the version of the EIP-712 specification used by the concrete implementation. abstract final EIP712Version version; + + Map toJson(); + + factory EIP712Base.fromJson(Map json) { + final version = EIP712Version.fromVersion(json["version"]); + switch (version) { + case EIP712Version.v1: + return EIP712Legacy.fromJson(json["types"]); + default: + return Eip712TypedData.fromJson(json, version: version); + } + } } /// Represents details about a type used in EIP-712 encoding. @@ -37,10 +66,7 @@ class Eip712TypeDetails { /// The type string representing the data type. final String type; - const Eip712TypeDetails({ - required this.name, - required this.type, - }); + const Eip712TypeDetails({required this.name, required this.type}); factory Eip712TypeDetails.fromJson(Map json) { return Eip712TypeDetails(name: json["name"], type: json["type"]); @@ -49,6 +75,10 @@ class Eip712TypeDetails { String toString() { return "name: $name type: $type"; } + + Map toJson() { + return {"name": name, "type": type}; + } } /// Represents typed data for EIP-712, implementing the EIP712Base interface. @@ -99,14 +129,14 @@ class Eip712TypedData implements EIP712Base { message: json["message"], version: version); } catch (e) { - throw const MessageException("invalid EIP712 json struct"); + throw const SolidityAbiException("invalid EIP712 json struct."); } } /// Encodes the typed data into a bytes, optionally hashing the result. /// If [hash] is true (default), the result is hashed using Keccak-256. @override - List encode([bool hash = true]) { + List encode({bool hash = true}) { final List encode = [ ..._EIP712Utils.eip191PrefixBytes, ..._EIP712Utils.structHash(this, _EIP712Utils.domainKeyName, domain), @@ -119,8 +149,21 @@ class Eip712TypedData implements EIP712Base { return encode; } - String encodeHex([bool hash = true]) { - return BytesUtils.toHexString(encode(hash)); + @override + String encodeHex({bool hash = true}) { + return BytesUtils.toHexString(encode(hash: hash)); + } + + @override + Map toJson() { + return { + "types": + types.map((k, v) => MapEntry(k, v.map((e) => e.toJson()).toList())), + "domain": domain, + "message": message, + "primaryType": primaryType, + "version": version.version + }; } } @@ -151,6 +194,14 @@ class Eip712TypedDataV1 { /// The value of the typed data field. final dynamic value; + + Map toJson() { + return { + "name": name, + "type": type, + "value": _EIP712Utils.eip712TypedDataV1ValueToJson(type, value) + }; + } } /// Represents a typed data structure for EIP-712 version 1. @@ -159,9 +210,10 @@ class EIP712Legacy implements EIP712Base { /// Constructor for EIP712Legacy, accepting a list of Eip712TypedDataV1 instances. const EIP712Legacy(this.typesData); - factory EIP712Legacy.fromJson(List> messages) { - return EIP712Legacy( - messages.map((e) => Eip712TypedDataV1.fromJson(e)).toList()); + factory EIP712Legacy.fromJson(List messages) { + return EIP712Legacy(messages + .map((e) => Eip712TypedDataV1.fromJson((e as Map).cast())) + .toList()); } /// List of Eip712TypedDataV1 instances representing the typed data fields. @@ -173,7 +225,7 @@ class EIP712Legacy implements EIP712Base { /// Encodes the typed data structure into a list of integers using EIP-712 version 1. @override - List encode() { + List encode({bool hash = true}) { // Extract values, types, and names from Eip712TypedDataV1 instances final values = typesData.map((e) => e.value).toList(); final types = typesData.map((e) => e.type).toList(); @@ -183,11 +235,24 @@ class EIP712Legacy implements EIP712Base { QuickCrypto.keccack256Hash(_EIP712Utils.legacyV1encode(types, values)); final namesHash = QuickCrypto.keccack256Hash(_EIP712Utils.legacyV1encode( List.generate(names.length, (index) => "string"), names)); - return QuickCrypto.keccack256Hash(_EIP712Utils.legacyV1encode( - ['bytes32', 'bytes32'], [namesHash, typesHash])); + final toBytes = _EIP712Utils.legacyV1encode( + ['bytes32', 'bytes32'], [namesHash, typesHash]); + if (!hash) { + return toBytes; + } + return QuickCrypto.keccack256Hash(toBytes); } - String encodeHex() { - return BytesUtils.toHexString(encode()); + @override + String encodeHex({bool hash = true}) { + return BytesUtils.toHexString(encode(hash: hash)); + } + + @override + Map toJson() { + return { + "types": typesData.map((e) => e.toJson()).toList(), + "version": version.version + }; } } diff --git a/lib/solidity/abi/eip712/utils.dart b/lib/solidity/abi/eip712/utils.dart index b90e776..6d66d8d 100644 --- a/lib/solidity/abi/eip712/utils.dart +++ b/lib/solidity/abi/eip712/utils.dart @@ -22,11 +22,12 @@ class _EIP712Utils { /// Ensures the input value is represented as bytes, handling different types. static List ensureBytes(String type, dynamic value) { if (!type.startsWith("bytes")) { - throw MessageException("invalid bytes type", details: {"type": type}); + throw const SolidityAbiException( + "Invalid data provided for bytes codec."); } if (value is! String && value is! List) { - throw MessageException("invalid input for this type", - details: {"type": type, "value": value}); + throw const SolidityAbiException( + "Invalid data provided for bytes codec."); } if (value is List) return BytesUtils.toBytes(value); return StringUtils.toBytes(value); @@ -38,7 +39,7 @@ class _EIP712Utils { final childType = match?.group(1); if (match != null) { if (value is! List) { - throw MessageException("invalid array input for this type", + throw SolidityAbiException("Invalid data provided for array codec.", details: {"type": type, "value": value}); } return value.map((e) => ensureCorrectValues(childType!, e)).toList(); @@ -57,7 +58,39 @@ class _EIP712Utils { if (type.startsWith("bytes")) { return ensureBytes(type, value); } - throw MessageException("unsupported type ", details: {"type": type}); + throw SolidityAbiException("Unsuported type. codec not found.", + details: {"type": type}); + } + } + + /// Ensures correct representation of values based on the specified type. + static dynamic eip712TypedDataV1ValueToJson(String type, dynamic value) { + final match = arrayRegex.firstMatch(type); + final childType = match?.group(1); + if (match != null) { + if (value is! List) { + throw SolidityAbiException("Invalid data provided for array codec.", + details: {"type": type, "value": value}); + } + return value + .map((e) => eip712TypedDataV1ValueToJson(childType!, e)) + .toList(); + } + if (type.startsWith("uint") || type.startsWith("int")) { + return value.toString(); + } + switch (type) { + case "address": + if (value is TronAddress) { + return value.toAddress(); + } else { + return (value as ETHAddress).address; + } + case "bool": + case "string": + return value; + default: + return BytesUtils.toHexString(value, prefix: "0x"); } } @@ -65,7 +98,7 @@ class _EIP712Utils { /// If the value is already an ETHAddress instance, it is returned as-is. /// If the value is a list of integers, it is interpreted as bytes and converted to an ETHAddress or TronAddress. /// If the value is a string, it is parsed to create an ETHAddress or TronAddress. - /// Throws a MessageException for invalid input. + /// Throws a [SolidityAbiException] for invalid input. static SolidityAddress ensureIsAddress(dynamic value) { if (value is ETHAddress) return value; if (value is List) { @@ -80,23 +113,26 @@ class _EIP712Utils { return TronAddress(value); } } - throw MessageException("invalid address", details: {"input": value}); + throw SolidityAbiException("Invalid data provided for address codec.", + details: {"input": value}); } /// Ensures that the input value is a boolean. - /// Throws a MessageException for invalid input. + /// Throws a [SolidityAbiException] for invalid input. static bool ensureBoolean(dynamic value) { if (value is! bool) { - throw MessageException("invalid boolean", details: {"input": value}); + throw SolidityAbiException("Invalid data provided for boolean codec.", + details: {"input": value}); } return value; } /// Ensures that the input value is a string. - /// Throws a MessageException for invalid input. + /// Throws a [SolidityAbiException] for invalid input. static String ensureString(dynamic value) { if (value is! String) { - throw MessageException("invalid string", details: {"input": value}); + throw SolidityAbiException("invalid data provided for string codec.", + details: {"input": value}); } return value; } @@ -111,14 +147,13 @@ class _EIP712Utils { for (Eip712TypeDetails field in typedData.types[type]!) { if (data[field.name] == null) { if (typedData.version == EIP712Version.v3) continue; - throw MessageException( - 'Cannot encode data: missing data for ${field.name}', + throw SolidityAbiException( + 'Invalid Eip712TypedData data. data mising for field ${field.name}', details: {'data': data, 'field': field}); } dynamic value = data[field.name]; final encodedValue = encodeValue(typedData, field.type, value); - types.add(encodedValue.item1); inputBytes.add(encodedValue.item2); } @@ -172,13 +207,13 @@ class _EIP712Utils { final isArray = extractArrayType(type); if (isArray != null) { if (data is! List) { - throw MessageException('Cannot encode data: value is not of array type', + throw SolidityAbiException('Invalid data provided for array codec.', details: {'input': data}); } if (isArray.item2 > 0 && data.length != isArray.item2) { - throw MessageException( - 'Cannot encode data: expected length of ${isArray.item2}, but got ${data.length}', + throw SolidityAbiException( + 'Invalid array length: expected ${isArray.item2}, but got ${data.length}', details: {'input': data}, ); } @@ -200,7 +235,6 @@ class _EIP712Utils { type == "string" ? StringUtils.encode(data) : data; return Tuple(bytes32TypeName, QuickCrypto.keccack256Hash(bytesData)); } - return Tuple(type, data); } diff --git a/lib/solidity/abi/exception/abi_exception.dart b/lib/solidity/abi/exception/abi_exception.dart new file mode 100644 index 0000000..80c0ce6 --- /dev/null +++ b/lib/solidity/abi/exception/abi_exception.dart @@ -0,0 +1,11 @@ +part of "package:on_chain/solidity/abi/abi.dart"; + +class SolidityAbiException extends BlockchainUtilsException { + @override + final Map? details; + + @override + final String message; + + const SolidityAbiException(this.message, {this.details}); +} diff --git a/lib/solidity/abi/types/address.dart b/lib/solidity/abi/types/address.dart index d51f79a..7970380 100644 --- a/lib/solidity/abi/types/address.dart +++ b/lib/solidity/abi/types/address.dart @@ -18,7 +18,8 @@ class AddressCoder implements ABICoder { result: params.tronTypes ? TronAddress.fromEthAddress(addrBytes) : ETHAddress.fromBytes(addrBytes), - consumed: ABIConst.uintBytesLength); + consumed: ABIConst.uintBytesLength, + name: params.name); } /// Encodes a BaseHexAddress to ABI-encoded bytes. @@ -31,7 +32,7 @@ class AddressCoder implements ABICoder { addrBytes = addrBytes.sublist(TronAddress.lengthInBytes - addrLength); } bytes.setAll(ABIConst.uintBytesLength - addrLength, addrBytes); - return EncoderResult(isDynamic: false, encoded: bytes); + return EncoderResult(isDynamic: false, encoded: bytes, name: params.name); } /// Legacy EIP-712 encoding for BaseHexAddress. @@ -42,6 +43,7 @@ class AddressCoder implements ABICoder { if (keepSize) return abiEncode(params, input); List addrBytes = input.toBytes(); addrBytes = addrBytes.sublist(addrBytes.length - addrLength); - return EncoderResult(isDynamic: false, encoded: input.toBytes()); + return EncoderResult( + isDynamic: false, encoded: input.toBytes(), name: params.name); } } diff --git a/lib/solidity/abi/types/array.dart b/lib/solidity/abi/types/array.dart index 1ff5fe1..91b738e 100644 --- a/lib/solidity/abi/types/array.dart +++ b/lib/solidity/abi/types/array.dart @@ -13,7 +13,7 @@ class ArrayCoder implements ABICoder> { encodedParams.isNotEmpty && encodedParams.first.isDynamic; bool isDynamic = param.item2 == -1; if (!isDynamic && input.length != param.item2) { - throw _ABIValidator.invalidArgrumentsLength; + throw const SolidityAbiException("Invalid argument length detected."); } if (isDynamic || dynamicItems) { final encode = _ABIUtils.encodeDynamicParams(encodedParams); @@ -23,13 +23,16 @@ class ArrayCoder implements ABICoder> { .encoded; return EncoderResult( isDynamic: true, - encoded: encodedParams.isEmpty ? length : [...length, ...encode]); + encoded: encodedParams.isEmpty ? length : [...length, ...encode], + name: params.name); } - return EncoderResult(isDynamic: true, encoded: encode); + return EncoderResult(isDynamic: true, encoded: encode, name: params.name); } final resultBytes = encodedParams.map((e) => e.encoded); return EncoderResult( - isDynamic: false, encoded: [for (final i in resultBytes) ...i]); + isDynamic: false, + encoded: [for (final i in resultBytes) ...i], + name: params.name); } /// Decodes an ABI-encoded array of arbitrary types. @@ -56,7 +59,8 @@ class ArrayCoder implements ABICoder> { consumed += decodeChild.consumed; result.add(decodeChild.result); } - return DecoderResult(result: result, consumed: consumed); + return DecoderResult( + result: result, consumed: consumed, name: params.name); } for (int i = 0; i < size; i++) { final decodeChild = _ABIUtils.decodeParamFromAbiParameter( @@ -64,7 +68,7 @@ class ArrayCoder implements ABICoder> { consumed += decodeChild.consumed; result.add(decodeChild.result); } - return DecoderResult(result: result, consumed: consumed); + return DecoderResult(result: result, consumed: consumed, name: params.name); } /// Legacy EIP-712 encoding for arrays of arbitrary types. @@ -77,6 +81,8 @@ class ArrayCoder implements ABICoder> { input.map((e) => param.item1.legacyEip712Encode(e, true)).toList(); final resultBytes = encodedParams.map((e) => e.encoded); return EncoderResult( - isDynamic: false, encoded: [for (final i in resultBytes) ...i]); + isDynamic: false, + encoded: [for (final i in resultBytes) ...i], + name: params.name); } } diff --git a/lib/solidity/abi/types/boolean.dart b/lib/solidity/abi/types/boolean.dart index 1fba3d9..f4ed8fd 100644 --- a/lib/solidity/abi/types/boolean.dart +++ b/lib/solidity/abi/types/boolean.dart @@ -13,7 +13,9 @@ class BooleanCoder implements ABICoder { BigintUtils.fromBytes(bytes.sublist(0, ABIConst.uintBytesLength)); _ABIValidator.validateBoolean(params, toBigInt); return DecoderResult( - result: toBigInt == BigInt.one, consumed: ABIConst.uintBytesLength); + result: toBigInt == BigInt.one, + consumed: ABIConst.uintBytesLength, + name: params.name); } /// Encodes a boolean value to ABI-encoded bytes. @@ -23,7 +25,7 @@ class BooleanCoder implements ABICoder { if (input) { bytes[bytes.length - 1] = 1; } - return EncoderResult(isDynamic: false, encoded: bytes); + return EncoderResult(isDynamic: false, encoded: bytes, name: params.name); } /// Legacy EIP-712 encoding for boolean values. @@ -36,6 +38,6 @@ class BooleanCoder implements ABICoder { } final bytes = List.filled(1, 0); bytes[0] = input ? 1 : 0; - return EncoderResult(isDynamic: false, encoded: bytes); + return EncoderResult(isDynamic: false, encoded: bytes, name: params.name); } } diff --git a/lib/solidity/abi/types/bytes.dart b/lib/solidity/abi/types/bytes.dart index ee4002c..7d5d051 100644 --- a/lib/solidity/abi/types/bytes.dart +++ b/lib/solidity/abi/types/bytes.dart @@ -23,7 +23,8 @@ class BytesCoder implements ABICoder> { _ABIValidator.validateBytesLength(bytes, size); return DecoderResult( result: remainingBytes.sublist(0, size), - consumed: consumed + partsCount * ABIConst.uintBytesLength); + consumed: consumed + partsCount * ABIConst.uintBytesLength, + name: params.name); } /// Encodes a byte array to ABI-encoded bytes. @@ -38,14 +39,15 @@ class BytesCoder implements ABICoder> { .encoded; encoded.setAll(0, number); encoded.setAll(ABIConst.uintBytesLength, input); - return EncoderResult(isDynamic: true, encoded: encoded); + return EncoderResult( + isDynamic: true, encoded: encoded, name: params.name); } final size = _ABIUtils.bytesSize(params.type); _ABIValidator.validateBytes(params.type, bytes: input, minLength: size!, maxLength: size); final bytes = List.filled(ABIConst.uintBytesLength, 0); bytes.setAll(0, input); - return EncoderResult(isDynamic: false, encoded: bytes); + return EncoderResult(isDynamic: false, encoded: bytes, name: params.name); } /// Legacy EIP-712 encoding for byte arrays. @@ -55,8 +57,8 @@ class BytesCoder implements ABICoder> { AbiParameter params, List input, bool keepSize) { final size = _ABIUtils.bytesSize(params.type); if (size != null && input.length != size) { - throw _ABIValidator.invalidBytesLength; + throw const SolidityAbiException("Invalid bytes length"); } - return EncoderResult(isDynamic: false, encoded: input); + return EncoderResult(isDynamic: false, encoded: input, name: params.name); } } diff --git a/lib/solidity/abi/types/function.dart b/lib/solidity/abi/types/function.dart index da81359..30b087f 100644 --- a/lib/solidity/abi/types/function.dart +++ b/lib/solidity/abi/types/function.dart @@ -9,7 +9,8 @@ class FunctionCoder implements ABICoder> { @override DecoderResult> decode(AbiParameter params, List bytes) { final decode = const BytesCoder().decode(AbiParameter.function, bytes); - return DecoderResult(result: decode.result, consumed: decode.consumed); + return DecoderResult( + result: decode.result, consumed: decode.consumed, name: params.name); } /// Encodes a function signature (bytes) to ABI-encoded bytes. diff --git a/lib/solidity/abi/types/numbers.dart b/lib/solidity/abi/types/numbers.dart index 9e8bd0e..df2bbe2 100644 --- a/lib/solidity/abi/types/numbers.dart +++ b/lib/solidity/abi/types/numbers.dart @@ -13,7 +13,8 @@ class NumbersCoder implements ABICoder { final nBytes = bytes.sublist(0, ABIConst.uintBytesLength); final big = BigintUtils.fromBytes(nBytes, sign: sign); _ABIValidator.isValidNumber(params.type, big); - return DecoderResult(result: big, consumed: ABIConst.uintBytesLength); + return DecoderResult( + result: big, consumed: ABIConst.uintBytesLength, name: params.name); } /// Encodes a numeric value (BigInt) to ABI-encoded bytes. @@ -21,7 +22,9 @@ class NumbersCoder implements ABICoder { EncoderResult abiEncode(AbiParameter params, BigInt input) { _ABIValidator.isValidNumber(params.type, input); return EncoderResult( - isDynamic: false, encoded: BigintUtils.toBytes(input, length: 32)); + isDynamic: false, + encoded: BigintUtils.toBytes(input, length: 32), + name: params.name); } /// Legacy EIP-712 encoding for numeric values (BigInt). @@ -34,6 +37,7 @@ class NumbersCoder implements ABICoder { return EncoderResult( isDynamic: false, encoded: BigintUtils.toBytes(input.toUnsigned(size * 8), - length: keepSize ? 32 : size)); + length: keepSize ? 32 : size), + name: params.name); } } diff --git a/lib/solidity/abi/types/string.dart b/lib/solidity/abi/types/string.dart index bd73037..269e68f 100644 --- a/lib/solidity/abi/types/string.dart +++ b/lib/solidity/abi/types/string.dart @@ -10,7 +10,9 @@ class StringCoder implements ABICoder { DecoderResult decode(AbiParameter params, List bytes) { final decode = const BytesCoder().decode(AbiParameter.bytes, bytes); return DecoderResult( - result: StringUtils.decode(decode.result), consumed: decode.consumed); + result: StringUtils.decode(decode.result), + consumed: decode.consumed, + name: params.name); } /// Encodes a string value to ABI-encoded bytes. diff --git a/lib/solidity/abi/types/tuple.dart b/lib/solidity/abi/types/tuple.dart index f454f54..29a7784 100644 --- a/lib/solidity/abi/types/tuple.dart +++ b/lib/solidity/abi/types/tuple.dart @@ -11,7 +11,7 @@ class TupleCoder implements ABICoder> { bool isDynamic = false; List encoded = []; if (input.length != params.components.length) { - throw _ABIValidator.invalidArgrumentsLength; + throw const SolidityAbiException("Invalid argument length detected."); } for (int i = 0; i < params.components.length; i++) { final paramComponent = params.components[i]; @@ -23,10 +23,13 @@ class TupleCoder implements ABICoder> { } if (isDynamic) { return EncoderResult( - isDynamic: true, encoded: _ABIUtils.encodeDynamicParams(encoded)); + isDynamic: true, + encoded: _ABIUtils.encodeDynamicParams(encoded), + name: params.name); } final re = encoded.map((e) => e.encoded).toList(); - return EncoderResult(isDynamic: false, encoded: [for (var i in re) ...i]); + return EncoderResult( + isDynamic: false, encoded: [for (var i in re) ...i], name: params.name); } /// Decodes a tuple of dynamic values from the given ABI-encoded bytes. @@ -35,7 +38,7 @@ class TupleCoder implements ABICoder> { int consumed = 0; if (params.components.isEmpty) { - return DecoderResult(result: [], consumed: consumed); + return DecoderResult(result: [], consumed: consumed, name: params.name); } int dynamicConsumed = 0; List result = []; @@ -44,10 +47,8 @@ class TupleCoder implements ABICoder> { DecoderResult decodedResult; if (childParam.isDynamic) { - DecoderResult offsetResult = const NumbersCoder().decode( - AbiParameter.uint32, - bytes.sublist(consumed), - ); + DecoderResult offsetResult = const NumbersCoder() + .decode(AbiParameter.uint32, bytes.sublist(consumed)); decodedResult = _ABIUtils.decodeParamFromAbiParameter( childParam, @@ -64,7 +65,10 @@ class TupleCoder implements ABICoder> { result.add(decodedResult.result); } - return DecoderResult(result: result, consumed: consumed + dynamicConsumed); + return DecoderResult( + result: result, + consumed: consumed + dynamicConsumed, + name: params.name); } /// Legacy EIP-712 encoding for tuple values. @@ -74,7 +78,7 @@ class TupleCoder implements ABICoder> { AbiParameter params, List input, bool keepSize) { List encoded = []; if (input.length != params.components.length) { - throw _ABIValidator.invalidArgrumentsLength; + throw const SolidityAbiException("Invalid argument length detected."); } for (int i = 0; i < params.components.length; i++) { final paramComponent = params.components[i]; @@ -83,6 +87,7 @@ class TupleCoder implements ABICoder> { encoded.add(result); } final re = encoded.map((e) => e.encoded).toList(); - return EncoderResult(isDynamic: false, encoded: [for (var i in re) ...i]); + return EncoderResult( + isDynamic: false, encoded: [for (var i in re) ...i], name: params.name); } } diff --git a/lib/solidity/abi/utils/utils.dart b/lib/solidity/abi/utils/utils.dart index eeb634b..05bf4da 100644 --- a/lib/solidity/abi/utils/utils.dart +++ b/lib/solidity/abi/utils/utils.dart @@ -69,9 +69,11 @@ class _ABIUtils { String sizeString = abi.type.substring(arrayParenthesisStart); int size = -1; if (sizeString != '[]') { - size = int.parse(sizeString.substring(1, sizeString.length - 1)); - if (size.isNaN) { - throw ArgumentError('Invalid fixed array size'); + final parseSize = + int.tryParse(sizeString.substring(1, sizeString.length - 1)); + if (parseSize == null) { + throw const SolidityAbiException( + "Invalid array type name. size in invalid."); } } return Tuple( @@ -102,39 +104,28 @@ class _ABIValidator { /// Regular expression for detecting size in ABI type. static final RegExp sizeDetectRegex = RegExp(r'\d+'); - /// Exception message for invalid argument length. - static const MessageException invalidArgrumentsLength = - MessageException("Invalid argument length detected."); - - /// Exception message for invalid bytes parameter. - static const MessageException invalidBytesParam = - MessageException("Invalid bytes input"); - - /// Exception message for invalid bytes length. - static const MessageException invalidBytesLength = - MessageException("Invalid bytes length"); - /// Validates bytes based on type, checking for length constraints. /// - /// Throws [invalidBytesParam] if the type is not "bytes" and - /// [invalidBytesLength] if the length constraints are violated. + /// Throws [SolidityAbiException] if the type is not "bytes" and + /// [SolidityAbiException] if the length constraints are violated. static validateBytes(String typeName, {List? bytes, int? maxLength, int? minLength}) { if (typeName.contains("bytes")) { if (bytes != null) { if (maxLength != null) { if (bytes.length > maxLength) { - throw invalidBytesLength; + throw const SolidityAbiException("Invalid bytes length"); } } if (minLength != null) { if (bytes.length < minLength) { - throw invalidBytesLength; + throw const SolidityAbiException("Invalid bytes length"); } } } } else { - throw invalidBytesParam; + throw const SolidityAbiException( + "Invalid data provided for bytes codec."); } } @@ -144,25 +135,26 @@ class _ABIValidator { static bool isSignNumber(String type) { if (type.startsWith("int")) return true; if (type.startsWith("uint")) return false; - throw ArgumentException("invalid type expected int or uint got $type"); + throw const SolidityAbiException("Invalid integer type name."); } /// Validates a boolean type. /// - /// Throws [ArgumentException] if the type is "bool" and the value is not BigInt.one or BigInt.zero. + /// Throws [SolidityAbiException] if the type is "bool" and the value is not BigInt.one or BigInt.zero. static void validateBoolean(AbiParameter param, BigInt val) { if (param.type == "bool" && (val == BigInt.one || val == BigInt.zero)) { return; } - throw const ArgumentException("invalid boolean"); + throw const SolidityAbiException( + "Invalid data provided for boolean codec."); } /// Validates the length of bytes to avoid decoding errors. /// - /// Throws [ArgumentError] if there are not enough bytes left to decode. + /// Throws [SolidityAbiException] if there are not enough bytes left to decode. static void validateBytesLength(List bytes, int length) { if (bytes.length < length) { - throw ArgumentError("Not enough bytes left to decode"); + throw const SolidityAbiException("Not enough bytes left to decode"); } } @@ -171,7 +163,7 @@ class _ABIValidator { /// This method ensures that the bit length and sign of the provided [value] /// match the expected characteristics based on the given [type]. /// - /// Throws [MessageException] if the provided [value] does not match the + /// Throws [SolidityAbiException] if the provided [value] does not match the /// expected bit length and sign for the given [type]. static void isValidNumber(String type, BigInt value) { int bitLength; @@ -181,10 +173,14 @@ class _ABIValidator { final spl = type.split("int"); bitLength = int.parse(spl[1]); sign = true; - } else { + } else if (type.startsWith("uint")) { final spl = type.split("uint"); bitLength = int.parse(spl[1]); sign = true; + } else { + throw SolidityAbiException( + "Invalid type name provided for number codec.", + details: {"type": type, "value": value}); } if (sign) { @@ -196,9 +192,10 @@ class _ABIValidator { return; } } - // ignore: empty_catches - } catch (e) {} - throw MessageException("invalid number for this type", + } catch (e) { + if (e is SolidityAbiException) rethrow; + } + throw SolidityAbiException("Invalid data provided for number codec.", details: {"type": type, "value": value}); } } diff --git a/lib/solidity/contract/contract_abi.dart b/lib/solidity/contract/contract_abi.dart index e65ce44..f197868 100644 --- a/lib/solidity/contract/contract_abi.dart +++ b/lib/solidity/contract/contract_abi.dart @@ -61,13 +61,27 @@ class ContractABI { functions.singleWhere((element) => element.name == name); /// Retrieves a function fragment from the contract ABI based from selector. - AbiFunctionFragment functionFromSelector(String selectorHex) { - final selector = BytesUtils.fromHexString(selectorHex) - .sublist(0, ABIConst.selectorLength); + AbiFunctionFragment functionFromSelector(List selectorBytes) { + final selector = selectorBytes.sublist(0, ABIConst.selectorLength); return functions.singleWhere( (element) => BytesUtils.bytesEqual(selector, element.selector)); } + /// Retrieves a function fragment from the contract ABI based from selector. + AbiFunctionFragment functionFromSelectorHex(String selectorHex) { + return functionFromSelector(BytesUtils.fromHexString(selectorHex)); + } + + AbiFunctionFragment? findFunctionFromSelector(List selectorBytes) { + try { + final selector = selectorBytes.sublist(0, ABIConst.selectorLength); + return functions.singleWhere( + (element) => BytesUtils.bytesEqual(selector, element.selector)); + } on StateError { + return null; + } + } + /// Retrieves an error fragment from the contract ABI from selector. AbiErrorFragment errorFromSelector(String selectorHex) { final selector = BytesUtils.fromHexString(selectorHex) diff --git a/lib/solidity/contract/fragments.dart b/lib/solidity/contract/fragments.dart index 05f0521..096c8cc 100644 --- a/lib/solidity/contract/fragments.dart +++ b/lib/solidity/contract/fragments.dart @@ -155,6 +155,14 @@ class AbiFunctionFragment implements AbiBaseFragment { return abi.result; } + // /// Decodes the output of the function from the encoded output bytes. + // List decodeOutput(List encodedOutput) { + // final abi = + // AbiParameter(name: "", type: "tuple", components: List.from(outputs)) + // .decode(encodedOutput); + // return abi.result; + // } + /// Decodes the output of the function from the encoded hexadecimal string output. List decodeOutputHex(String encodedOutput) { return decodeOutput(BytesUtils.fromHexString(encodedOutput)); diff --git a/lib/tron/src/address/tron_address.dart b/lib/tron/src/address/tron_address.dart index e8da459..e8214e2 100644 --- a/lib/tron/src/address/tron_address.dart +++ b/lib/tron/src/address/tron_address.dart @@ -5,13 +5,13 @@ import 'package:on_chain/ethereum/src/address/evm_address.dart'; /// Class representing a Tron address, implementing the BaseHexAddress interface class TronAddress implements SolidityAddress { - /// Private constructor for internal use, initializing with address and hexAddress - const TronAddress._(this._address, this._hexAddress); - /// Private fields to store the address and its hexadecimal representation final String _address; final String _hexAddress; + /// Private constructor for internal use, initializing with address and hexAddress + const TronAddress._(this._address, this._hexAddress); + /// Factory method to create a TronAddress from a Tron public key represented as a list of integers factory TronAddress.fromPublicKey(List keyBytes) { try { @@ -99,4 +99,13 @@ class TronAddress implements SolidityAddress { String toHex() { return _hexAddress; } + + @override + operator ==(other) { + if (other is! TronAddress) return false; + return _address == other._address; + } + + @override + int get hashCode => _address.hashCode ^ _hexAddress.hashCode; } diff --git a/lib/tron/src/models/contract/account/account_create.dart b/lib/tron/src/models/contract/account/account_create.dart index ef75fd8..0a31013 100644 --- a/lib/tron/src/models/contract/account/account_create.dart +++ b/lib/tron/src/models/contract/account/account_create.dart @@ -30,7 +30,7 @@ class AccountCreateContract extends TronBaseContract { /// Account type. The external account type is Normal, and this field will not be displayed in the return value type: decode .getResult(3) - ?.to((e) => AccountType.fromValue(e)) ?? + ?.castTo((e) => AccountType.fromValue(e)) ?? AccountType.normal, ); } diff --git a/lib/tron/src/models/contract/account/account_permission_update_contract.dart b/lib/tron/src/models/contract/account/account_permission_update_contract.dart index 063a20c..dba3ba4 100644 --- a/lib/tron/src/models/contract/account/account_permission_update_contract.dart +++ b/lib/tron/src/models/contract/account/account_permission_update_contract.dart @@ -26,7 +26,7 @@ class AccountPermissionUpdateContract extends TronBaseContract { owner: Permission.deserialize(decode.getField(2)), witness: decode .getResult(3) - ?.to>((e) => Permission.deserialize(e)), + ?.castTo>((e) => Permission.deserialize(e)), actives: decode .getFields>(4) .map((e) => Permission.deserialize(e)) diff --git a/lib/tron/src/models/contract/account/authority.dart b/lib/tron/src/models/contract/account/authority.dart index e8d7b68..7d7a045 100644 --- a/lib/tron/src/models/contract/account/authority.dart +++ b/lib/tron/src/models/contract/account/authority.dart @@ -17,7 +17,7 @@ class Authority extends TronProtocolBufferImpl { return Authority( account: decode .getResult(1) - ?.to>((e) => AccountId.deserialize(e)), + ?.castTo>((e) => AccountId.deserialize(e)), permissionName: decode.getField(2)); } diff --git a/lib/tron/src/models/contract/balance/block_balance.dart b/lib/tron/src/models/contract/balance/block_balance.dart index 73a15b6..818499c 100644 --- a/lib/tron/src/models/contract/balance/block_balance.dart +++ b/lib/tron/src/models/contract/balance/block_balance.dart @@ -24,7 +24,7 @@ class BlockBalanceTrace extends TronProtocolBufferImpl { return BlockBalanceTrace( blockIdentifier: decode .getResult(1) - ?.to>( + ?.castTo>( (e) => BlockBalanceTraceBlockIdentifier.deserialize(e)), timestamp: decode.getField(2), ); diff --git a/lib/tron/src/models/contract/balance/delegate_resource_contract.dart b/lib/tron/src/models/contract/balance/delegate_resource_contract.dart index 7159469..3dc6181 100644 --- a/lib/tron/src/models/contract/balance/delegate_resource_contract.dart +++ b/lib/tron/src/models/contract/balance/delegate_resource_contract.dart @@ -19,7 +19,7 @@ class DelegateResourceContract extends TronBaseContract { final decode = TronProtocolBufferImpl.decode(bytes); return DelegateResourceContract( ownerAddress: TronAddress.fromBytes(decode.getField(1)), - resource: decode.getResult(2)?.to( + resource: decode.getResult(2)?.castTo( (e) => ResourceCode.fromValue(decode.getField(2))), balance: decode.getField(3), receiverAddress: TronAddress.fromBytes(decode.getField(4)), diff --git a/lib/tron/src/models/contract/balance/freez_balance_contract.dart b/lib/tron/src/models/contract/balance/freez_balance_contract.dart index 3195c44..26cddc6 100644 --- a/lib/tron/src/models/contract/balance/freez_balance_contract.dart +++ b/lib/tron/src/models/contract/balance/freez_balance_contract.dart @@ -27,7 +27,7 @@ class FreezeBalanceContract extends TronBaseContract { orElse: ResourceCode.bandWidth), receiverAddress: decode .getResult(15) - ?.to>((e) => TronAddress.fromBytes(e)), + ?.castTo>((e) => TronAddress.fromBytes(e)), ); } diff --git a/lib/tron/src/models/contract/balance/freez_balance_v2_contract.dart b/lib/tron/src/models/contract/balance/freez_balance_v2_contract.dart index 4fb07cc..e71ae90 100644 --- a/lib/tron/src/models/contract/balance/freez_balance_v2_contract.dart +++ b/lib/tron/src/models/contract/balance/freez_balance_v2_contract.dart @@ -25,7 +25,7 @@ class FreezeBalanceV2Contract extends TronBaseContract { frozenBalance: decode.getField(2), resource: decode .getResult(3) - ?.to((e) => ResourceCode.fromValue(e))); + ?.castTo((e) => ResourceCode.fromValue(e))); } /// Account address diff --git a/lib/tron/src/models/contract/balance/transaction_balance_trace_operation.dart b/lib/tron/src/models/contract/balance/transaction_balance_trace_operation.dart index 48dda27..bb575e9 100644 --- a/lib/tron/src/models/contract/balance/transaction_balance_trace_operation.dart +++ b/lib/tron/src/models/contract/balance/transaction_balance_trace_operation.dart @@ -23,7 +23,7 @@ class TransactionBalanceTraceOperation extends TronProtocolBufferImpl { operationIdentifier: decode.getField(1), address: decode .getResult(2) - ?.to>((e) => TronAddress.fromBytes(e)), + ?.castTo>((e) => TronAddress.fromBytes(e)), amount: decode.getField(3)); } diff --git a/lib/tron/src/models/contract/balance/undelegate_resource_contract.dart b/lib/tron/src/models/contract/balance/undelegate_resource_contract.dart index 1be0b77..7ed340d 100644 --- a/lib/tron/src/models/contract/balance/undelegate_resource_contract.dart +++ b/lib/tron/src/models/contract/balance/undelegate_resource_contract.dart @@ -29,7 +29,7 @@ class UnDelegateResourceContract extends TronBaseContract { balance: decode.getField(3), resource: decode .getResult(2) - ?.to((e) => ResourceCode.fromValue(e)), + ?.castTo((e) => ResourceCode.fromValue(e)), receiverAddress: TronAddress.fromBytes(decode.getField(4))); } diff --git a/lib/tron/src/models/contract/balance/unfreez_balance_contract.dart b/lib/tron/src/models/contract/balance/unfreez_balance_contract.dart index a138740..e12d4e9 100644 --- a/lib/tron/src/models/contract/balance/unfreez_balance_contract.dart +++ b/lib/tron/src/models/contract/balance/unfreez_balance_contract.dart @@ -21,7 +21,7 @@ class UnfreezeBalanceContract extends TronBaseContract { ownerAddress: TronAddress.fromBytes(decode.getField(1)), resource: decode .getResult(10) - ?.to((e) => ResourceCode.fromValue(e)), + ?.castTo((e) => ResourceCode.fromValue(e)), receiverAddress: TronAddress.fromBytes(decode.getField(15))); } diff --git a/lib/tron/src/models/contract/balance/unfreez_balance_v2_contract.dart b/lib/tron/src/models/contract/balance/unfreez_balance_v2_contract.dart index 9795eb7..35fd7b7 100644 --- a/lib/tron/src/models/contract/balance/unfreez_balance_v2_contract.dart +++ b/lib/tron/src/models/contract/balance/unfreez_balance_v2_contract.dart @@ -20,7 +20,7 @@ class UnfreezeBalanceV2Contract extends TronBaseContract { ownerAddress: TronAddress.fromBytes(decode.getField(1)), resource: decode .getResult(3) - ?.to((e) => ResourceCode.fromValue(e)), + ?.castTo((e) => ResourceCode.fromValue(e)), unfreezeBalance: decode.getField(2)); } diff --git a/lib/tron/src/models/contract/smart_contract/smart_contract.dart b/lib/tron/src/models/contract/smart_contract/smart_contract.dart index a154fa8..fea883c 100644 --- a/lib/tron/src/models/contract/smart_contract/smart_contract.dart +++ b/lib/tron/src/models/contract/smart_contract/smart_contract.dart @@ -29,7 +29,7 @@ class SmartContract extends TronProtocolBufferImpl { originAddress: TronAddress.fromBytes(decode.getField(1)), bytecode: decode.getField(4), callValue: decode.getField(5), - abi: decode.getResult(3)?.to>( + abi: decode.getResult(3)?.castTo>( (e) => SmartContractABI.deserialize(e)), consumeUserResourcePercent: decode.getField(6), name: decode.getField(7), @@ -39,7 +39,7 @@ class SmartContract extends TronProtocolBufferImpl { version: decode.getField(11), contractAddress: decode .getResult(2) - ?.to>((e) => TronAddress.fromBytes(e))); + ?.castTo>((e) => TronAddress.fromBytes(e))); } /// Create a new [SmartContract] instance with specified parameters. diff --git a/lib/tron/src/protbuf/decoder.dart b/lib/tron/src/protbuf/decoder.dart index 366a7ae..aed0827 100644 --- a/lib/tron/src/protbuf/decoder.dart +++ b/lib/tron/src/protbuf/decoder.dart @@ -203,7 +203,7 @@ extension QuickProtocolBufferResult on ProtocolBufferDecoderResult { }); } - E to(E Function(T e) toe) { + E castTo(E Function(T e) toe) { return toe(cast()); } } diff --git a/pubspec.yaml b/pubspec.yaml index 2fb5d6b..4e13c02 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: on_chain description: Streamline Ethereum, Tron, Solana and Cardano operations. Effortlessly create transactions, interact with smart contracts, sign, and send transactions. -version: 3.7.0 +version: 3.8.0 homepage: "https://github.com/mrtnetwork/on_chain" repository: "https://github.com/mrtnetwork/on_chain" Author: mrhaydari.t@gmail.com @@ -23,7 +23,7 @@ dependencies: dev_dependencies: - test: ^1.25.2 + test: ^1.25.8 lints: ^4.0.0 flutter_lints: ^4.0.0 # For information on the generic Dart part of this file, see the diff --git a/test/abi/eip712/v1_test.dart b/test/abi/eip712/v1_test.dart index 02809af..ab59705 100644 --- a/test/abi/eip712/v1_test.dart +++ b/test/abi/eip712/v1_test.dart @@ -11,6 +11,9 @@ void main() { ]); expect(eip.encodeHex(), "7bcdd3ffeab400ef2294ebf69b2defd370bd29ac576c2569b7ea2c48f49ab1ae"); + EIP712Base json = EIP712Base.fromJson(eip.toJson()); + expect(json.encodeHex(), + "7bcdd3ffeab400ef2294ebf69b2defd370bd29ac576c2569b7ea2c48f49ab1ae"); final eip2 = EIP712Legacy.fromJson([ {"type": "string", "name": "Message", "value": "Hi, Alice!"}, @@ -24,6 +27,9 @@ void main() { ]); expect(eip2.encodeHex(), "587de5fa3769d620daa02c842288a6a11279f07cc7d0ada7ed947757206097c2"); + json = EIP712Base.fromJson(eip2.toJson()); + expect(json.encodeHex(), + "587de5fa3769d620daa02c842288a6a11279f07cc7d0ada7ed947757206097c2"); final eip3 = EIP712Legacy.fromJson([ {"type": "string", "name": "Message", "value": "Hi, Alice!"}, {"type": "bool", "name": "bool", "value": false}, @@ -36,6 +42,9 @@ void main() { ]); expect(eip3.encodeHex(), "276933d1b5ed61a561d7fcc1278954351104dac0531de663ac19b04523477087"); + json = EIP712Base.fromJson(eip3.toJson()); + expect(json.encodeHex(), + "276933d1b5ed61a561d7fcc1278954351104dac0531de663ac19b04523477087"); final eip4 = EIP712Legacy.fromJson([ {"type": "string", "name": "Message", "value": "Hi, Alice!"}, {"type": "bool", "name": "bool", "value": false}, @@ -63,6 +72,9 @@ void main() { ]); expect(eip4.encodeHex(), "8c2f569069fb99db364df030056a376c06015c6753dbab3274fe3a26b0b6e1e9"); + json = EIP712Base.fromJson(eip4.toJson()); + expect(json.encodeHex(), + "8c2f569069fb99db364df030056a376c06015c6753dbab3274fe3a26b0b6e1e9"); final eip5 = EIP712Legacy.fromJson([ {"type": "string", "name": "Message", "value": "Hi, Alice!"}, {"type": "bool", "name": "bool", "value": false}, @@ -100,6 +112,9 @@ void main() { ]); expect(eip5.encodeHex(), "b404f458127884da5d8987d62ffbed79121d320c898adff167f865ac4f1fe1bf"); + json = EIP712Base.fromJson(eip5.toJson()); + expect(json.encodeHex(), + "b404f458127884da5d8987d62ffbed79121d320c898adff167f865ac4f1fe1bf"); final eip6 = EIP712Legacy.fromJson([ {"type": "string", "name": "Message", "value": "Hi, Alice!"}, {"type": "bool", "name": "bool", "value": false}, @@ -147,6 +162,9 @@ void main() { ]); expect(eip6.encodeHex(), "3241a9139b03132118b331f536f876b4c5406b91c36f0828ad9dabb6fceff284"); + json = EIP712Base.fromJson(eip6.toJson()); + expect(json.encodeHex(), + "3241a9139b03132118b331f536f876b4c5406b91c36f0828ad9dabb6fceff284"); final eip7 = EIP712Legacy.fromJson([ {"type": "string", "name": "Message", "value": "Hi, Alice!"}, {"type": "bool", "name": "bool", "value": false}, @@ -204,6 +222,9 @@ void main() { ]); expect(eip7.encodeHex(), "f5d4906262038a59437eef1a975a89669c4feb648556178550b1ab2c35b2eb9c"); + json = EIP712Base.fromJson(eip7.toJson()); + expect(json.encodeHex(), + "f5d4906262038a59437eef1a975a89669c4feb648556178550b1ab2c35b2eb9c"); final eip8 = EIP712Legacy.fromJson([ {"type": "int256", "name": "Message", "value": "-250000000000"}, { @@ -214,5 +235,8 @@ void main() { ]); expect(eip8.encodeHex(), "2049bac5b8074309076b343a0fbdb8d021b8312496753e710d970abae91e5d77"); + json = EIP712Base.fromJson(eip8.toJson()); + expect(json.encodeHex(), + "2049bac5b8074309076b343a0fbdb8d021b8312496753e710d970abae91e5d77"); }); } diff --git a/test/abi/eip712/v4_test.dart b/test/abi/eip712/v4_test.dart index a69b352..6c29661 100644 --- a/test/abi/eip712/v4_test.dart +++ b/test/abi/eip712/v4_test.dart @@ -43,6 +43,10 @@ void main() { expect(test1.encodeHex(), "133a8cb1be5f78d001317fe4426e33a19b1dfc687f3da76d11fc8f65884f282f"); + EIP712Base json = EIP712Base.fromJson(test1.toJson()); + + expect(json.encodeHex(), + "133a8cb1be5f78d001317fe4426e33a19b1dfc687f3da76d11fc8f65884f282f"); final Eip712TypedData test2 = Eip712TypedData.fromJson({ "domain": { @@ -70,6 +74,9 @@ void main() { }); expect(test2.encodeHex(), "c782eff790ec37ff428c9718c596f8113fd6c461043196505fd9b6a917204907"); + json = EIP712Base.fromJson(test2.toJson()); + expect(json.encodeHex(), + "c782eff790ec37ff428c9718c596f8113fd6c461043196505fd9b6a917204907"); }); test("v3", () { @@ -77,7 +84,6 @@ void main() { "domain": { "chainId": BigInt.from(80001), "name": "Example App", - // "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1", }, "message": { @@ -99,5 +105,9 @@ void main() { }, version: EIP712Version.v3); expect(test1.encodeHex(), "184dd59e6ca7b7b98ad9b5e02c0e38ab6951bdd854a16d2cf7aaaf6a5ad485d3"); + + EIP712Base json = EIP712Base.fromJson(test1.toJson()); + expect(json.encodeHex(), + "184dd59e6ca7b7b98ad9b5e02c0e38ab6951bdd854a16d2cf7aaaf6a5ad485d3"); }); }