Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/wet-tools-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@delvtech/hyperdrive-js": patch
---

Add zap logic to ReadHyperdrive
15 changes: 15 additions & 0 deletions apps/sdk-sandbox/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,28 @@ import { privateKeyToAccount } from "viem/accounts";

export const publicClient: PublicClient = createPublicClient({
transport: http(process.env.RPC_URL),
chain: {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like the update to viem now requires an explicit chain.

id: 707,
name: "Zaps Cloudchain",
nativeCurrency: {
name: "ETH",
symbol: "ETH",
decimals: 18,
},
rpcUrls: {
default: {
http: [process.env.RPC_URL!],
},
},
},
});

export const walletClient = process.env.WALLET_PRIVATE_KEY
? createWalletClient({
account: privateKeyToAccount(
process.env.WALLET_PRIVATE_KEY as `0x${string}`,
),
chain: publicClient.chain,
transport: http(process.env.RPC_URL),
})
: undefined;
2 changes: 1 addition & 1 deletion apps/sdk-sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@delvtech/hyperdrive-appconfig": "*",
"@delvtech/hyperdrive-js": "*",
"@delvtech/hyperdrive-wasm": "*",
"viem": "^2.9.2"
"viem": "^2.22.19"
},
"devDependencies": {
"@types/node": "^20.14.2",
Expand Down
288 changes: 275 additions & 13 deletions apps/sdk-sandbox/scripts/example.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,283 @@
import { Drift } from "@delvtech/drift";
import { viemAdapter } from "@delvtech/drift-viem";
import { ReadHyperdrive } from "@delvtech/hyperdrive-js";
import { publicClient } from "../client";
import { fixed } from "@delvtech/fixed-point-wasm";
import { appConfig } from "@delvtech/hyperdrive-appconfig";
import {
ReadHyperdrive,
ReadWriteHyperdrive,
zapAbi,
} from "@delvtech/hyperdrive-js";
import { Address, encodePacked, maxInt256 } from "viem";
import { publicClient, walletClient } from "../client";

const drift = new Drift(viemAdapter({ publicClient }));
const zapsConfig = appConfig.zaps[707];
const drift = new Drift(viemAdapter({ publicClient, walletClient }));

const pool = new ReadHyperdrive({
address: "0x324395D5d835F84a02A75Aa26814f6fD22F25698",
const poolAddress = "0x324395D5d835F84a02A75Aa26814f6fD22F25698";
const earliestBlock = 20180617n;

// Write instance for transactions
const writePool = new ReadWriteHyperdrive({
address: poolAddress,
drift,
earliestBlock,
});

// Read instance (includes zapAddress)
const readPool = new ReadHyperdrive({
address: poolAddress,
drift,
auxiliaryContractAddress: zapsConfig.address,
earliestBlock,
});

const kind = await pool.getKind();
const config = await pool.getPoolConfig();
const poolContract = drift.contract({
abi: writePool.contract.abi,
address: poolAddress,
});

// SAMPLE ASSET ID AND MATURITY
const assetId: bigint =
452312848583266388373324160190187140051835877600158453279131187532665273856n;
const maturity = 1754611200n;

async function openLongPosition() {
try {
const account = walletClient?.account.address as Address;

const beforeDetails = await readPool.getOpenLongDetails({
account,
assetId,
options: { block: "latest" },
});
console.log("openLongDetails before:", beforeDetails);

const { result, request } = await publicClient.simulateContract({
abi: writePool.contract.abi,
address: poolAddress,
chain: publicClient.chain,
functionName: "openLong",
account,
gas: 16125042n,
args: [
BigInt(30e18), // 30 base tokens (DAI)
1n,
1n,
{
asBase: true,
destination: account,
extraData: "0x",
},
],
});

const openTxHash = await walletClient?.writeContract({
...request,
});
if (!openTxHash) throw new Error("No open transaction hash received");

const txReceipt = await publicClient.waitForTransactionReceipt({
hash: openTxHash,
});
await drift.cache.clear();

if (!txReceipt) throw new Error("No open transaction receipt received");
if (txReceipt.status !== "success") {
console.error("Open Transaction failed:", txReceipt);
throw new Error("Open Transaction failed");
}
console.log("Open tx receipt status:", txReceipt.status);

const afterDetails = await readPool.getOpenLongDetails({
account,
assetId,
});
console.log("openLongDetails after:", afterDetails);

// Approve zap (auxiliary) contract to manage the long position
const approvalReceipt = await writePool.contract.write("setApproval", {
amount: maxInt256,
tokenID: assetId,
operator: zapsConfig.address,
});
console.log("Approval tx hash:", approvalReceipt);

// Simulate closeLong to preview base amount out
const { result: previewBaseAmountOut } =
await publicClient.simulateContract({
abi: writePool.contract.abi,
address: poolAddress,
chain: publicClient.chain,
functionName: "closeLong",
account,
args: [
maturity,
BigInt(40e18), // 40 base tokens (DAI)
1n,
{
asBase: true,
destination: account,
extraData: "0x",
},
],
});
console.log("Preview base out:", fixed(previewBaseAmountOut).format());

// Execute the zap close operation
const swapTx = await walletClient
?.writeContract({
abi: zapAbi,
chain: publicClient.chain,
address: zapsConfig.address,
functionName: "closeLongZap",
gas: 16125042n,
args: [
poolAddress,
maturity,
BigInt(40e18),
1n,
{
destination: zapsConfig.address,
asBase: true,
extraData: "0x",
},
{
amountIn: previewBaseAmountOut,
amountOutMinimum: 1n,
deadline: (await publicClient.getBlock()).timestamp + 60n,
path: encodePacked(
["address", "uint24", "address"],
[
"0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI
100, // 0.01% fee tier
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
],
),
recipient: account,
},
false,
],
})
.catch((err) => {
console.error("closeLongZap failed:", err);
throw err;
});
if (!swapTx) throw new Error("No close position transaction hash received");

const receipt = await publicClient.waitForTransactionReceipt({
hash: swapTx,
});
await drift.cache.clear();

const openLongDetailsAfterZap = await readPool.getOpenLongDetails({
account,
assetId,
});
console.log("openLongDetails after zap close:", openLongDetailsAfterZap);

return txReceipt;
} catch (error) {
console.error("Failed to open long position:", error);
throw error;
}
}

async function closeAllPositions() {
const account = walletClient?.account.address as Address;
const block = await publicClient.getBlock();

// Example: manually defined positions
const manualPositions = [
{
assetId:
452312848583266388373324160190187140051835877600158453279131187532665273856n,
maturity: 1754611200n,
bondAmount: 104902143926345435824n, // Example: 100 bonds
},
// Add more positions as needed
];

for (const position of manualPositions) {
try {
console.log("\nProcessing position:", position);

// Approve zap contract to manage the position
const approvalReceipt = await poolContract.write("setApproval", {
amount: maxInt256,
tokenID: position.assetId,
operator: zapsConfig.address,
});
console.log("Approval tx hash:", approvalReceipt);

// Preview base out from closeLong
const { result: previewBaseAmountOut } =
await publicClient.simulateContract({
abi: writePool.contract.abi,
address: poolAddress,
functionName: "closeLong",
account,
args: [
position.maturity,
position.bondAmount,
1n,
{
asBase: true,
destination: account,
extraData: "0x",
},
],
});
console.log("Preview base out:", fixed(previewBaseAmountOut).format());

// Execute zap close for the position
const swapTx = await walletClient?.writeContract({
abi: zapAbi,
chain: publicClient.chain,
address: zapsConfig.address,
functionName: "closeLongZap",
args: [
poolAddress,
position.maturity,
position.bondAmount,
1n,
{
destination: zapsConfig.address,
asBase: true,
extraData: "0x",
},
{
amountIn: previewBaseAmountOut,
amountOutMinimum: 1n,
deadline: (await publicClient.getBlock()).timestamp + 60n,
path: encodePacked(
["address", "uint24", "address"],
[
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
100,
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
],
),
recipient: account,
},
false,
],
});
const openLongDetails = await writePool.getOpenLongDetails({
account,
assetId: position.assetId,
});
console.log("openLongDetails:", openLongDetails);
console.log("Closed position tx hash:", swapTx);
} catch (error) {
console.error(`Failed to close position ${position.assetId}:`, error);
}
}
}

async function main() {
// Uncomment the function call you need
// await openLongPosition();
await closeAllPositions();
}

console.log(`
address: ${pool.address}
kind: ${kind}
baseToken: ${config.baseToken}
sharesToken: ${config.vaultSharesToken}
`);
main().catch(console.error);
Loading
Loading