Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API create bid with "nftAddress" param #73

Open
adambor opened this issue Sep 13, 2024 · 14 comments
Open

API create bid with "nftAddress" param #73

adambor opened this issue Sep 13, 2024 · 14 comments

Comments

@adambor
Copy link

adambor commented Sep 13, 2024

If I try to to call the create bid endpoint for collections (https://open-api.unisat.io/v3/market/collection/auction/create_bid), with the following data:

{
	"auctionId": "ehv4oe9ev7ff4vm45xfbzjsijvqpmls9",
	"bidPrice": 18000,
	"address": "...",
	"pubkey": "...",
	"feeRate": 10,
	"utxos": [
		{
			"txid": "...",
			"index": 0
		}
	],
	"nftAddress": "..."
}

I get a response saying:

{
	"code": -113,
	"msg": "If you use the receive address, you need to bind first",
	"data": null
}

What does this mean and how do I "bind first" through the API?

@cloud6605
Copy link
Collaborator

You needn't set the nftAddress, it's only for multi-address wallet, such as Xverse, hiro.
Please refer to https://docs.unisat.io/dev/unisat-developer-center/unisat-marketplace/brc20-marketplace to learn how to use the parameters.

image

@Dill777
Copy link

Dill777 commented Sep 18, 2024

I'm actually trying to do it too, but receiving problem:

{
  btcAddress: 'bc1q5fqwlf88dtu9qa2jkvwhjr94k07cerfau3mh9s',
  nftAddress: 'bc1p7n462ru9wtfgclwedfgp0eqe6w40pjwt7wvkxgdmzue2y6ptdkvsuzzmu2',
  sign: '6b96d3f0165b11b448163825f67a894e8cb1e2fcaa44f203ce6f704c77c84c1c3b73735840b75c25b9d8b11c1448785c5f10f0d5a746f0482b95a4d2fc9f94d3'
}
{
  code: -1,
  msg: 'Signature.fromCompact: Expected 64-byte hex',
  data: null
}

Can you help? @cloud6605

@adambor
Copy link
Author

adambor commented Sep 18, 2024

You needn't set the nftAddress, it's only for multi-address wallet, such as Xverse, hiro.
Please refer to https://docs.unisat.io/dev/unisat-developer-center/unisat-marketplace/brc20-marketplace to learn how to use the parameters.

image

Well, I guess I need to use the nftAddress param... My use case:

A HD wallet with many different UTXOs across many different p2wpkh addresses for payments & single derived p2tr address for ordinals. So I need to pay for the ordinal with the p2wpkh utxos - using the "utxos" param (and receive change to p2wpkh change address - using the "address" param) & receive the ordinal to the single derived p2tr address - using the "nftAddress" param. How do I accomplish this with the API?

@adambor
Copy link
Author

adambor commented Sep 18, 2024

I'm actually trying to do it too, but receiving problem:

{
  btcAddress: 'bc1q5fqwlf88dtu9qa2jkvwhjr94k07cerfau3mh9s',
  nftAddress: 'bc1p7n462ru9wtfgclwedfgp0eqe6w40pjwt7wvkxgdmzue2y6ptdkvsuzzmu2',
  sign: '6b96d3f0165b11b448163825f67a894e8cb1e2fcaa44f203ce6f704c77c84c1c3b73735840b75c25b9d8b11c1448785c5f10f0d5a746f0482b95a4d2fc9f94d3'
}
{
  code: -1,
  msg: 'Signature.fromCompact: Expected 64-byte hex',
  data: null
}

Can you help? @cloud6605

What's the endpoint you are using for this? Any pointers to docs about this procedure?

@Dill777
Copy link

Dill777 commented Sep 18, 2024

I'm actually trying to do it too, but receiving problem:

{
  btcAddress: 'bc1q5fqwlf88dtu9qa2jkvwhjr94k07cerfau3mh9s',
  nftAddress: 'bc1p7n462ru9wtfgclwedfgp0eqe6w40pjwt7wvkxgdmzue2y6ptdkvsuzzmu2',
  sign: '6b96d3f0165b11b448163825f67a894e8cb1e2fcaa44f203ce6f704c77c84c1c3b73735840b75c25b9d8b11c1448785c5f10f0d5a746f0482b95a4d2fc9f94d3'
}
{
  code: -1,
  msg: 'Signature.fromCompact: Expected 64-byte hex',
  data: null
}

Can you help? @cloud6605

What's the endpoint you are using for this? Any pointers to docs about this procedure?

Bind btcAddress and nftAddress.

https://open-api.unisat.io/v3/market/brc20/auction/bind

@adambor
Copy link
Author

adambor commented Sep 18, 2024

I'm actually trying to do it too, but receiving problem:

{
  btcAddress: 'bc1q5fqwlf88dtu9qa2jkvwhjr94k07cerfau3mh9s',
  nftAddress: 'bc1p7n462ru9wtfgclwedfgp0eqe6w40pjwt7wvkxgdmzue2y6ptdkvsuzzmu2',
  sign: '6b96d3f0165b11b448163825f67a894e8cb1e2fcaa44f203ce6f704c77c84c1c3b73735840b75c25b9d8b11c1448785c5f10f0d5a746f0482b95a4d2fc9f94d3'
}
{
  code: -1,
  msg: 'Signature.fromCompact: Expected 64-byte hex',
  data: null
}

Can you help? @cloud6605

What's the endpoint you are using for this? Any pointers to docs about this procedure?

Bind btcAddress and nftAddress.

https://open-api.unisat.io/v3/market/brc20/auction/bind

Thank you! So that's the endpoint I am looking for! How are you producing the signature?

@Dill777
Copy link

Dill777 commented Sep 18, 2024

Thank you! So that's the endpoint I am looking for! How are you producing the signature?

const keyPair = ECPair.fromWIF(wif);

const { address: btcAddress } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey! });

