Skip to content

Commit

Permalink
feat: add StackingDAO protocol (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
pradel authored Mar 24, 2024
1 parent 7a98647 commit d15a28d
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 4 deletions.
23 changes: 23 additions & 0 deletions chainhooks/stackingdao/stacking-dao-core-v1.deposit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"chain": "stacks",
"uuid": "1",
"name": "stacking-dao-core-v1.deposit",
"version": 1,
"networks": {
"mainnet": {
"if_this": {
"scope": "contract_call",
"contract_identifier": "SP4SZE494VC2YC5JYG7AYFQ44F5Q4PYV7DVMDPBG.stacking-dao-core-v1",
"method": "deposit"
},
"then_that": {
"http_post": {
"url": "http://localhost:3000/api/chainhooks/stackingdao/deposit",
"authorization_header": "Bearer dev-api-token"
}
},
"start_block": 132118,
"decode_clarity_values": false
}
}
}
23 changes: 23 additions & 0 deletions chainhooks/stackingdao/stacking-dao-core-v1.withdraw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"chain": "stacks",
"uuid": "1",
"name": "stacking-dao-core-v1.withdraw",
"version": 1,
"networks": {
"mainnet": {
"if_this": {
"scope": "contract_call",
"contract_identifier": "SP4SZE494VC2YC5JYG7AYFQ44F5Q4PYV7DVMDPBG.stacking-dao-core-v1",
"method": "withdraw"
},
"then_that": {
"http_post": {
"url": "http://localhost:3000/api/chainhooks/stackingdao/withdraw",
"authorization_header": "Bearer dev-api-token"
}
},
"start_block": 132118,
"decode_clarity_values": false
}
}
}
Binary file added public/protocols/stackingdao.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions src/app/api/chainhooks/stackingdao/deposit/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { db } from "@/db/db";
import { type InsertTransaction, transactionTable } from "@/db/schema";
import { conflictUpdateSetAllColumns } from "@/db/utils";
import type {
ChainhookPayload,
ChainhookReceiptEventFTMintEvent,
ChainhookReceiptEventSTXTransferEvent,
} from "@/lib/chainhooks";
import { getOrInsertToken } from "@/lib/currencies";

export const dynamic = "force-dynamic";

export async function POST(request: Request) {
const data: ChainhookPayload = await request.json();

const transactionsToInsert = data.apply[0].transactions
.filter((transactionToProcess) => transactionToProcess.metadata.success)
.map((transactionToProcess) => {
const sender = transactionToProcess.metadata.sender;
const transferEvents = transactionToProcess.metadata.receipt.events
// Events are not always in order so we sort them by index
.sort((a, b) => a.position.index - b.position.index)
.filter(
(
event,
): event is
| ChainhookReceiptEventSTXTransferEvent
| ChainhookReceiptEventFTMintEvent =>
event.type === "STXTransferEvent" || event.type === "FTMintEvent",
);
const stxTransferEvent = transferEvents.filter(
(event) =>
event.type === "STXTransferEvent" && event.data.sender === sender,
)[0];
const stStxMintEvent = transferEvents.filter(
(event): event is ChainhookReceiptEventFTMintEvent =>
event.type === "FTMintEvent" && event.data.recipient === sender,
)[0];

return {
txId: transactionToProcess.transaction_identifier.hash,
protocol: "stackingdao",
blockHeight: BigInt(data.apply[0].block_identifier.index),
timestamp: new Date(data.apply[0].timestamp * 1000),
sender,
action: "stackingdao-deposit",
data: {
outAmount: BigInt(stxTransferEvent.data.amount),
outToken: "STX",
inAmount: BigInt(stStxMintEvent.data.amount),
inToken: stStxMintEvent.data.asset_identifier,
},
} satisfies InsertTransaction;
});

for (const transaction of transactionsToInsert) {
await getOrInsertToken(transaction.data.inToken);
await getOrInsertToken(transaction.data.outToken);
}

if (transactionsToInsert.length > 0) {
await db
.insert(transactionTable)
.values(transactionsToInsert)
.onConflictDoUpdate({
target: transactionTable.txId,
set: conflictUpdateSetAllColumns(transactionTable),
});
}

return Response.json({ ok: true });
}
72 changes: 72 additions & 0 deletions src/app/api/chainhooks/stackingdao/withdraw/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { db } from "@/db/db";
import { type InsertTransaction, transactionTable } from "@/db/schema";
import { conflictUpdateSetAllColumns } from "@/db/utils";
import type {
ChainhookPayload,
ChainhookReceiptEventFTBurnEvent,
ChainhookReceiptEventSTXTransferEvent,
} from "@/lib/chainhooks";
import { getOrInsertToken } from "@/lib/currencies";

