Skip to content

Commit

Permalink
✨ Add atst SDK
Browse files Browse the repository at this point in the history
✨ add everything else

🎨 move the casting to the sdk

♻️ clean up

Build sdk with tsup

Implement the package

remove the comment

Promise.all it

use readContracts

start finishing everyithing

finish sdk untested

add vite tests for the sdk

add all the reading tests

add rest of tests and functionality

last test

implementation is done

start on readmes

add todos

fix package versions

fix circleci

changeset

revert sdk change
  • Loading branch information
Will Cory authored and Will Cory committed Feb 28, 2023
1 parent bde2eac commit 37a5b68
Show file tree
Hide file tree
Showing 18 changed files with 496 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/atst/src/constants/defaultDataType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @internal
* Default data type for attestations
*/
export const DEFAULT_DATA_TYPE = 'string' as const
5 changes: 5 additions & 0 deletions packages/atst/src/constants/defaultRpcUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @internal
* Default RPC URL for Optimism
*/
export const DEFAULT_RPC_URL = 'https://mainnet.optimism.io'
11 changes: 11 additions & 0 deletions packages/atst/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
// 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 { writeAttestation } from './lib/writeAttestation'
export { abi } from './lib/abi'
export { parseAttestationBytes } from './lib/parseAttestationBytes'
// types
export type { AttestationReadParams } from './types/AttestationReadParams'
export type { WagmiBytes } from './types/WagmiBytes'
export type { DataTypeOption } from './types/DataTypeOption'
11 changes: 11 additions & 0 deletions packages/atst/src/lib/__snapshots__/logger.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -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"`;
42 changes: 42 additions & 0 deletions packages/atst/src/lib/parseAttestationBytes.spec.ts
Original file line number Diff line number Diff line change
@@ -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)
})
})
29 changes: 29 additions & 0 deletions packages/atst/src/lib/parseAttestationBytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { BigNumber } from 'ethers'
import { toUtf8String } from 'ethers/lib/utils.js'

import type { DataTypeOption } from '../types/DataTypeOption'
import * as logger from './logger'
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)
}
logger.warn(`unrecognized dataType ${dataType satisfies never}`)
return attestationBytes
}
71 changes: 71 additions & 0 deletions packages/atst/src/lib/prepareWriteAttestation.spec.ts
Original file line number Diff line number Diff line change
@@ -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"'
)
})
})
24 changes: 24 additions & 0 deletions packages/atst/src/lib/prepareWriteAttestation.ts
Original file line number Diff line number Diff line change
@@ -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],
})
}
41 changes: 41 additions & 0 deletions packages/atst/src/lib/readAttestation.spec.ts
Original file line number Diff line number Diff line change
@@ -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"'
)
})
})
36 changes: 36 additions & 0 deletions packages/atst/src/lib/readAttestation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Address } from '@wagmi/core'

import { DEFAULT_DATA_TYPE } from '../constants/defaultDataType'
import type { DataTypeOption } 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
}
62 changes: 62 additions & 0 deletions packages/atst/src/lib/readAttestations.spec.ts
Original file line number Diff line number Diff line change
@@ -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",
]
`
)
})
})
Loading

0 comments on commit 37a5b68

Please sign in to comment.