타입스크립트, 0x, 테라폼으로 폴리곤의 리미트 오더 자동화 방법
2025-01-16 10:07:16소개
디지털 자산의 거래는 빠르고 효율적이어야 하며, 이로 인해 자동화에 대한 관심이 높아지고 있습니다. 이번 블로그에서는 타입스크립트 서비스와 0x Swap API를 사용해 폴리곤 네트워크에서 리미트 오더를 자동화하는 방법을 소개합니다. 특히 AWS Lambda와 Terraform을 활용한 배포 과정도 살펴볼 예정입니다.
리미트 오더 정의
리미트 오더는 특정 자산의 가격이 사전에 지정된 목표 가격에 도달할 때 자산을 교환하는 규칙을 정의하는 구조입니다. 이 오더는 가격이 목표보다 높거나 낮을 때 거래를 실행할지 여부를 지정합니다. 이는 특정 자산에서 다른 자산으로의 거래를 기록하며 고유한 ID를 부여하여 데이터베이스에서 참조할 수 있도록 합니다.
import { EntityWithId } from "@lib/utils/entities/EntityWithId"
import { TransferDirection } from "@lib/utils/TransferDirection"
import { LimitOrderAsset } from "./LimitOrderAsset"
type LimitOrderCondition = "more" | "less"
export type LimitOrder = EntityWithId & {
asset: LimitOrderAsset
condition: LimitOrderCondition
targetPrice: number
swap: Record<TransferDirection, LimitOrderAsset>
}
폴리곤을 통한 거래 비용 절감
폴리곤은 이더리움의 레이어 2 솔루션으로, 낮은 거래 비용을 제공합니다. 이번 예시에서는 래핑된 이더리움(WETH)과 USDC 두 자산 간의 거래를 중심으로 설명하겠습니다. 이를 위해 두 자산의 ERC20 주소와 가격 공급자 ID도 포함하여 Coingecko에서 그들의 가격을 가져올 수 있도록 합니다.
import { Address } from "viem"
import { polygon } from "viem/chains"
export const limitOrderAssets = ["weth", "usdc"] as const
export type LimitOrderAsset = (typeof limitOrderAssets)[number]
export const limitOrderAssetAddress: Record<LimitOrderAsset, Address> = {
weth: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
}
export const limitOrderAssetPriceProividerId: Record<LimitOrderAsset, string> = {
weth: "polygon:weth",
usdc: "polygon:usdc",
}
export const limitOrderChain = polygon
DynamoDB에 리미트 오더 저장
리미트 오더를 저장하기 위해 DynamoDB 테이블을 활용합니다. RadzionKit이라는 라이브러리를 사용하여 CRUD 작업을 위한 필수 기능을 빠르게 정의할 수 있습니다.
import { getPickParams } from "@lib/dynamodb/getPickParams"
import { totalScan } from "@lib/dynamodb/totalScan"
import { LimitOrder } from "../entities/LimitOrder"
import { getEnvVar } from "../getEnvVar"
import { DeleteCommand, PutCommand } from "@aws-sdk/lib-dynamodb"
import { dbDocClient } from "@lib/dynamodb/client"
import { updateItem } from "@lib/dynamodb/updateItem"
const tableName = getEnvVar("LIMIT_ORDERS_TABLE_NAME")
export const getLimitOrderItemParams = (id: string) => ({
TableName: tableName,
Key: {
id,
},
})
export const getAllLimitOrders = async <T extends (keyof LimitOrder)[]>(
attributes?: T,
) => {
return totalScan<Pick<LimitOrder, T[number]>>({
TableName: tableName,
...getPickParams(attributes),
})
}
export const deleteLimitOrder = (id: string) => {
const command = new DeleteCommand(getLimitOrderItemParams(id))
return dbDocClient.send(command)
}
export const deleteAllLimitOrders = async () => {
const alerts = await getAllLimitOrders(["id"])
return Promise.all(alerts.map(({ id }) => deleteLimitOrder(id)))
}
export const putLimitOrder = (user: LimitOrder) => {
const command = new PutCommand({
TableName: tableName,
Item: user,
})
return dbDocClient.send(command)
}
export const updateLimitOrder = async (
id: string,
fields: Partial<LimitOrder>,
) => {
return updateItem({
tableName: tableName,
key: { id },
fields,
})
}
환경 변수 관리
타입 안전성을 갖춘 환경 변수 액세스를 보장하기 위해 getEnvVar 유틸리티를 사용합니다. 이는 애플리케이션의 환경 변수를 관리하는 단일 소스로 작용합니다.
type VariableName =
| "LIMIT_ORDERS_TABLE_NAME"
| "SENTRY_KEY"
| "SECRETS"
| "TELEGRAM_BOT_CHAT_ID"
export const getEnvVar = <T extends string>(name: VariableName): T => {
const value = process.env[name]
if (!value) {
throw new Error(`Missing ${name} environment variable`)
}
return value as T
}
샘플 리미트 오더 설정
WETH의 가격이 $3,800을 초과할 때 USDC를 매도하는 리미트 오더의 구성을 예시로 보여드립니다. 단일 사용자에 맞춰진 서비스이므로, 기존 리미트 오더를 모두 삭제하고 새 것으로 대체하여 프로세스를 단순화할 수 있습니다.
import { deleteAllLimitOrders, putLimitOrder } from "../db/limitOrders"
import { LimitOrder } from "../entities/LimitOrder"
const partialItems: Omit<LimitOrder, "id">[] = [
{
asset: "weth",
condition: "more",
targetPrice: 3800,
swap: {
from: "weth",
to: "usdc",
},
},
]
const items: LimitOrder[] = partialItems.map((value, index) => ({
...value,
id: index.toString(),
}))
const setLimitOrders = async () => {
await deleteAllLimitOrders()
await Promise.all(items.map(putLimitOrder))
}
setLimitOrders()
알림 추가하기: 텔레그램 봇 활용
리미트 오더를 위한 스왑을 실행할 때 알림을 받는 것은 유익합니다. 텔레그램 봇을 통합함으로써 쉽게 이를 이룰 수 있습니다.
import { TransferDirection } from "@lib/utils/TransferDirection"
import { getEnvVar } from "../getEnvVar"
import TelegramBot from "node-telegram-bot-api"
import { LimitOrderAsset } from "../entities/LimitOrderAsset"
import { getSecret } from "../getSercret"
type Input = {
swap: Record<TransferDirection, LimitOrderAsset>
asset: LimitOrderAsset
price: number
}
export const sendSwapNotification = async ({ price, asset, swap }: Input) => {
const token = await getSecret("telegramBotToken")
const bot = new TelegramBot(token)
const message = `Executed a swap from ${swap.from} to ${swap.to} at ${asset} price of ${price}`
return bot.sendMessage(getEnvVar("TELEGRAM_BOT_CHAT_ID"), message)
}
AWS Secrets Manager를 통한 민감 정보 보안
보안을 강화하기 위해 텔레그램 봇 토큰과 같은 민감 정보를 AWS Secrets Manager에 저장합니다. 이 접근 방식은 서비스에 필요한 모든 비밀 정보를 중앙에서 관리할 수 있게 합니다. 성능 최적화를 위해 getSecrets 함수를 메모이제이션하여 응답을 캐시하여 반복 요청을 줄입니다.
import { getEnvVar } from "./getEnvVar"
import {
SecretsManagerClient,
GetSecretValueCommand,
} from "@aws-sdk/client-secrets-manager"
import { memoizeAsync } from "@lib/utils/memoizeAsync"
import { shouldBePresent } from "@lib/utils/assert/shouldBePresent"
import { assertField } from "@lib/utils/record/assertField"
type SecretName = "accountPrivateKey" | "zeroXApiKey" | "tele
결론
이 글에서는 타입스크립트와 0x Swap API를 사용하여 폴리곤에서 리미트 오더를 자동화하는 방법을 알아보았습니다. AWS Lambda와 테라폼을 사용한 서비스 배포 방법도 살펴보았습니다. 자동화된 거래는 디지털 자산 시장에서 큰 이점을 제공하며, 이를 통해 사용자는 더 효율적이고 효과적인 거래를 진행할 수 있습니다.