Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a506332
feat: add swap strategy
royvardhan May 5, 2025
2cd8065
feat: use batch txn feature for swaps and fill order
royvardhan May 7, 2025
bb89f74
refactor: replace 1inch calls with UniswapRouter02
royvardhan May 12, 2025
c4b3e31
feat: calculate swap operations based on current balances
royvardhan May 13, 2025
e6a8d8e
feat: update config with deployed addresses, add test
royvardhan May 13, 2025
54b87c1
test: handle pair creation and approvals
royvardhan May 14, 2025
5f339db
test: fix univ2 contract issues and update deployments
royvardhan May 14, 2025
41ccabe
feat: deploy BatchExecutor and use storage ovveride for token balances
royvardhan May 19, 2025
d75dd42
Merge branch 'main' into feat/filler-swap-strategy
royvardhan May 19, 2025
0ad4159
test: use indexer stream to check filled status
royvardhan May 20, 2025
1d3af00
test: update link
royvardhan May 20, 2025
2687b93
test: fix relaying and use erc20 token as inputs
royvardhan May 20, 2025
4da62b2
filler tests pass
royvardhan May 20, 2025
3c69c7a
feat: add fill,post,swap gas estimate caching to speed up execution
royvardhan May 20, 2025
4f212d5
refactor: use in-memory cache
royvardhan May 20, 2025
5db23d9
feat: add safe service, resolve pr comment
royvardhan May 22, 2025
60d0b01
feat: rm safeService, deploy universal router and add it, debug
royvardhan May 22, 2025
050e5da
feat: update deployments after fixing initCodeHash
royvardhan May 22, 2025
d012a78
test pass with universal router integration
royvardhan May 22, 2025
e172b1a
feat: add findBestProtocol
royvardhan May 23, 2025
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 packages/filler/src/config/chains.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ IntentGateway = "0xb6C27F4beF379d0b5e2fe3Bb36c248D6B71f91A6"
Host = "0x8Aa0Dea6D675d785A882967Bf38183f6117C09b7"
UniswapRouter02 = "0x32F9D2b43d923092Ba84152557D88e92e12440b1"
UniswapV2Factory = "0x61158087cfD798a4Af9C8aA7A45E19f0aF1978Dd"
BatchExecutor = "0xbC6Afc74F75EFE4aA2e363F8335842c20C69942d"
BatchExecutor = "0x4CC58B5D8FBf838d062E4b21F75C327835B5F0ef"