export const dynamic = "force-dynamic";

export async function POST(request: Request) {
const data: ChainhookPayload = await request.json();

const transactionsToInsert = data.apply[0].transactions
.filter((transactionToProcess) => transactionToProcess.metadata.success)
.map((transactionToProcess) => {
const sender = transactionToProcess.metadata.sender;
const transferEvents = transactionToProcess.metadata.receipt.events
// Events are not always in order so we sort them by index
.sort((a, b) => a.position.index - b.position.index)
.filter(
(
event,
): event is
| ChainhookReceiptEventSTXTransferEvent
| ChainhookReceiptEventFTBurnEvent =>
event.type === "STXTransferEvent" || event.type === "FTBurnEvent",
);
const stxTransferEvent = transferEvents.filter(
(event) =>
event.type === "STXTransferEvent" && event.data.recipient === sender,
)[0];
const stStxBurnEvent = transferEvents.filter(
(event): event is ChainhookReceiptEventFTBurnEvent =>
event.type === "FTBurnEvent",
)[0];

return {
txId: transactionToProcess.transaction_identifier.hash,
protocol: "stackingdao",
blockHeight: BigInt(data.apply[0].block_identifier.index),
timestamp: new Date(data.apply[0].timestamp * 1000),
sender,
action: "stackingdao-withdraw",
data: {
inAmount: BigInt(stxTransferEvent.data.amount),
inToken: "STX",
outAmount: BigInt(stStxBurnEvent.data.amount),
outToken: stStxBurnEvent.data.asset_identifier,
},
} satisfies InsertTransaction;
});

for (const transaction of transactionsToInsert) {
await getOrInsertToken(transaction.data.inToken);
await getOrInsertToken(transaction.data.outToken);
}

if (transactionsToInsert.length > 0) {
await db
.insert(transactionTable)
.values(transactionsToInsert)
.onConflictDoUpdate({
target: transactionTable.txId,
set: conflictUpdateSetAllColumns(transactionTable),
});
}

return Response.json({ ok: true });
}
28 changes: 28 additions & 0 deletions src/components/Transaction/Action/StackingDAO.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type {
SelectTransactionActionStackingDAODeposit,
SelectTransactionActionStackingDAOWithdraw,
} from "@/db/transactions";
import { displayPrice } from "@/lib/currencies";
import { Text } from "@radix-ui/themes";

interface TransactionActionStackingDAOProps {
transaction:
| SelectTransactionActionStackingDAODeposit
| SelectTransactionActionStackingDAOWithdraw;
}

export const TransactionActionStackingDAO = ({
transaction,
}: TransactionActionStackingDAOProps) => {
return (
<>
<Text color="gray">
{transaction.action === "stackingdao-deposit" ? "Deposit" : "Withdraw"}
</Text>{" "}
{displayPrice(transaction.data.outAmount, transaction.outToken.decimals)}{" "}
{transaction.outToken.symbol} <Text color="gray">for</Text>{" "}
{displayPrice(transaction.data.inAmount, transaction.inToken.decimals)}{" "}
{transaction.inToken.symbol}
</>
);
};
5 changes: 5 additions & 0 deletions src/components/Transaction/TransactionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Link from "next/link";
import { TimeAgo } from "../Shared/TimeAgo";
import { TransactionActionAddLiquidity } from "./Action/AddLiquidity";
import { TransactionActionRemoveLiquidity } from "./Action/RemoveLiquidity";
import { TransactionActionStackingDAO } from "./Action/StackingDAO";
import { TransactionActionSwap } from "./Action/Swap";

