Examples

// npm i clvm-lib bech32
import { Program } from "clvm-lib";
import { bech32m } from "bech32";
declare global {
interface Window {
chia: any;
goby: any;
}
}
type Amount = string | number;
type HexString = string;
interface Coin {
parent_coin_info: string;
puzzle_hash: string;
amount: number;
}
interface CoinSpend {
coin: Coin;
puzzle_reveal: HexString
solution: HexString
}
export interface SpendBundle {
coin_spends: CoinSpend[]
aggregated_signature: HexString
}
interface TakeOfferParams {
offer: string;
}
interface AssetAmount {
assetId: string;
amount: Amount,
}
interface TransferParams {
to: string;
amount: Amount;
assetId: string;
memos?: HexString[];
}
interface CreateOfferParams {
offerAssets: AssetAmount[];
requestAssets: AssetAmount[];
}
interface AssetBalanceResp {
confirmed: string;
spendable: string;
spendableCoinCount: number;
}
interface getAssetCoinsParams {
type: string|null;
assetId?: string|null;
includedLocked?: boolean;
offset?: number;
limit?: number;
}
export interface SpendableCoin {
coin: Coin;
coinName: string;
puzzle: string;
confirmedBlockIndex: number;
locked: boolean;
lineageProof?: {
parentName?: string;
innerPuzzleHash?: string;
amount?: number;
}
}
interface sendTransactionParams {
spendBundle: SpendBundle;
}
// stay the same as [transaction_ack](https://docs.chia.net/docs/10protocol/wallet_protocol/#transaction_ack)
enum MempoolInclusionStatus {
SUCCESS = 1, // Transaction added to mempool
PENDING = 2, // Transaction not yet added to mempool
FAILED = 3 // Transaction was invalid and dropped
}
interface TransactionResp {
status: MempoolInclusionStatus;
error?: string;
}
interface SignMessageParams {
message: string;
publicKey: string;
}
export interface Wallet {
chainId(): Promise<string>
filterUnlockedCoins(params: {coinNames: string[]}): Promise<string[]>
walletSwitchChain(params: {chainId: string}): Promise<null>
connect(params?: {eager?: boolean}): Promise<boolean>
transfer(params: TransferParams): Promise<{
id: string;
}> // @deprecated
takeOffer(params: TakeOfferParams): Promise<{
transaction_id: string;
transaction: SpendBundle;
}> // @deprecated
createOffer(params: CreateOfferParams): Promise<{
transaction_id: string;
offer: string;
}> // @deprecated
getPublicKeys(params?: {limit?: number, offset?: number}): Promise<string[]>
signCoinSpends(params: {coinSpends: CoinSpend[]}): Promise<string>
getAssetBalance(params: {type: string|null, assetId: string|null}): Promise<AssetBalanceResp>
getAssetCoins(params: getAssetCoinsParams): Promise<SpendableCoin[]>
sendTransaction(params: sendTransactionParams): Promise<TransactionResp[]>
signMessage(params: SignMessageParams): Promise<string>
}
const wallet = new Proxy({} as Wallet, {
get(obj, key) {
return function (params: any) {
return window.chia.request({ method: key, params });
};
},
});
const payToDelegatedOrHiddenPuzzle = Program.deserializeHex('ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080')
const MAP_HEX: Record<string, number> = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
a: 10,
b: 11,
c: 12,
d: 13,
e: 14,
f: 15,
A: 10,
B: 11,
C: 12,
D: 13,
E: 14,
F: 15,
};
function fromHex(hex: string): Uint8Array {
const bytes = new Uint8Array(Math.floor(hex.length / 2));
let i;
for (i = 0; i < bytes.length; i++) {
const a = MAP_HEX[hex[i * 2]];
const b = MAP_HEX[hex[i * 2 + 1]];
if (a === undefined || b === undefined) {
break;
}
bytes[i] = (a << 4) | b;
}
return i === bytes.length ? bytes : bytes.slice(0, i);
}
function bech32mEncode(data: string, prefix: string) {
return bech32m.encode(prefix, bech32m.toWords(fromHex(data)))
}
function uft8ToHex(s: string) {
const encoded = new TextEncoder().encode(s)
return "0x" + Array.from(encoded).map(i => i.toString(16).padStart(2, '0')).join('')
}
function stripHexPrefix(s: string) {
return s.startsWith('0x') ? s.slice(2) : s
}
// example 1, how to get xch address
function get_xch_address() {
wallet.getPublicKeys().then((publicKeys) => {
// synthetic publickeys
const pubkey = publicKeys[0]
console.log('synthetic publickeys: ', pubkey)
const puzzle = payToDelegatedOrHiddenPuzzle.curry([
Program.fromHex(stripHexPrefix(pubkey)),
])
const puzzleHash = puzzle.hashHex();
const address = bech32mEncode(puzzleHash, 'xch')
console.log('puzzleHash: ', puzzleHash, 'address: ', address)
}
)
}
// example 2, how to sign message with public key
function sign_with_publick_key() {
wallet.getPublicKeys().then((publicKeys) => {
// synthetic publickeys
const pubkey = publicKeys[0]
const message = uft8ToHex('hello world');
wallet.signMessage({message, publicKey: pubkey}).then((signature) => {
console.log('signature: ', signature)
}).catch((e) => {
console.log('error: ', e)
})
})
}
// example 3, how to sign message with nft
function sign_with_nft() {
wallet.getAssetCoins({type: 'nft'}).then((spendableCoins) => {
console.log(spendableCoins)
// for example, select the first nft
const nftPuzzle = Program.deserializeHex(stripHexPrefix(spendableCoins[0].puzzle));
const [singletonMod, singletonArgs] = nftPuzzle.uncurry()!;
const nftInnerPuzzle = singletonArgs[1]
const [nftMod, nftArgs] = nftInnerPuzzle.uncurry()!;
const innerPuzzle = nftArgs[3]
let p2Puzzle = innerPuzzle
const [mod, args] = innerPuzzle.uncurry()!;
// NFT_OWNERSHIP_LAYER
if (mod.hashHex() == "c5abea79afaa001b5427dfa0c8cf42ca6f38f5841b78f9b3c252733eb2de2726") {
p2Puzzle = args[3]
}
console.log('address: ', p2Puzzle.hashHex())
// get synthetic publickey
const [stdMod, stdArgs] = p2Puzzle.uncurry()!;
const syntheticPublicKey = stdArgs[0]
console.log('syntheticPublicKey: ', syntheticPublicKey.toHex())
const message = uft8ToHex('hello world');
wallet.signMessage({message, publicKey: syntheticPublicKey.toHex()}).then((signature) => {
console.log('signature: ', signature)
}
).catch((e) => {
console.log('error: ', e)
}
)
})
}
// example 4, how to sign message with did
function sign_with_did() {
wallet.getAssetCoins({type: 'did'}).then((spendableCoins) => {
console.log(spendableCoins)
const didInfos = spendableCoins.map((coin) => {
const didPuzzle = Program.deserializeHex(stripHexPrefix(spendableCoins[0].puzzle));
const [singletonMod, singletonArgs] = didPuzzle.uncurry()!;
const didInnerPuzzle = singletonArgs[1]
const [didMod, didArgs] = didInnerPuzzle.uncurry()!;
const launcherId = singletonArgs[0].rest.first.toHex();
const didId = bech32mEncode(launcherId, 'did:chia:')
// get synthetic publickey
const p2Puzzle = didArgs[0]
const p2PuzzleHash = p2Puzzle.hashHex()
const address = bech32mEncode(p2PuzzleHash, 'xch')
const [stdMod, stdArgs] = p2Puzzle.uncurry()!;
const syntheticPublicKey = stdArgs[0]
console.log('syntheticPublicKey: ', syntheticPublicKey.toHex())
return {
didId,
syntheticPublicKey,
p2PuzzleHash,
address
}
});
// choose first did
const didInfo = didInfos[0]
console.log('didId: ', didInfo.didId, 'address: ', didInfo.address)
const message = uft8ToHex('hello world');
wallet.signMessage({message, publicKey: didInfo.syntheticPublicKey.toHex()}).then((signature) => {
console.log('signature: ', signature)
}
).catch((e) => {
console.log('error: ', e)
}
)
})
}