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 packages/btcindexer/src/btcindexer-http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { Indexer } from "./btcindexer";
import { networks } from "bitcoinjs-lib";
import { SuiClient } from "./sui-client";

const NBTC_MODULE = "nbtc";
const MINT_FUNCTION = "mint";

export class HIndexer {
public nbtcAddr: string;
public suiFallbackAddr: string;
Expand All @@ -20,6 +23,8 @@ export class HIndexer {
const suiClient = new SuiClient({
suiNetwork: env.SUI_NETWORK,
suiPackageId: env.SUI_PACKAGE_ID,
suiModule: NBTC_MODULE,
suiFunction: MINT_FUNCTION,
suiNbtcObjectId: env.NBTC_OBJECT_ID,
suiLightClientObjectId: env.LIGHT_CLIENT_OBJECT_ID,
suiSignerMnemonic: env.SUI_SIGNER_MNEMONIC,
Expand Down
2 changes: 2 additions & 0 deletions packages/btcindexer/src/btcindexer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ function mkMockD1() {
const SUI_CLIENT_CONFIG: SuiClientConfig = {
suiNetwork: "testnet",
suiPackageId: "0xPACKAGE",
suiModule: "test",
suiFunction: "mint",
suiNbtcObjectId: "0xNBTC",
suiLightClientObjectId: "0xLIGHTCLIENT",
suiSignerMnemonic:
Expand Down
58 changes: 58 additions & 0 deletions packages/btcindexer/src/sui-client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { describe, it, assert } from "vitest";
import { Indexer } from "./btcindexer";
import { SuiClient, SuiClientConfig } from "./sui-client";
import { Block, networks } from "bitcoinjs-lib";

const REGTEST_DATA = {
BLOCK_HEX:
"000000305c2f30d99ad69f247638613dcca7f455159e252878ea6fe10bbc4574a0076914d98ada655d950ac6507d14584fa679d12fb5203c384e52ef292d063fe29b1b645c4b6d68ffff7f200200000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff04022f0100ffffffff02de82814a0000000016001477174bfb906c0e52d750eac4b40fd86746ad50550000000000000000266a24aa21a9ed27611410788b35b819e10a0227f40f9bbc70df824e3a30879552f004b48451210120000000000000000000000000000000000000000000000000000000000000000000000000020000000001017fec0755f3524b89ad45383343f992a0d5cb797a695b59e30f2fc80794f001050000000000fdffffff032202089200000000160014b125723e78c2d779e3e299dfd95d72e9a067a0b780f0fa02000000001600144cc99479ada301056d78a8f3676cfb404d696abe00000000000000000d6a0b3078313233343536373839024730440220755610ff6b6fdea530c20d11b7765816beb75e16ce78fa200a7da25e251a7eb9022078e78bed1cc38822cd5dce3982e23c3cd401415ae72050b00c5f8b3441a2c178012103ef55b72bddf4960ddbb12a9a04f61f91fb613aa99b472115f25a5f8686e6c3f200000000",
TX_ID: "2060dfd3cdbffb7db6c968357f3c9df91b52a4cef5c02fad0b0836b0f25cc4ca",
BLOCK_HEIGHT: 303,
};

const SUI_CLIENT_CONFIG: SuiClientConfig = {
suiNetwork: "devnet",
suiPackageId: "0x7a03af034ade1d5b4072ba4fdb9650bd5ce0cd4dcab40f0563540be0ebbe824b",
suiModule: "indexer_test",
suiFunction: "mint",
suiNbtcObjectId: "0xd93cc7f6d91100990f9fa8ca11d533a69254e2f716ab69a22c6cc4e9a49a9374",
suiLightClientObjectId:
"0xd93cc7f6d91100990f9fa8ca11d533a69254e2f716ab69a22c6cc4e9a49a9374",
suiSignerMnemonic:
"your mnemonic your mnemonic your mnemonic your mnemonic your mnemonic your mnemonic",
};

// NOTE: skip to prevent this test from running in CI
describe.skip("Sui Contract Integration", () => {
it("should successfully call the mint function on devnet", { timeout: 60000 }, async () => {
const suiClient = new SuiClient(SUI_CLIENT_CONFIG);
const indexer = new Indexer(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{} as any,
"bcrt1qfnyeg7dd5vqs2mtc4rekwm8mgpxkj647p39zhw",
"fallback",
networks.regtest,
suiClient,
);
const block = Block.fromHex(REGTEST_DATA.BLOCK_HEX);
const txIndex = block.transactions?.findIndex(
(tx) => tx.getId() === REGTEST_DATA.TX_ID,
);
assert(txIndex);
const targetTx = block.transactions?.[txIndex ?? -1];
assert(targetTx);

const tree = indexer.constructMerkleTree(block);
assert(tree);
const proofResult = indexer.getTxProof(tree, targetTx);
assert(proofResult);

const success = await suiClient.tryMintNbtc(
targetTx,
REGTEST_DATA.BLOCK_HEIGHT,
txIndex,
proofResult,
);
assert.isTrue(success);
});
});
19 changes: 14 additions & 5 deletions packages/btcindexer/src/sui-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ProofResult } from "./btcindexer";
export interface SuiClientConfig {
suiNetwork: "testnet" | "mainnet" | "devnet";
suiPackageId: string;
suiModule: string;
suiFunction: string;
suiNbtcObjectId: string;
suiLightClientObjectId: string;
suiSignerMnemonic: string;
Expand All @@ -17,13 +19,17 @@ export class SuiClient {
private client: Client;
private signer: Ed25519Keypair;
private packageId: string;
private module: string;
private function: string;
private nbtcObjectId: string;
private lightClientObjectId: string;

constructor(config: SuiClientConfig) {
this.client = new Client({ url: getFullnodeUrl(config.suiNetwork) });
this.signer = Ed25519Keypair.deriveKeypair(config.suiSignerMnemonic);
this.packageId = config.suiPackageId;
this.module = config.suiModule;
this.function = config.suiFunction;
this.nbtcObjectId = config.suiNbtcObjectId;
this.lightClientObjectId = config.suiLightClientObjectId;
}
Expand All @@ -35,9 +41,12 @@ export class SuiClient {
proof: ProofResult,
): Promise<void> {
const tx = new SuiTransaction();
const target = `${this.packageId}::nbtc::mint` as const;
const target = `${this.packageId}::${this.module}::${this.function}` as const;
const serializedTx = serializeBtcTx(transaction);

// NOTE: the contract is expecting the proofs to be in big-endian format, while the bitcon-js lib operates internally on little-endian.
const proofBigEndian = proof.proofPath.map((p) => Array.from(Buffer.from(p).reverse()));

tx.moveCall({
target: target,
arguments: [
Expand All @@ -49,15 +58,15 @@ export class SuiClient {
tx.pure.u32(serializedTx.outputCount),
tx.pure.vector("u8", serializedTx.outputs),
tx.pure.vector("u8", serializedTx.lockTime),
tx.pure.vector(
"vector<u8>",
proof.proofPath.map((p) => Array.from(p)),
),
tx.pure.vector("vector<u8>", proofBigEndian),
tx.pure.u64(blockHeight),
tx.pure.u64(txIndex),
],
});

// TODO: should we move it to config or set it as a constant
tx.setGasBudget(1000000000);

const result = await this.client.signAndExecuteTransaction({
signer: this.signer,
transaction: tx,
Expand Down