Skip to content

Commit

Permalink
0.26.0
Browse files Browse the repository at this point in the history
- Utilizing simpleledger/slp-mdm package, update associated unit tests
- Update BigNumber library
  • Loading branch information
jcramer committed Apr 7, 2020
1 parent 84737c7 commit dfa0d10
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 430 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,10 @@ Running the unit tests require node.js v8.15+.

# Change Log

### 0.26.0
- Utilizing simpleledger/slp-mdm package, update associated unit tests
- Update BigNumber library

### 0.25.6
- Update unit tests and examples for bchd based network
- Patch missing tokenIdHex in getTokenInformation after recent refactoring
Expand Down
1 change: 0 additions & 1 deletion lib/localvalidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ export class LocalValidator implements SlpValidator {
const txn: Bitcore.Transaction = new Bitcore.Transaction(this.cachedRawTransactions[txid]);
let slpmsg: SlpTransactionDetails;
try {
// @ts-ignore
slpmsg = this.cachedValidations[txid].details = this.slp.parseSlpOutputScript(txn.outputs[0]._scriptBuffer);
if (slpmsg.transactionType === SlpTransactionType.GENESIS) {
slpmsg.tokenIdHex = txid;
Expand Down
295 changes: 35 additions & 260 deletions lib/slptokentype1.ts
Original file line number Diff line number Diff line change
@@ -1,267 +1,42 @@
import { Utils } from './utils';
import BigNumber from 'bignumber.js';
import BigNumber from "bignumber.js";
import { TokenType1 } from "slp-mdm";

export class SlpTokenType1 {
static get lokadIdHex() { return "534c5000" }

static buildGenesisOpReturn(ticker: string|null, name: string|null, documentUri:string|null, documentHashHex: string|null, decimals: number, batonVout:number|null, initialQuantity:BigNumber, type=0x01) {
let script: (number|number[])[] = [];

// OP Return Prefix
script.push(0x6a)

// Lokad Id
let lokadId = Buffer.from(this.lokadIdHex, 'hex')
script.push(Utils.getPushDataOpcode(lokadId))
lokadId.forEach((item) => script.push(item))

// Token Version/Type
if(![0x01, 0x41, 0x81].includes(type))
throw Error("Unable to create Genesis for this token type.")
let tokenVersionType = type;
script.push(Utils.getPushDataOpcode([tokenVersionType]))
script.push(tokenVersionType)

// Transaction Type
let transactionType = Buffer.from('GENESIS')
script.push(Utils.getPushDataOpcode(transactionType))
transactionType.forEach((item) => script.push(item))

// Ticker
if (ticker && typeof ticker !== 'string'){
throw Error("ticker must be a string")
} else if (!ticker || ticker.length === 0) {
[0x4c, 0x00].forEach((item) => script.push(item))
} else {
let tickerBuf = Buffer.from(ticker, 'utf8')
script.push(Utils.getPushDataOpcode(tickerBuf))
tickerBuf.forEach((item) => script.push(item))
}

// Name
if (name && typeof name !== 'string') {
throw Error("name must be a string")
} else if (!name || name.length === 0) {
[0x4c, 0x00].forEach((item) => script.push(item))
} else {
let nameBuf = Buffer.from(name, 'utf8')
script.push(Utils.getPushDataOpcode(nameBuf))
nameBuf.forEach((item) => script.push(item))
}

// Document URL
if (documentUri && typeof documentUri !== 'string') {
throw Error("documentUri must be a string")
} else if (!documentUri || documentUri.length === 0) {
[0x4c, 0x00].forEach((item) => script.push(item))
} else {
let documentUriBuf = Buffer.from(documentUri, 'ascii')
script.push(Utils.getPushDataOpcode(documentUriBuf))
documentUriBuf.forEach((item) => script.push(item))
}

// check Token Document Hash should be hexademical chracters.
var re = /^[0-9a-fA-F]+$/;

// Document Hash
if (!documentHashHex || documentHashHex.length === 0) {
[0x4c, 0x00].forEach((item) => script.push(item))
} else if (documentHashHex.length === 64 && re.test(documentHashHex)) {
let documentHashBuf = Buffer.from(documentHashHex, 'hex')
script.push(Utils.getPushDataOpcode(documentHashBuf))
documentHashBuf.forEach((item) => script.push(item))
} else {
throw Error("Document hash must be provided as a 64 character hex string")
}

// Decimals
if (decimals === null || decimals === undefined || decimals < 0 || decimals > 9) {
throw Error("Decimals property must be in range 0 to 9")
} else {
script.push(Utils.getPushDataOpcode([decimals]))
script.push(decimals)
}

// Baton Vout
if (batonVout === null || batonVout === undefined) {
[0x4c, 0x00].forEach((item) => script.push(item))
} else {
if (batonVout < 2 || batonVout > 255 || !(typeof batonVout == 'number'))
throw Error("Baton vout must a number and greater than 1 and less than 256.")

script.push(Utils.getPushDataOpcode([batonVout]))
script.push(batonVout)
}

// Initial Quantity
let MAX_QTY = new BigNumber('18446744073709551615');

try {
initialQuantity.absoluteValue()
} catch(_) {
throw Error("Amount must be an instance of BigNumber");
}

if (initialQuantity.isGreaterThan(MAX_QTY))
throw new Error("Maximum genesis value exceeded. Reduce input quantity below 18446744073709551615.");

if (initialQuantity.isLessThan(0))
throw Error("Genesis quantity must be greater than 0.");

if (!initialQuantity.modulo(1).isEqualTo(new BigNumber(0)))
throw Error("Genesis quantity must be a whole number.");

let initialQuantityBuf = Utils.int2FixedBuffer(initialQuantity)
script.push(Utils.getPushDataOpcode(initialQuantityBuf))
initialQuantityBuf.forEach((item) => script.push(item))

let encodedScript = Utils.encodeScript(script)
if (encodedScript.length > 223) {
throw Error("Script too long, must be less than 223 bytes.")
}
return encodedScript
}

static buildSendOpReturn(tokenIdHex: string, outputQtyArray: BigNumber[], type=0x01) {
let script: (number|number[])[] = [];

// OP Return Prefix
script.push(0x6a)

// Lokad Id
let lokadId = Buffer.from(this.lokadIdHex, 'hex')
script.push(Utils.getPushDataOpcode(lokadId))
lokadId.forEach((item) => script.push(item))

// Token Version/Type
if(![0x01, 0x41, 0x81].includes(type))
throw Error("Unable to create Genesis for this token type.")
let tokenVersionType = type
script.push(Utils.getPushDataOpcode([tokenVersionType]))
script.push(tokenVersionType)

// Transaction Type
let transactionType = Buffer.from('SEND')
script.push(Utils.getPushDataOpcode(transactionType))
transactionType.forEach((item) => script.push(item))

// Token Id
// check Token Id should be hexademical chracters.
let re = /^([A-Fa-f0-9]{2}){32,32}$/;
if (typeof tokenIdHex !== 'string' || !re.test(tokenIdHex)) {
throw Error("TokenIdHex must be provided as a 64 character hex string.")
}
let tokenId = Buffer.from(tokenIdHex, 'hex')
script.push(Utils.getPushDataOpcode(tokenId))
tokenId.forEach((item) => script.push(item))

// Output Quantities
if (outputQtyArray.length > 19) {
throw Error("Cannot have more than 19 SLP token outputs.")
}
if (outputQtyArray.length < 1) {
throw Error("Cannot have less than 1 SLP token output.")
}
outputQtyArray.forEach((outputQty) => {
try {
outputQty.absoluteValue()
} catch(_) {
throw Error("Amount must be an instance of BigNumber");
static get lokadIdHex() { return "534c5000"; }

public static buildGenesisOpReturn(
ticker: string|null,
name: string|null,
documentUrl: string|null,
documentHashHex: string|null,
decimals: number,
batonVout: number|null,
initialQuantity: BigNumber,
type= 0x01,
) {
if (decimals === null || decimals === undefined) {
throw Error("Decimals property must be in range 0 to 9");
}

let MAX_QTY = new BigNumber('18446744073709551615');

if (outputQty.isGreaterThan(MAX_QTY))
throw new Error("Maximum value exceeded. Reduce input quantity below 18446744073709551615.");

if (outputQty.isLessThan(0))
throw Error("All Send outputs must be greater than 0.");

if (!outputQty.modulo(1).isEqualTo(new BigNumber(0)))
throw Error("All Send outputs must be a whole number.");

let qtyBuffer = Utils.int2FixedBuffer(outputQty)
script.push(Utils.getPushDataOpcode(qtyBuffer))
qtyBuffer.forEach((item) => script.push(item))
})

let encodedScript = Utils.encodeScript(script)
if (encodedScript.length > 223) {
throw Error("Script too long, must be less than 223 bytes.")
}
return encodedScript
if (ticker !== null && typeof ticker !== "string") {
throw Error("ticker must be a string");
}
if (name !== null && typeof name !== "string") {
throw Error("name must be a string");
}
let res = TokenType1.genesis(
ticker || "", name || "", documentUrl || "",
documentHashHex || "", decimals || 0, batonVout, initialQuantity);
if (res.length > 223) {
throw Error("Script too long, must be less than or equal to 223 bytes.");
}
return res;
}

static buildMintOpReturn(tokenIdHex: string, batonVout: number|null, mintQuantity: BigNumber, type=0x01) {
let script: (number|number[])[] = [];

// OP Return Prefix
script.push(0x6a)

// Lokad Id
let lokadId = Buffer.from(this.lokadIdHex, 'hex')
script.push(Utils.getPushDataOpcode(lokadId))
lokadId.forEach((item) => script.push(item))

// Token Version/Type
if(![0x01, 0x81].includes(type))
throw Error("Unable to create Genesis for this token type.")
let tokenVersionType = type
script.push(Utils.getPushDataOpcode([tokenVersionType]))
script.push(tokenVersionType)

// Transaction Type
let transactionType = Buffer.from('MINT')
script.push(Utils.getPushDataOpcode(transactionType))
transactionType.forEach((item) => script.push(item))

// Token Id
// check Token Id should be hexademical chracters.
let re = /^([A-Fa-f0-9]{2}){32,32}$/;
if (typeof tokenIdHex !== 'string' || !re.test(tokenIdHex)) {
throw Error("TokenIdHex must be provided as a 64 character hex string.")
}
let tokenId = Buffer.from(tokenIdHex, 'hex')
script.push(Utils.getPushDataOpcode(tokenId))
tokenId.forEach((item) => script.push(item))

// Baton Vout
if (batonVout === null || batonVout === undefined) {
[0x4c, 0x00].forEach((item) => script.push(item))
} else {
if (batonVout < 2 || batonVout > 255 || !(typeof batonVout == 'number'))
throw Error("Baton vout must a number and greater than 1 and less than 256.")

script.push(Utils.getPushDataOpcode([batonVout]))
script.push(batonVout)
}

// Initial Quantity
let MAX_QTY = new BigNumber('18446744073709551615');

try {
mintQuantity.absoluteValue()
} catch(_) {
throw Error("Amount must be an instance of BigNumber");
}

if (mintQuantity.isGreaterThan(MAX_QTY))
throw new Error("Maximum mint value exceeded. Reduce input quantity below 18446744073709551615.");

if (mintQuantity.isLessThan(0))
throw Error("Mint quantity must be greater than 0.");

if (!mintQuantity.modulo(1).isEqualTo(new BigNumber(0)))
throw Error("Mint quantity must be a whole number.");

let initialQuantityBuf = Utils.int2FixedBuffer(mintQuantity)
script.push(Utils.getPushDataOpcode(initialQuantityBuf))
initialQuantityBuf.forEach((item) => script.push(item))
public static buildSendOpReturn(tokenIdHex: string, outputQtyArray: BigNumber[], type= 0x01) {
return TokenType1.send(tokenIdHex, outputQtyArray);
}

let encodedScript = Utils.encodeScript(script)
if (encodedScript.length > 223) {
throw Error("Script too long, must be less than 223 bytes.")
}
return encodedScript
public static buildMintOpReturn(tokenIdHex: string, batonVout: number|null, mintQuantity: BigNumber, type= 0x01) {
return TokenType1.mint(tokenIdHex, batonVout, mintQuantity);
}
}
}
1 change: 0 additions & 1 deletion lib/transactionhelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,6 @@ export class TransactionHelpers {
throw Error("Was not able to set input script for index="+s.index);

// actually set the input's scriptSig property

const script = new Bitcore.Script(bip62Encoded);
txn.inputs[s.index].setScript(script);
// console.log("scriptSig for index", s.input_index, ":", bip62Encoded.toString('hex'))
Expand Down
24 changes: 0 additions & 24 deletions lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,30 +128,6 @@ export class Utils {
return (new BigNumber(amount.readUInt32BE(0).toString())).multipliedBy(2 ** 32).plus(amount.readUInt32BE(4).toString());
}

// This is for encoding Script in scriptPubKey OP_RETURN scripts, where BIP62.3 does not apply
public static encodeScript(script: (number|number[])[]) {
const bufferSize = <number> script.reduce((acc: number, cur) => {
if (Array.isArray(cur)) { return acc + cur.length; }
else { return acc + 1; }
}, 0);

const buffer = Buffer.allocUnsafe(bufferSize);
let offset = 0;
script.forEach((scriptItem) => {
if (Array.isArray(scriptItem)) {
scriptItem.forEach((item) => {
buffer.writeUInt8(item, offset);
offset += 1;
});
} else {
buffer.writeUInt8(scriptItem, offset);
offset += 1;
}
});

return buffer;
}

public static buildSlpUri(address: string, amountBch?: number, amountToken?: number, tokenId?: string): string {
let uri = "";
if (!this.isSlpAddress(address)) {
Expand Down
Loading

0 comments on commit dfa0d10

Please sign in to comment.