Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first pass at syndicate integration #549

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/delta/ponder.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createConfig } from '@ponder/core'
import { addresses, idRegistryABI, postGatewayABI } from 'scrypt'
import { http } from 'viem'
import { Hex, http } from 'viem'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get rid of this added Hex? not seeing any other changes to Delta so should be unncessary? just in terms of keeping the commit trail on delta clean


export default createConfig({
networks: {
Expand Down
1 change: 1 addition & 0 deletions apps/site/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*
# local env files
.env*.local
.env
.env.*

# vercel
.vercel
Expand Down
61 changes: 34 additions & 27 deletions apps/site/app/api/post/route.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,48 @@
import { novaPubClient } from '@/config/publicClient'
import { Defender } from '@openzeppelin/defender-sdk'
import { ethers } from 'ethers'
import type { NextRequest } from 'next/server'
import { addresses, postGatewayABI } from 'scrypt'
import type { Hex } from 'viem'
import { syndicate } from '@/config/syndicateClient'
import { addresses } from 'scrypt'
import { waitUntilTx } from '@/lib'

export async function POST(req: NextRequest) {
const post = await req.json()

const credentials = {
relayerApiKey: process.env.NONCE_API_UNO,
relayerApiSecret: process.env.NONCE_SECRET_UNO,
}

try {
const defenderClient = new Defender(credentials)
const provider = defenderClient.relaySigner.getProvider()
const signer = defenderClient.relaySigner.getSigner(provider, {
speed: 'fast',
})

const postGateway = new ethers.Contract(
addresses.postGateway.nova,
postGatewayABI,
signer as unknown as ethers.Signer,
)
const projectId = process.env.SYNDICATE_PROJECT_ID_POSTGATEWAY

const tx = await postGateway.post(post)
if (!projectId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets move as much of this as possible into the syndicate config. did a lil gist brainstorming here https://gist.github.com/0xTranqui/3a00d776f4065db0c77106685f941d24

throw new Error(
'SYNDICATE_PROJECT_ID_POSTGATEWAY is not defined in environment variables.',
)
}

await novaPubClient.waitForTransactionReceipt({
hash: tx.hash as Hex,
const postTx = await syndicate.transact.sendTransaction({
projectId: projectId,
contractAddress: addresses.postGateway.nova,
chainId: 42170,
functionSignature:
'post((address signer, (uint256 rid, uint256 timestamp, uint8 msgType, bytes msgBody) message, uint16 hashType, bytes32 hash, uint16 sigType, bytes sig) post)',
args: {
post: post,
},
})

return new Response(JSON.stringify({ success: true, hash: tx.hash }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
console.log({ postTx })

// Use the waitUntilTx function to wait for the transaction to be processed
const successfulTxHash = await waitUntilTx({
projectID: projectId,
txID: postTx.transactionId,
})

console.log({ successfulTxHash })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should prob remove most/all of these console logs before merging? what u think


return new Response(
JSON.stringify({ success: true, hash: successfulTxHash }),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
},
)
} catch (error) {
let errorMessage = 'Unknown error'
let statusCode = 500
Expand Down
57 changes: 30 additions & 27 deletions apps/site/app/api/postBatch/route.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,44 @@
import { novaPubClient } from '@/config/publicClient'
import { Defender } from '@openzeppelin/defender-sdk'
import { ethers } from 'ethers'
import type { NextRequest } from 'next/server'
import { addresses, postGatewayABI } from 'scrypt'
import type { Hex } from 'viem'
import { addresses } from 'scrypt'
import { syndicate } from '@/config/syndicateClient'
import { waitUntilTx } from '@/lib'

export async function POST(req: NextRequest) {
const postsArray = await req.json()
console.log({ postsArray })

const credentials = {
relayerApiKey: process.env.NONCE_API_UNO,
relayerApiSecret: process.env.NONCE_SECRET_UNO,
}

try {
const defenderClient = new Defender(credentials)
const provider = defenderClient.relaySigner.getProvider()
const signer = defenderClient.relaySigner.getSigner(provider, {
speed: 'fast',
})
const projectId = process.env.SYNDICATE_PROJECT_ID_POSTGATEWAY

const postGateway = new ethers.Contract(
addresses.postGateway.nova,
postGatewayABI,
signer as unknown as ethers.Signer,
)
if (!projectId) {
throw new Error(
'SYNDICATE_PROJECT_ID_POSTGATEWAY is not defined in environment variables.',
)
}

const tx = await postGateway.postBatch(postsArray)
await novaPubClient.waitForTransactionReceipt({
hash: tx.hash as Hex,
const postBatchTx = await syndicate.transact.sendTransaction({
projectId: projectId,
contractAddress: addresses.postGateway.nova,
chainId: 42170,
functionSignature:
'postBatch((address signer, (uint256 rid, uint256 timestamp, uint8 msgType, bytes msgBody) message, uint16 hashType, bytes32 hash, uint16 sigType, bytes sig)[] posts)',
args: {
posts: postsArray,
},
})

return new Response(JSON.stringify({ success: true, hash: tx.hash }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
// Use the waitUntilTx function to wait for the transaction to be processed
const successfulTxHash = await waitUntilTx({
projectID: projectId,
txID: postBatchTx.transactionId,
})

return new Response(
JSON.stringify({ success: true, hash: successfulTxHash }),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
},
)
} catch (error) {
let errorMessage = 'Unknown error'
let statusCode = 500
Expand Down
13 changes: 13 additions & 0 deletions apps/site/config/syndicateClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SyndicateClient } from '@syndicateio/syndicate-node'

export const syndicate = new SyndicateClient({
token: () => {
const apiKey = process.env.SYNDICATE_API_KEY
if (typeof apiKey === 'undefined') {
throw new Error(
'SYNDICATE_API_KEY is not defined in environment variables.',
)
}
return apiKey
},
})
92 changes: 92 additions & 0 deletions apps/site/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,98 @@ type User = {
sig: string
}

export interface TransactionAttempt {
block: number
blockCreatedAt: string
chainId: number
createdAt: string
hash: string
nonce: number
reverted: boolean
signedTxn: string
status: string
transactionId: string
updatedAt: string
walletAddress: string
}

export interface SyndicateApiResponse {
chainId: number
contractAddress: string
createdAt: string
data: string
decodedData: object
functionSignature: string
invalid: boolean
projectId: string
transactionAttempts: TransactionAttempt[]
transactionId: string
updatedAt: string
value: string
}

export interface WaitUntilTxOptions {
projectID: string
txID: string
maxAttempts?: number
every?: number
}

export const getTransactionRequest = async ({
projectID,
txID,
}: Pick<WaitUntilTxOptions, 'projectID' | 'txID'>) => {
const response = await fetch(
`https://api.syndicate.io/wallet/project/${projectID}/request/${txID}`,
{
method: 'GET',
headers: { Authorization: `Bearer ${process.env.SYNDICATE_API_KEY}` },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better dx to accept api key as a prop? instead of loading in from env here? like we do for w3supload down below?

},
)
if (!response.ok) {
throw new Error(`Failed to get transaction request: ${response.statusText}`)
}
return response.json()
}

export async function waitUntilTx({
projectID,
txID,
maxAttempts = 20,
every = 1000,
}: WaitUntilTxOptions) {
let currAttempts = 0
let transactionHash = null

while (!transactionHash) {
await new Promise((resolve) => setTimeout(resolve, every))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the delay is better suited for coming at the end of the loop, and only if its necssary (ie there will be another loop cuz was uncessful) like we do in this similar function that we use to wait for appearnace in ponder db? https://github.com/1ifeworld/river/blob/main/apps/site/lib/getTxnInclusion.ts


if (currAttempts >= maxAttempts) {
throw new Error('Max attempts reached')
}

const txAttempts = (await getTransactionRequest({ projectID, txID }))
?.transactionAttempts

console.log({ txAttempts })

if (
txAttempts &&
txAttempts.length > 0 &&
txAttempts[txAttempts.length - 1].status === 'PENDING' &&
!txAttempts[txAttempts.length - 1].reverted
) {
transactionHash = txAttempts[txAttempts.length - 1].hash
console.log(transactionHash)
break
}

currAttempts += 1
}

return transactionHash
}

/* API ROUTES */

// This is in to help with serialization of bigints during json stringify
Expand Down
1 change: 1 addition & 0 deletions apps/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@syndicateio/syndicate-node": "^0.0.449",
"@vercel/analytics": "^1.0.2",
"@vercel/kv": "^1.0.0",
"base64url": "^3.0.1",
Expand Down
40 changes: 40 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.