diff --git a/packages/atst/src/constants/defaultRpcUrl.ts b/packages/atst/src/constants/defaultRpcUrl.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4c15dc4f0913a430936de709d0a3ad3b186d902
--- /dev/null
+++ b/packages/atst/src/constants/defaultRpcUrl.ts
@@ -0,0 +1,5 @@
+/**
+ * @internal
+ * Default RPC URL for Optimism
+ */
+export const DEFAULT_RPC_URL = 'https://mainnet.optimism.io'
diff --git a/packages/atst/src/index.ts b/packages/atst/src/index.ts
index 533c9a8bde83ed83d22792d69ba52a1565afba71..1c5b0793ac7ef12983068578340945a965c8009d 100644
--- a/packages/atst/src/index.ts
+++ b/packages/atst/src/index.ts
@@ -1,2 +1,15 @@
 // constants
 export { ATTESTATION_STATION_ADDRESS } from './constants/attestationStationAddress'
+// lib
+export { readAttestation } from './lib/readAttestation'
+export { readAttestations } from './lib/readAttestations'
+export { prepareWriteAttestation } from './lib/prepareWriteAttestation'
+export { prepareWriteAttestations } from './lib/prepareWriteAttestations'
+export { writeAttestation } from './lib/writeAttestation'
+export { abi } from './lib/abi'
+export { parseAttestationBytes } from './lib/parseAttestationBytes'
+export { stringifyAttestationBytes } from './lib/stringifyAttestationBytes'
+// types
+export type { AttestationReadParams } from './types/AttestationReadParams'
+export type { WagmiBytes } from './types/WagmiBytes'
+export type { DataTypeOption } from './types/DataTypeOption'
diff --git a/packages/atst/src/lib/__snapshots__/logger.spec.ts.snap b/packages/atst/src/lib/__snapshots__/logger.spec.ts.snap
new file mode 100644
index 0000000000000000000000000000000000000000..7f267959435955c2afc382e36a1b11be3b052a6e
--- /dev/null
+++ b/packages/atst/src/lib/__snapshots__/logger.spec.ts.snap
@@ -0,0 +1,11 @@
+// Vitest Snapshot v1
+
+exports[`logger > \${level}() > logs message "error" 1`] = `"error"`;
+
+exports[`logger > \${level}() > logs message "info" 1`] = `"info"`;
+
+exports[`logger > \${level}() > logs message "log" 1`] = `"log"`;
+
+exports[`logger > \${level}() > logs message "success" 1`] = `"success"`;
+
+exports[`logger > \${level}() > logs message "warn" 1`] = `"warn"`;
diff --git a/packages/atst/src/lib/parseAttestationBytes.spec.ts b/packages/atst/src/lib/parseAttestationBytes.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..738a64b946e98709f4a532aa50c216262fc9ec66
--- /dev/null
+++ b/packages/atst/src/lib/parseAttestationBytes.spec.ts
@@ -0,0 +1,42 @@
+import { BigNumber } from 'ethers'
+import { toUtf8Bytes } from 'ethers/lib/utils.js'
+import { expect, describe, it } from 'vitest'
+
+import { WagmiBytes } from '../types/WagmiBytes'
+import { parseAttestationBytes } from './parseAttestationBytes'
+
+describe(parseAttestationBytes.name, () => {
+  it('works for strings', () => {
+    const str = 'Hello World'
+    const bytes = BigNumber.from(toUtf8Bytes(str)).toHexString() as WagmiBytes
+    expect(parseAttestationBytes(bytes, 'string')).toBe(str)
+  })
+
+  it('works for numbers', () => {
+    const num = 123
+    const bytes = BigNumber.from(num).toHexString() as WagmiBytes
+    expect(parseAttestationBytes(bytes, 'number')).toBe(num.toString())
+  })
+
+  it('works for addresses', () => {
+    const addr = '0x1234567890123456789012345678901234567890'
+    const bytes = BigNumber.from(addr).toHexString() as WagmiBytes
+    expect(parseAttestationBytes(bytes, 'address')).toBe(addr)
+  })
+
+  it('works for booleans', () => {
+    const bytes = BigNumber.from(1).toHexString() as WagmiBytes
+    expect(parseAttestationBytes(bytes, 'bool')).toBe('true')
+  })
+
+  it('should work for raw bytes', () => {
+    const bytes = '0x420'
+    expect(parseAttestationBytes(bytes, 'bytes')).toBe(bytes)
+  })
+
+  it('should return raw bytes for invalid type', () => {
+    const bytes = '0x420'
+    // @ts-expect-error - this is a test for an error case
+    expect(parseAttestationBytes(bytes, 'foo')).toBe(bytes)
+  })
+})
diff --git a/packages/atst/src/lib/parseAttestationBytes.ts b/packages/atst/src/lib/parseAttestationBytes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..46de7fea14007767d7cf5478364c3737435f8646
--- /dev/null
+++ b/packages/atst/src/lib/parseAttestationBytes.ts
@@ -0,0 +1,28 @@
+import { BigNumber } from 'ethers'
+import { toUtf8String } from 'ethers/lib/utils.js'
+
+import type { DataTypeOption } from '../types/DataTypeOption'
+import type { WagmiBytes } from '../types/WagmiBytes'
+
+export const parseAttestationBytes = (
+  attestationBytes: WagmiBytes,
+  dataType: DataTypeOption
+) => {
+  if (dataType === 'bytes') {
+    return attestationBytes
+  }
+  if (dataType === 'number') {
+    return BigNumber.from(attestationBytes).toString()
+  }
+  if (dataType === 'address') {
+    return BigNumber.from(attestationBytes).toHexString()
+  }
+  if (dataType === 'bool') {
+    return BigNumber.from(attestationBytes).gt(0) ? 'true' : 'false'
+  }
+  if (dataType === 'string') {
+    return attestationBytes && toUtf8String(attestationBytes)
+  }
+  console.warn(`unrecognized dataType ${dataType satisfies never}`)
+  return attestationBytes
+}
diff --git a/packages/atst/src/lib/prepareWriteAttestation.spec.ts b/packages/atst/src/lib/prepareWriteAttestation.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..750348fd6c852da1851a351ff647c24a636803b1
--- /dev/null
+++ b/packages/atst/src/lib/prepareWriteAttestation.spec.ts
@@ -0,0 +1,71 @@
+import { connect, createClient } from '@wagmi/core'
+import { providers, Wallet } from 'ethers'
+import { expect, describe, it, beforeAll } from 'vitest'
+import { MockConnector } from '@wagmi/core/connectors/mock'
+
+import { prepareWriteAttestation } from './prepareWriteAttestation'
+import { readAttestation } from './readAttestation'
+
+const creator = '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3'
+const about = '0x2335022c740d17c2837f9C884Bfe4fFdbf0A95D5'
+const key = 'optimist.base-uri'
+
+const chainId = 10
+
+const provider = new providers.JsonRpcProvider(
+  {
+    url: 'http://localhost:8545',
+  },
+  chainId
+)
+
+const wallet = Wallet.createRandom({ provider })
+
+createClient({
+  provider,
+})
+
+beforeAll(async () => {
+  await connect({
+    connector: new MockConnector({
+      options: {
+        chainId,
+        signer: new Wallet(wallet.privateKey, provider),
+      },
+    }),
+  })
+})
+
+describe(prepareWriteAttestation.name, () => {
+  it('Should correctly prepare an attestation', async () => {
+    const result = await prepareWriteAttestation(about, key, 'hello world')
+
+    expect(result.address).toMatchInlineSnapshot(
+      '"0xEE36eaaD94d1Cc1d0eccaDb55C38bFfB6Be06C77"'
+    )
+    expect(result.chainId).toMatchInlineSnapshot('10')
+    expect(result.functionName).toMatchInlineSnapshot('"attest"')
+    expect(result.mode).toMatchInlineSnapshot('"prepared"')
+    expect(result.request.gasLimit).toMatchInlineSnapshot(`
+      {
+        "hex": "0xd6c9",
+        "type": "BigNumber",
+      }
+    `)
+  })
+
+  it('should throw an error if key is longer than 32 bytes', async () => {
+    const dataType = 'string'
+
+    await expect(
+      readAttestation(
+        creator,
+        about,
+        'this is a key that is way longer than 32 bytes so this key should throw an error matching the inline snapshot',
+        dataType
+      )
+    ).rejects.toThrowErrorMatchingInlineSnapshot(
+      '"Key is longer than the max length of 32 for attestation keys"'
+    )
+  })
+})
diff --git a/packages/atst/src/lib/prepareWriteAttestation.ts b/packages/atst/src/lib/prepareWriteAttestation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c3e9eebbdda12fc26d9b3b826b1038f24f823076
--- /dev/null
+++ b/packages/atst/src/lib/prepareWriteAttestation.ts
@@ -0,0 +1,24 @@
+import { Address, prepareWriteContract } from '@wagmi/core'
+import { formatBytes32String } from 'ethers/lib/utils.js'
+
+import { ATTESTATION_STATION_ADDRESS } from '../constants/attestationStationAddress'
+import { WagmiBytes } from '../types/WagmiBytes'
+import { abi } from './abi'
+import { stringifyAttestationBytes } from './stringifyAttestationBytes'
+
+export const prepareWriteAttestation = async (
+  about: Address,
+  key: string,
+  value: string | WagmiBytes | number | boolean,
+  chainId = 10,
+  contractAddress: Address = ATTESTATION_STATION_ADDRESS
+) => {
+  const formattedKey = formatBytes32String(key) as WagmiBytes
+  return prepareWriteContract({
+    address: contractAddress,
+    abi,
+    functionName: 'attest',
+    chainId,
+    args: [about, formattedKey, stringifyAttestationBytes(value) as WagmiBytes],
+  })
+}
diff --git a/packages/atst/src/lib/prepareWriteAttestations.spec.ts b/packages/atst/src/lib/prepareWriteAttestations.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bb49dbe21373eb704a54aa2c0055be9084be5304
--- /dev/null
+++ b/packages/atst/src/lib/prepareWriteAttestations.spec.ts
@@ -0,0 +1,77 @@
+import { connect, createClient } from '@wagmi/core'
+import { providers, Wallet } from 'ethers'
+import { expect, describe, it, beforeAll } from 'vitest'
+import { MockConnector } from '@wagmi/core/connectors/mock'
+
+import { readAttestation } from './readAttestation'
+import { prepareWriteAttestations } from './prepareWriteAttestations'
+
+const creator = '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3'
+const about = '0x2335022c740d17c2837f9C884Bfe4fFdbf0A95D5'
+const key = 'optimist.base-uri'
+
+const chainId = 10
+
+const provider = new providers.JsonRpcProvider(
+  {
+    url: 'http://localhost:8545',
+  },
+  chainId
+)
+
+const wallet = Wallet.createRandom({ provider })
+
+createClient({
+  provider,
+})
+
+beforeAll(async () => {
+  await connect({
+    connector: new MockConnector({
+      options: {
+        chainId,
+        signer: new Wallet(wallet.privateKey, provider),
+      },
+    }),
+  })
+})
+
+describe(prepareWriteAttestations.name, () => {
+  it('Should correctly prepare an attestation', async () => {
+    const result = await prepareWriteAttestations([
+      {
+        about,
+        key,
+        value: 'hello world',
+      },
+    ])
+
+    expect(result.address).toMatchInlineSnapshot(
+      '"0xEE36eaaD94d1Cc1d0eccaDb55C38bFfB6Be06C77"'
+    )
+    expect(result.chainId).toMatchInlineSnapshot('10')
+    expect(result.functionName).toMatchInlineSnapshot('"attest"')
+    expect(result.mode).toMatchInlineSnapshot('"prepared"')
+    expect(result.request.gasLimit).toMatchInlineSnapshot(`
+      {
+        "hex": "0xd9ce",
+        "type": "BigNumber",
+      }
+    `)
+  })
+
+  it('should throw an error if key is longer than 32 bytes', async () => {
+    const dataType = 'string'
+
+    await expect(
+      readAttestation(
+        creator,
+        about,
+        'this is a key that is way longer than 32 bytes so this key should throw an error matching the inline snapshot',
+        dataType
+      )
+    ).rejects.toThrowErrorMatchingInlineSnapshot(
+      '"Key is longer than the max length of 32 for attestation keys"'
+    )
+  })
+})
diff --git a/packages/atst/src/lib/prepareWriteAttestations.ts b/packages/atst/src/lib/prepareWriteAttestations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..da2162597ed1d4faa89224315a9bbfdcf0a13cd5
--- /dev/null
+++ b/packages/atst/src/lib/prepareWriteAttestations.ts
@@ -0,0 +1,38 @@
+import { Address, prepareWriteContract } from '@wagmi/core'
+import { formatBytes32String } from 'ethers/lib/utils.js'
+
+import { ATTESTATION_STATION_ADDRESS } from '../constants/attestationStationAddress'
+import { WagmiBytes } from '../types/WagmiBytes'
+import { abi } from './abi'
+import { stringifyAttestationBytes } from './stringifyAttestationBytes'
+
+type Attestation = {
+  about: Address
+  key: string
+  value: string | WagmiBytes | number | boolean
+}
+
+export const prepareWriteAttestations = async (
+  attestations: Attestation[],
+  chainId = 10,
+  contractAddress: Address = ATTESTATION_STATION_ADDRESS
+) => {
+  const formattedAttestations = attestations.map((attestation) => {
+    const formattedKey = formatBytes32String(attestation.key) as WagmiBytes
+    const formattedValue = stringifyAttestationBytes(
+      attestation.value
+    ) as WagmiBytes
+    return {
+      about: attestation.about,
+      key: formattedKey,
+      val: formattedValue,
+    } as const
+  })
+  return prepareWriteContract({
+    address: contractAddress,
+    abi,
+    functionName: 'attest',
+    chainId,
+    args: [formattedAttestations],
+  })
+}
diff --git a/packages/atst/src/lib/readAttestation.spec.ts b/packages/atst/src/lib/readAttestation.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5df88a8852163999ffab44bb5287f9eb0fc559f6
--- /dev/null
+++ b/packages/atst/src/lib/readAttestation.spec.ts
@@ -0,0 +1,41 @@
+import { createClient } from '@wagmi/core'
+import { providers } from 'ethers'
+import { expect, describe, it } from 'vitest'
+
+import { readAttestation } from './readAttestation'
+
+const creator = '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3'
+const about = '0x2335022c740d17c2837f9C884Bfe4fFdbf0A95D5'
+const key = 'optimist.base-uri'
+const dataType = 'string'
+
+const provider = new providers.JsonRpcProvider({
+  url: 'http://localhost:8545',
+})
+
+createClient({
+  provider,
+})
+
+describe(readAttestation.name, () => {
+  it('should return the attestation from attestation station', async () => {
+    const result = await readAttestation(creator, about, key, dataType)
+
+    expect(result).toMatchInlineSnapshot(
+      '"https://assets.optimism.io/4a609661-6774-441f-9fdb-453fdbb89931-bucket/optimist-nft/attributes"'
+    )
+  })
+
+  it('should throw an error if key is longer than 32 bytes', async () => {
+    await expect(
+      readAttestation(
+        creator,
+        about,
+        'this is a key that is way longer than 32 bytes so this key should throw an error matching the inline snapshot',
+        dataType
+      )
+    ).rejects.toThrowErrorMatchingInlineSnapshot(
+      '"Key is longer than the max length of 32 for attestation keys"'
+    )
+  })
+})
diff --git a/packages/atst/src/lib/readAttestation.ts b/packages/atst/src/lib/readAttestation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ccaa057dab8f3491b64d466be14cb6d866f4b0cc
--- /dev/null
+++ b/packages/atst/src/lib/readAttestation.ts
@@ -0,0 +1,35 @@
+import type { Address } from '@wagmi/core'
+
+import { DataTypeOption, DEFAULT_DATA_TYPE } from '../types/DataTypeOption'
+import { readAttestations } from './readAttestations'
+
+/**
+ * reads attestation from the attestation station contract
+ *
+ * @param attestationRead - the parameters for reading an attestation
+ * @returns attestation result
+ * @throws Error if key is longer than 32 bytes
+ * @example
+ * const attestation = await readAttestation(
+ * {
+ *  creator: creatorAddress,
+ * about: aboutAddress,
+ * key: 'my_key',
+ * },
+ */
+export const readAttestation = async (
+  creator: Address,
+  about: Address,
+  key: string,
+  dataType: DataTypeOption = DEFAULT_DATA_TYPE,
+  contractAddress: Address = '0xEE36eaaD94d1Cc1d0eccaDb55C38bFfB6Be06C77'
+) => {
+  const [result] = await readAttestations({
+    creator,
+    about,
+    key,
+    dataType,
+    contractAddress,
+  })
+  return result
+}
diff --git a/packages/atst/src/lib/readAttestations.spec.ts b/packages/atst/src/lib/readAttestations.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6fd5ba2f5805ff397862bb6c1de6836b824f4e33
--- /dev/null
+++ b/packages/atst/src/lib/readAttestations.spec.ts
@@ -0,0 +1,62 @@
+import { createClient } from '@wagmi/core'
+import { providers } from 'ethers'
+import { expect, describe, it } from 'vitest'
+
+import { readAttestation } from './readAttestation'
+import { readAttestations } from './readAttestations'
+
+const creator = '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3'
+const about = '0x2335022c740d17c2837f9C884Bfe4fFdbf0A95D5'
+const key = 'optimist.base-uri'
+
+const provider = new providers.JsonRpcProvider({
+  url: 'http://localhost:8545',
+})
+
+createClient({
+  provider,
+})
+
+describe(readAttestation.name, () => {
+  it('should return attestations from attestation station', async () => {
+    const dataType = 'string'
+
+    const result = await readAttestations(
+      {
+        creator,
+        about,
+        key,
+        dataType,
+      },
+      {
+        creator,
+        about,
+        key,
+        dataType: 'bool',
+      },
+      {
+        creator,
+        about,
+        key,
+        dataType: 'bytes',
+      },
+      {
+        creator,
+        about,
+        key,
+        dataType: 'number',
+      }
+    )
+
+    expect(result).toMatchInlineSnapshot(
+      `
+      [
+        "https://assets.optimism.io/4a609661-6774-441f-9fdb-453fdbb89931-bucket/optimist-nft/attributes",
+        "true",
+        "0x68747470733a2f2f6173736574732e6f7074696d69736d2e696f2f34613630393636312d363737342d343431662d396664622d3435336664626238393933312d6275636b65742f6f7074696d6973742d6e66742f61747472696275746573",
+        "9665973469795080068873111198635018086067645613429821071805084917303478255842407465257371959707311987533859075426222329066766033171696373249109388415320911537042272090516917683029511016473045453921068327933733922308146003731827",
+      ]
+    `
+    )
+  })
+})
diff --git a/packages/atst/src/lib/readAttestations.ts b/packages/atst/src/lib/readAttestations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af1cca23fea4cec17c3b7acdc7e3957ea6f518e4
--- /dev/null
+++ b/packages/atst/src/lib/readAttestations.ts
@@ -0,0 +1,67 @@
+import { readContracts } from '@wagmi/core'
+import { formatBytes32String } from 'ethers/lib/utils.js'
+
+import { ATTESTATION_STATION_ADDRESS } from '../constants/attestationStationAddress'
+import type { AttestationReadParams } from '../types/AttestationReadParams'
+import { DEFAULT_DATA_TYPE } from '../types/DataTypeOption'
+import type { WagmiBytes } from '../types/WagmiBytes'
+import { abi } from './abi'
+import { parseAttestationBytes } from './parseAttestationBytes'
+
+/**
+ * reads attestations from the attestation station contract
+ *
+ * @returns an array of attestation values
+ * @throws Error if key is longer than 32 bytes
+ * @example
+ * const attestations = await readAttestations(
+ *  {
+ *    creator: creatorAddress,
+ *    about: aboutAddress,
+ *    key: 'my_key',
+ *    allowFailure: false,
+ *  },
+ *  {
+ *    creator: creatorAddress2,
+ *    about: aboutAddress2,
+ *    key: 'my_key',
+ *    dataType: 'number',
+ *    contractAddress: '0x1234',
+ *    allowFailure: false,
+ *   },
+ * )
+ */
+export const readAttestations = async (
+  ...attestationReads: Array<AttestationReadParams>
+) => {
+  const calls = attestationReads.map((attestation) => {
+    const {
+      creator,
+      about,
+      key,
+      contractAddress = ATTESTATION_STATION_ADDRESS,
+      allowFailure = false,
+    } = attestation
+    if (key.length > 32) {
+      throw new Error(
+        'Key is longer than the max length of 32 for attestation keys'
+      )
+    }
+    return {
+      address: contractAddress,
+      abi,
+      functionName: 'attestations',
+      args: [creator, about, formatBytes32String(key) as WagmiBytes],
+      allowFailure,
+    } as const
+  })
+
+  const results = await readContracts({
+    contracts: calls,
+  })
+
+  return results.map((dataBytes, i) => {
+    const dataType = attestationReads[i].dataType ?? DEFAULT_DATA_TYPE
+    return parseAttestationBytes(dataBytes, dataType)
+  })
+}
diff --git a/packages/atst/src/lib/stringifyAttestationBytes.ts b/packages/atst/src/lib/stringifyAttestationBytes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..424958d76aa444681a312be8088683a64298920e
--- /dev/null
+++ b/packages/atst/src/lib/stringifyAttestationBytes.ts
@@ -0,0 +1,26 @@
+import { Address } from '@wagmi/core'
+import { BigNumber } from 'ethers'
+import { isAddress, isHexString, toUtf8Bytes } from 'ethers/lib/utils.js'
+
+import { WagmiBytes } from '../types/WagmiBytes'
+
+export const stringifyAttestationBytes = (
+  bytes: WagmiBytes | string | Address | number | boolean
+) => {
+  if (typeof bytes === 'number') {
+    return BigNumber.from(bytes).toHexString()
+  }
+  if (typeof bytes === 'boolean') {
+    return bytes ? '0x1' : '0x0'
+  }
+  if (isAddress(bytes)) {
+    return bytes
+  }
+  if (isHexString(bytes)) {
+    return bytes
+  }
+  if (typeof bytes === 'string') {
+    return toUtf8Bytes(bytes)
+  }
+  throw new Error(`unrecognized bytes type ${bytes satisfies never}`)
+}
diff --git a/packages/atst/src/lib/writeAttestation.spec.ts b/packages/atst/src/lib/writeAttestation.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..46bd7d3637125ba223bcc212441b160a3b0c7871
--- /dev/null
+++ b/packages/atst/src/lib/writeAttestation.spec.ts
@@ -0,0 +1,10 @@
+import { writeContract } from '@wagmi/core'
+import { describe, expect, it } from 'vitest'
+
+import { writeAttestation } from './writeAttestation'
+
+describe(writeAttestation.name, () => {
+  it('rexports writeContract from @wagmi/core', () => {
+    expect(writeAttestation).toBe(writeContract)
+  })
+})
diff --git a/packages/atst/src/lib/writeAttestation.ts b/packages/atst/src/lib/writeAttestation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14db0d35d6c376ca8d1aeaa5329f6c9d2f6b1e5a
--- /dev/null
+++ b/packages/atst/src/lib/writeAttestation.ts
@@ -0,0 +1,15 @@
+import { writeContract } from '@wagmi/core'
+export { prepareWriteAttestation } from './prepareWriteAttestation'
+
+export { abi } from './abi'
+
+/**
+ * Writes an attestation to the blockchain
+ * Same function as `writeContract` from @wagmi/core
+ * To use first use prepareWriteContract
+ *
+ * @example
+ * const config = await prepareAttestation(about, key, value)
+ * const tx = await writeAttestation(config)
+ */
+export const writeAttestation = writeContract
diff --git a/packages/atst/src/types/AttestationReadParams.ts b/packages/atst/src/types/AttestationReadParams.ts
new file mode 100644
index 0000000000000000000000000000000000000000..71bb6c12baad54da9a51fdddaf5b3443025a1430
--- /dev/null
+++ b/packages/atst/src/types/AttestationReadParams.ts
@@ -0,0 +1,15 @@
+import { Address } from '@wagmi/core'
+
+import { DataTypeOption } from './DataTypeOption'
+
+/**
+ * The parameters for reading bulk attestations
+ */
+export interface AttestationReadParams {
+  creator: Address
+  about: Address
+  key: string
+  dataType?: DataTypeOption
+  contractAddress?: Address
+  allowFailure?: boolean
+}
diff --git a/packages/atst/src/types/DataTypeOption.ts b/packages/atst/src/types/DataTypeOption.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7159fc77b9d0c70787d39b522652fc02ff4dd07c
--- /dev/null
+++ b/packages/atst/src/types/DataTypeOption.ts
@@ -0,0 +1,28 @@
+import { z } from 'zod'
+
+/**
+ * @internal
+ * Default data type for attestations
+ */
+export const DEFAULT_DATA_TYPE = 'string' as const
+
+/**
+ * Zod validator for the DataType type
+ * string | bytes | number | bool | address
+ */
+export const dataTypeOptionValidator = z
+  .union([
+    z.literal('string'),
+    z.literal('bytes'),
+    z.literal('number'),
+    z.literal('bool'),
+    z.literal('address'),
+  ])
+  .optional()
+  .default('string').describe(`Zod validator for the DataType type
+ string | bytes | number | bool | address`)
+
+/**
+ * Options for attestation data type
+ */
+export type DataTypeOption = z.infer<typeof dataTypeOptionValidator>
diff --git a/packages/atst/src/types/WagmiBytes.ts b/packages/atst/src/types/WagmiBytes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4d127df57557b90f04e19f8ba926b72f05baf2f
--- /dev/null
+++ b/packages/atst/src/types/WagmiBytes.ts
@@ -0,0 +1,5 @@
+/**
+ * @internal
+ * WagmiBytes is a type that represents a hex string with a length of 32 bytes.
+ */
+export type WagmiBytes = `0x${string}`
diff --git a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol
index dcf3521414906c931792103a18fd964f6598085a..6a13d7d1033e9eddf99c288ef24360d77c34cc83 100644
--- a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol
+++ b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol
@@ -96,11 +96,11 @@ abstract contract ResourceMetering is Initializable {
 
             // Update base fee by adding the base fee delta and clamp the resulting value between
             // min and max.
-            int256 newBaseFee = Arithmetic.clamp(
-                int256(uint256(params.prevBaseFee)) + baseFeeDelta,
-                MINIMUM_BASE_FEE,
-                MAXIMUM_BASE_FEE
-            );
+            int256 newBaseFee = Arithmetic.clamp({
+                _value: int256(uint256(params.prevBaseFee)) + baseFeeDelta,
+                _min: MINIMUM_BASE_FEE,
+                _max: MAXIMUM_BASE_FEE
+            });
 
             // If we skipped more than one block, we also need to account for every empty block.
             // Empty block means there was no demand for deposits in that block, so we should
@@ -109,15 +109,15 @@ abstract contract ResourceMetering is Initializable {
                 // Update the base fee by repeatedly applying the exponent 1-(1/change_denominator)
                 // blockDiff - 1 times. Simulates multiple empty blocks. Clamp the resulting value
                 // between min and max.
-                newBaseFee = Arithmetic.clamp(
-                    Arithmetic.cdexp(
-                        newBaseFee,
-                        BASE_FEE_MAX_CHANGE_DENOMINATOR,
-                        int256(blockDiff - 1)
-                    ),
-                    MINIMUM_BASE_FEE,
-                    MAXIMUM_BASE_FEE
-                );
+                newBaseFee = Arithmetic.clamp({
+                    _value: Arithmetic.cdexp({
+                        _coefficient: newBaseFee,
+                        _denominator: BASE_FEE_MAX_CHANGE_DENOMINATOR,
+                        _exponent: int256(blockDiff - 1)
+                    }),
+                    _min: MINIMUM_BASE_FEE,
+                    _max: MAXIMUM_BASE_FEE
+                });
             }
 
             // Update new base fee, reset bought gas, and update block number.
@@ -141,7 +141,7 @@ abstract contract ResourceMetering is Initializable {
         // division by zero for L1s that don't support 1559 or to avoid excessive gas burns during
         // periods of extremely low L1 demand. One-day average gas fee hasn't dipped below 1 gwei
         // during any 1 day period in the last 5 years, so should be fine.
-        uint256 gasCost = resourceCost / Math.max(block.basefee, 1000000000);
+        uint256 gasCost = resourceCost / Math.max(block.basefee, 1 gwei);
 
         // Give the user a refund based on the amount of gas they used to do all of the work up to
         // this point. Since we're at the end of the modifier, this should be pretty accurate. Acts
diff --git a/packages/contracts-periphery/contracts/universal/op-nft/AttestationStation.sol b/packages/contracts-periphery/contracts/universal/op-nft/AttestationStation.sol
index c6c3d083bdde080239402b4135d8fbfbb1962c6e..762612f74d87d11f4beda973293542e3836da401 100644
--- a/packages/contracts-periphery/contracts/universal/op-nft/AttestationStation.sol
+++ b/packages/contracts-periphery/contracts/universal/op-nft/AttestationStation.sol
@@ -68,7 +68,7 @@ contract AttestationStation is Semver {
     /**
      * @notice Allows anyone to create attestations.
      *
-     * @param _attestations An array of attestation data.
+     * @param _attestations An array of AttestationData structs.
      */
     function attest(AttestationData[] calldata _attestations) external {
         uint256 length = _attestations.length;