interface TransactionRowProps {
Expand Down Expand Up @@ -86,6 +87,10 @@ export const TransactionRow = ({ transaction }: TransactionRowProps) => {
{transaction.action === "remove-liquidity" ? (
<TransactionActionRemoveLiquidity transaction={transaction} />
) : null}
{transaction.action === "stackingdao-deposit" ||
transaction.action === "stackingdao-withdraw" ? (
<TransactionActionStackingDAO transaction={transaction} />
) : null}
</Text>
</div>
);
Expand Down
21 changes: 20 additions & 1 deletion src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type {
ActionData,
ActionDataAddLiquidity,
ActionDataRemoveLiquidity,
ActionDataStackingDAODeposit,
ActionDataStackingDAOWithdraw,
ActionDataSwap,
} from "@/lib/actions";
import type { Protocol } from "@/lib/protocols";
Expand Down Expand Up @@ -55,10 +57,27 @@ export type SelectTransactionActionRemoveLiquidityTyped = SelectTransaction & {
action: "remove-liquidity";
data: ActionDataRemoveLiquidity;
};

/**
* StackingDAO
*/
export type SelectTransactionActionStackingDAODepositTyped =
SelectTransaction & {
action: "stackingdao-deposit";
data: ActionDataStackingDAODeposit;
};
export type SelectTransactionActionStackingDAOWithdrawTyped =
SelectTransaction & {
action: "stackingdao-withdraw";
data: ActionDataStackingDAOWithdraw;
};

export type SelectTransactionTyped =
| SelectTransactionActionSwapTyped
| SelectTransactionActionAddLiquidityTyped
| SelectTransactionActionRemoveLiquidityTyped;
| SelectTransactionActionRemoveLiquidityTyped
| SelectTransactionActionStackingDAODepositTyped
| SelectTransactionActionStackingDAOWithdrawTyped;

export const tokenTable = sqliteTable("token", {
/**
Expand Down
34 changes: 33 additions & 1 deletion src/db/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
type SelectToken,
type SelectTransactionActionAddLiquidityTyped,
type SelectTransactionActionRemoveLiquidityTyped,
type SelectTransactionActionStackingDAODepositTyped,
type SelectTransactionActionStackingDAOWithdrawTyped,
type SelectTransactionActionSwapTyped,
type SelectTransactionTyped,
tokenTable,
Expand All @@ -38,10 +40,24 @@ export type SelectTransactionActionRemoveLiquidity =
tokenY: SelectToken;
};

export type SelectTransactionActionStackingDAODeposit =
SelectTransactionActionStackingDAODepositTyped & {
inToken: SelectToken;
outToken: SelectToken;
};

export type SelectTransactionActionStackingDAOWithdraw =
SelectTransactionActionStackingDAOWithdrawTyped & {
inToken: SelectToken;
outToken: SelectToken;
};

export type SelectTransactionAction =
| SelectTransactionActionSwap
| SelectTransactionActionAddLiquidity
| SelectTransactionActionRemoveLiquidity;
| SelectTransactionActionRemoveLiquidity
| SelectTransactionActionStackingDAODeposit
| SelectTransactionActionStackingDAOWithdraw;

export const getTransactions = async ({
protocol,
Expand Down Expand Up @@ -74,6 +90,10 @@ export const getTransactions = async ({
tokenIds.push(transaction.data.tokenX, transaction.data.tokenY);
} else if (transaction.action === "remove-liquidity") {
tokenIds.push(transaction.data.tokenX, transaction.data.tokenY);
} else if (transaction.action === "stackingdao-deposit") {
tokenIds.push(transaction.data.inToken, transaction.data.outToken);
} else if (transaction.action === "stackingdao-withdraw") {
tokenIds.push(transaction.data.inToken, transaction.data.outToken);
}
});
const uniqueTokenIds = Array.from(new Set([...tokenIds]));
Expand Down Expand Up @@ -113,6 +133,18 @@ export const getTransactions = async ({
tokenX: tokenMap[transaction.data.tokenX],
tokenY: tokenMap[transaction.data.tokenY],
};
} else if (transaction.action === "stackingdao-deposit") {
return {
...transaction,
inToken: tokenMap[transaction.data.inToken],
outToken: tokenMap[transaction.data.outToken],
};
} else if (transaction.action === "stackingdao-withdraw") {
return {
...transaction,
inToken: tokenMap[transaction.data.inToken],
outToken: tokenMap[transaction.data.outToken],
};
}
return transaction;
});
Expand Down
Loading

0 comments on commit d15a28d

Please sign in to comment.