diff --git a/packages/ur-registry-avalanche/__tests__/AvalancheSignRequest.test.ts b/packages/ur-registry-avalanche/__tests__/AvalancheSignRequest.test.ts index a77ad7b2..5fc95548 100644 --- a/packages/ur-registry-avalanche/__tests__/AvalancheSignRequest.test.ts +++ b/packages/ur-registry-avalanche/__tests__/AvalancheSignRequest.test.ts @@ -8,16 +8,14 @@ describe("avalanche-sign-request", () => { "00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000000089544000000000000000000000000100000001512e7191685398f00663e12197a3d8f6012d9ea300000001db720ad6707915cc4751fb7e5491a3af74e127a1d81817abe9438590c0833fe10000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000000989680000000010000000000000000", "hex" ); - const mfp = "1250B6BC"; - const xpub = - "xpub661MyMwAqRbcFFDMuFiGQmA1EqWxxgDLdtNvxxiucf9qkfoVrvwgnYyshxWoewWtkZ1aLhKoVDrpeDvn1YRqxX2szhGKi3UiSEv1hYRMF8q"; - const walletIndex = 0; + const derivationPath = "m/44'/133'/0'/0/0"; + const utxos = []; + const avalancheSignRequest = AvalancheSignRequest.constructAvalancheRequest( avalancheData, - mfp, - xpub, - walletIndex + derivationPath, + utxos ); const request = AvalancheSignRequest.fromDataItem( diff --git a/packages/ur-registry-avalanche/package.json b/packages/ur-registry-avalanche/package.json index 6a36f9e1..6ac32224 100644 --- a/packages/ur-registry-avalanche/package.json +++ b/packages/ur-registry-avalanche/package.json @@ -1,6 +1,6 @@ { "name": "@keystonehq/bc-ur-registry-avalanche", - "version": "0.0.5", + "version": "0.0.6", "description": "bc-ur-registry extension for Avalanche", "main": "dist/index.cjs", "module": "dist/index.mjs", diff --git a/packages/ur-registry-avalanche/src/AvalancheSignRequest.ts b/packages/ur-registry-avalanche/src/AvalancheSignRequest.ts index b469a03b..7e066f45 100644 --- a/packages/ur-registry-avalanche/src/AvalancheSignRequest.ts +++ b/packages/ur-registry-avalanche/src/AvalancheSignRequest.ts @@ -3,34 +3,33 @@ import { RegistryItem, extend, DataItemMap, + CryptoKeypath, } from "@keystonehq/bc-ur-registry"; import { ExtendedRegistryTypes } from "./RegistryType"; import * as uuid from "uuid"; +import { AvalancheUtxo, AvalancheUtxoData } from "./AvalancheUtxo"; const { RegistryTypes } = extend; type signRequestProps = { requestId?: Buffer; data: Buffer; - mfp: Buffer; - xpub: string; - walletIndex: number; + derivationPath: CryptoKeypath; + utxos: AvalancheUtxo[]; }; enum Keys { requestId = 1, - signData = 2, - mfp = 3, - xpub = 6, - walletIndex = 7, + signData, + derivationPath, + utxos, } - export class AvalancheSignRequest extends RegistryItem { private requestId?: Buffer; private data: Buffer; - private mfp: Buffer; - private xpub: string; - private walletIndex: number; + private derivationPath: CryptoKeypath; + private utxos: AvalancheUtxo[]; + getRegistryType = () => ExtendedRegistryTypes.AVALANCHE_SIGN_REQUEST; @@ -38,13 +37,14 @@ export class AvalancheSignRequest extends RegistryItem { super(); this.requestId = args.requestId; this.data = args.data; - this.mfp = args.mfp; - this.xpub = args.xpub; - this.walletIndex = args.walletIndex; + this.derivationPath = args.derivationPath; + this.utxos = args.utxos; } public getRequestId = () => this.requestId; public getSignData = () => this.data; + public getUtxos = () => this.utxos; + public getDerivationPath = () => this.derivationPath; public toDataItem = () => { const map: DataItemMap = {}; @@ -56,40 +56,40 @@ export class AvalancheSignRequest extends RegistryItem { } map[Keys.signData] = Buffer.from(this.data); - map[Keys.mfp] = this.mfp.readUInt32BE(0); - map[Keys.xpub] = this.xpub; - map[Keys.walletIndex] = Number(this.walletIndex); + map[Keys.derivationPath] = this.derivationPath; + map[Keys.utxos] = this.utxos.map((utxo) => { + const res = utxo.toDataItem(); + res.setTag(utxo.getRegistryType().getTag()); + return res; + }); return new DataItem(map); }; public static fromDataItem = (dataItem: DataItem) => { const map = dataItem.getData(); - const masterFingerprint = Buffer.alloc(4); - const _masterFingerprint = map[Keys.mfp]; - masterFingerprint.writeUInt32BE(_masterFingerprint, 0); const requestId = map[Keys.requestId] ? map[Keys.requestId].getData() : undefined; const data = map[Keys.signData]; - const xpub = map[Keys.xpub]; - const walletIndex = map[Keys.signData]; + const derivationPath = map[Keys.signData]; + const utxos: AvalancheUtxo[] = map[Keys.utxos].map((utxo: DataItem) => + AvalancheUtxo.fromDataItem(utxo) + ); return new AvalancheSignRequest({ requestId, data, - xpub, - walletIndex, - mfp: masterFingerprint, + derivationPath, + utxos, }); }; public static constructAvalancheRequest( data: Buffer, - mfp: string, - xpub: string, - walletIndex: number, - requestId?: string | Buffer + derivationPath: CryptoKeypath, + utxos: AvalancheUtxoData[], + requestId?: string | Buffer, ) { let _requestId; if (typeof requestId === "string") { @@ -99,13 +99,15 @@ export class AvalancheSignRequest extends RegistryItem { } else { _requestId = Buffer.from(uuid.parse(uuid.v4()) as Uint8Array); } + const avalancheUtxos = utxos.map((utxo) => + AvalancheUtxo.constructAvalancheUtxo(utxo) + ); return new AvalancheSignRequest({ data, requestId: _requestId, - mfp: Buffer.from(mfp, "hex"), - xpub, - walletIndex, + derivationPath, + utxos: avalancheUtxos, }); } } diff --git a/packages/ur-registry-avalanche/src/AvalancheUtxo.ts b/packages/ur-registry-avalanche/src/AvalancheUtxo.ts new file mode 100644 index 00000000..cf7198d4 --- /dev/null +++ b/packages/ur-registry-avalanche/src/AvalancheUtxo.ts @@ -0,0 +1,100 @@ +import { + CryptoKeypath, + extend, + DataItem, + PathComponent, + RegistryItem, + DataItemMap, + } from "@keystonehq/bc-ur-registry"; + import { ExtendedRegistryTypes } from "./RegistryType"; + + const { decodeToDataItem } = extend; + + enum Keys { + txid = 1, + vout, + path, + } + + export interface AvalancheUtxoProps { + txid: Buffer; + vout: number; + path: CryptoKeypath; + } + + export interface AvalancheUtxoData { + txid: string; + vout: number; + path: string; + } + + export class AvalancheUtxo extends RegistryItem { + private txid: Buffer; + private vout: number; + private path: CryptoKeypath; + + getRegistryType = () => ExtendedRegistryTypes.AVALANCHE_UTXO; + + constructor(args: AvalancheUtxoProps) { + super(); + this.txid = args.txid; + this.vout = args.vout; + this.path = args.path; + } + + public getTxid = () => this.txid; + public getVout = () => this.vout; + public getKeyPath = () => this.path.getPath(); + + public toDataItem = () => { + const map: DataItemMap = {}; + map[Keys.txid] = this.txid; + map[Keys.vout] = this.vout; + + const keyPath = this.path.toDataItem(); + keyPath.setTag(this.path.getRegistryType().getTag()); + map[Keys.path] = keyPath; + + return new DataItem(map); + }; + + public static fromDataItem = (dataItem: DataItem) => { + const map = dataItem.getData(); + const txid = map[Keys.txid]; + const vout = map[Keys.vout]; + const path = CryptoKeypath.fromDataItem(map[Keys.path]); + + return new AvalancheUtxo({ + txid, + vout, + path, + }); + }; + + public static fromCBOR = (_cborPayload: Buffer) => { + const dataItem = decodeToDataItem(_cborPayload); + return AvalancheUtxo.fromDataItem(dataItem); + }; + + public static constructAvalancheUtxo({ + txid, + vout, + path, + }: AvalancheUtxoData) { + const paths = path.replace(/[m|M]\//, "").split("/"); + const hdPathObject = new CryptoKeypath( + paths.map((path) => { + const index = parseInt(path.replace("'", "")); + const isHardened = path.endsWith("'"); + return new PathComponent({ index, hardened: isHardened }); + }), + ); + + return new AvalancheUtxo({ + txid: Buffer.from(txid, "hex"), + vout, + path: hdPathObject, + }); + } + } + \ No newline at end of file diff --git a/packages/ur-registry-avalanche/src/RegistryType.ts b/packages/ur-registry-avalanche/src/RegistryType.ts index b132b902..110eb5b1 100644 --- a/packages/ur-registry-avalanche/src/RegistryType.ts +++ b/packages/ur-registry-avalanche/src/RegistryType.ts @@ -3,4 +3,5 @@ import { RegistryType } from "@keystonehq/bc-ur-registry"; export const ExtendedRegistryTypes = { AVALANCHE_SIGN_REQUEST: new RegistryType("avax-sign-request", 8301), AVALANCHE_SIGNATURE: new RegistryType("avax-signature", 8302), + AVALANCHE_UTXO: new RegistryType("avax-utxo", 8303), };