[chains.gnosis-chiado]
type = "evm"
Expand Down
2 changes: 1 addition & 1 deletion packages/filler/src/core/filler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class IntentFiller {
const result = await bestStrategy.executeOrder(order)
console.log(`Order execution result:`, result)
if (result.success) {
this.monitor.emit("orderFilled", { orderId: order.id })
this.monitor.emit("orderFilled", { orderId: order.id, hash: result.txHash })
}
return result
} catch (error) {
Expand Down
230 changes: 180 additions & 50 deletions packages/filler/src/tests/filler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
bytes20ToBytes32,
ADDRESS_ZERO,
bytes32ToBytes20,
postRequestCommitment,
} from "hyperbridge-sdk"
import { describe, it, expect } from "vitest"
import { ConfirmationPolicy } from "@/config/confirmation-policy"
Expand Down Expand Up @@ -86,6 +87,7 @@ describe.sequential("Basic", () => {
chainConfigs,
fillerConfig,
gnosisChiadoPublicClient,
bscHandler,
} = await setUp()

const strategies = [new BasicFiller(process.env.PRIVATE_KEY as HexString)]
Expand Down Expand Up @@ -150,45 +152,106 @@ describe.sequential("Basic", () => {
const detectedOrder = await orderDetectedPromise
console.log("Order successfully detected by event monitor:", detectedOrder)

const orderFilledPromise = new Promise<string>((resolve) => {
const orderFilledPromise = new Promise<{ orderId: string; hash: string }>((resolve) => {
const eventMonitor = intentFiller.monitor
if (!eventMonitor) {
console.error("Event monitor not found on intentFiller")
resolve("")
resolve({ orderId: "", hash: "" })
return
}

eventMonitor.on("orderFilled", (data: { orderId: string }) => {
console.log("Order filled by event monitor:", data.orderId)
resolve(data.orderId)
eventMonitor.on("orderFilled", (data: { orderId: string; hash: string }) => {
console.log("Order filled by event monitor:", data.orderId, data.hash)
resolve(data)
})
})

const orderFilledId = await orderFilledPromise
console.log("Order filled:", orderFilledId)
const { orderId, hash: filledHash } = await orderFilledPromise
console.log("Order filled:", orderId, filledHash)

const filledReceipt = await gnosisChiadoPublicClient.waitForTransactionReceipt({
hash: filledHash as `0x${string}`,
confirmations: 1,
})

let isFilled = await checkIfOrderFilled(
orderFilledId as HexString,
orderId as HexString,
gnosisChiadoPublicClient,
gnosisChiadoIntentGateway.address,
)

expect(isFilled).toBe(true)

console.log("Checking if order is filled at the source chain...")
await new Promise((resolve) => setTimeout(resolve, 60 * 1000))

isFilled = await checkIfOrderFilled(orderFilledId as HexString, bscPublicClient, bscIntentGateway.address)
let maxAttempts = 20
while (!isFilled && maxAttempts > 0) {
console.log("Order not filled at the source chain, retrying storage check in 30 seconds...")
console.log("Max storage checks left:", maxAttempts)
await new Promise((resolve) => setTimeout(resolve, 30 * 1000))
isFilled = await checkIfOrderFilled(orderFilledId as HexString, bscPublicClient, bscIntentGateway.address)
maxAttempts--
// parse EvmHost PostRequestEvent emitted in the transcation logs
const event = parseEventLogs({ abi: EVM_HOST, logs: filledReceipt.logs })[0]

if (event.eventName !== "PostRequestEvent") {
throw new Error("Unexpected Event type")
}

expect(isFilled).toBe(true)
const request = event.args
console.log("PostRequestEvent", { request })
const commitment = postRequestCommitment(request).commitment

for await (const status of indexer.postRequestStatusStream(commitment)) {
console.log(JSON.stringify(status, null, 4))
switch (status.status) {
case RequestStatus.SOURCE_FINALIZED: {
console.log(
`Status ${status.status}, Transaction: https://gargantua.statescan.io/#/extrinsics/${status.metadata.transactionHash}`,
)
break
}
case RequestStatus.HYPERBRIDGE_DELIVERED: {
console.log(
`Status ${status.status}, Transaction: https://gargantua.statescan.io/#/extrinsics/${status.metadata.transactionHash}`,
)
break
}
case RequestStatus.HYPERBRIDGE_FINALIZED: {
console.log(
`Status ${status.status}, Transaction: https://gnosis-chiado.blockscout.com/tx/${status.metadata.transactionHash}`,
)
const { args, functionName } = decodeFunctionData({
abi: HandlerV1_ABI,
data: status.metadata.calldata,
})

expect(functionName).toBe("handlePostRequests")

try {
const hash = await bscHandler.write.handlePostRequests(args as any, {
Comment thread
Wizdave97 marked this conversation as resolved.
Outdated
account: privateKeyToAccount(process.env.PRIVATE_KEY as HexString),
chain: bscTestnet,
})
await bscPublicClient.waitForTransactionReceipt({
hash,
confirmations: 1,
})

console.log(`Transaction submitted: https://gnosis-chiado.blockscout.com/tx/${hash}`)

// Now check if the order is filled at the source chain
isFilled = await checkIfOrderFilled(
orderId as HexString,
bscPublicClient,
bscIntentGateway.address,
)
expect(isFilled).toBe(true)
} catch (e) {
console.error("Error self-relaying: ", e)
}

break
}
case RequestStatus.DESTINATION: {
console.log(
`Status ${status.status}, Transaction: https://gnosis-chiado.blockscout.com/tx/${status.metadata.transactionHash}`,
Comment thread
Wizdave97 marked this conversation as resolved.
Outdated
)
break
}
}
}

intentFiller.stop()
}, 1_000_000)
Expand All @@ -205,11 +268,14 @@ describe.sequential("Basic", () => {
gnosisChiadoIntentGateway,
bscHandler,
bscChapelId,
chainConfigService,
} = await setUp()

const daiAsset = chainConfigService.getDaiAsset(bscChapelId)

const inputs: TokenInfo[] = [
{
token: "0x0000000000000000000000000000000000000000000000000000000000000000",
token: bytes20ToBytes32(daiAsset),
amount: 100n,
},
]
Expand Down Expand Up @@ -373,6 +439,7 @@ describe.sequential("Basic", () => {
feeTokenGnosisChiadoAddress,
bscChapelId,
bscWalletClient,
gnosisChiadoHandler,
} = await setUp()

// Create a new intent filler with StableSwapFiller strategy
Expand Down Expand Up @@ -477,52 +544,107 @@ describe.sequential("Basic", () => {
console.log("Order successfully detected by event monitor:", detectedOrder)

// Monitor for order filling
const orderFilledPromise = new Promise<string>((resolve) => {
const orderFilledPromise = new Promise<{ orderId: string; hash: string }>((resolve) => {
const eventMonitor = newIntentFiller.monitor
if (!eventMonitor) {
console.error("Event monitor not found on intentFiller")
resolve("")
resolve({ orderId: "", hash: "" })
return
}

eventMonitor.on("orderFilled", (data: { orderId: string }) => {
console.log("Order filled by event monitor:", data.orderId)
resolve(data.orderId)
eventMonitor.on("orderFilled", (data: { orderId: string; hash: string }) => {
console.log("Order filled by event monitor:", data.orderId, data.hash)
resolve(data)
})
})

const orderFilledId = await orderFilledPromise
console.log("Order filled:", orderFilledId)

// Verify order is filled on destination chain
let isFilled = await checkIfOrderFilled(orderFilledId as HexString, bscPublicClient, bscIntentGateway.address)

expect(isFilled).toBe(true)
const { orderId, hash: filledHash } = await orderFilledPromise
console.log("Order filled:", orderId, filledHash)

// Verify order is filled on source chain
console.log("Checking if order is filled at the source chain...")
await new Promise((resolve) => setTimeout(resolve, 60 * 1000))
const filledReceipt = await gnosisChiadoPublicClient.waitForTransactionReceipt({
hash: filledHash as `0x${string}`,
confirmations: 1,
})

isFilled = await checkIfOrderFilled(
orderFilledId as HexString,
let isFilled = await checkIfOrderFilled(
orderId as HexString,
gnosisChiadoPublicClient,
gnosisChiadoIntentGateway.address,
)
let maxAttempts = 20
while (!isFilled && maxAttempts > 0) {
console.log("Order not filled at the source chain, retrying storage check in 30 seconds...")
console.log("Max storage checks left:", maxAttempts)
await new Promise((resolve) => setTimeout(resolve, 30 * 1000))
isFilled = await checkIfOrderFilled(
orderFilledId as HexString,
gnosisChiadoPublicClient,
gnosisChiadoIntentGateway.address,
)
maxAttempts--
}

expect(isFilled).toBe(true)

// parse EvmHost PostRequestEvent emitted in the transcation logs
const event = parseEventLogs({ abi: EVM_HOST, logs: filledReceipt.logs })[0]

if (event.eventName !== "PostRequestEvent") {
throw new Error("Unexpected Event type")
}

const request = event.args
console.log("PostRequestEvent", { request })
const commitment = postRequestCommitment(request).commitment

for await (const status of indexer.postRequestStatusStream(commitment)) {
console.log(JSON.stringify(status, null, 4))
switch (status.status) {
case RequestStatus.SOURCE_FINALIZED: {
console.log(
`Status ${status.status}, Transaction: https://gargantua.statescan.io/#/extrinsics/${status.metadata.transactionHash}`,
)
break
}
case RequestStatus.HYPERBRIDGE_DELIVERED: {
console.log(
`Status ${status.status}, Transaction: https://gargantua.statescan.io/#/extrinsics/${status.metadata.transactionHash}`,
)
break
}
case RequestStatus.HYPERBRIDGE_FINALIZED: {
console.log(
`Status ${status.status}, Transaction: https://gnosis-chiado.blockscout.com/tx/${status.metadata.transactionHash}`,
)
const { args, functionName } = decodeFunctionData({
abi: HandlerV1_ABI,
data: status.metadata.calldata,
})

expect(functionName).toBe("handlePostRequests")

try {
const hash = await gnosisChiadoHandler.write.handlePostRequests(args as any, {
account: privateKeyToAccount(process.env.PRIVATE_KEY as HexString),
chain: gnosisChiado,
})
await gnosisChiadoPublicClient.waitForTransactionReceipt({
hash,
confirmations: 1,
})

console.log(`Transaction submitted: https://gnosis-chiado.blockscout.com/tx/${hash}`)

// Now check if the order is filled at the source chain
isFilled = await checkIfOrderFilled(
orderId as HexString,
gnosisChiadoPublicClient,
gnosisChiadoIntentGateway.address,
)
expect(isFilled).toBe(true)
} catch (e) {
console.error("Error self-relaying: ", e)
}

break
}
case RequestStatus.DESTINATION: {
console.log(
`Status ${status.status}, Transaction: https://gnosis-chiado.blockscout.com/tx/${status.metadata.transactionHash}`,
)
break
}
}
}

newIntentFiller.stop()
}, 1_000_000)
})
Expand Down Expand Up @@ -579,6 +701,7 @@ async function setUp() {
const bscIsmpHostAddress = "0x8Aa0Dea6D675d785A882967Bf38183f6117C09b7" as HexString
const gnosisChiadoIsmpHostAddress = "0x58a41b89f4871725e5d898d98ef4bf917601c5eb" as HexString
const bscHandlerAddress = "0x4638945E120846366cB7Abc08DB9c0766E3a663F" as HexString
const gnosisChiadoHandlerAddress = "0x4638945E120846366cB7Abc08DB9c0766E3a663F" as HexString
const bscIntentGateway = getContract({
address: intentGatewayAddress as HexString,
abi: INTENT_GATEWAY_ABI,
Expand Down Expand Up @@ -609,6 +732,12 @@ async function setUp() {
client: { public: bscPublicClient, wallet: bscWalletClient },
})

const gnosisChiadoHandler = getContract({
address: gnosisChiadoHandlerAddress,
abi: HandlerV1_ABI,
client: { public: gnosisChiadoPublicClient, wallet: gnosisChiadoWalletClient },
})

return {
chainClientManager,
bscWalletClient,
Expand All @@ -630,6 +759,7 @@ async function setUp() {
chainConfigService,
fillerConfig,
chainConfigs,
gnosisChiadoHandler,
}
}

Expand Down
5 changes: 3 additions & 2 deletions packages/sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,11 +583,12 @@ export class IndexerClient {

logger.trace("`Request` found")
const chain = await getChain(this.config.dest)
const timeoutStream = this.timeoutStream(request.timeoutTimestamp, chain)
const timeoutStream =
request.timeoutTimestamp > 0 ? this.timeoutStream(request.timeoutTimestamp, chain) : undefined
const statusStream = this.postRequestStatusStreamInternal(hash)

logger.trace("Listening for events")
const combined = mergeRace(timeoutStream, statusStream)
const combined = timeoutStream ? mergeRace(timeoutStream, statusStream) : statusStream

logger.trace("Listening for events")
let item = await combined.next()
Expand Down