console.log(keyPair.publicKey!.toString('hex'));

if (!btcAddress) {
  console.error('Failed to derive btcAddress from the provided private key.');
  process.exit(1);
}

const nftAddress: string = 'bc1p7n462ru9wtfgclwedfgp0eqe6w40pjwt7wvkxgdmzue2y6ptdkvsuzzmu2';

if (!nftAddress) {
  console.error('nftAddress must be provided.');
  process.exit(1);
}

// Prepare the message
const timestamp: number = Date.now();
const message: string = `Please confirm that
Payment Address: ${nftAddress}
Ordinals Address: ${btcAddress}`;

// Sign the message
let signature: Buffer;
try {
  signature = bitcoinMessage.sign(message, keyPair.privateKey!, keyPair.compressed);
} catch (error) {
  console.error('Error signing the message:', (error as Error).message);
  process.exit(1);
}

const signatureWithoutRecovery: Buffer = signature.slice(1);

// Convert the signature to base64
const signatureBase64: string = signatureWithoutRecovery.toString('hex');

console.log(signatureBase64.length);
console.log(signatureBase64);

@cloud6605 Am I right ?

but it's not working for me, so if you will manage it -> let me know please

@adambor
Copy link
Author

adambor commented Sep 18, 2024

Thank you! So that's the endpoint I am looking for! How are you producing the signature?

const keyPair = ECPair.fromWIF(wif);

const { address: btcAddress } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey! });

console.log(keyPair.publicKey!.toString('hex'));

if (!btcAddress) {
  console.error('Failed to derive btcAddress from the provided private key.');
  process.exit(1);
}

const nftAddress: string = 'bc1p7n462ru9wtfgclwedfgp0eqe6w40pjwt7wvkxgdmzue2y6ptdkvsuzzmu2';

if (!nftAddress) {
  console.error('nftAddress must be provided.');
  process.exit(1);
}

// Prepare the message
const timestamp: number = Date.now();
const message: string = `Please confirm that
Payment Address: ${nftAddress}
Ordinals Address: ${btcAddress}`;

// Sign the message
let signature: Buffer;
try {
  signature = bitcoinMessage.sign(message, keyPair.privateKey!, keyPair.compressed);
} catch (error) {
  console.error('Error signing the message:', (error as Error).message);
  process.exit(1);
}

const signatureWithoutRecovery: Buffer = signature.slice(1);

// Convert the signature to base64
const signatureBase64: string = signatureWithoutRecovery.toString('hex');

console.log(signatureBase64.length);
console.log(signatureBase64);

@cloud6605 Am I right ?

but it's not working for me, so if you will manage it -> let me know please

Based on the docs you should be signing by the ordinals address (p2tr), not by the payment address. Anyway I am trying to do that but also getting the same error as you do, so there might be some issue on the unisat's API side. Btw I am also using bitcoinjs-lib, but bip322-js lib for signing the message - you also should use that because bitcoinjs-message is 4 years old and doesn't support p2tr (and you need to sign by the ordinals p2tr address).

@Dill777
Copy link

Dill777 commented Sep 18, 2024

Based on the docs you should be signing by the ordinals address (p2tr), not by the payment address. Anyway I am trying to do that but also getting the same error as you do, so there might be some issue on the unisat's API side. Btw I am also using bitcoinjs-lib, but bip322-js lib for signing the message - you also should use that because bitcoinjs-message is 4 years old and doesn't support p2tr (and you need to sign by the ordinals p2tr address).

Could you share the code?

@adambor
Copy link
Author

adambor commented Sep 18, 2024

Could you share the code?

