Skip to content

Commit 35ec7db

Browse files
committed
chore: dogecoin testing
1 parent 48f9bd9 commit 35ec7db

File tree

13 files changed

+1541
-7
lines changed

13 files changed

+1541
-7
lines changed

cspell-custom-words.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,16 @@ behaviour
1919
Berachain
2020
bigset
2121
Bigtable
22-
Blackhole
2322
blackhole
23+
Blackhole
2424
blackholed
25+
Blackholed
26+
blackholing
2527
borsh
2628
bscscan
2729
BUILDKIT
2830
bytecodes
2931
callstack
30-
Blackhole
31-
Blackholed
32-
blackholing
33-
blackholed
3432
ccqlistener
3533
CCTP
3634
celestia
@@ -56,6 +54,7 @@ Cyfrin
5654
datagram
5755
denoms
5856
devnet
57+
Dogecoin
5958
dymension
6059
Dymension
6160
ethcrypto
@@ -105,6 +104,7 @@ Keccak
105104
kevm
106105
KEVM
107106
keymap
107+
keypair
108108
keytool
109109
klaytn
110110
Klaytn
@@ -169,8 +169,8 @@ readyz
169169
regen
170170
reinit
171171
reobservation
172-
reobservations
173172
Reobservation
173+
reobservations
174174
Reobservations
175175
reobserved
176176
repoint
@@ -239,8 +239,8 @@ wormchaind
239239
Wormholescan
240240
wormscan
241241
wormscanurl
242+
XFER
242243
xlayer
243244
xpla
244-
XFER
245245
XPLA
246246
Zellic

testing/dogecoin/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*.json
2+
!package.json
3+
.env
4+
.env.*
5+
node_modules

testing/dogecoin/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Dogecoin Multisig Bridge Test
2+
3+
This folder contains a script to test deposits and withdrawals via the Wormhole testnet guardian's 5-of-7 multisig Dogecoin Delegated Manager Set.
4+
5+
## Testing Artifacts
6+
7+
https://doge-testnet-explorer.qed.me/tx/8d55cbffc83f27ec16fb3da9c550857533169233c6683f8d97986b14b928de81
8+
https://doge-testnet-explorer.qed.me/tx/2916f3063844f0e2721f81041c3dc2ac02790d24ce3a1b31b8c1fb2e908d52c0
9+
10+
## Prerequisites
11+
12+
### Bun
13+
14+
```bash
15+
curl -fsSL https://bun.sh/install | bash
16+
```
17+
18+
### Install Dependencies
19+
20+
```bash
21+
bun ci
22+
```
23+
24+
## Dogecoin Testnet Wallet (dogecoin-keygen.ts)
25+
26+
Generates a Dogecoin testnet keypair with WIF-encoded private key and P2PKH address.
27+
28+
```bash
29+
bun run dogecoin-keygen.ts
30+
```
31+
32+
## Solana Devnet Wallet (solana-keygen.ts)
33+
34+
Generates a Solana devnet keypair.
35+
36+
```bash
37+
bun run solana-keygen.ts
38+
```
39+
40+
Note: As the Delegated Manager Set feature is currently permissioned, in order for this test to work, your Solana devnet address has to be in the allowlist of the running testnet guardian.
41+
42+
## Testnet Deposit Script (deposit-testnet.ts)
43+
44+
Deposits DOGE to a P2SH multisig address controlled by the delegated manager set. The script builds a custom redeem script with embedded metadata (emitter chain, emitter contract, sub-address seed) and creates a 5-of-7 multisig.
45+
46+
### Prerequisites
47+
48+
- `dogecoin-testnet-keypair.json` - Funded Dogecoin wallet (from `dogecoin-keygen.ts`)
49+
- `solana-devnet-keypair.json` - Solana keypair for emitter contract address (from `solana-keygen.ts`)
50+
51+
### Usage
52+
53+
```bash
54+
bun run deposit-testnet.ts
55+
```
56+
57+
## Testnet Withdraw Script (withdraw-testnet.ts)
58+
59+
Withdraws DOGE from the P2SH multisig address back to the original sender. Collects 5-of-7 signatures from the delegated manager set to satisfy the multisig requirement.
60+
61+
### Prerequisites
62+
63+
- `solana-devnet-keypair.json` - Same Solana keypair used for deposit
64+
- `deposit-info.json` - Created by running `deposit.ts`
65+
66+
### Usage
67+
68+
```bash
69+
bun run withdraw-testnet.ts
70+
```

testing/dogecoin/bun.lock

Lines changed: 192 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import * as bitcoin from "bitcoinjs-lib";
2+
import { loadManagerKeys } from "./manager";
3+
import {
4+
ECPair,
5+
EMITTER_CHAIN,
6+
buildRedeemScript,
7+
dogecoinTestnet,
8+
loadSolanaEmitterContract,
9+
} from "./redeem-script";
10+
import {
11+
DOGECOIN_CHAIN_ID,
12+
FEE,
13+
KOINU_PER_DOGE,
14+
broadcastTx,
15+
explorerTxUrl,
16+
fetchRawTx,
17+
fetchUtxos,
18+
} from "./shared";
19+
20+
// Load emitter contract and manager keys
21+
const emitterContract = await loadSolanaEmitterContract();
22+
23+
const {
24+
mThreshold,
25+
nTotal,
26+
pubkeys: managerPubkeys,
27+
} = await loadManagerKeys(DOGECOIN_CHAIN_ID);
28+
29+
console.log("\nManager keys:");
30+
for (let i = 0; i < managerPubkeys.length; i++) {
31+
console.log(` ${i}: ${managerPubkeys[i]!.toString("hex")}`);
32+
}
33+
34+
// Build the redeem script (using Solana pubkey as recipient_address)
35+
const redeemScript = buildRedeemScript({
36+
emitterChain: EMITTER_CHAIN,
37+
emitterContract,
38+
recipientAddress: emitterContract, // Use Solana public key as recipient_address
39+
managerPubkeys,
40+
mThreshold,
41+
nTotal,
42+
});
43+
44+
console.log("\nRedeem script length:", redeemScript.length, "bytes");
45+
console.log("Redeem script (hex):", redeemScript.toString("hex"));
46+
47+
// Generate P2SH address from redeem script
48+
const p2sh = bitcoin.payments.p2sh({
49+
redeem: { output: new Uint8Array(redeemScript) },
50+
network: dogecoinTestnet,
51+
});
52+
53+
console.log("\nP2SH Address:", p2sh.address);
54+
console.log(
55+
"Script Hash:",
56+
p2sh.hash ? Buffer.from(p2sh.hash).toString("hex") : undefined,
57+
);
58+
59+
// Read sender's keypair
60+
const senderKeypair = await Bun.file("dogecoin-testnet-keypair.json").json();
61+
const senderKeyPair = ECPair.fromWIF(
62+
senderKeypair.privateKeyWIF,
63+
dogecoinTestnet,
64+
);
65+
const senderAddress = senderKeypair.address;
66+
67+
console.log("\nSender address:", senderAddress);
68+
69+
// Amount to send (in koinu) - 1 DOGE = 100,000,000 koinu
70+
const AMOUNT_TO_SEND = KOINU_PER_DOGE; // 1 DOGE
71+
72+
console.log("\nFetching UTXOs...");
73+
const utxos = await fetchUtxos(senderAddress);
74+
console.log(`Found ${utxos.length} UTXOs`);
75+
76+
if (utxos.length === 0) {
77+
console.error("No UTXOs available. Please fund the sender address first.");
78+
console.log(`Get testnet coins from: https://faucet.doge.toys/`);
79+
process.exit(1);
80+
}
81+
82+
// Calculate total available
83+
const totalAvailable = utxos.reduce(
84+
(sum: number, utxo: any) => sum + utxo.value,
85+
0,
86+
);
87+
console.log(`Total available: ${totalAvailable / KOINU_PER_DOGE} DOGE`);
88+
89+
if (totalAvailable < AMOUNT_TO_SEND + FEE) {
90+
console.error(
91+
`Insufficient funds. Need ${(AMOUNT_TO_SEND + FEE) / KOINU_PER_DOGE} DOGE`,
92+
);
93+
process.exit(1);
94+
}
95+
96+
// Build transaction
97+
const psbt = new bitcoin.Psbt({ network: dogecoinTestnet });
98+
99+
// Add inputs
100+
let inputSum = 0;
101+
for (const utxo of utxos) {
102+
// Fetch raw transaction for the UTXO
103+
const rawTxHex = await fetchRawTx(utxo.txid);
104+
105+
psbt.addInput({
106+
hash: utxo.txid,
107+
index: utxo.vout,
108+
nonWitnessUtxo: new Uint8Array(Buffer.from(rawTxHex, "hex")),
109+
});
110+
111+
inputSum += utxo.value;
112+
if (inputSum >= AMOUNT_TO_SEND + FEE) break;
113+
}
114+
115+
// Add output to P2SH address
116+
psbt.addOutput({
117+
address: p2sh.address!,
118+
value: BigInt(AMOUNT_TO_SEND),
119+
});
120+
121+
// Add change output if needed
122+
const change = inputSum - AMOUNT_TO_SEND - FEE;
123+
if (change > 0) {
124+
psbt.addOutput({
125+
address: senderAddress,
126+
value: BigInt(change),
127+
});
128+
}
129+
130+
// Sign all inputs
131+
psbt.signAllInputs(senderKeyPair);
132+
psbt.finalizeAllInputs();
133+
134+
// Extract and broadcast
135+
const tx = psbt.extractTransaction();
136+
const txHex = tx.toHex();
137+
138+
console.log("\nTransaction hex:", txHex);
139+
console.log("Transaction ID:", tx.getId());
140+
141+
console.log("\nBroadcasting transaction...");
142+
try {
143+
const txid = await broadcastTx(txHex);
144+
console.log("Transaction broadcast successfully!");
145+
console.log("TXID:", txid);
146+
console.log(`\nView on explorer: ${explorerTxUrl(txid)}`);
147+
148+
// Save deposit info
149+
const depositInfo = {
150+
txid: tx.getId(),
151+
amount: AMOUNT_TO_SEND,
152+
p2shAddress: p2sh.address,
153+
redeemScript: redeemScript.toString("hex"),
154+
senderAddress,
155+
timestamp: new Date().toISOString(),
156+
};
157+
await Bun.write("deposit-info.json", JSON.stringify(depositInfo, null, 2));
158+
console.log("\nDeposit info saved to deposit-info.json");
159+
} catch (error) {
160+
console.error("Broadcast failed:", error);
161+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as bitcoin from "bitcoinjs-lib";
2+
import * as ecc from "tiny-secp256k1";
3+
import { ECPairFactory } from "ecpair";
4+
5+
bitcoin.initEccLib(ecc);
6+
const ECPair = ECPairFactory(ecc);
7+
8+
// Dogecoin Testnet network parameters
9+
const dogecoinTestnet: bitcoin.Network = {
10+
messagePrefix: "\x19Dogecoin Signed Message:\n",
11+
bech32: "tdge",
12+
bip32: {
13+
public: 0x043587cf,
14+
private: 0x04358394,
15+
},
16+
pubKeyHash: 0x71, // addresses start with 'n'
17+
scriptHash: 0xc4,
18+
wif: 0xf1, // WIF prefix for testnet
19+
};
20+
21+
// Generate a new keypair
22+
const keyPair = ECPair.makeRandom({ network: dogecoinTestnet });
23+
24+
// Get private key in WIF format
25+
const privateKeyWIF = keyPair.toWIF();
26+
27+
// Get public key
28+
const publicKey = Buffer.from(keyPair.publicKey).toString("hex");
29+
30+
// Generate address (P2PKH)
31+
const { address } = bitcoin.payments.p2pkh({
32+
pubkey: new Uint8Array(Buffer.from(keyPair.publicKey)),
33+
network: dogecoinTestnet,
34+
});
35+
36+
// Save to JSON file
37+
const keypairData = {
38+
network: "dogecoin-testnet",
39+
privateKeyWIF,
40+
publicKey,
41+
address,
42+
generatedAt: new Date().toISOString(),
43+
};
44+
45+
await Bun.write(
46+
"dogecoin-testnet-keypair.json",
47+
JSON.stringify(keypairData, null, 2),
48+
);
49+
50+
console.log("Keypair saved to dogecoin-testnet-keypair.json");

0 commit comments

Comments
 (0)