Transaction

292860143a1df2794ceb51713eae35d870ae29a5e2b724a4c98c2f1f3a62b5c4
2020-11-22 11:40:45
0.00002935 BSV
(
0.00010697 BSV
-
0.00007762 BSV
)
100 sat/KB
1
248,397
29,340 B

3 Outputs

Total Output:
0.00007762 BSV
  • j"19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAutM>qimport "util.scrypt"; // Copyright (c) 2020 MatterPool Inc., Attila Aros (attila@matterpool.io) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. // // NOT TESTED - WORK IN PROGRESS // // ---------------------------------------------------------------------------------------------- // SuperAsset21a (SA21 for short) (Alpha) // // * Fungible-Token Smart Contract for Bitcoin. // * 21-way merge, 21-way split, 21-way atomic swap, redemption and issuance config options. // // ---------------------------------------------------------------------------------------------- // // How this is different from SA10? (https://blog.matterpool.io/launch-of-superasset-sa10-nft-smart-contract-for-bitcoin/) // - Ability to merge colored inputs (up to 21 at once) into a single output // - Ability to split a colored input into multiple outputs (up to 21 at once) // // How this is different from SA20? (https://media.bitcoinfiles.org/6e2b25a9b05aad13498d33fb0f563d7cf2c2505dd6cde41985bf9592b267d227) // - 'Issue' function for issuer baton allowing an issuer to increase supply or cap supply by melting the issuer baton. // - 21-way transfers and 21-way merges // - Atomic swap for up to 16 SA21 assets with up to 5 BSV (P2PKH outputs) // - Flexible permissioned redeem options (full or joint permissioned with `flag`) // - Arbitrary changeOutputs (as long as they are not to the same script) // Features of SA21a: // - Mint, transfer, update, and melt tokens that retain their identity and satoshi _total outputs balance_ until melted. // - Supports seperate funding inputs and change outputs with ANYONECANPAY // - Infuse satoshis to be locked into the child outputs for it's entire lifecycle of each output. // - Replay protection with globally unique ID passed on as an 'identity baton' // - Can destroy the token via "melt" and retrieve the satoshis // - Identical trust guarantees as the plain "non-colored" Satoshi UTXO // - Users can trivially verify authenticity and title history by requesting it in it's entirety from the seller // - Wallets and indexers can proactively index these UTXO's via a blind pattern match for the minting pattern (see below) // - Simplfied Payment Verification (SPV) and 0-conf works as expected with all the properties afforded to the native Satoshi // - *** Atomic Swap *** Atomic swap between different SA21 assets and regular cash BSV, up to 16+5 assets at a time // - *** Permission Options *** Flexible options: ownerSig only, issuerSig only, either or ownerSig issuerSig, and finally both sigs required // - *** Composability *** Arbitrary change inputs and outputs // - *** Issuer Supply Increase *** Issuer can increase supply and attach data or txid of contract or agreement terms with each issuance. // - *** Issuer optional *** When set the issuer can melt back coins or increase supply if the issuer baton is not destroyed. // Lifecycle: // Deploy, Issue Baton Usage Contract Code and State Layout: // // ========================================================================== // | // SA21a STATIC CODE | OP_RETURN tokenID(36B) issuerPublicKey(33B) flags(2B) ownerPublicKey(33B) [ dataPayload(32B) ] // | // ========================================================================== // With the only difference after the UTXO was issued being the different semanttic meaning of the 5th argument from // arbitrary data payload to hashPrevouts for merges // Regular Usage Contract Code and State Layout: // ========================================================================== // | // SA21a STATIC CODE | OP_RETURN tokenID(36B) issuerPublicKey(33B) flags(2B) ownerPublicKey(33B) [ hashPrevouts(32B) ] // | // ========================================================================== // Learn more: // - Announcement: https://blog.matterpool.io/launch-of-superasset-sa10-nft-smart-contract-for-bitcoin/ // - SuperAsset white paper: https://bitcoinfiles.org/t/d6c18189966ea060452bcf59157235f2e15df3abf7383d9d450acff69cf29181 // - Github: https://github.com/MatterPool/superasset-js // // Example transactions: // - Pending // // HOW TO INITIALLY DEPLOY: // // 1. Start with the base static code part of the output: // Script Hex Static Part of compiled code // // 2. Add an OP_RETURN and 0x24 (36) to the front of the null (all zeroes) minting assetId like this: // `6a24000000000000000000000000000000000000000000000000000000000000000000000000` // // 3. Add 0x21 (33) to the front of the public key of the issuer (used for redeem function). Leave blank for full permissionless. // `2102a73978cad0ee427d204830da1578b028df8b113eb0da1578cad028df8b11f713` // // 4. Add 0x21 (33) to the front of the public key of the first initial owner: // `2102f739ee427d204830da1578b028df8b113eb0cadf51e43bc21972423bd545e7c5` // // 5. Concatenate the hex strings in Steps 1 + 2 + 3 and broadcast the transaction with the desired number of satoshis to lock up. // // Alternatively, it is more convenient to use superasset-js or scrypt boilerplate https://github.com/scrypt-sv/boilerplate contract SuperAsset21a { // Set this to compiled size of the smart contract (ie: before the OP_RETURN 0x6a24) // Make sure to convert to hex and then bytes below with 'ContractCodeVarInt' static int ContractCodeLen = 19556; // 0x4C64 // According to minimal push varint bytes (less than 0x100000) // b'fd' varint type and LE is 0x644c of 0x4C64 = ContractCodeLen static bytes ContractCodeVarInt = b'fd644c'; static int pubKeyLen = 33; static int assetIdLen = 36; static int issuerPubKeyIdx = 37; static int issuerPubKeyIdxEnd = 70; static int flagsIdx = 70; static int flagsIdxEnd = 71; static int ownerPubKeyIdx = 72; static int ownerPubKeyIdxEnd = 105; static int dataTxIdx = 106; static int ContractCodeIdentityIdxEnd = SuperAsset21a.ContractCodeLen + 2 + SuperAsset21a.assetIdLen; // Build optimized output with hardcoded varint length for when we know the length of the data // // @param outputScript - bytes of script to write // @param outputSatoshis - Satoshis in this output. static function buildOutputOpt(bytes outputScript, int outputSatoshis): bytes { return num2bin(outputSatoshis, Util.OutputValueLen) + SuperAsset21a.ContractCodeVarInt + outputScript; } // Build the mandatory fields for the output representing this asset // @param lockingScript - the script being unlocked. // @param assetId - assetId is conserved // @param issuer - issuer (if set) of this asset // @param flags - Flags for this output // @param receiver - receiver of this asset (ie: new owner) static function buildOutputFields(bytes lockingScript, bytes assetId, PubKey issuer, bytes flags, PubKey receiver): bytes { return lockingScript[ : SuperAsset21a.ContractCodeLen] + b'6a24' + assetId + b'21' + issuer + flags + b'21' + receiver; } // Build the mandatory fields for the output representing this asset issuance baton // @param lockingScript - the script being unlocked. // @param assetId - assetId is conserved // @param issuer - issuer (if set) of this asset // @param flags - Flags for this baton // @param receiver - receiver of this asset (ie: new owner) // @param data - txid or hash of anything. Permissive push data size allowing up to 255 bytes (not violating minimal push is left to the client) static function buildOutputFieldsIssuanceData(bytes lockingScript, bytes assetId, PubKey issuer, bytes flags, PubKey receiver, bytes data): bytes { return lockingScript[ : SuperAsset21a.ContractCodeLen] + b'6a24' + assetId + b'21' + issuer + flags + b'21' + receiver + Util.toLEUnsigned(len(data), 1) + data; } // Split to multiple outputs (1 to many) // // @param lockingScript - the script being unlocked. // @param assetId - assetId is conserved // @param issuer - issuer (if set) of this asset // @param receivers[21] - Up to 20 different or same receivers for each of the outputs. // @param receiverSats[21] - Satoshis to give each receiver. Quantity must be conserved // @param conservedSatoshis - The amount that must be conserved. static function splitOutputs(bytes lockingScript, bytes assetId, bytes flags, PubKey issuer, PubKey[21] receivers, int[21] receiverSats, int conservedSatoshis): bytes { bytes bytesOutput = b''; int accumulatedSatoshis = 0; int i = 0; bool done = false; loop (21) { if (!done) { // Receiver i being set to null (0x00) indicates no more outputs or receivers. if (receivers[i] == b'00') { done = true; } else { // Do not allow it to be split to a null/empty ownerPubKey require(receivers[i] != num2bin(0, SuperAsset21a.pubKeyLen)); accumulatedSatoshis = accumulatedSatoshis + receiverSats[i]; bytesOutput = bytesOutput + SuperAsset21a.buildOutputOpt( SuperAsset21a.buildOutputFields(lockingScript, assetId, issuer, flags, receivers[i]), receiverSats[i] ); } i = i + 1; } } require(conservedSatoshis == accumulatedSatoshis); return bytesOutput; } // Atomic swap SA21 and BSV assets (Upto 16+5 in one transaction) // // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param senderSig - the signature of owner of parent input that is authorizing this transaction // @param inputAssetIds[16] - Up to 16 different or similar SA21 assets // @param receivers[16] - Receivers of those assets // @param cashReceivers[5] - Up to 5 cash receivers (in addition to arbitrary change output) // @param valuePartCash[5] - Receivers of BSV cash // @param prevTxPart1[16] - First part of tx before first occurrence of lockingScript (ie: in the first unlocking script input of prevTx from the preimage) // @param prevTxPart2[16] - Second part of tx after the first occurrence of lockingScript, but before the 'value' of tthe first output script // @param valuePart[16] - Total value transfered in Satoshis // @param prevTxPart3[16] Total value transfered in Satoshis // @param changeOutput - Script to send change to // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) public function swap(SigHashPreimage txPreimage, Sig senderSig, bytes[16] inputAssetIds, bytes[16] receivers, Ripemd160[5] cashReceivers, int[5] valuePartCash, bytes[16] prevTxPart1, bytes[16] prevTxPart2, int[16] valuePart, bytes[16] prevTxPart3, bytes changeOutput, int changeSatoshis) { // Allow anyone to fund this operation, commit to all the outputs. // Todo: Use Tx.checkPreimageOpt. Update after alpha phase over. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); bytes lockingScript = Util.scriptCode(txPreimage); // Make sure to add 2 bytes to skip the push data for the assetID (36 bytes of the outpoint in little-endian) bytes dataState = lockingScript[ SuperAsset21a.ContractCodeLen + 2: ]; bytes assetId = dataState[ : SuperAsset21a.assetIdLen ]; // Note that the assetId is in little endian to make it easier to match by outpoint bytes flags = dataState[ SuperAsset21a.flagsIdx : SuperAsset21a.flagsIdxEnd ]; bytes ownerPubKey = dataState[ SuperAsset21a.ownerPubKeyIdx : SuperAsset21a.ownerPubKeyIdxEnd ]; bytes issuerPubKey = dataState[ SuperAsset21a.issuerPubKeyIdx : SuperAsset21a.issuerPubKeyIdxEnd ]; // Validate and authorize the transaction (public key is 33 bytes) require(checkSig(senderSig, PubKey(ownerPubKey))); PubKey issuer = PubKey(issuerPubKey); int totalInputSatoshis = 0; bytes prevOuts = b''; int iCnt = 0; bool done = false; loop (16) { if (!done) { if (prevTxPart1[iCnt] == b'00') { done = true; } else { totalInputSatoshis = totalInputSatoshis + valuePart[iCnt]; // Build up tx from parts to construct the prevout // Note that the final byte sequence is '00000000' because we know the mergeable input was at output position 0 prevOuts = prevOuts + hash256(prevTxPart1[iCnt] + lockingScript + prevTxPart2[iCnt] + Util.toLEUnsigned(valuePart[iCnt], 8) + lockingScript + b'6a24' + inputAssetIds[iCnt] + b'21' + PubKey(issuerPubKey) + prevTxPart3[iCnt]) + b'00000000'; } iCnt = iCnt + 1; } } require(hash256(prevOuts) == Util.hashPrevouts(txPreimage)); bytes bytesOutput = b''; int oCnt = 0; bool oDone = false; loop (16) { if (!oDone) { // Receiver i being set to null (0x00) indicates no more outputs or receivers. if (receivers[oCnt] == b'00') { oDone = true; } else { // Receiver is one of the colored assets colored asset // Do not allow it to be split to a null/empty ownerPubKey require(receivers[oCnt] != num2bin(0, SuperAsset21a.pubKeyLen)); bytesOutput = bytesOutput + SuperAsset21a.buildOutputOpt( SuperAsset21a.buildOutputFields(lockingScript, inputAssetIds[oCnt], issuer, flags, PubKey(receivers[oCnt])), valuePart[oCnt] ); } oCnt = oCnt + 1; } } int cashCnt = 0; bool cashDone = false; loop (5) { if (!cashDone) { // Receiver i being set to null (0x00) indicates no more outputs or receivers. if (cashReceivers[cashCnt] == b'00') { cashDone = true; } else { // Receiver is a non-colored output to be created bytesOutput = bytesOutput + Util.buildOutput(Util.buildPublicKeyHashScript(cashReceivers[cashCnt]), valuePartCash[cashCnt]); } cashCnt = cashCnt + 1; } } // If the output change is the same length, then make sure it cannot be the same token, but anything else is okay // this allows the token to be fully composable with change outputs to other smart contracts. if (len(changeOutput) == SuperAsset21a.ContractCodeIdentityIdxEnd ) { require (changeOutput[ : SuperAsset21a.ContractCodeIdentityIdxEnd] != lockingScript[ : SuperAsset21a.ContractCodeIdentityIdxEnd]); } // Create 1 or many outputs that split the value, and conserve the asset type and value. require( hash256( bytesOutput + Util.buildOutput(changeOutput, changeSatoshis)) == Util.hashOutputs(txPreimage) ); } // Merge up to 21 inputs into a single output. // // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param senderSig - the signature of owner of parent input that is authorizing this transaction // @param receiver - Pubkey (33B) // @param prevTxPart1 - First part of tx before first occurrence of lockingScript (ie: in the first unlocking script input of prevTx from the preimage) // @param prevTxPart2 - Second part of tx after the first occurrence of lockingScript, but before the 'value' of tthe first output script // @param valuePart - Total value transfered in Satoshis // @param prevTxPart3 Total value transfered in Satoshis // @param changeOutput - Script to send change to // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) public function merge(SigHashPreimage txPreimage, Sig senderSig, PubKey receiver, bytes[21] prevTxPart1, bytes[21] prevTxPart2, int[21] valuePart, bytes[21] prevTxPart3, bytes changeOutput, int changeSatoshis) { // Allow anyone to fund this operation, commit to all the outputs. // Todo: Use Tx.checkPreimageOpt. Update after alpha phase over. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); bytes lockingScript = Util.scriptCode(txPreimage); // Make sure to add 2 bytes to skip the push data for the assetID (36 bytes of the outpoint in little-endian) bytes dataState = lockingScript[ SuperAsset21a.ContractCodeLen + 2: ]; bytes assetId = dataState[ : SuperAsset21a.assetIdLen ]; // Note that the assetId is in little endian to make it easier to match by outpoint bytes flags = dataState[ SuperAsset21a.flagsIdx : SuperAsset21a.flagsIdxEnd ]; bytes ownerPubKey = dataState[ SuperAsset21a.ownerPubKeyIdx : SuperAsset21a.ownerPubKeyIdxEnd ]; bytes issuerPubKey = dataState[ SuperAsset21a.issuerPubKeyIdx : SuperAsset21a.issuerPubKeyIdxEnd ]; // Validate and authorize the transaction (public key is 33 bytes) require(checkSig(senderSig, PubKey(ownerPubKey))); PubKey issuer = PubKey(issuerPubKey); int totalInputSatoshis = 0; bytes prevOuts = b''; int i = 0; bool done = false; loop (21) { if (!done) { if (prevTxPart1[i] == b'00') { done = true; } else { totalInputSatoshis = totalInputSatoshis + valuePart[i]; // Build up tx from parts to construct the prevout // Note that the final byte sequence is '00000000' because we know the mergeable input was at output position 0 prevOuts = prevOuts + hash256(prevTxPart1[i] + lockingScript + prevTxPart2[i] + Util.toLEUnsigned(valuePart[i], 8) + lockingScript + b'6a24' + assetId + b'21' + PubKey(issuerPubKey) + prevTxPart3[i]) + b'00000000'; } i = i + 1; } } require(hash256(prevOuts) == Util.hashPrevouts(txPreimage)); require(receiver != num2bin(0, SuperAsset21a.pubKeyLen)); // If the output change is the same length, then make sure it cannot be the same token, but anything else is okay // this allows the token to be fully composable with change outputs to other smart contracts. if (len(changeOutput) == SuperAsset21a.ContractCodeIdentityIdxEnd ) { require (changeOutput[ : SuperAsset21a.ContractCodeIdentityIdxEnd] != lockingScript[ : SuperAsset21a.ContractCodeIdentityIdxEnd]); } require( hash256( Util.buildOutput( SuperAsset21a.buildOutputFields(lockingScript, assetId, issuer, flags, receiver) + b'20' + Util.hashPrevouts(txPreimage), totalInputSatoshis ) + Util.buildOutput(changeOutput, changeSatoshis)) == Util.hashOutputs(txPreimage) ); } // // Transfer and optionally split the asset to multiple outputs // // Minting is implicit after the first transfer when starting from after deploying this contract with // initial minting assetID=0x000000000000000000000000000000000000000000000000000000000000000000000000 // // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param senderSig - the signature of owner of parent input that is authorizing this transaction // @param receivers - Pubkey (33B) // @param receiverSats - Receiver sats for output (must sum to less than total input) // @param changeOutput - Script to send change to // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) public function transfer(SigHashPreimage txPreimage, Sig senderSig, PubKey[21] receivers, int[21] receiverSats, bytes changeOutput, int changeSatoshis) { // Allow anyone to fund this operation, commit to all the outputs. // Todo: Use Tx.checkPreimageOpt. Update after alpha phase over. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); bytes lockingScript = Util.scriptCode(txPreimage); // Make sure to add 2 bytes to skip the push data for the assetID (36 bytes of the outpoint) bytes dataState = lockingScript[ SuperAsset21a.ContractCodeLen + 2: ]; bytes assetId = dataState[ : SuperAsset21a.assetIdLen ]; // Note that the assetId is in little endian to make it easier to match by outpoint bytes ownerPubKey = dataState[ SuperAsset21a.ownerPubKeyIdx : SuperAsset21a.ownerPubKeyIdxEnd ]; bytes issuerPubKey = dataState[ SuperAsset21a.issuerPubKeyIdx : SuperAsset21a.issuerPubKeyIdxEnd ]; bytes flags = dataState[ SuperAsset21a.flagsIdx : SuperAsset21a.flagsIdxEnd ]; // Validate and authorize the transaction (public key is 33 bytes) require(checkSig(senderSig, PubKey(dataState[ SuperAsset21a.ownerPubKeyIdx : SuperAsset21a.ownerPubKeyIdxEnd ]))); // If the output change is the same length, then make sure it cannot be the same token, but anything else is okay // this allows the token to be fully composable with change outputs to other smart contracts. if (len(changeOutput) == SuperAsset21a.ContractCodeIdentityIdxEnd ) { require (changeOutput[ : SuperAsset21a.ContractCodeIdentityIdxEnd] != lockingScript[ : SuperAsset21a.ContractCodeIdentityIdxEnd]); } require( hash256( SuperAsset21a.splitOutputs(lockingScript, assetId, flags, PubKey(ownerPubKey), receivers, receiverSats, Util.value(txPreimage)) + Util.buildOutput(changeOutput, changeSatoshis)) == Util.hashOutputs(txPreimage) ); } // // Issue // // Only the initial issuer can create more supply. If the issuer was set to null, then supply is capped // // Issuer has a "issuer" baton they can pass along with ownerPubKey is null. If the issuer melts their issuer baton // Then the supply is capped. // // Minting is implicit after the first transfer when starting from after deploying this contract with // initial minting assetID=0x000000000000000000000000000000000000000000000000000000000000000000000000 // // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param issuerSig - the signature of issuer that is authorizing this transaction // @param receiver - Pubkey (33B) // @param additionalSupply - Additional supply to inflate and send to receiver // @param issueBatonSatoshis - Non-zero amount of Satoshis to pass onto next baton (with null ownerPubKey). // @param data - Up to 255 bytes (minimal push rules). Recommend setting this to a txid or hash of the terms of agreement. // @param changeOutput - Script to send change to // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) public function issue(SigHashPreimage txPreimage, Sig issuerSig, PubKey receiver, int additionalSupply, int issueBatonSatoshis, bytes data, bytes changeOutput, int changeSatoshis) { // Allow anyone to fund this operation, commit to all the outputs. // Todo: Use Tx.checkPreimageOpt. Update after alpha phase over. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); bytes lockingScript = Util.scriptCode(txPreimage); // Make sure to add 2 bytes to skip the push data for the assetID (36 bytes of the outpoint) bytes dataState = lockingScript[ SuperAsset21a.ContractCodeLen + 2: ]; bytes assetId = dataState[ : SuperAsset21a.assetIdLen ]; // Note that the assetId is in little endian to make it easier to match by outpoint bytes ownerPubKey = dataState[ SuperAsset21a.ownerPubKeyIdx : SuperAsset21a.ownerPubKeyIdxEnd ]; bytes issuerPubKey = dataState[ SuperAsset21a.issuerPubKeyIdx : SuperAsset21a.issuerPubKeyIdxEnd ]; bytes flags = dataState[ SuperAsset21a.flagsIdx : SuperAsset21a.flagsIdxEnd ]; // Only allow issuing supply if this is un-owned (ie: from the issuing baton) require(ownerPubKey == num2bin(0, SuperAsset21a.pubKeyLen)); // Validate and authorize the transaction for the issuer require(checkSig(issuerSig, PubKey(dataState[ SuperAsset21a.issuerPubKeyIdx : SuperAsset21a.issuerPubKeyIdxEnd ]))); // If the assetId is the minting pattern (36 0's) then set it to the outpoint being spent. if (assetId == num2bin(0, SuperAsset21a.assetIdLen)) { // Then the assetId must be equal to the outpoint that this input is spending. assetId = Util.outpoint(txPreimage); } // If the output change is the same length, then make sure it cannot be the same token, but anything else is okay // this allows the token to be fully composable with change outputs to other smart contracts. if (len(changeOutput) == SuperAsset21a.ContractCodeIdentityIdxEnd ) { require (changeOutput[ : SuperAsset21a.ContractCodeIdentityIdxEnd] != lockingScript[ : SuperAsset21a.ContractCodeIdentityIdxEnd]); } // Create the split supply transfer an issue baton. Make sure 'flags' is passed on from minting to be immutable. require( hash256( SuperAsset21a.buildOutputOpt( SuperAsset21a.buildOutputFields(lockingScript, assetId, PubKey(issuerPubKey), flags, receiver), additionalSupply ) + SuperAsset21a.buildOutputOpt( SuperAsset21a.buildOutputFieldsIssuanceData(lockingScript, assetId, PubKey(issuerPubKey), flags, PubKey(b'000000000000000000000000000000000000000000000000000000000000000000'), data), issueBatonSatoshis ) + Util.buildOutput(changeOutput, changeSatoshis) ) == Util.hashOutputs(txPreimage) ); } // // Redeem the token. // // "Melt" and returns the Satoshis to regular circulation. // // @param ownerSig - Signature of owner authorizing transaction // @param issuerSig - Signature of issuer (if provided) authorizing the transaction. Issuer can override owner. Set issuer to 0x00..00 for permission-less. // @param receiverOutput - Receiver of Satoshi value being melted out. // @param txPreimage - Pre-image of current tx, so that we can impose constraints on the outputs // @param changeOutput - Script to send change to // @param changeSatoshis - Amount of satoshis to use (from the funding inputs) for change (thereby able to miner miner fees with difference) public function redeem(Sig ownerSig, Sig issuerSig, bytes receiverOutput, SigHashPreimage txPreimage, bytes changeOutput, int changeSatoshis) { // Allow anyone to fund this operation, commit to all the outputs. // Todo: Use Tx.checkPreimageOpt. Update after alpha phase over. require(Util.checkPreimageSigHashType(txPreimage, SigHash.ANYONECANPAY | SigHash.ALL | SigHash.FORKID)); bytes lockingScript = Util.scriptCode(txPreimage); bytes dataState = lockingScript[ SuperAsset21a.ContractCodeLen + 2: ]; // Validate and authorize the transaction by owner bytes ownerPubKey = dataState[ SuperAsset21a.ownerPubKeyIdx : SuperAsset21a.ownerPubKeyIdxEnd ]; bytes issuerPubKey = dataState[ SuperAsset21a.issuerPubKeyIdx : SuperAsset21a.issuerPubKeyIdxEnd ]; bytes flags = dataState[ SuperAsset21a.flagsIdx : SuperAsset21a.flagsIdxEnd ]; // If there was no issuer, then rely on the owner to sign if (issuerPubKey == num2bin(0, SuperAsset21a.pubKeyLen)) { require(checkSig(ownerSig, PubKey(ownerPubKey))); } else { // Case 0: Single permissioned mode requiring only owner to sign to redeem if (flags == b'00') { require(checkSig(issuerSig, PubKey(ownerPubKey))); } // Case 1: AND permissioned both issuer and owner must sign together to redeem if (flags == b'01') { require(checkSig(issuerSig, PubKey(issuerPubKey)) && checkSig(ownerSig, PubKey(ownerPubKey))); } // Case 2: OR permissioned with either both can be used to sign if (flags == b'02') { require(checkSig(issuerSig, PubKey(issuerPubKey)) || checkSig(ownerSig, PubKey(ownerPubKey))); } } // If the output change is the same length, then make sure it cannot be the same token, but anything else is okay // this allows the token to be fully composable with change outputs to other smart contracts. if (len(changeOutput) == SuperAsset21a.ContractCodeIdentityIdxEnd ) { require (changeOutput[ : SuperAsset21a.ContractCodeIdentityIdxEnd] != lockingScript[ : SuperAsset21a.ContractCodeIdentityIdxEnd]); } if (len(receiverOutput) == SuperAsset21a.ContractCodeIdentityIdxEnd ) { require (receiverOutput[ : SuperAsset21a.ContractCodeIdentityIdxEnd] != lockingScript[ : SuperAsset21a.ContractCodeIdentityIdxEnd]); } // Set the address receiving the regular Satoshis // and return change require(hash256( Util.buildOutput(receiverOutput, Util.value(txPreimage)) + Util.buildOutput(changeOutput, changeSatoshis)) == Util.hashOutputs(txPreimage) ); } } text/plain; charset=utf-8UTF-8(a85a9720-2ca4-11eb-a3f2-016f07df4189.txt
    https://whatsonchain.com/tx/292860143a1df2794ceb51713eae35d870ae29a5e2b724a4c98c2f1f3a62b5c4