Here it is, just be sure to get the version 6.1.6 of the bitcoinjs-lib (7 doesn't work well for me for some reason), and version 2.1.0 of ecpair.

const bitcoin = require("bitcoinjs-lib"); //version 6.1.6
const ecc = require('@bitcoinerlab/secp256k1'); //version 1.1.1
const {ECPairFactory} = require("ecpair"); //version 2.1.0
const {Signer} =  require("bip322-js"); //version 2.0.0
const bip371 = require('bitcoinjs-lib/src/psbt/bip371');
bitcoin.initEccLib(ecc);

const ECPair = ECPairFactory(ecc);

const p2wpkhKey = ECPair.makeRandom();
const p2trKey = ECPair.makeRandom();

const p2wpkh = bitcoin.payments.p2wpkh({pubkey: p2wpkhKey.publicKey});
const p2tr = bitcoin.payments.p2tr({internalPubkey: bip371.toXOnly(p2trKey.publicKey)});

console.log("p2wpkh: ", p2wpkh.address);
console.log("p2tr: ", p2tr.address);

const toSignStr = `Please confirm that\nPayment Address: ${p2wpkh.address}\nOrdinals Address: ${p2tr.address}`;

const signatureBase64 = Signer.sign(p2trKey.toWIF(), p2tr.address, toSignStr);
console.log("Signature: ", signatureBase64);
console.log("Signature hex: ", Buffer.from(signatureBase64, 'base64').toString("hex"));

It prints out the signature in base64 and also in hex, you can get just the signature by stripping first 2 bytes (witness push count & signature push size) and the last 1 byte (sighash)

@Dill777
Copy link

Dill777 commented Sep 18, 2024

Could you share the code?

Here it is, just be sure to get the version 6.1.6 of the bitcoinjs-lib (7 doesn't work well for me for some reason), and version 2.1.0 of ecpair.

const bitcoin = require("bitcoinjs-lib"); //version 6.1.6
const ecc = require('@bitcoinerlab/secp256k1'); //version 1.1.1
const {ECPairFactory} = require("ecpair"); //version 2.1.0
const {Signer} =  require("bip322-js"); //version 2.0.0
const bip371 = require('bitcoinjs-lib/src/psbt/bip371');
bitcoin.initEccLib(ecc);

const ECPair = ECPairFactory(ecc);

const p2wpkhKey = ECPair.makeRandom();
const p2trKey = ECPair.makeRandom();

const p2wpkh = bitcoin.payments.p2wpkh({pubkey: p2wpkhKey.publicKey});
const p2tr = bitcoin.payments.p2tr({internalPubkey: bip371.toXOnly(p2trKey.publicKey)});

console.log("p2wpkh: ", p2wpkh.address);
console.log("p2tr: ", p2tr.address);

const toSignStr = `Please confirm that\nPayment Address: ${p2wpkh.address}\nOrdinals Address: ${p2tr.address}`;

const signatureBase64 = Signer.sign(p2trKey.toWIF(), p2tr.address, toSignStr);
console.log("Signature: ", signatureBase64);
console.log("Signature hex: ", Buffer.from(signatureBase64, 'base64').toString("hex"));

It prints out the signature in base64 and also in hex, you can get just the signature by stripping first 2 bytes (witness push count & signature push size) and the last 1 byte (sighash)

Maybe we can try with this Signer:

import {Signer as BTCSigner} from "bitcoinjs-lib/src/psbt";

Saw in some reps. You can check if you want. So you have the same error, yes ? it's really strange.

@adambor
Copy link
Author

adambor commented Sep 18, 2024

Maybe we can try with this Signer:

import {Signer as BTCSigner} from "bitcoinjs-lib/src/psbt";

That can only sign a specific hash, doesn't really help, because bip322 is about how you construct that hash (it is actually an invalid bitcoin transaction).

Saw in some reps. You can check if you want. So you have the same error, yes ? it's really strange.

Yep, if I send the hex output of the bip322-js library (full witness) I get:

{
	"btcAddress": "bc1q6rx2tx2f3q6g4khpqrzqlh549emrl5qakcx42e",
	"nftAddress": "bc1pgkrwm6c4g56l6jk4kpfar29nghjmzs7fmrdxmcxyk6a7dcuxlfxqd7emjc",
	"sign": "01419e2ddd587ff0e23b12ef176df1d96804dfa20f1da6438c389e40656e743ebeec71e57ae46f05d365fc466b7e261e61d08d46289a38f193fd483d82bbbfd6a84501"
}
{
	"code": -1,
	"msg": "Invalid padding: string should have whole number of bytes",
	"data": null
}

And if I send just the signature (strip first 2 bytes & last byte), I get:

{
	"btcAddress": "bc1q6rx2tx2f3q6g4khpqrzqlh549emrl5qakcx42e",
	"nftAddress": "bc1pgkrwm6c4g56l6jk4kpfar29nghjmzs7fmrdxmcxyk6a7dcuxlfxqd7emjc",
	"sign": "9e2ddd587ff0e23b12ef176df1d96804dfa20f1da6438c389e40656e743ebeec71e57ae46f05d365fc466b7e261e61d08d46289a38f193fd483d82bbbfd6a845"
}
{
	"code": -1,
	"msg": "Signature.fromCompact: Expected 64-byte hex",
	"data": null
}

@adambor
Copy link
Author

adambor commented Sep 18, 2024

I am also wondering why this binding procedure is even required (especially when used over the API), to me seems like just a UX hurdle.

@Dill777
Copy link

Dill777 commented Sep 19, 2024

I am also wondering why this binding procedure is even required (especially when used over the API), to me seems like just a UX hurdle.

Maybe @cloud6605 could help, for now I see only one way -> use Taproot address

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants