Effect API
Advanced Effect API
Loop decoder also exposes Effect API interfaces.
To get started with using the Decoder, first, you have to provide the RPC Provider and a ContractLoader Service.
- Create an RPC Provider
import { PublicClient, PublicClientObject } from "@3loop/transaction-decoder";import { Effect } from "effect";
const getPublicClient = ( chainID: number,): Effect.Effect<never, UnknownNetwork, PublicClientObject> => { if (chainID === 5) { return Effect.succeed({ client: createPublicClient({ transport: http(GOERLI_RPC), }), }); } return Effect.fail(new UnknownNetwork(chainID));};
- Create the AbiStore
AbiStore
serves as a repository for obtaining and caching the contract ABI necessary for decoding transaction data. In a real-world scenario, it may be preferable to retrieve this data from a database. In the following example, we will be hardcoding all the necessary information.
To create a new AbiStore
service you will need to implement two methods set
and get
.
const AbiStoreLive = Layer.succeed( AbiStore, AbiStore.of({ strategies: { default: [] }, set: ({ address = {}, func = {}, event = {} }) => Effect.sync(() => { // NOTE: Ignore caching as we relay only on local abis }), get: ({ address, signature, event }) => Effect.sync(() => { const signatureAbiMap = { "0x3593564c": "execute(bytes,bytes[],uint256)", "0x0902f1ac": "getReserves()", "0x36c78516": "transferFrom(address,address,uint160,address) ", "0x70a08231": "balanceOf(address)", "0x022c0d9f": "swap(uint256,uint256,address,bytes)", "0x2e1a7d4d": "withdraw(uint256)", };
const abi = signatureAbiMap[signature];
if (abi) { return abi; }
return null; }), }),);
- Create the ContractMetaStore
Similarly to AbiStore, but returns all the contract meta data
export const MetaStoreLive = Layer.succeed( ContractMetaStore, ContractMetaStore.of({ get: ({ address, chainID }) => Effect.sync(() => { return { address: request.address, chainID: request.chainID, contractName: 'Mock Contract', contractAddress: request.address, tokenSymbol: 'MOCK', decimals: 18, type: ContractType.ERC20, } }), set: ({ address, chainID }) => Effect.sync(() => { // NOTE: Ignore for now }), })
- Create a context using the services we created above
const LoadersLayer = Layer.provideMerge(AbiStoreLive, MetaStoreLive);const PublicClientLive = Layer.succeed( PublicClient, PublicClient.of({ _tag: "PublicClient", getPublicClient: getPublicClient }),);
const MainLayer = Layer.provideMerge(PublicClientLive, LoadersLayer);
- Fetch and decode a transaction
const program = Effect.gen(function* (_) { const hash = "0xab701677e5003fa029164554b81e01bede20b97eda0e2595acda81acf5628f75"; const chainID = 5;
return yield* _(decodeTransactionByHash(hash, chainID));});
- Finally provide the context and run the program
const customRuntime = pipe( Layer.toRuntime(MainLayer), Effect.scoped, Effect.runSync,);const result = await program.pipe( Effect.provideSomeRuntime(customRuntime), Effect.runPromise,);