|
| 1 | +--- |
| 2 | +title: SQL Data Store |
| 3 | +description: An implementation of the Data Store interface using a SQL database based on @effect/sql |
| 4 | +sidebar: |
| 5 | + order: 4 |
| 6 | +--- |
| 7 | + |
| 8 | +To use the default SQL stores you can import `SqlAbiStore` and `SqlContractMetaStore ` from the `@3loop/transaction-decoder/sql`. |
| 9 | + |
| 10 | +Given that the SQL stores are based on `@effect/sql` it inherits its SQL Client abstraction. For example we will use a Sqlite client for bun: `SqliteClient` from `@effect/sql-sqlite-bun` package. |
| 11 | + |
| 12 | +### Example |
| 13 | + |
| 14 | +This example implements a CLI that will use Sqlite as a cache for the ABIs and Contract Metadata stores. It will decode any transaction by chain id an transaction hash. The more its is used the more data will be cached in the database, thus making it faster to decode transactions. |
| 15 | + |
| 16 | +You can add it directly into your project or create a new one. |
| 17 | + |
| 18 | +```shell |
| 19 | +$ mkdir transaction-decoder-cli && cd transaction-decoder-cli && bun init |
| 20 | +``` |
| 21 | + |
| 22 | +We will start by installing the necessary dependencies: |
| 23 | + |
| 24 | +```shell |
| 25 | +$ bun i viem effect @effect/sql @effect/sql-sqlite-bun @3loop/transaction-decoder |
| 26 | +``` |
| 27 | + |
| 28 | +Then we will create a `index.ts` file with the following content: |
| 29 | + |
| 30 | +```typescript |
| 31 | +import { SqlAbiStore, SqlContractMetaStore } from '@3loop/transaction-decoder/sql' |
| 32 | +import { |
| 33 | + decodeTransactionByHash, |
| 34 | + EtherscanV2StrategyResolver, |
| 35 | + FourByteStrategyResolver, |
| 36 | + PublicClient, |
| 37 | +} from '@3loop/transaction-decoder' |
| 38 | +import { SqliteClient } from '@effect/sql-sqlite-bun' |
| 39 | +import { Effect, Layer } from 'effect' |
| 40 | +import { createPublicClient, http, type Hex } from 'viem' |
| 41 | + |
| 42 | +const AbiStoreLive = SqlAbiStore.make({ |
| 43 | + default: [ |
| 44 | + EtherscanV2StrategyResolver({ |
| 45 | + apikey: process.env.ETHERSCAN_API_KEY, |
| 46 | + }), |
| 47 | + FourByteStrategyResolver(), |
| 48 | + ], |
| 49 | +}) |
| 50 | + |
| 51 | +const SqlLive = SqliteClient.layer({ |
| 52 | + filename: 'db.sqlite', |
| 53 | +}) |
| 54 | + |
| 55 | +export const RPCProviderLive = Layer.succeed( |
| 56 | + PublicClient, |
| 57 | + PublicClient.of({ |
| 58 | + _tag: 'PublicClient', |
| 59 | + getPublicClient: (chainID: number) => { |
| 60 | + return Effect.succeed({ |
| 61 | + client: createPublicClient({ |
| 62 | + transport: http(process.env[`RPC_${chainID}`]), |
| 63 | + }), |
| 64 | + config: { |
| 65 | + traceAPI: 'none', |
| 66 | + }, |
| 67 | + }) |
| 68 | + }, |
| 69 | + }), |
| 70 | +) |
| 71 | + |
| 72 | +const MetaStoreLive = SqlContractMetaStore.make() |
| 73 | +const DataLayer = Layer.mergeAll(RPCProviderLive, SqlLive) |
| 74 | +const LoadersLayer = Layer.mergeAll(AbiStoreLive, MetaStoreLive) |
| 75 | +const MainLayer = Layer.provideMerge(LoadersLayer, DataLayer) |
| 76 | + |
| 77 | +function main() { |
| 78 | + const [, , chainID, hash] = Bun.argv |
| 79 | + |
| 80 | + const runnable = Effect.provide(decodeTransactionByHash(hash as Hex, Number(chainID)), MainLayer) |
| 81 | + |
| 82 | + Effect.runPromise(runnable) |
| 83 | + .then(console.log) |
| 84 | + .catch((error: unknown) => { |
| 85 | + console.error('Decode error', JSON.stringify(error, null, 2)) |
| 86 | + return undefined |
| 87 | + }) |
| 88 | +} |
| 89 | + |
| 90 | +main() |
| 91 | +``` |
| 92 | + |
| 93 | +Now you can run this script with bun: |
| 94 | + |
| 95 | +``` |
| 96 | +$ ETHERSCAN_API_KEY='YOUR_API_KEY' RPC_1=https://rpc.ankr.com/eth bun run index.ts 1 0xc0bd04d7e94542e58709f51879f64946ff4a744e1c37f5f920cea3d478e115d7 |
| 97 | +``` |
0 commit comments