-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add an OmniLayer example to tests #1176
Comments
Undecided whether our examples are the right place for that... |
That makes sense |
@caffeinum have you tried the new payments API? Specifically |
@caffeinum Modified your example to work with bitcoinjs-lib v4 (Marked changes with const bitcoin = require('bitcoinjs-lib')
const request = require('request-promise-native')
const net = process.env.NETWORK === 'testnet'
? bitcoin.networks.testnet
: bitcoin.networks.bitcoin
const API = net === bitcoin.networks.testnet
? `https://test-insight.swap.online/insight-api`
: `https://insight.bitpay.com/api`
const fetchUnspents = (address) =>
request(`${API}/addr/${address}/utxo/`).then(JSON.parse)
const broadcastTx = (txRaw) =>
request.post(`${API}/tx/send`, {
json: true,
body: {
rawtx: txRaw,
},
})
const createSimpleSend = async (fetchUnspents, alice_pair, recipient_address/*, amount = 10*/) => {
const tx = new bitcoin.TransactionBuilder(net)
const alice_p2pkh = bitcoin.payments.p2pkh({
pubkey: alice_pair.publicKey,
network: net
}).address // NEW** New Payments API for p2pkh
const unspents = await fetchUnspents(alice_p2pkh)
const fundValue = 546 // dust
const feeValue = 5000
const totalUnspent = unspents.reduce((summ, { satoshis }) => summ + satoshis, 0)
const skipValue = totalUnspent - fundValue - feeValue
if (totalUnspent < feeValue + fundValue) {
throw new Error(`Total less than fee: ${totalUnspent} < ${feeValue} + ${fundValue}`)
}
unspents.forEach(({ txid, vout }) => tx.addInput(txid, vout, 0xfffffffe))
const simple_send = [
"6f6d6e69", // omni
"0000", // version
"00000000001f", // 31 for Tether
"000000003B9ACA00" // amount = 10 * 100 000 000 in HEX
].join('')
const data = [ Buffer.from(simple_send, "hex") ] // NEW** data must be an Array(Buffer)
const omniOutput = bitcoin.payments.embed({ data }).output // NEW** Payments API
tx.addOutput(recipient_address, fundValue) // should be first!
tx.addOutput(omniOutput, 0)
tx.addOutput(alice_p2pkh, skipValue)
// NEW** tx.inputs was deprecated, since you use unspents 1-for-1 to make inputs I used unspents.
unspents.forEach((unspent, index) => {
tx.sign(index, alice_pair)
})
return tx
}
// Construct tx
const alice = bitcoin.ECPair.fromWIF(process.env.ALICE_WIF, net)
const bobby = bitcoin.ECPair.makeRandom({ network: net })
const amount = null // not used
// NEW** Used new payments API for bobby
const omni_tx = createSimpleSend(fetchUnspents, alice, bitcoin.payments.p2pkh({ pubkey: bobby.publicKey, network: net }).address, amount)
const auto_send = false
omni_tx.then(tx => {
const txRaw = tx.buildIncomplete()
console.log('hash', txRaw.getId())
console.log(`"${txRaw.toHex()}"`)
console.log(txRaw)
if (auto_send) {
broadcastTx(txRaw.toHex())
}
}) |
|
I don't know if that is great idea...
I don't know why. |
This assumes you use all items of the Array. (array.length === txb.inputCount) Perhaps a getter for txb.inputCount and outputCount that grabs the current count internally... Then we could do: Array(txb.inputCount).fill(0).forEach((item, index) => {
console.log(index)
}) But at that point, a for loop looks cleaner and easier to understand... :-/ |
@junderw yes, you should always use all the unspents provided. If you don't want to use them all... filter them to what you need. See |
What about when they don't have the unspents? Like a rawtx. If we don't want them to touch the internals of Tx and Txb then how should they gather how many inputs they have? |
@junderw great question. We don't want to encourage merely iterating over the equivalent of |
PSBT to the rescue! |
@junderw is it though? |
@dcousens hahahahahahahahahaha Well............... it's better than not having it IMO. |
" tx.addOutput(recipient_address, fundValue) // should be first! ",why that it should be first , i have used the code ,but the signd transaction is something wrong at the referenceaddress . |
Read the Omni Protocol documents. It should be first because the Omni protocol requires it. |
@Nick-49 which |
I have found the "bug" of my code . And i have solved it. thank you~ @caffeinum @junderw |
Hi, It should be change dynamically? |
@ssssssu12 yes, its the amount of Omni tokens to send |
This is a detailed demo |
@yugasun |
@junderw thanks for your correction, I made a mistake. |
It is generate a transaction successfully but that Tx Hash not reflect on Omni Blockchain. |
@rohitsahu21 well, tx was not published to bitpay either: https://insight.bitpay.com/tx/ba2ae595f03874b7773548102e12cfe3d6cf0be85791f81b76ed1b2d85b10cce Did you use NETWORK=mainnet? |
@caffeinum No i'm using Testnet for Testing purpose |
@rohitsahu21 omniexplorer doesn't work for testnet. You need to run your own OmniLayer node if you want to try OMNI tokens on bitcoin testnet. Also, I didn't test this script on testnet, you should check all the values. You can contact me at telegram @caffeinum if you need some extra help. |
Hi @caffeinum @junderw , I'm looking to develop a USDT wallet (NodeJs) without the need to setup my own Omnilayer node. Would really appreciate for any advice or help regarding the following:
Any help would be greatly appreciate you guys! Thanks! |
|
https://www.bitgo.com/api/v1/tx/fee This is also a good API, Bitgo has an ok fee estimator. feeByBlockTarget["3"] for instance is "satoshis per kB cost if you want your transaction confirmed within the next 3 blocks" but imo, Bitgo overestimates a tad, so 3 should get in the next block most of the time. |
The example works offline! Only two functions:
That was arbitrary, you can use any value. However, there's one pitfall: to estimate fee well, you need to know tx size, but to know tx size, you need to know all the inputs and outputs. However, you could use this simple formula for standard P2SH/P2PKH, only modify it to include const txSize = numInputs * 146 + numOutputsWithoutOmni * 33 + 10 + omniOutputSize Extra reading:
I use these nodes for estimation: https://bitcoinfees.earn.com/api/v1/fees/recommended They give estimate in sat/byte (sat/kb for blockcypher), so to know actual fee, you need to multiply that by tx size. See above ^ P.S. Also, as for offline signing, see Metamask's PR for external signers: MetaMask/metamask-extension#6143, and github.com/flightwallet and https://www.parity.io/signer/ as an examples of offline signers. Probably you can benefit from there examples if you're doing offline wallet |
Hi @junderw @caffeinum , It seems like it should be this:
instead of: const simple_send = [ The only problem i'm facing now is to programmatically convert decimals to HEX signed 2s complement string. |
No. You need to encode into 8 bytes if you want to support omni. Your idea to use only 4 bytes will break for values over 42.94 USDT 6 bytes can support up to 2.8 million USDT ish. but you should support 8 bytes using a bigint or bignumber library. |
Noted @junderw , i'm looking for a function to convert the HEX value with padded leading zeros to fill an 8-byte length amount. Update:
|
@zinxer here's my version of const toPaddedHexString = (num, len) => {
const str = num.toString(16)
return "0".repeat(len - str.length) + str
} Usage: const simple_send = [
"6f6d6e69", // omni
"0000", // tx type
"0000", // version
// "0000001f", // 31 for Tether
toPaddedHexString(coin, 8),
// "000000003B9ACA00" // amount = 10 * 100 000 000 in HEX
toPaddedHexString(Math.floor(amount * 100000000), 16),
].join('') (from https://github.com/swaponline/swap.core/tree/master/src/swap.swaps/usdt) |
Noted thanks guys! Was wondering how would adding another wallet as part of the input where it's funds are used to pay the network fee while the other's btc is used as dust.
Thanks, |
Updated the example to use the new PSBT from v5.1 const bitcoin = require('bitcoinjs-lib')
const request = require('request-promise-native')
const net = process.env.NETWORK === 'testnet'
? bitcoin.networks.testnet
: bitcoin.networks.bitcoin
const API = net === bitcoin.networks.testnet
? `https://test-insight.swap.online/insight-api`
: `https://insight.bitpay.com/api`
const fetchUnspents = (address) =>
request(`${API}/addr/${address}/utxo/`).then(JSON.parse)
// *NEW need full raw tx for all non-segwit inputs (to verify the value before signing)
const getRawTx = (txid) =>
request(`${API}/rawtx/${txid}/`).then(JSON.parse).then(data => Buffer.from(data.rawtx, 'hex'))
const broadcastTx = (txRaw) =>
request.post(`${API}/tx/send`, {
json: true,
body: {
rawtx: txRaw,
},
})
const createSimpleSend = async (fetchUnspents, alice_pair, recipient_address/*, amount = 10*/) => {
// *NEW PSBT class
const psbt = new bitcoin.Psbt({ network: net })
const alice_p2pkh = bitcoin.payments.p2pkh({
pubkey: alice_pair.publicKey,
network: net
}).address
const unspents = await fetchUnspents(alice_p2pkh)
const fundValue = 546 // dust
const feeValue = 5000
const totalUnspent = unspents.reduce((summ, { satoshis }) => summ + satoshis, 0)
const skipValue = totalUnspent - fundValue - feeValue
if (totalUnspent < feeValue + fundValue) {
throw new Error(`Total less than fee: ${totalUnspent} < ${feeValue} + ${fundValue}`)
}
for (let i = 0; i < unspents.length; i++) {
const nonWitnessUtxo = await getRawTx(unspents[i].txid)
psbt.addInput({
hash: unspents[i].txid,
index: unspents[i].vout,
sequence: 0xfffffffe,
nonWitnessUtxo, // *NEW This will allow us to verify you are signing the correct values for inputs
})
}
const simple_send = [
"6f6d6e69", // omni
"0000", // version
"00000000001f", // 31 for Tether
"000000003B9ACA00" // amount = 10 * 100 000 000 in HEX
].join('')
const data = [ Buffer.from(simple_send, "hex") ]
const omniOutput = bitcoin.payments.embed({ data }).output
psbt.addOutput({ address: recipient_address, value: fundValue }) // should be first!
psbt.addOutput({ script: omniOutput, value: 0 })
psbt.addOutput({ address: alice_p2pkh, value: skipValue })
// *NEW sign all inputs with one method call
psbt.signAllInputs(alice_pair)
return psbt
}
// Construct tx
const alice = bitcoin.ECPair.fromWIF(process.env.ALICE_WIF, net)
const bobby = bitcoin.ECPair.makeRandom({ network: net })
const amount = null // not used
const omni_tx = createSimpleSend(fetchUnspents, alice, bitcoin.payments.p2pkh({ pubkey: bobby.publicKey, network: net }).address, amount)
const auto_send = false
omni_tx.then(psbt => {
// *NEW must finalize before TX extraction. (this helps for multisig)
const txRaw = psbt.finalizeAllInputs().extractTransaction()
console.log('hash', txRaw.getId())
console.log(`"${txRaw.toHex()}"`)
console.log(txRaw)
if (auto_send) {
broadcastTx(txRaw.toHex())
}
}) |
v5.1 Does this instance not work on the test network? |
You didn't broadcast it to the network. I broadcasted it for you. it should show up soon. |
@junderw
I tested this script in January and it was ok, but not now, do you have a good solution? |
something's wrong with the remote API you're using |
@junderw Yes, I also see that this API cannot be accessed. Do you have any other remote API that can be used?Thank you very much! |
No... unfortunately a lot of the testnet block explorers are unreliable. For bitcoinjs-lib we use regtest-server docker container. So when we run integration tests a small docker container with a simple REST server and a regtest bitcoin node are spun up and you can use the regtest-client library to interface with it. Check the integration tests to see how it is used. $ docker pull junderw/bitcoinjs-regtest-server
$ docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server Then once you have this running locally, import { RegtestUtils } from 'regtest-client';
const APIPASS = 'satoshi';
const APIURL = 'http://127.0.0.1:8080/1';
const regtestUtils = new RegtestUtils({ APIPASS, APIURL }); Then you can do things like await regtestUtils.mine(10) To mine 10 blocks etc. It is much better for testing / messing around. |
however, this server does not verify Omni protocol... sooooooo, I don't know what to tell you about that. |
Yes, thank you. |
hello: recipient_address = mm1oDPm8vjBfdv92yaMacMGfwpdvfFTeoM const WIF = 'L3w9mnJVSqYM9adQomcbwXPRj1rjui5aoVXXnmQ5Yf8qLfy1ZYyL'
const alice = bitcoin.ECPair.fromWIF(WIF)
const psbt = new bitcoin.Psbt({ network: bitcoin.networks.testnet })
utxo.forEach((item, index, arr) => {
psbt.addInput({
hash: item.txid,
index: item.vout,
nonWitnessUtxo: Buffer.from(item.raw, 'hex'),
})
})
const fee = 1000
const reback = balance - 546 - 1000
const simple_send = [
'6f6d6e69', // omni
'0000', // version
toPaddedHexString(31, 8), // 31 for Tether
,
toPaddedHexString(Math.floor(0.1 * 100000000), 16), // amount = 10 * 100 000 000 in HEX
,
].join('')
const data = [Buffer.from(simple_send, 'hex')]
const omniOutput = bitcoin.payments.embed({ data }).output
psbt.addOutput({ address: 'mm1oDPm8vjBfdv92yaMacMGfwpdvfFTeoM', value: 546 }) // should be first!
psbt.addOutput({ script: omniOutput, value: 0 }) //0.1 omni
psbt.addOutput({ address: 'ms7f7uEipu9r4B925XoK8mcJ2zLcNbJiJn', value: reback })
utxo.forEach((item, index, arr) => {
psbt.signInput(index, alice)
})
psbt.validateSignaturesOfInput(0)
psbt.finalizeAllInputs()
const tx = psbt.extractTransaction().toHex()
console.log(tx) and I broadcast tx Here are two IDS I broadcast I transferred 0.1 and 0.2 omnin udst to the target address respectively I don't know how many usdts I have left. I don't know if I have transferred them, At first, the target address sent me a usdt of Omni, but he said he didn't receive 0.1 and 0.2 from me I hope someone can help me out. Thank you very much |
https://blockexplorer.one/btc/testnet/address/ms7f7uEipu9r4B925XoK8mcJ2zLcNbJiJn/tokens This site will show you the omni tokens on testnet for your address. It seems to show a bunch of weird transactions (property ID 1) and it shows you sending tons of USDT even though you never received any. (1000000000 USDT, but you never received 1000000000 USDT) |
Thank you very much for your reply. I'm glad to find out the tokens. I haven't got a clue before Later, I knew that someone had transferred a usdt to me, so I changed the quantity to 0.1 and 0.2 and sent them twice, that is to say, neither of them succeeded, but in fact, none of them succeeded. The first time was because I didn't have so many usdts, const simple_send = [
'6f6d6e69', // omni
'0000', // version
toPaddedHexString(31, 8), // 31 for Tether
,
toPaddedHexString(Math.floor(0.1 * 100000000), 16), // amount = 10 * 100 000 000 in HEX
,
].join('') toPaddedHexString(Math.floor(0.1 * 100000000), 16), What's wrong with that |
You have 0 USDT on the address ms7f7uEipu9r4B925XoK8mcJ2zLcNbJiJn. |
hello and you say I have 0 USDT on the address ms7f7uEipu9r4B925XoK8mcJ2zLcNbJiJn. My understanding is USDT Property ID is 31 ,Is that so? through https://blockexplorer.one/btc/testnet/address/ms7f7uEipu9r4B925XoK8mcJ2zLcNbJiJn/tokens I have 1.2 ( Property ID : 1 ),I don't know what it's called What I want to ask now is, if I need to transfer this 1.2 coin, How should be filled in, simple_send |
Here you should select 1 instead of 31 ( |
I wanted to use this OmniLayer protocol, but have found no examples in the whole internet. I think it would be good to include such examples in the tests.
Here's a small snippet: https://gist.github.com/caffeinum/f64a51ce55d5ac9075bb2f5f2f439c0d
The text was updated successfully, but these errors were encountered: