import * as ed25519 from '@noble/ed25519';
import { blake3 } from '@noble/hashes/blake3';
import { sha512 } from '@noble/hashes/sha512';
import { bytesToHex, hexToBytes } from '@noble/ciphers/utils';
ed25519.etc.sha512Sync = (...m) => {
const h = sha512.create();
for (const part of m) h.update(part);
return h.digest();
};
const TX_DOMAIN_TAG = 'sigil/transaction/v1\n';
async function rpc<T>(method: string, params: unknown): Promise<T> {
const res = await fetch(process.env.SIGIL_RPC_URL!, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
});
const body = await res.json() as { result?: T; error?: { code: number; message: string } };
if (body.error) throw new Error(`${body.error.code}: ${body.error.message}`);
if (body.result === undefined) throw new Error('missing result');
return body.result;
}
async function main() {
const privateKey = hexToBytes(process.env.SIGIL_SIGNING_KEY_HEX!);
const publicKey = await ed25519.getPublicKey(privateKey);
const senderDid = process.env.SIGIL_SENDER_DID!;
const recipientDid = process.env.RECIPIENT_DID!;
const amount = Number(process.env.AMOUNT_BASE_UNITS);
const { nonce } = await rpc<{ nonce: number }>('sigil_getBalance', { did: senderDid });
const tx = {
chain_id: process.env.SIGIL_CHAIN_ID,
sender_did: senderDid,
nonce,
gas_limit: 200_000,
gas_price: 1,
tx_type: {
Transfer: {
recipient_did: recipientDid,
amount,
memo: 'cookbook/transfer',
},
},
timestamp: new Date().toISOString(),
};
const transcript = TX_DOMAIN_TAG + JSON.stringify(tx);
const digest = blake3(new TextEncoder().encode(transcript));
const signature = await ed25519.sign(digest, privateKey);
const { tx_hash } = await rpc<{ tx_hash: string }>('sigil_sendTransaction', {
tx,
signature: bytesToHex(signature),
signer_public_key: bytesToHex(publicKey),
});
console.log('submitted:', tx_hash);
for (let i = 0; i < 20; i += 1) {
const r = await rpc<{ status: string; block_height: number | null }>(
'sigil_getTransactionReceipt',
{ tx_hash },
);
if (r && r.status !== 'pending') {
console.log('final:', r);
return;
}
await new Promise((resolve) => setTimeout(resolve, 1_500));
}
throw new Error('receipt did not finalise');
}
main().catch((err) => { console.error(err); process.exit(1); });