From 653fd6bad1cba8e325cccf8b5ee955f89c38af09 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Tue, 23 Apr 2024 14:18:44 -0700 Subject: [PATCH 01/24] add `DIVIDEND` support --- docs/actions/DIVIDEND.md | 29 ++-- indexer/includes/actions/dividend.php | 199 +++++++++++++++++++++++++- indexer/includes/functions.php | 52 +++++++ indexer/includes/protocol_changes.php | 2 +- 4 files changed, 264 insertions(+), 18 deletions(-) diff --git a/docs/actions/DIVIDEND.md b/docs/actions/DIVIDEND.md index 99f1de1..0d29096 100644 --- a/docs/actions/DIVIDEND.md +++ b/docs/actions/DIVIDEND.md @@ -2,34 +2,35 @@ This command pays a dividend to `token` holders of a `token`. ## PARAMS -| Name | Type | Description | -| --------------- | ------ | ---------------------------------------- | -| `VERSION` | String | Broadcast Format Version | -| `TYPE` | String | Tick type (1=TICK, 2=ASSET) | -| `TICK` | String | Any valid `TICK` or `ASSET` | -| `DIVIDEND_TICK` | String | The `token` that dividends are paid in | -| `AMOUNT` | String | Amount of `tokens` to pay out per `UNIT` | +| Name | Type | Description | +| --------------- | ------ | --------------------------------------- | +| `VERSION` | String | Broadcast Format Version | +| `TICK` | String | 1 to 250 characters in length | +| `DIVIDEND_TICK` | String | The `token` that dividends are paid in | +| `AMOUNT` | String | Amount of `token` to pay out per `UNIT` | +| `MEMO` | String | An optional memo to include | ## Formats ### Version `0` -- `VERSION|TYPE|TICK|DIVIDEND_TICK|AMOUNT` +- `VERSION|TICK|DIVIDEND_TICK|AMOUNT|MEMO` ## Examples ``` -bt:DIVIDEND|0|1|BRRR|BACON|1 +bt:DIVIDEND|0|BRRR|BACON|1 This example pays a dividend of 1 BACON token to every holder of 1 BRRR token ``` ``` -bt:DIVIDEND|0|2|TEST|BACON|1 -This example pays a dividend of 1 BACON token to every holder of 1 TEST ASSET +bt:DIVIDEND|0|TEST|BACON|1 +This example pays a dividend of 1 BACON token to every holder of 1 TEST token ``` ## Rules -- Dividends may only be paid out by the current `token` owner -- Dividends may be used on any `TICK` or `ASSET` +- Dividends may be used on any `TICK` +- If `TICK` or `DIVIDEND_TICK` is non-divisible, `AMOUNT` must be integer ## Notes - `UNIT` - A specific unit of measure (1 or 1.0) -- To send large amounts of `tokens` to users, see the `AIRDROP` or `SEND` commands \ No newline at end of file +- To send large amounts of `tokens` to users, see the `AIRDROP` or `SEND` commands +- If `TICK` is divisible and `DIVIDEND_TICK` is non-divisble, quantities under 1.0 will receive no `DIVIDEND_TICK` diff --git a/indexer/includes/actions/dividend.php b/indexer/includes/actions/dividend.php index fd41113..e8e1a56 100644 --- a/indexer/includes/actions/dividend.php +++ b/indexer/includes/actions/dividend.php @@ -7,13 +7,206 @@ * - TICK - 1 to 250 characters in length * - DIVIDEND_TICK - 1 to 250 characters in length * - AMOUNT - The quantity of DIVIDEND_TICK rewarded per UNIT + * - MEMO - An optional memo to include * * FORMATS: * 0 = VERSION|TICK|DIVIDEND_TICK|AMOUNT ********************************************************************/ function btnsDividend($params=null, $data=null, $error=null){ - global $mysqli, $reparse; - // Coming soon -} + global $mysqli, $reparse, $addresses, $tickers; + + // Define list of known FORMATS + $formats = array( + 0 => 'VERSION|TICK|DIVIDEND_TICK|AMOUNT|MEMO' + ); + + /***************************************************************** + * DEBUGGING - Force params + ****************************************************************/ + // $str = "0|BACONTITS|DIVIDENDTEST1|1|testing dividends"; + // $params = explode('|',$str); + + // Validate that broadcast format is known + $format = getFormatVersion($params[0]); + if(!$error && ($format===NULL || !in_array($format,array_keys($formats)))) + $error = 'invalid: VERSION (unknown)'; + + // Parse PARAMS using given VERSION format and update BTNS transaction data object + if(!$error) + $data = setActionParams($data, $params, $formats[$format]); + + // Get information on BTNS token + $btInfo = getTokenInfo($data->TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX); + $btInfo2 = getTokenInfo($data->DIVIDEND_TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX); + + // Set divisible flags + $divisible = ($btInfo->DECIMALS==0) ? 0 : 1; + $divisible2 = ($btInfo2->DECIMALS==0) ? 0 : 1; + + // Clone the raw data for storage in dividends table + $dividend = clone($data); + + // Get SOURCE address preferences + $preferences = getAddressPreferences($data->SOURCE, $data->BLOCK_INDEX, $data->TX_INDEX); + + // Copy base BTNS transaction data object into fees object + $fees = clone($data); + $fees->TICK = 'GAS'; + $fees->AMOUNT = 0; + $fees->METHOD = ($preferences->FEE_PREFERENCE==1) ? 1 : 2; // 1=Destroy, 2=Donate + + // Validate TICK exists + if(!$error && !$btInfo) + $error = 'invalid: TICK (unknown)'; + + // Validate DIVIDEND_TICK exists + if(!$error && !$btInfo2) + $error = 'invalid: DIVIDEND_TICK (unknown)'; + + // Define placeholders for holders and balances + $holder = []; + $balances = []; + + // Get list of TICK holders and SOURCE address balances + if(!$error){ + $holders = getHolders($data->TICK, $data->BLOCK_INDEX, $data->TX_INDEX); + $balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); + } + + /************************************************************* + * FORMAT Validations + ************************************************************/ + + // Verify AMOUNT format valid for TICK and DIVIDEND_TICK + if(!$error && isset($data->AMOUNT) && (!isValidAmountFormat($divisible2, $data->AMOUNT) || !isValidAmountFormat($divisible, $data->AMOUNT))) + $error = "invalid: AMOUNT (format)"; + + /************************************************************* + * General Validations + ************************************************************/ + + // Verify no pipe in MEMO (BTNS uses pipe as field delimiter) + if(!$error && strpos($data->MEMO,'|')!==false) + $error = 'invalid: MEMO (pipe)'; + + // Verify no semicolon in MEMO (BTNS uses semicolon as action delimiter) + if(!$error && strpos($data->MEMO,';')!==false) + $error = 'invalid: MEMO (semicolon)'; + + // Remove SOURCE address from the TICK holders list + unset($holders[$data->SOURCE]); + + // Handle TICK and DIVIDEND_TICK divisibility mismatches by cleaning up the holders list + if(!$error && $divisible!=$divisible2){ + foreach($holders as $addr => $amount){ + + // Convert amount to integer + $holders[$addr] = bcadd($amount,0,0); + + // Remove any 0 quantity records + if($holders[$addr]<1) + unset($holders[$addr]); + } + } + + // Determine total amount of TICK + $data->HOLDER_TOTAL = 0; + foreach($holders as $addr => $amount) + $data->HOLDER_TOTAL = bcadd($data->HOLDER_TOTAL, $amount, $btInfo->DECIMALS); + + // Determine total amount of DIVIDEND_TICK + $data->DIVIDEND_TOTAL = bcmul($data->HOLDER_TOTAL, $data->AMOUNT, $btInfo2->DECIMALS); + + // Verify SOURCE has enough balances to cover total dividend amount + if(!$error && !hasBalance($balances, $data->DIVIDEND_TICK, $data->DIVIDEND_TOTAL)) + $error = 'invalid: insufficient funds (DIVIDEND_TICK)'; + // Adjust balances to reduce by dividend total + if(!$error) + $balances = debitBalances($balances, $data->DIVIDEND_TICK, $data->DIVIDEND_TOTAL); + + // Calculate total number of database hits for this DIVIDEND + $db_hits = count($holders) * 2; // 1 credits, 1 balances + $db_hits += 4; // 1 debits, 1 balances, 1 dividend + + // Determine total transaction FEE based on database hits + $data->FEE_TICK = 'GAS'; + $data->FEE_AMOUNT = getTransactionFee($db_hits, $data->FEE_TICK); + + // Verify SOURCE has enough balances to cover FEE AMOUNT + if(!$error && !hasBalance($balances, $data->FEE_TICK, $data->FEE_AMOUNT)) + $error = 'invalid: insufficient funds (FEE)'; + + // Adjust balances to reduce by FEE amount + if(!$error) + $balances = debitBalances($balances, $data->FEE_TICK, $data->FEE_AMOUNT); + + // Determine final status + $data->STATUS = $dividend->STATUS = $status = ($error) ? $error : 'valid'; + + // Print status message + print "\n\t DIVIDEND : {$data->TICK} : {$data->DIVIDEND_TICK} : {$data->AMOUNT} : {$data->STATUS}"; + + // Create record in dividend table + createDividend($dividend); + + // If this was a valid transaction, then add records to the credits and debits array + if($status=='valid'){ + + // Store the SOURCE, DIVIDEND_TICK, and FEE_TICK in addresses list + addAddressTicker($data->SOURCE, [$data->DIVIDEND_TICK, $data->FEE_TICK]); + + // Debit DIVIDEND_TOTAL from SOURCE address + createDebit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->DIVIDEND_TICK, $data->DIVIDEND_TOTAL, $data->SOURCE); + + // Debit FEE_AMOUNT from SOURCE address + createDebit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $data->SOURCE); + + // Loop through TICK holders and credit them with DIVIDEND_TICK amount + foreach($holders as $address => $amount){ + $dividend_amount = bcmul($amount, $data->AMOUNT, $btInfo2->DECIMALS); + + // Credit TICK holders with DIVIDEND_TICK AMOUNT + createCredit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->DIVIDEND_TICK, $dividend_amount, $address); + + // Store the recipient ADDRESS and TICK in addresses list + addAddressTicker($address, $data->DIVIDEND_TICK); + } + + // Update FEES object with to AMOUNT + $fees->AMOUNT = bcadd($fees->AMOUNT, $data->FEE_AMOUNT, 8); + + // Handle using FEE according the the users ADDRESS preferences + if($preferences->FEE_PREFERENCE>1){ + + // Determine what address to donate to + $address = ($preferences->FEE_PREFERENCE==2) ? DONATE_ADDRESS_1 : DONATE_ADDRESS_2; + + // Update the $fees object with the destination address + $fees->DESTINATION = $address; + + // Store the donation ADDRESS and TICK in addresses list + addAddressTicker($address, $data->FEE_TICK); + + // Credit donation address with FEE_AMOUNT + createCredit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $fees->DESTINATION); + } + + // Create record of FEE in `fees` table + createFeeRecord($fees); + } + + // If this is a reparse, bail out before updating balances + if($reparse) + return; + + // Store the SOURCE and TICKERS in addresses list + addAddressTicker($data->SOURCE, $tickers); + + // Update address balances + updateBalances(array_keys($addresses)); + + // Update supply for tokens + updateTokens($tickers); +} ?> \ No newline at end of file diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index 52bc348..93c2822 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -2427,4 +2427,56 @@ function createFeeRecord( $data=null ){ } } +// Create record in `dividends` table +function createDividend( $data=null ){ + global $mysqli; + $tick_id = createTicker($data->TICK); + $dividend_tick_id = createTicker($data->DIVIDEND_TICK); + $source_id = createAddress($data->SOURCE); + $tx_hash_id = createTransaction($data->TX_HASH); + $memo_id = createMemo($data->MEMO); + $status_id = createStatus($data->STATUS); + $tx_index = $mysqli->real_escape_string($data->TX_INDEX); + $amount = $mysqli->real_escape_string($data->AMOUNT); + $block_index = $mysqli->real_escape_string($data->BLOCK_INDEX); + // Check if record already exists + $sql = "SELECT + tx_index + FROM + dividends + WHERE + tick_id='{$tick_id}' AND + dividend_tick_id='{$dividend_tick_id}' AND + source_id='{$source_id}' AND + amount='{$amount}' AND + tx_hash_id='{$tx_hash_id}'"; + $results = $mysqli->query($sql); + if($results){ + if($results->num_rows){ + // UPDATE record + $sql = "UPDATE + dividends + SET + tx_index='{$tx_index}', + block_index='{$block_index}', + memo_id='{$memo_id}', + status_id='{$status_id}' + WHERE + tick_id='{$tick_id}' AND + dividend_tick_id='{$dividend_tick_id}' AND + source_id='{$source_id}' AND + amount='{$amount}' AND + tx_hash_id='{$tx_hash_id}'"; + } else { + // INSERT record + $sql = "INSERT INTO dividends (tx_index, tick_id, source_id, dividend_tick_id, amount, memo_id, tx_hash_id, block_index, status_id) values ('{$tx_index}','{$tick_id}', '{$source_id}', '{$dividend_tick_id}', '{$amount}','{$memo_id}', '{$tx_hash_id}', '{$block_index}', '{$status_id}')"; + } + $results = $mysqli->query($sql); + if(!$results) + byeLog('Error while trying to create / update a record in the dividends table'); + } else { + byeLog('Error while trying to lookup record in dividends table'); + } +} + ?> diff --git a/indexer/includes/protocol_changes.php b/indexer/includes/protocol_changes.php index 6c74ce7..e92dac5 100644 --- a/indexer/includes/protocol_changes.php +++ b/indexer/includes/protocol_changes.php @@ -18,7 +18,7 @@ 'CALLBACK' => array(0, 10, 0, 9999999, 999999999), 'DESTROY' => array(0, 10, 0, 789742, 2473585), 'DISPENSER' => array(0, 10, 0, 9999999, 999999999), - 'DIVIDEND' => array(0, 10, 0, 9999999, 999999999), + 'DIVIDEND' => array(0, 10, 0, 789742, 2473585), 'ISSUE' => array(0, 10, 0, 789742, 2473585), 'LIST' => array(0, 10, 0, 789742, 2473585), 'MINT' => array(0, 10, 0, 789742, 2473585), From 7f4979d1525abdffc1979b2f231a881fc8d2179b Mon Sep 17 00:00:00 2001 From: J-Dog Date: Tue, 23 Apr 2024 14:21:48 -0700 Subject: [PATCH 02/24] add dividends table --- indexer/sql/dividends.sql | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 indexer/sql/dividends.sql diff --git a/indexer/sql/dividends.sql b/indexer/sql/dividends.sql new file mode 100644 index 0000000..b453388 --- /dev/null +++ b/indexer/sql/dividends.sql @@ -0,0 +1,22 @@ +DROP TABLE IF EXISTS dividends; +CREATE TABLE dividends ( + tx_index INTEGER UNSIGNED, -- Unique transaction index + tx_hash_id INTEGER UNSIGNED, -- id of record in index_transactions + block_index INTEGER UNSIGNED, -- block index of DIVIDEND transaction + source_id INTEGER UNSIGNED, -- id of record in index_addresses table + tick_id INTEGER UNSIGNED, -- id of record in index_ticks + dividend_tick_id INTEGER UNSIGNED, -- id of record in index_ticks + amount VARCHAR(250), -- Amount of token per unit + memo_id INTEGER UNSIGNED, -- id of record in index_memos table + status_id INTEGER UNSIGNED -- id of record in index_statuses table +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE INDEX tx_index ON dividends (tx_index); +CREATE INDEX source_id ON dividends (source_id); +CREATE INDEX tx_hash_id ON dividends (tx_hash_id); +CREATE INDEX block_index ON dividends (block_index); +CREATE INDEX tick_id ON dividends (tick_id); +CREATE INDEX dividend_tick_id ON dividends (dividend_tick_id); +CREATE INDEX memo_id ON dividends (memo_id); +CREATE INDEX status_id ON dividends (status_id); + From fc381e8edcd8a0172cc49ad73b5a758ed2ec0ab2 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Thu, 25 Apr 2024 00:24:43 -0700 Subject: [PATCH 03/24] exclude source from dividends --- docs/actions/DIVIDEND.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/actions/DIVIDEND.md b/docs/actions/DIVIDEND.md index 0d29096..7ba3241 100644 --- a/docs/actions/DIVIDEND.md +++ b/docs/actions/DIVIDEND.md @@ -34,3 +34,4 @@ This example pays a dividend of 1 BACON token to every holder of 1 TEST token - `UNIT` - A specific unit of measure (1 or 1.0) - To send large amounts of `tokens` to users, see the `AIRDROP` or `SEND` commands - If `TICK` is divisible and `DIVIDEND_TICK` is non-divisble, quantities under 1.0 will receive no `DIVIDEND_TICK` +- `SOURCE` address is excluded from receiving dividends From 6f91d679528f98489ba84664a2c6de9d3c05d067 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Thu, 25 Apr 2024 11:51:41 -0700 Subject: [PATCH 04/24] add `SWEEP` support --- docs/actions/SWEEP.md | 22 ++-- indexer/includes/actions/sweep.php | 180 ++++++++++++++++++++++++-- indexer/includes/functions.php | 118 ++++++++++++++++- indexer/includes/protocol_changes.php | 2 +- indexer/sql/sweeps.sql | 21 +++ 5 files changed, 319 insertions(+), 24 deletions(-) create mode 100644 indexer/sql/sweeps.sql diff --git a/docs/actions/SWEEP.md b/docs/actions/SWEEP.md index 3f2cf82..4a45224 100644 --- a/docs/actions/SWEEP.md +++ b/docs/actions/SWEEP.md @@ -2,28 +2,28 @@ This command transfers all `token` balances and/or ownerships to a destination address ## PARAMS -| Name | Type | Description | -| ----------------- | ------ | ----------------------------------------------------------------- | -| `VERSION` | String | Broadcast Format Version | -| `DESTINATION` | String | address where `token` shall be swept | -| `SWEEP_BALANCES` | String | Indicates if address `token` balances should be swept (default=1) | -| `SWEEP_OWNERSHIP` | String | Indicates if address `token` balances should be swept (default=1) | -| `MEMO` | String | Optional memo to include | +| Name | Type | Description | +| ------------- | ------ | ------------------------------------------------------------------ | +| `VERSION` | String | Broadcast Format Version | +| `DESTINATION` | String | address where `token` shall be swept | +| `BALANCES` | String | Indicates if address `token` balances should be swept (default=1) | +| `OWNERSHIPS` | String | Indicates if address `token` ownership should be swept (default=1) | +| `MEMO` | String | Optional memo to include | ## Formats ### Version `0` -- `VERSION|DESTINATION|SWEEP_BALANCES|SWEEP_OWNERSHIP|MEMO` +- `VERSION|DESTINATION|BALANCES|OWNERSHIPS|MEMO` ## Examples ``` bt:SWEEP|0|1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev|1|1 -This example sweeps both token balances and ownership from the broadcasting address to 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev +This example sweeps both token balances and ownerships from the broadcasting address to 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev ``` ``` -bt:SWEEP|0|1BoogrfDADPLQpq8LMASmWQUVYDp4t2hF9|0|1` -This example sweeps only token ownership from the broadcasting address to 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev +bt:SWEEP|0|1BoogrfDADPLQpq8LMASmWQUVYDp4t2hF9|0|1 +This example sweeps only token ownerships from the broadcasting address to 1BoogrfDADPLQpq8LMASmWQUVYDp4t2hF9 ``` ## Rules diff --git a/indexer/includes/actions/sweep.php b/indexer/includes/actions/sweep.php index 959aa15..fb2c1ae 100644 --- a/indexer/includes/actions/sweep.php +++ b/indexer/includes/actions/sweep.php @@ -3,18 +3,182 @@ * sweep.php - SWEEP command * * PARAMS: - * VERSION - Broadcast Format Version - * DESTINATION - address where `token` shall be swept - * SWEEP_BALANCES - Indicates if address `token` balances should be swept (default=1) - * SWEEP_OWNERSHIP - Indicates if address `token` balances should be swept (default=1) - * MEMO - Optional memo to include + * VERSION - Broadcast Format Version + * DESTINATION - address where `token` shall be swept + * BALANCES - Indicates if address `token` balances should be swept (default=1) + * OWNERSHIPS - Indicates if address `token` ownerships should be swept (default=1) + * MEMO - Optional memo to include * * FORMATS: - * 0 = VERSION|DESTINATION|SWEEP_BALANCES|SWEEP_OWNERSHIP|MEMO + * 0 = VERSION|DESTINATION|BALANCES|OWNERSHIP|MEMO ********************************************************************/ function btnsSweep($params=null, $data=null, $error=null){ - global $mysqli, $reparse; - // Coming soon + global $mysqli, $reparse, $addresses, $tickers; + + // Define list of known FORMATS + $formats = array( + 0 => 'VERSION|DESTINATION|BALANCES|OWNERSHIPS|MEMO' + ); + + /***************************************************************** + * DEBUGGING - Force params + ****************************************************************/ + // $str = "0|JDOG|1|"; + // $params = explode('|',$str); + + // Validate that broadcast format is known + $format = getFormatVersion($params[0]); + if(!$error && ($format===NULL || !in_array($format,array_keys($formats)))) + $error = 'invalid: VERSION (unknown)'; + + // Parse PARAMS using given VERSION format and update BTNS transaction data object + if(!$error) + $data = setActionParams($data, $params, $formats[$format]); + + // Get SOURCE address preferences, balances, and ownership information + $preferences = getAddressPreferences($data->SOURCE, $data->BLOCK_INDEX, $data->TX_INDEX); + $balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); + $ownerships = getAddressOwnership($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); + + // Copy base BTNS transaction data object into fees object + $fees = clone($data); + $fees->TICK = 'GAS'; + $fees->AMOUNT = 0; + $fees->METHOD = ($preferences->FEE_PREFERENCE==1) ? 1 : 2; // 1=Destroy, 2=Donate + + /***************************************************************** + * FORMAT Validations + ****************************************************************/ + + // Verify DESTINATION address format + if(!$error && isset($data->DESTINATION) && !isCryptoAddress($data->DESTINATION)) + $error = "invalid: DESTINATION (format)"; + + // Verify BALANCES format is valid (0 or 1) + if(!$error && isset($data->BALANCES) && !in_array($data->BALANCES,array(0,1))) + $error = "invalid: BALANCES (format)"; + + // Verify OWNERSHIP format is valid (0 or 1) + if(!$error && isset($data->OWNERSHIP) && !in_array($data->OWNERSHIP,array(0,1))) + $error = "invalid: OWNERSHIP (format)"; + + // Set default values for BALANCES and OWNERSHIP (default = 1) + $data->BALANCES = (isset($data->BALANCES)) ? $data->BALANCES : 1; + $data->OWNERSHIPS = (isset($data->OWNERSHIPS)) ? $data->OWNERSHIPS : 1; + + // Clone the raw data for storage in sweeps table + $sweep = clone($data); + + /***************************************************************** + * General Validations + ****************************************************************/ + + // Verify no pipe in MEMO (BTNS uses pipe as field delimiter) + if(!$error && strpos($data->MEMO,'|')!==false) + $error = 'invalid: MEMO (pipe)'; + + // Verify no semicolon in MEMO (BTNS uses semicolon as action delimiter) + if(!$error && strpos($data->MEMO,';')!==false) + $error = 'invalid: MEMO (semicolon)'; + + // Calculate total number of database hits for this SWEEP + $db_hits = 1; // 1 sweeps + $db_hits += ($data->BALANCES) ? bcmul(count($balances),3,0) : 0; // 1 debits, 1 credits, 1 balances + $db_hits += ($data->OWNERSHIP) ? count($ownerships) : 0; // 1 issues + + // Determine total transaction FEE based on database hits + $data->FEE_TICK = 'GAS'; + $data->FEE_AMOUNT = getTransactionFee($db_hits, $data->FEE_TICK); + + // Verify SOURCE has enough balances to cover FEE AMOUNT + if(!$error && !hasBalance($balances, $data->FEE_TICK, $data->FEE_AMOUNT)) + $error = 'invalid: insufficient funds (FEE)'; + + // Adjust balances to reduce by FEE amount + if(!$error) + $balances = debitBalances($balances, $data->FEE_TICK, $data->FEE_AMOUNT); + + // Determine final status + $data->STATUS = $sweep->STATUS = $status = ($error) ? $error : 'valid'; + + // Print status message + print "\n\t SWEEP : {$data->DESTINATION} : {$data->STATUS}"; + + // Create record in sweeps table + createSweep($sweep); + + // If this was a valid transaction, then handle the actual sweep actions + if($status=='valid'){ + + // Store the SOURCE and FEE_TICK in addresses and tickers arrays + addAddressTicker($data->SOURCE, $data->FEE_TICK); + + // Debit FEE_AMOUNT from SOURCE address + createDebit('SWEEP', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $data->SOURCE); + + // Transfer Balances + if($data->BALANCES==1){ + foreach($balances as $tick_id => $amount){ + $tick = getTicker($tick_id); + + // Debit token amount from SOURCE and credit to DESTINATION + createDebit('SWEEP', $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $data->SOURCE); + createCredit('SWEEP', $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $data->DESTINATION); + + // Store the SOURCE, DESTINATION and TICK in addresses and tickers arrays + addAddressTicker($data->SOURCE, $tick); + addAddressTicker($data->DESTINATION, $tick); + } + } + + // Transfer token ownerships + if($data->OWNERSHIPS==1){ + // Copy base BTNS transaction data object into issue object + $issue = $data; + $issue->TRANSFER = $data->DESTINATION; + foreach($ownerships as $tick){ + $issue->TICK = $tick; + + // Create issue record for transfer of ownership + createIssue($issue); + + // Store the DESTINATION and TICK in addresses and tickers arrays + addAddressTicker($data->DESTINATION, $tick); + } + } + + // Update FEES object with to AMOUNT + $fees->AMOUNT = bcadd($fees->AMOUNT, $data->FEE_AMOUNT, 8); + + // Handle using FEE according the the users ADDRESS preferences + if($preferences->FEE_PREFERENCE>1){ + + // Determine what address to donate to + $address = ($preferences->FEE_PREFERENCE==2) ? DONATE_ADDRESS_1 : DONATE_ADDRESS_2; + + // Update the $fees object with the destination address + $fees->DESTINATION = $address; + + // Store the donation ADDRESS and TICK in addresses list + addAddressTicker($address, $data->FEE_TICK); + + // Credit donation address with FEE_AMOUNT + createCredit('SWEEP', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $fees->DESTINATION); + } + + // Create record of FEE in `fees` table + createFeeRecord($fees); + + // If this is a reparse, bail out before updating balances and token information + if($reparse) + return; + + // Update address balances + updateBalances(array_keys($addresses)); + + // Update supply for tokens + updateTokens($tickers); + } } ?> \ No newline at end of file diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index 93c2822..6340cb8 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -1795,8 +1795,9 @@ function hasBalance($balances=null, $tick=null, $amount=null){ // Handle deducting TICK AMOUNT from balances and return updated balances array function debitBalances($balances=null, $tick=null, $amount=null){ - $balance = (isset($balances[$tick])) ? $balances[$tick] : 0; - $balances[$tick] = $balance - $amount; + $tick_id = createTicker($tick); + $balance = (isset($balances[$tick_id])) ? $balances[$tick_id] : 0; + $balances[$tick_id] = $balance - $amount; return $balances; } @@ -1853,9 +1854,9 @@ function sanityCheck( $block=null ){ $supplyB = getTokenSupplyBalance($tick); // Supply from balances table $supplyC = getTokenSupply($tick); // Supply from credits/debits tables if($supplyA!=$supplyB) - byeLog("SanityError: balances table supply does not match token supply : {$tick}"); + byeLog("SanityError: balances table supply does not match token supply : {$tick} ({$supplyB} != {$supplyA})"); if($supplyA!=$supplyC) - byeLog("SanityError: credits/debits table supply does not match token supply : {$tick}"); + byeLog("SanityError: credits/debits table supply does not match token supply : {$tick} ({$supplyC} != {$supplyA})"); } } @@ -2479,4 +2480,113 @@ function createDividend( $data=null ){ } } +// Get list of tokens owned by a given address +function getAddressOwnership($address=null, $block_index=null, $tx_index=null){ + global $mysqli; + $address_id = createAddress($address); + $data = []; + $whereSql = ""; + if(isset($block_index) && is_numeric($block_index)) + $whereSql .= " AND block_index <= {$block_index}"; + if(isset($tx_index) && is_numeric($tx_index)) + $whereSql .= " AND tx_index < '{$tx_index}'"; + // get list of issuances where address is SOURCE or TRANSFER (list of tokens) + $sql = "SELECT + t.tick, + i.tick_id + FROM + issues i, + index_tickers t + WHERE + t.id=i.tick_id AND + (source_id='{$address_id}' OR transfer_id='{$address_id}') + {$whereSql} + ORDER BY tick DESC"; + $results = $mysqli->query($sql); + if($results){ + if($results->num_rows){ + while($row = $results->fetch_assoc()){ + $row = (object) $row; + $info = getTokenInfo($row->tick, null, $block_index, $tx_index); + if($info->OWNER==$address) + array_push($data, $row->tick); + } + } + } else { + byeLog('Error while trying to lookup records in issues table'); + } + return $data; +} + + +// Create record in `sweeps` table +function createSweep( $data=null ){ + global $mysqli; + $tick_id = createTicker($data->TICK); + $source_id = createAddress($data->SOURCE); + $destination_id = createAddress($data->DESTINATION); + $tx_hash_id = createTransaction($data->TX_HASH); + $memo_id = createMemo($data->MEMO); + $status_id = createStatus($data->STATUS); + $tx_index = $mysqli->real_escape_string($data->TX_INDEX); + $balances = $mysqli->real_escape_string($data->BALANCES); + $ownerships = $mysqli->real_escape_string($data->OWNERSHIPS); + $block_index = $mysqli->real_escape_string($data->BLOCK_INDEX); + // Check if record already exists + $sql = "SELECT + tx_index + FROM + sweeps + WHERE + source_id='{$source_id}' AND + tx_hash_id='{$tx_hash_id}'"; + $results = $mysqli->query($sql); + if($results){ + if($results->num_rows){ + // UPDATE record + $sql = "UPDATE + sweeps + SET + tx_index='{$tx_index}', + block_index='{$block_index}', + destination_id='{$destination_id}', + balances='{$balances}', + ownerships='{$ownerships}', + memo_id='{$memo_id}', + status_id='{$status_id}' + WHERE + source_id='{$source_id}' AND + tx_hash_id='{$tx_hash_id}'"; + } else { + // INSERT record + $sql = "INSERT INTO sweeps (tx_index, source_id, destination_id, balances, ownerships, memo_id, tx_hash_id, block_index, status_id) values ('{$tx_index}', '{$source_id}','{$destination_id}', '{$balances}', '{$ownerships}', '{$memo_id}', '{$tx_hash_id}', '{$block_index}', '{$status_id}')"; + } + $results = $mysqli->query($sql); + if(!$results) + byeLog('Error while trying to create / update a record in the dividends table'); + } else { + byeLog('Error while trying to lookup record in dividends table'); + } +} + +// Get TICK for a given tick_id +function getTicker( $tick_id=null ){ + global $mysqli; + if(!isset($tick_id) || $tick_id=='') + return 0; + // Truncate description to 250 chars + $tick_id = $mysqli->real_escape_string($tick_id); + $results = $mysqli->query("SELECT tick FROM index_tickers WHERE id='{$tick_id}' LIMIT 1"); + if($results){ + if($results->num_rows){ + $row = $results->fetch_assoc(); + return $row['tick']; + } + } else { + byeLog('Error while trying to lookup ticker in index_tickers table'); + } +} + + + ?> diff --git a/indexer/includes/protocol_changes.php b/indexer/includes/protocol_changes.php index e92dac5..c09e1dc 100644 --- a/indexer/includes/protocol_changes.php +++ b/indexer/includes/protocol_changes.php @@ -25,7 +25,7 @@ 'RUG' => array(0, 10, 0, 9999999, 999999999), 'SEND' => array(0, 10, 0, 789742, 2473585), 'SLEEP' => array(0, 10, 0, 9999999, 999999999), - 'SWEEP' => array(0, 10, 0, 9999999, 999999999), + 'SWEEP' => array(0, 10, 0, 789742, 2473585), // Define protocol changes (ALL LOWER Case) // 'name' => array(0, 10, 0, 0, 0), diff --git a/indexer/sql/sweeps.sql b/indexer/sql/sweeps.sql new file mode 100644 index 0000000..fb81ceb --- /dev/null +++ b/indexer/sql/sweeps.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS sweeps; +CREATE TABLE sweeps ( + tx_index INTEGER UNSIGNED, -- Unique transaction index + tx_hash_id INTEGER UNSIGNED, -- id of record in index_transactions + block_index INTEGER UNSIGNED, -- block index of SWEEP transaction + balances INTEGER UNSIGNED, -- Indicates if token balances should be swept + ownerships INTEGER UNSIGNED, -- Indicates if token ownerships should be swept + source_id INTEGER UNSIGNED, -- id of record in index_addresses table + destination_id INTEGER UNSIGNED, -- id of record in index_addresses table + memo_id INTEGER UNSIGNED, -- id of record in index_memos table + status_id INTEGER UNSIGNED -- id of record in index_statuses table +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE INDEX tx_index ON sweeps (tx_index); +CREATE INDEX source_id ON sweeps (source_id); +CREATE INDEX destination_id ON sweeps (destination_id); +CREATE INDEX tx_hash_id ON sweeps (tx_hash_id); +CREATE INDEX block_index ON sweeps (block_index); +CREATE INDEX memo_id ON sweeps (memo_id); +CREATE INDEX status_id ON sweeps (status_id); + From 3c8ecab4f8ebffe06c5ee3cabc690ad4a2c7cc21 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Thu, 25 Apr 2024 13:55:32 -0700 Subject: [PATCH 05/24] cleanup --- indexer/includes/compare.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/indexer/includes/compare.php b/indexer/includes/compare.php index a3b0454..5bf69a5 100644 --- a/indexer/includes/compare.php +++ b/indexer/includes/compare.php @@ -66,25 +66,6 @@ function btnsCompare($database=null){ {$whereSql} ORDER BY tx_index ASC {$limitSql}"; - // Support OLD style database with index_tx_types instead of index_actions - // TODO: Remove - if(in_array($db,array('BTNS_Counterparty_Old','BTNS_Counterparty_Testnet_Old','BTNS_Dogeparty_Old','BTNS_Dogeparty_Testnet_Old'))){ - $sql = "SELECT - t1.tx_index, - t2.hash as tx_hash, - a.type as action - FROM - {$db}.transactions t1, - {$db}.index_transactions t2, - {$db}.index_tx_types a - WHERE - a.id=t1.type_id AND - t2.id=t1.tx_hash_id - {$whereSql} - ORDER BY tx_index ASC - {$limitSql}"; - } - $results = $mysqli->query($sql); if($results){ if($results->num_rows){ From 0b8483ce36f50f100afe6c4a1f122be3beaca413 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Fri, 26 Apr 2024 16:09:41 -0700 Subject: [PATCH 06/24] cleanup transaction fees code --- indexer/includes/actions/airdrop.php | 68 ++++++--------------------- indexer/includes/actions/dividend.php | 43 +++-------------- indexer/includes/actions/sweep.php | 47 ++++-------------- indexer/includes/functions.php | 59 ++++++++++++++++++++++- indexer/indexer.php | 3 ++ 5 files changed, 92 insertions(+), 128 deletions(-) diff --git a/indexer/includes/actions/airdrop.php b/indexer/includes/actions/airdrop.php index 0de28c2..75355e5 100644 --- a/indexer/includes/actions/airdrop.php +++ b/indexer/includes/actions/airdrop.php @@ -17,7 +17,7 @@ * ********************************************************************/ function btnsAirdrop($params=null, $data=null, $error=null){ - global $mysqli, $reparse, $addresses, $tickers; + global $mysqli, $reparse, $addresses, $tickers, $credits, $debits; // Define list of known FORMATS $formats = array( @@ -89,22 +89,16 @@ function btnsAirdrop($params=null, $data=null, $error=null){ // Get source address balances $balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); - // Get SOURCE address preferences - $preferences = getAddressPreferences($data->SOURCE, $data->BLOCK_INDEX, $data->TX_INDEX); + // Create the fees object + $fees = createFeesObject($data); // Store original error value $origError = $error; - // Array of credits and debits + // reset credits and debits arrays $credits = []; $debits = []; - // Copy base BTNS transaction data object into fees object - $fees = clone($data); - $fees->TICK = 'GAS'; - $fees->AMOUNT = 0; - $fees->METHOD = ($preferences->FEE_PREFERENCE==1) ? 1 : 2; // 1=Destroy, 2=Donate - // Loop through airdrops and process each foreach($airdrops as $info){ @@ -197,8 +191,7 @@ function btnsAirdrop($params=null, $data=null, $error=null){ $db_hits += 4; // 1 debits, 1 balances, 1 airdrops // Determine total transaction FEE based on database hits - $airdrop->FEE_TICK = 'GAS'; - $airdrop->FEE_AMOUNT = getTransactionFee($db_hits, $airdrop->FEE_TICK); + $fees->AMOUNT = getTransactionFee($db_hits, $fees->TICK); // Verify SOURCE has enough balances to cover TICK total DEBIT amount if(!$error && !hasBalance($balances, $airdrop->TICK, $airdrop->DEBIT)) @@ -209,12 +202,12 @@ function btnsAirdrop($params=null, $data=null, $error=null){ $balances = debitBalances($balances, $airdrop->TICK, $airdrop->DEBIT); // Verify SOURCE has enough balances to cover FEE AMOUNT - if(!$error && !hasBalance($balances, $airdrop->FEE_TICK, $airdrop->FEE_AMOUNT)) + if(!$error && !hasBalance($balances, $fees->TICK, $fees->AMOUNT)) $error = 'invalid: insufficient funds (FEE)'; // Adjust balances to reduce by FEE amount if(!$error) - $balances = debitBalances($balances, $airdrop->TICK, $airdrop->DEBIT); + $balances = debitBalances($balances, $fees->TICK, $fees->AMOUNT); // Determine final status $airdrop->STATUS = $status = ($error) ? $error : 'valid'; @@ -229,33 +222,13 @@ function btnsAirdrop($params=null, $data=null, $error=null){ if($status=='valid'){ // Store the SOURCE and TICK in addresses list - addAddressTicker($airdrop->SOURCE, [$airdrop->TICK, $airdrop->FEE_TICK]); - - // Debit SOURCE with total DEBIT and FEE_AMOUNT - array_push($debits, array($airdrop->TICK, $airdrop->DEBIT)); - array_push($debits, array($airdrop->FEE_TICK, $airdrop->FEE_AMOUNT)); - - // Update FEES object with to AMOUNT - $fees->AMOUNT = bcadd($fees->AMOUNT, $airdrop->FEE_AMOUNT, 8); - - // Handle using FEE according the the users ADDRESS preferences - if($preferences->FEE_PREFERENCE>1){ - - // Determine what address to donate to - $address = ($preferences->FEE_PREFERENCE==2) ? DONATE_ADDRESS_1 : DONATE_ADDRESS_2; + addAddressTicker($airdrop->SOURCE, [$airdrop->TICK, $fees->TICK]); - // Update the $fees object with the destination address - $fees->DESTINATION = $address; + // Debit SOURCE with total DEBIT + array_push($debits, array($airdrop->TICK, $airdrop->DEBIT)); - // Store the donation ADDRESS and TICK in addresses list - addAddressTicker($address, $airdrop->FEE_TICK); - - // Credit donation address with FEE_AMOUNT - array_push($credits, array($airdrop->FEE_TICK, $airdrop->FEE_AMOUNT, $address)); - } - - // Create record of FEE in `fees` table - createFeeRecord($fees); + // Handle any transaction FEE according the users's ADDRESS preferences + processTransactionFees('AIRDROP', $fees); // Loop through recipient addresses foreach($recipients as $address){ @@ -269,21 +242,8 @@ function btnsAirdrop($params=null, $data=null, $error=null){ } } - // Consolidate the credit and debit records to write as few records as possible - $debits = consolidateCreditDebitRecords('debits', $debits); - $credits = consolidateCreditDebitRecords('credits', $credits); - - // Create records in debits table - foreach($debits as $debit){ - [$tick, $amount] = $debit; - createDebit('AIRDROP', $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $data->SOURCE); - } - - // Create records in credits table - foreach($credits as $credit){ - [$tick, $amount, $destination] = $credit; - createCredit('AIRDROP', $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $destination); - } + // Process any transaction credit/debit records + processTransactionCreditsDebits('AIRDROP', $data); // If this is a reparse, bail out before updating balances if($reparse) diff --git a/indexer/includes/actions/dividend.php b/indexer/includes/actions/dividend.php index e8e1a56..8608404 100644 --- a/indexer/includes/actions/dividend.php +++ b/indexer/includes/actions/dividend.php @@ -46,14 +46,8 @@ function btnsDividend($params=null, $data=null, $error=null){ // Clone the raw data for storage in dividends table $dividend = clone($data); - // Get SOURCE address preferences - $preferences = getAddressPreferences($data->SOURCE, $data->BLOCK_INDEX, $data->TX_INDEX); - - // Copy base BTNS transaction data object into fees object - $fees = clone($data); - $fees->TICK = 'GAS'; - $fees->AMOUNT = 0; - $fees->METHOD = ($preferences->FEE_PREFERENCE==1) ? 1 : 2; // 1=Destroy, 2=Donate + // Create the fees object + $fees = createFeesObject($data); // Validate TICK exists if(!$error && !$btInfo) @@ -130,16 +124,15 @@ function btnsDividend($params=null, $data=null, $error=null){ $db_hits += 4; // 1 debits, 1 balances, 1 dividend // Determine total transaction FEE based on database hits - $data->FEE_TICK = 'GAS'; - $data->FEE_AMOUNT = getTransactionFee($db_hits, $data->FEE_TICK); + $fees->AMOUNT = getTransactionFee($db_hits, $fees->TICK); // Verify SOURCE has enough balances to cover FEE AMOUNT - if(!$error && !hasBalance($balances, $data->FEE_TICK, $data->FEE_AMOUNT)) + if(!$error && !hasBalance($balances, $fees->TICK, $fees->AMOUNT)) $error = 'invalid: insufficient funds (FEE)'; // Adjust balances to reduce by FEE amount if(!$error) - $balances = debitBalances($balances, $data->FEE_TICK, $data->FEE_AMOUNT); + $balances = debitBalances($balances, $fees->TICK, $fees->AMOUNT); // Determine final status $data->STATUS = $dividend->STATUS = $status = ($error) ? $error : 'valid'; @@ -159,8 +152,8 @@ function btnsDividend($params=null, $data=null, $error=null){ // Debit DIVIDEND_TOTAL from SOURCE address createDebit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->DIVIDEND_TICK, $data->DIVIDEND_TOTAL, $data->SOURCE); - // Debit FEE_AMOUNT from SOURCE address - createDebit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $data->SOURCE); + // Handle any transaction FEE according the users's ADDRESS preferences + processTransactionFees('DIVIDEND', $fees); // Loop through TICK holders and credit them with DIVIDEND_TICK amount foreach($holders as $address => $amount){ @@ -172,28 +165,6 @@ function btnsDividend($params=null, $data=null, $error=null){ // Store the recipient ADDRESS and TICK in addresses list addAddressTicker($address, $data->DIVIDEND_TICK); } - - // Update FEES object with to AMOUNT - $fees->AMOUNT = bcadd($fees->AMOUNT, $data->FEE_AMOUNT, 8); - - // Handle using FEE according the the users ADDRESS preferences - if($preferences->FEE_PREFERENCE>1){ - - // Determine what address to donate to - $address = ($preferences->FEE_PREFERENCE==2) ? DONATE_ADDRESS_1 : DONATE_ADDRESS_2; - - // Update the $fees object with the destination address - $fees->DESTINATION = $address; - - // Store the donation ADDRESS and TICK in addresses list - addAddressTicker($address, $data->FEE_TICK); - - // Credit donation address with FEE_AMOUNT - createCredit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $fees->DESTINATION); - } - - // Create record of FEE in `fees` table - createFeeRecord($fees); } // If this is a reparse, bail out before updating balances diff --git a/indexer/includes/actions/sweep.php b/indexer/includes/actions/sweep.php index fb2c1ae..95042ee 100644 --- a/indexer/includes/actions/sweep.php +++ b/indexer/includes/actions/sweep.php @@ -35,18 +35,14 @@ function btnsSweep($params=null, $data=null, $error=null){ if(!$error) $data = setActionParams($data, $params, $formats[$format]); - // Get SOURCE address preferences, balances, and ownership information - $preferences = getAddressPreferences($data->SOURCE, $data->BLOCK_INDEX, $data->TX_INDEX); + // Get SOURCE address balances and ownership information $balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); $ownerships = getAddressOwnership($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); - // Copy base BTNS transaction data object into fees object - $fees = clone($data); - $fees->TICK = 'GAS'; - $fees->AMOUNT = 0; - $fees->METHOD = ($preferences->FEE_PREFERENCE==1) ? 1 : 2; // 1=Destroy, 2=Donate + // Create the fees object + $fees = createFeesObject($data); - /***************************************************************** + /***************************************************************** * FORMAT Validations ****************************************************************/ @@ -87,16 +83,15 @@ function btnsSweep($params=null, $data=null, $error=null){ $db_hits += ($data->OWNERSHIP) ? count($ownerships) : 0; // 1 issues // Determine total transaction FEE based on database hits - $data->FEE_TICK = 'GAS'; - $data->FEE_AMOUNT = getTransactionFee($db_hits, $data->FEE_TICK); + $fees->AMOUNT = getTransactionFee($db_hits, $fees->TICK); // Verify SOURCE has enough balances to cover FEE AMOUNT - if(!$error && !hasBalance($balances, $data->FEE_TICK, $data->FEE_AMOUNT)) + if(!$error && !hasBalance($balances, $fees->TICK, $fees->AMOUNT)) $error = 'invalid: insufficient funds (FEE)'; // Adjust balances to reduce by FEE amount if(!$error) - $balances = debitBalances($balances, $data->FEE_TICK, $data->FEE_AMOUNT); + $balances = debitBalances($balances, $fees->TICK, $fees->AMOUNT); // Determine final status $data->STATUS = $sweep->STATUS = $status = ($error) ? $error : 'valid'; @@ -111,10 +106,10 @@ function btnsSweep($params=null, $data=null, $error=null){ if($status=='valid'){ // Store the SOURCE and FEE_TICK in addresses and tickers arrays - addAddressTicker($data->SOURCE, $data->FEE_TICK); + addAddressTicker($data->SOURCE, $fees->TICK); - // Debit FEE_AMOUNT from SOURCE address - createDebit('SWEEP', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $data->SOURCE); + // Handle any transaction FEE according the users's ADDRESS preferences + processTransactionFees('SWEEP', $fees); // Transfer Balances if($data->BALANCES==1){ @@ -147,28 +142,6 @@ function btnsSweep($params=null, $data=null, $error=null){ } } - // Update FEES object with to AMOUNT - $fees->AMOUNT = bcadd($fees->AMOUNT, $data->FEE_AMOUNT, 8); - - // Handle using FEE according the the users ADDRESS preferences - if($preferences->FEE_PREFERENCE>1){ - - // Determine what address to donate to - $address = ($preferences->FEE_PREFERENCE==2) ? DONATE_ADDRESS_1 : DONATE_ADDRESS_2; - - // Update the $fees object with the destination address - $fees->DESTINATION = $address; - - // Store the donation ADDRESS and TICK in addresses list - addAddressTicker($address, $data->FEE_TICK); - - // Credit donation address with FEE_AMOUNT - createCredit('SWEEP', $data->BLOCK_INDEX, $data->TX_HASH, $data->FEE_TICK, $data->FEE_AMOUNT, $fees->DESTINATION); - } - - // Create record of FEE in `fees` table - createFeeRecord($fees); - // If this is a reparse, bail out before updating balances and token information if($reparse) return; diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index 6340cb8..80554bf 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -2587,6 +2587,63 @@ function getTicker( $tick_id=null ){ } } - +// Create the basic $fees object used to calculate platform transaction fees +function createFeesObject($data=null){ + // Get SOURCE address preferences + $preferences = getAddressPreferences($data->SOURCE, $data->BLOCK_INDEX, $data->TX_INDEX); + // Copy base BTNS transaction data object into fees object + $fees = clone($data); + $fees->TICK = 'GAS'; + $fees->AMOUNT = 0; + $fees->METHOD = ($preferences->FEE_PREFERENCE==1) ? 1 : 2; // 1=Destroy, 2=Donate + return $fees; +} + +// Process any transaction FEE according the user's ADDRESS preferences +function processTransactionFees($action=null, $fees=null){ + global $credits, $debits; + $immediate = true; + if(in_array($action,array('AIRDROP'))) + $immediate = false; + // Debit FEE from SOURCE + if($immediate){ + createDebit($action, $fees->BLOCK_INDEX, $fees->TX_HASH, $fees->TICK, $fees->AMOUNT, $fees->SOURCE); + } else { + array_push($debits, array($fees->TICK, $fees->AMOUNT)); + } + // Handle using FEE according the the users ADDRESS preferences + if($fees->METHOD>1){ + // Determine what address to donate to + $fees->DESTINATION = ($fees->METHOD==2) ? DONATE_ADDRESS_1 : DONATE_ADDRESS_2; + // Store the donation ADDRESS and TICK in addresses list + addAddressTicker($fees->DESTINATION, $fees->TICK); + // Credit donation address with FEE + if($immediate){ + createCredit($action, $fees->BLOCK_INDEX, $fees->TX_HASH, $fees->TICK, $fees->AMOUNT, $fees->DESTINATION); + } else { + array_push($credits, array($fees->TICK, $fees->AMOUNT, $fees->DESTINATION)); + } + } + // Create record of FEE in `fees` table + createFeeRecord($fees); +} + +// Process any transaction credit/debit records +function processTransactionCreditsDebits($action=null, $data=null){ + global $credits, $debits; + // Consolidate the credit and debit records to write as few records as possible + $debits = consolidateCreditDebitRecords('debits', $debits); + $credits = consolidateCreditDebitRecords('credits', $credits); + // Create records in debits table + foreach($debits as $debit){ + [$tick, $amount] = $debit; + createDebit($action, $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $data->SOURCE); + } + // Create records in credits table + foreach($credits as $credit){ + [$tick, $amount, $destination] = $credit; + createCredit($action, $data->BLOCK_INDEX, $data->TX_HASH, $tick, $amount, $destination); + } +} ?> diff --git a/indexer/indexer.php b/indexer/indexer.php index 93186a6..8dc54a8 100755 --- a/indexer/indexer.php +++ b/indexer/indexer.php @@ -65,6 +65,9 @@ // Define global assoc arrays to track address/ticker changes $addresses = []; $tickers = []; +// Define global assoc arrays to track debits/credits changes +$credits = []; +$debits = []; // Handle rollbacks if($rollback) From bd795d3e3a6bba35a531e9644573662b032222c8 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Sun, 28 Apr 2024 22:22:10 -0700 Subject: [PATCH 07/24] Consolidate sends by DESTINATION and TICK --- indexer/includes/actions/send.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/indexer/includes/actions/send.php b/indexer/includes/actions/send.php index 5ba304e..631b05d 100644 --- a/indexer/includes/actions/send.php +++ b/indexer/includes/actions/send.php @@ -87,6 +87,17 @@ function btnsSend($params=null, $data=null, $error=null){ $ticks[$tick] = getTokenInfo($tick, null, $data->BLOCK_INDEX, $data->TX_INDEX); } + // Consolidate sends by DESTINATION and TICK + $keys = []; + foreach($sends as $info){ + [$tick, $amount, $destination, $memo] = $info; + $key = $destination . '|' . $tick; + if(isset($keys[$key])) + $amount = bcadd($amount, strval($keys[$key][1]), $ticks[$tick]->DECIMALS); + $keys[$key] = array($tick, $amount, $destination, $memo); + } + $sends = array_values($keys); + // Get source address balances $balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); From af6f395b3903333550a071c44704078b173bfa12 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 29 Apr 2024 11:15:40 -0700 Subject: [PATCH 08/24] update`LOCK_CALLBACK` to lock against `CALLBACK` command --- docs/actions/ISSUE.md | 6 +++--- indexer/includes/actions/issue.php | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/actions/ISSUE.md b/docs/actions/ISSUE.md index f73d8e8..f729a06 100644 --- a/docs/actions/ISSUE.md +++ b/docs/actions/ISSUE.md @@ -20,7 +20,7 @@ This command creates or issues a `BTNS` `token` | `LOCK_DESCRIPTION` | String | Lock `token` against `DESCRIPTION` changes | | `LOCK_RUG` | String | Lock `token` against `RUG` command | | `LOCK_SLEEP` | String | Lock `token` against `SLEEP` command | -| `LOCK_CALLBACK` | String | Lock `token` `CALLBACK` info | +| `LOCK_CALLBACK` | String | Lock `token` against `CALLBACK` command | | `CALLBACK_BLOCK` | String | Enable `CALLBACK` command after `CALLBACK_BLOCK` | | `CALLBACK_TICK` | String | `TICK` `token` users get when `CALLBACK` command is used | | `CALLBACK_AMOUNT` | String | `TICK` `token` amount that users get when `CALLBACK` command is used | @@ -119,11 +119,11 @@ This example issues a TEST token with a max supply of 100, and a maximum mint of - `ISSUE` `TICK` with `LOCK_MINT` set to `1` to permanently prevent use of the `MINT` command - `ISSUE` `TICK` with `LOCK_RUG` set to `1` to permanently prevent use of the `RUG` command - `ISSUE` `TICK` with `LOCK_SLEEP` set to `1` to permanently prevent use of the `SLEEP` command -- `ISSUE` `TICK` with `LOCK_CALLBACK` set to `1` to permanently lock `CALLBACK_BLOCK`, `CALLBACK_TICK`, and `CALLBACK_AMOUNT` +- `ISSUE` `TICK` with `LOCK_CALLBACK` set to `1` to permanently prevent use of the `CALLBACK` command - `DESCRIPTION` can contain a URL to a an icon to use for this token (48x48 standard size) - `DESCRIPTION` can contain a URL to a JSON file with additional information - `DESCRIPTION` can NOT contain any pipe `|` or semi-colon `;` characters, as these are reserved -- `CALLBACK_BLOCK`, `CALLBACK_TICK`, and `CALLBACK_AMOUNT` can be edited via `ISSUE` action until `LOCK_CALLBACK` is set to `1` +- `CALLBACK_BLOCK`, `CALLBACK_TICK`, and `CALLBACK_AMOUNT` can be edited via `ISSUE` action if `token` supply is NOT distributed - `DEPLOY` `ACTION` can be used for backwards-compatability with BRC20/SRC20 `DEPLOY` - By default any address can interact with a BTNS token, to change this behavior use `ALLOW_LIST` and `BLOCK_LIST` - If `TICK` contains any unicode characters, then `TICK` should be `base64` encoded diff --git a/indexer/includes/actions/issue.php b/indexer/includes/actions/issue.php index d2392de..a62d102 100644 --- a/indexer/includes/actions/issue.php +++ b/indexer/includes/actions/issue.php @@ -251,6 +251,10 @@ function btnsIssue( $params=null, $data=null, $error=null){ if(!$error && $btInfo && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK > $data->BLOCK_INDEX) $error = 'invalid: CALLBACK_BLOCK (block index)'; + // Verify CALLBACK_BLOCK can not be changed if supply is distributed + if(!$error && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK!=$btnInfo->CALLBACK_BLOCK && $isDistributed) + $error = 'invalid: CALLBACK_BLOCK (supply distributed)'; + // Verify CALLBACK_TICK can not be changed if supply is distributed if(!$error && isset($data->CALLBACK_TICK) && $data->CALLBACK_TICK!=$btnInfo->CALLBACK_TICK && $isDistributed) $error = 'invalid: CALLBACK_TICK (supply distributed)'; From 226184f18e0cc0312b9f63dfe1f22680689bac13 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 29 Apr 2024 11:26:19 -0700 Subject: [PATCH 09/24] cleanup format `4` callback params --- docs/actions/ISSUE.md | 2 +- indexer/includes/actions/issue.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/actions/ISSUE.md b/docs/actions/ISSUE.md index f729a06..a9c4dd6 100644 --- a/docs/actions/ISSUE.md +++ b/docs/actions/ISSUE.md @@ -46,7 +46,7 @@ This command creates or issues a `BTNS` `token` - `VERSION|TICK|LOCK_MAX_SUPPLY|LOCK_MAX_MINT|LOCK_DESCRIPTION|LOCK_RUG|LOCK_SLEEP|LOCK_CALLBACK|LOCK_MINT|LOCK_MINT_SUPPLY` ### Version `4` - Edit `CALLBACK` `PARAMS` -- `VERSION|TICK|LOCK_CALLBACK|CALLBACK_BLOCK|CALLBACK_TICK` +- `VERSION|TICK|CALLBACK_BLOCK|CALLBACK_TICK|CALLBACK_AMOUNT` ### Version `5` - Edit `LIST` `PARAMS` - `VERSION|TICK|ALLOW_LIST|BLOCK_LIST` diff --git a/indexer/includes/actions/issue.php b/indexer/includes/actions/issue.php index a62d102..ca1d83d 100644 --- a/indexer/includes/actions/issue.php +++ b/indexer/includes/actions/issue.php @@ -45,7 +45,7 @@ function btnsIssue( $params=null, $data=null, $error=null){ 1 => 'VERSION|TICK|DESCRIPTION', 2 => 'VERSION|TICK|MAX_MINT|MINT_SUPPLY|TRANSFER_SUPPLY|MINT_ADDRESS_MAX|MINT_START_BLOCK|MINT_STOP_BLOCK', 3 => 'VERSION|TICK|LOCK_MAX_SUPPLY|LOCK_MAX_MINT|LOCK_DESCRIPTION|LOCK_RUG|LOCK_SLEEP|LOCK_CALLBACK|LOCK_MINT|LOCK_MINT_SUPPLY', - 4 => 'VERSION|TICK|LOCK_CALLBACK|CALLBACK_BLOCK|CALLBACK_TICK' + 4 => 'VERSION|TICK|CALLBACK_BLOCK|CALLBACK_TICK|CALLBACK_AMOUNT' ); // Define list of AMOUNT and LOCK fields (used in validations) From fc1e44192e2f7bb83f9b3cd97e6387ab7f9fde3f Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 29 Apr 2024 12:16:41 -0700 Subject: [PATCH 10/24] fix FEE_TICK reference --- indexer/includes/actions/dividend.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indexer/includes/actions/dividend.php b/indexer/includes/actions/dividend.php index 8608404..a0b1456 100644 --- a/indexer/includes/actions/dividend.php +++ b/indexer/includes/actions/dividend.php @@ -146,8 +146,8 @@ function btnsDividend($params=null, $data=null, $error=null){ // If this was a valid transaction, then add records to the credits and debits array if($status=='valid'){ - // Store the SOURCE, DIVIDEND_TICK, and FEE_TICK in addresses list - addAddressTicker($data->SOURCE, [$data->DIVIDEND_TICK, $data->FEE_TICK]); + // Store the SOURCE, DIVIDEND_TICK, and fee TICK in addresses list + addAddressTicker($data->SOURCE, [$data->DIVIDEND_TICK, $fees->TICK]); // Debit DIVIDEND_TOTAL from SOURCE address createDebit('DIVIDEND', $data->BLOCK_INDEX, $data->TX_HASH, $data->DIVIDEND_TICK, $data->DIVIDEND_TOTAL, $data->SOURCE); From 1c31dee7de07a7fad417ee7f78846cfb2699ea88 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 29 Apr 2024 12:18:51 -0700 Subject: [PATCH 11/24] cleanup --- indexer/includes/actions/sweep.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indexer/includes/actions/sweep.php b/indexer/includes/actions/sweep.php index 95042ee..26c5e92 100644 --- a/indexer/includes/actions/sweep.php +++ b/indexer/includes/actions/sweep.php @@ -105,7 +105,7 @@ function btnsSweep($params=null, $data=null, $error=null){ // If this was a valid transaction, then handle the actual sweep actions if($status=='valid'){ - // Store the SOURCE and FEE_TICK in addresses and tickers arrays + // Store the SOURCE and fee TICK in addresses and tickers arrays addAddressTicker($data->SOURCE, $fees->TICK); // Handle any transaction FEE according the users's ADDRESS preferences From 9c85437e4f3aeb2df8e72e4f8992b87a7634ad43 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 29 Apr 2024 13:28:00 -0700 Subject: [PATCH 12/24] add `cleanupHolders()` function --- indexer/includes/actions/dividend.php | 15 +++------------ indexer/includes/functions.php | 12 +++++++++++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/indexer/includes/actions/dividend.php b/indexer/includes/actions/dividend.php index a0b1456..8efefeb 100644 --- a/indexer/includes/actions/dividend.php +++ b/indexer/includes/actions/dividend.php @@ -90,18 +90,9 @@ function btnsDividend($params=null, $data=null, $error=null){ // Remove SOURCE address from the TICK holders list unset($holders[$data->SOURCE]); - // Handle TICK and DIVIDEND_TICK divisibility mismatches by cleaning up the holders list - if(!$error && $divisible!=$divisible2){ - foreach($holders as $addr => $amount){ - - // Convert amount to integer - $holders[$addr] = bcadd($amount,0,0); - - // Remove any 0 quantity records - if($holders[$addr]<1) - unset($holders[$addr]); - } - } + // Handle TICK and DIVIDEND_TICK divisibility mismatches by cleaning up the holders list (only deal with integer amounts) + if(!$error && $divisible!=$divisible2) + $holders = cleanupHolders($holders); // Determine total amount of TICK $data->HOLDER_TOTAL = 0; diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index 80554bf..e1cfc90 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -1623,6 +1623,7 @@ function setActionParams($data=null, $params=null, $format=null){ // @param {tick} string Ticker name // @param {block_index} integer Block Index // @param {tx_index} integer tx_index of transaction +// TODO: Add support for 'escrowed' tokens (dispensers, orders, bets) function getHolders( $tick=null, $block_index=null, $tx_index=null ){ global $mysqli, $dbase; $holders = []; @@ -2518,7 +2519,6 @@ function getAddressOwnership($address=null, $block_index=null, $tx_index=null){ return $data; } - // Create record in `sweeps` table function createSweep( $data=null ){ global $mysqli; @@ -2646,4 +2646,14 @@ function processTransactionCreditsDebits($action=null, $data=null){ } } +// Handle cleaning up the holders list to only deal with integer amounts +function cleanupHolders($holders){ + foreach($holders as $addr => $amount){ + $holders[$addr] = bcadd($amount,0,0); + if($holders[$addr]<1) + unset($holders[$addr]); + } + return $holders; +} + ?> From 1f7e8a5ef64a5667a08487f68ac0fc210887cbb8 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 29 Apr 2024 13:53:24 -0700 Subject: [PATCH 13/24] fix `CALLBACK_BLOCK` verification --- indexer/includes/actions/issue.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/indexer/includes/actions/issue.php b/indexer/includes/actions/issue.php index ca1d83d..377b6e6 100644 --- a/indexer/includes/actions/issue.php +++ b/indexer/includes/actions/issue.php @@ -243,12 +243,8 @@ function btnsIssue( $params=null, $data=null, $error=null){ if(!$error && $btInfo && $btnInfo->LOCK_CALLBACK && isset($data->CALLBACK_AMOUNT) && $data->CALLBACK_AMOUNT!=$btnInfo->CALLBACK_AMOUNT) $error = 'invalid: CALLBACK_AMOUNT (locked)'; - // Verify CALLBACK_BLOCK only increases in value - if(!$error && $btInfo && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK < $btnInfo->CALLBACK_BLOCK) - $error = 'invalid: CALLBACK_BLOCK (decreased)'; - // Verify CALLBACK_BLOCK is greater than current block index - if(!$error && $btInfo && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK > $data->BLOCK_INDEX) + if(!$error && $btInfo && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK < $data->BLOCK_INDEX) $error = 'invalid: CALLBACK_BLOCK (block index)'; // Verify CALLBACK_BLOCK can not be changed if supply is distributed From 15dab845a5ca2f456ca6e0db223a657aec5019e5 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Tue, 14 May 2024 14:16:01 -0400 Subject: [PATCH 14/24] fixed issue with `getTokenInfo()` setting DECIMALS incorrectly --- indexer/includes/functions.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index e1cfc90..c9c06de 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -927,6 +927,7 @@ function getTokenInfo($tick=null, $tick_id=null, $block_index=null, $tx_index=nu 'OWNER' => ($row->transfer) ? $row->transfer : $row->owner, 'MAX_SUPPLY' => $row->max_supply, 'MAX_MINT' => $row->max_mint, + // Force decimal precision to a integer value 'DECIMALS' => (isset($row->decimals)) ? intval($row->decimals) : 0, 'DESCRIPTION' => $row->description, 'LOCK_MAX_SUPPLY' => $row->lock_max_supply, @@ -953,6 +954,9 @@ function getTokenInfo($tick=null, $tick_id=null, $block_index=null, $tx_index=nu if(substr($key,0,5)=='LOCK_') if($data[$key]==1) continue; + // Prevent changing decimal precision + if($key=='DECIMALS' && $data[$key]>$value) + continue; // Skip setting value if value is null or empty (use last explicit value) if(!isset($value) || $value=='') continue; From 5a0f3707b216f9f3c7adbb2c9769cfd92705505e Mon Sep 17 00:00:00 2001 From: J-Dog Date: Sun, 19 May 2024 19:02:05 -0700 Subject: [PATCH 15/24] cleanup / callback checks --- indexer/includes/actions/issue.php | 34 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/indexer/includes/actions/issue.php b/indexer/includes/actions/issue.php index 377b6e6..f670170 100644 --- a/indexer/includes/actions/issue.php +++ b/indexer/includes/actions/issue.php @@ -129,6 +129,10 @@ function btnsIssue( $params=null, $data=null, $error=null){ } } + // Get information on CALLBACK_TICK + if($data->CALLBACK_TICK) + $cbInfo = getTokenInfo($data->CALLBACK_TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX); + /***************************************************************** * FORMAT Validations ****************************************************************/ @@ -136,12 +140,16 @@ function btnsIssue( $params=null, $data=null, $error=null){ // Set divisible first based on if token exist, if not, use DECIMALS in request $divisible = ($data->DECIMALS==0) ? 0 : 1; if($btInfo) - $divisible = ($btInfo->DECIMALS==0) ? 0 : 1; + $divisible = ($btInfo->DECIMALS==0) ? 0 : 1; + + // Set CALLBACK_TICK divisibillity flag + $divisible2 = ($cbInfo->DECIMALS==0) ? 0 : 1; // Verify AMOUNT field formats foreach($fieldList['AMOUNT'] as $name){ $value = $issue->{$name}; - if(!$error && isset($value) && !isValidAmountFormat($divisible, $value)) + $div = ($name=='CALLBACK_AMOUNT') ? $divisible2 : $divisible; + if(!$error && isset($value) && !isValidAmountFormat($div, $value)) $error = "invalid: {$name} (format)"; } @@ -184,7 +192,7 @@ function btnsIssue( $params=null, $data=null, $error=null){ $error = 'invalid: DECIMALS (min/max)'; // Verify DECIMALS cannot be changed after supply has been issued - if(!$error && isset($data->DECIMALS) && $btnInfo->SUPPLY > 0 && $data->DECIMALS!=$btnInfo->DECIMALS) + if(!$error && isset($data->DECIMALS) && $btInfo->SUPPLY > 0 && $data->DECIMALS!=$btInfo->DECIMALS) $error = 'invalid: DECIMALS (locked)'; // Verify TRANSFER addresses @@ -216,11 +224,11 @@ function btnsIssue( $params=null, $data=null, $error=null){ $error = 'invalid: MINT_ADDRESS_MAX < MAX_MINT'; // Verify MAX_SUPPLY can not be changed if LOCK_MAX_SUPPLY is enabled - if(!$error && $btInfo && $btnInfo->LOCK_MAX_SUPPLY && isset($data->MAX_SUPPLY) && $data->MAX_SUPPLY!=$btnInfo->MAX_SUPPLY) + if(!$error && $btInfo && $btInfo->LOCK_MAX_SUPPLY && isset($data->MAX_SUPPLY) && $data->MAX_SUPPLY!=$btInfo->MAX_SUPPLY) $error = 'invalid: MAX_SUPPLY (locked)'; // Verify MAX_MINT can not be changed if LOCK_MAX_MINT is enabled - if(!$error && $btInfo && $btnInfo->LOCK_MAX_MINT && isset($data->MAX_MINT) && $data->MAX_MINT!=$btnInfo->MAX_MINT) + if(!$error && $btInfo && $btInfo->LOCK_MAX_MINT && isset($data->MAX_MINT) && $data->MAX_MINT!=$btInfo->MAX_MINT) $error = 'invalid: MAX_MINT (locked)'; // Verify DESCRIPTION is less than or equal to MAX_TOKEN_DESCRIPTION @@ -228,35 +236,35 @@ function btnsIssue( $params=null, $data=null, $error=null){ $error = 'invalid: DESCRIPTION (length)'; // Verify DESCRIPTION can not be changed if LOCK_DESCRIPTION is enabled - if(!$error && $btInfo && $btnInfo->LOCK_DESCRIPTION && isset($data->DESCRIPTION) && $data->DESCRIPTION!=$btnInfo->DESCRIPTION) + if(!$error && $btInfo && $btInfo->LOCK_DESCRIPTION && isset($data->DESCRIPTION) && $data->DESCRIPTION!=$btInfo->DESCRIPTION) $error = 'invalid: DESCRIPTION (locked)'; // Verify CALLBACK_BLOCK can not be changed if LOCK_CALLBACK is enabled - if(!$error && $btInfo && $btnInfo->LOCK_CALLBACK && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK!=$btnInfo->CALLBACK_BLOCK) + if(!$error && $btInfo && $btInfo->LOCK_CALLBACK && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK!=$btInfo->CALLBACK_BLOCK) $error = 'invalid: CALLBACK_BLOCK (locked)'; // Verify CALLBACK_TICK can not be changed if LOCK_CALLBACK is enabled - if(!$error && $btInfo && $btnInfo->LOCK_CALLBACK && isset($data->CALLBACK_TICK) && $data->CALLBACK_TICK!=$btnInfo->CALLBACK_TICK) + if(!$error && $btInfo && $btInfo->LOCK_CALLBACK && isset($data->CALLBACK_TICK) && $data->CALLBACK_TICK!=$btInfo->CALLBACK_TICK) $error = 'invalid: CALLBACK_TICK (locked)'; // Verify CALLBACK_TICK can not be changed if LOCK_CALLBACK is enabled - if(!$error && $btInfo && $btnInfo->LOCK_CALLBACK && isset($data->CALLBACK_AMOUNT) && $data->CALLBACK_AMOUNT!=$btnInfo->CALLBACK_AMOUNT) + if(!$error && $btInfo && $btInfo->LOCK_CALLBACK && isset($data->CALLBACK_AMOUNT) && $data->CALLBACK_AMOUNT!=$btInfo->CALLBACK_AMOUNT) $error = 'invalid: CALLBACK_AMOUNT (locked)'; // Verify CALLBACK_BLOCK is greater than current block index - if(!$error && $btInfo && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK < $data->BLOCK_INDEX) + if(!$error && $btInfo && isset($issue->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK < $data->BLOCK_INDEX) $error = 'invalid: CALLBACK_BLOCK (block index)'; // Verify CALLBACK_BLOCK can not be changed if supply is distributed - if(!$error && isset($data->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK!=$btnInfo->CALLBACK_BLOCK && $isDistributed) + if(!$error && isset($issue->CALLBACK_BLOCK) && $data->CALLBACK_BLOCK!=$btInfo->CALLBACK_BLOCK && $isDistributed) $error = 'invalid: CALLBACK_BLOCK (supply distributed)'; // Verify CALLBACK_TICK can not be changed if supply is distributed - if(!$error && isset($data->CALLBACK_TICK) && $data->CALLBACK_TICK!=$btnInfo->CALLBACK_TICK && $isDistributed) + if(!$error && isset($issue->CALLBACK_TICK) && $data->CALLBACK_TICK!=$btInfo->CALLBACK_TICK && $isDistributed) $error = 'invalid: CALLBACK_TICK (supply distributed)'; // Verify CALLBACK_AMOUNT can not be changed if supply is distributed - if(!$error && isset($data->CALLBACK_AMOUNT) && $data->CALLBACK_AMOUNT!=$btnInfo->CALLBACK_AMOUNT && $isDistributed) + if(!$error && isset($issue->CALLBACK_AMOUNT) && $data->CALLBACK_AMOUNT!=$btInfo->CALLBACK_AMOUNT && $isDistributed) $error = 'invalid: CALLBACK_AMOUNT (supply distributed)'; // Verify ALLOW_LIST is a valid list of addresses From c895d78d0dee86aa205e6113bfa3fa7ee553a8d0 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Sun, 19 May 2024 19:30:01 -0700 Subject: [PATCH 16/24] cleanup callback_amount --- indexer/includes/actions/issue.php | 2 +- indexer/sql/tokens.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indexer/includes/actions/issue.php b/indexer/includes/actions/issue.php index f670170..e6f8f51 100644 --- a/indexer/includes/actions/issue.php +++ b/indexer/includes/actions/issue.php @@ -143,7 +143,7 @@ function btnsIssue( $params=null, $data=null, $error=null){ $divisible = ($btInfo->DECIMALS==0) ? 0 : 1; // Set CALLBACK_TICK divisibillity flag - $divisible2 = ($cbInfo->DECIMALS==0) ? 0 : 1; + $divisible2 = ($cbInfo && $cbInfo->DECIMALS>0) ? 1 : 0; // Verify AMOUNT field formats foreach($fieldList['AMOUNT'] as $name){ diff --git a/indexer/sql/tokens.sql b/indexer/sql/tokens.sql index 9ee27ae..6c72dd6 100644 --- a/indexer/sql/tokens.sql +++ b/indexer/sql/tokens.sql @@ -18,7 +18,7 @@ CREATE TABLE tokens ( lock_callback TINYINT(1) NOT NULL DEFAULT 0, -- Locks CALLBACK_BLOCK/TICK/AMOUNT callback_block INTEGER UNSIGNED, -- block_index after which CALLBACK cand be used callback_tick_id INTEGER UNSIGNED, -- id of record in index_tickers table - callback_amount BIGINT UNSIGNED, -- AMOUNT users get if CALLBACK + callback_amount VARCHAR(250), -- AMOUNT users get if CALLBACK allow_list_id INTEGER UNSIGNED NOT NULL default 0, -- id of record in index_transactions table block_list_id INTEGER UNSIGNED NOT NULL default 0, -- id of record in index_transactions table mint_address_max VARCHAR(250), -- Maximum amount of supply an address can MINT From 2e8a361c86841b0ee74c64de217c7b87f0c7ec75 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Sun, 19 May 2024 19:31:10 -0700 Subject: [PATCH 17/24] fix issue with forcing callback_amount to wrong divisibility --- indexer/includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index c9c06de..6b7adba 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -477,7 +477,7 @@ function createToken( $data=null ){ $max_mint = bcmul($max_mint,1,$decimals); $mint_supply = bcmul($mint_supply,1,$decimals); $mint_address_max = bcmul($mint_address_max,1,$decimals); - $callback_amount = bcmul($callback_amount,1,$decimals); + // $callback_amount = bcmul($callback_amount,1,$decimals); } $supply = $mysqli->real_escape_string($supply); $max_supply = $mysqli->real_escape_string($max_supply); From 553beeee861a4ede79882bc980c8dace53922f76 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 20 May 2024 12:26:13 -0700 Subject: [PATCH 18/24] add basic `CALLBACK` support todo: decimal precision still needs some work --- docs/actions/CALLBACK.md | 3 +- indexer/includes/actions/callback.php | 197 +++++++++++++++++++++++++- indexer/includes/functions.php | 54 +++++++ indexer/includes/protocol_changes.php | 2 +- indexer/sql/callbacks.sql | 22 +++ 5 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 indexer/sql/callbacks.sql diff --git a/docs/actions/CALLBACK.md b/docs/actions/CALLBACK.md index acc0a24..bdbc635 100644 --- a/docs/actions/CALLBACK.md +++ b/docs/actions/CALLBACK.md @@ -6,11 +6,12 @@ This command performs a callback on a `token`. | --------- | ------ | ----------------------------- | | `VERSION` | String | Broadcast Format Version | | `TICK` | String | 1 to 250 characters in length | +| `MEMO` | String | An optional memo to include | ## Formats ### Version `0` -- `VERSION|TICK` +- `VERSION|TICK|MEMO` ## Examples ``` diff --git a/indexer/includes/actions/callback.php b/indexer/includes/actions/callback.php index 37e6248..a9a19ad 100644 --- a/indexer/includes/actions/callback.php +++ b/indexer/includes/actions/callback.php @@ -5,13 +5,204 @@ * PARAMS: * - VERSION - Broadcast Format Version * - TICK - 1 to 250 characters in length + * - MEMO - An optional memo to include * * FORMATS: - * 0 = VERSION|TICK + * 0 = VERSION|TICK|MEMO ********************************************************************/ function btnsCallback($params=null, $data=null, $error=null){ - global $mysqli, $reparse; - // Coming soon + global $mysqli, $reparse, $addresses, $tickers; + + // Define list of known FORMATS + $formats = array( + 0 => 'VERSION|TICK|MEMO' + ); + + /***************************************************************** + * DEBUGGING - Force params + ****************************************************************/ + // $str = "0|JDOG|1|"; + // $params = explode('|',$str); + + // Validate that broadcast format is known + $format = getFormatVersion($params[0]); + if(!$error && ($format===NULL || !in_array($format,array_keys($formats)))) + $error = 'invalid: VERSION (unknown)'; + + // Parse PARAMS using given VERSION format and update BTNS transaction data object + if(!$error) + $data = setActionParams($data, $params, $formats[$format]); + + // Get information on BTNS token + $btInfo = getTokenInfo($data->TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX); + + // Clone the raw data for storage in callbacks table + $callback = clone($data); + + // Create the fees object + $fees = createFeesObject($data); + + // Define placeholders for holders and balances + $holder = []; + $balances = []; + + // Get list of TICK holders and SOURCE address balances + if(!$error){ + $holders = getHolders($data->TICK, $data->BLOCK_INDEX, $data->TX_INDEX); + $balances = getAddressBalances($data->SOURCE, null, $data->BLOCK_INDEX, $data->TX_INDEX); + } + + // Validate TICK exists + if(!$error && !$btInfo) + $error = 'invalid: TICK (unknown)'; + + // Verify only token OWNER can do CALLBACK + if(!$error && $data->SOURCE!=$btInfo->OWNER) + $error = 'invalid: SOURCE (not authorized)'; + + // Get information on CALLBACK_TICK token + if(!$error && isset($btInfo->CALLBACK_TICK)) + $btInfo2 = getTokenInfo($btInfo->CALLBACK_TICK, null, $data->BLOCK_INDEX, $data->TX_INDEX); + + // Validate CALLBACK_TICK exists + if(!$error && !$btInfo2) + $error = 'invalid: CALLBACK_TICK (unknown)'; + + // Set divisible flags + $divisible = ($btInfo->DECIMALS==0) ? 0 : 1; + $divisible2 = ($btInfo2->DECIMALS==0) ? 0 : 1; + + // Populate callback object with callback data + if($btInfo){ + $callback->CALLBACK_TICK = $btInfo->CALLBACK_TICK; + $callback->CALLBACK_AMOUNT = $btInfo->CALLBACK_AMOUNT; + } + + /***************************************************************** + * ACTION Validations + ****************************************************************/ + + // Verify CALLBACK is allowed + if(!$error && isset($btInfo->LOCK_CALLBACK) && $btInfo->LOCK_CALLBACK==1) + $error = "invalid: LOCK_CALLBACK"; + + /***************************************************************** + * FORMAT Validations + ****************************************************************/ + + // Verify CALLBACK_BLOCK format + if(!$error && $btInfo && isset($btInfo->CALLBACK_BLOCK) && $btInfo->CALLBACK_BLOCK!=intval($btInfo->CALLBACK_BLOCK)) + $error = 'invalid: CALLBACK_BLOCK (format)'; + + // Verify CALLBACK_AMOUNT format + if(!$error && isset($btInfo->CALLBACK_AMOUNT) && !isValidAmountFormat($divisible2, $btInfo->CALLBACK_AMOUNT)) + $error = "invalid: CALLBACK_AMOUNT (format)"; + + /***************************************************************** + * General Validations + ****************************************************************/ + + // Verify CALLBACK_BLOCK is less than or equal to current block index + if(!$error && $btInfo && isset($btInfo->CALLBACK_BLOCK) && $btInfo->CALLBACK_BLOCK > $data->BLOCK_INDEX) + $error = 'invalid: CALLBACK_BLOCK (block index)'; + + // Loop through holders and determine CALLBACK_TOTAL + $total = (object)[]; + $total->TICK = 0; + $total->CALLBACK_TICK = 0; + foreach($holders as $address => $amount){ + // Skip including SOURCE address in total calculations + if($address==$data->SOURCE) + continue; + $callback_amount = bcmul($amount, $btInfo->CALLBACK_AMOUNT, $btInfo2->DECIMALS); + $total->TICK = bcadd($amount, $total->TICK, $btInfo->DECIMALS); + $total->CALLBACK_TICK = bcadd($callback_amount, $total->CALLBACK_TICK, $btInfo2->DECIMALS); + } + + // Verify SOURCE has enough balances to cover total callback amount + if(!$error && !hasBalance($balances, $btInfo->CALLBACK_TICK, $total->CALLBACK_TICK)) + $error = 'invalid: insufficient funds (CALLBACK_TICK)'; + + // Adjust balances to reduce by callback total + if(!$error) + $balances = debitBalances($balances, $btInfo->CALLBACK_TICK, $data->CALLBACK_TICK); + + // Calculate total number of database hits for this CALLBACK + $db_hits = count($holders) * 3; // 1 debits, 1 credits, 1 balances + $db_hits += 4; // 1 debits, 1 credits, 1 balances, 1 callback + + // Determine total transaction FEE based on database hits + $fees->AMOUNT = getTransactionFee($db_hits, $fees->TICK); + + // Verify SOURCE has enough balances to cover FEE AMOUNT + if(!$error && !hasBalance($balances, $fees->TICK, $fees->AMOUNT)) + $error = 'invalid: insufficient funds (FEE)'; + + // Adjust balances to reduce by FEE amount + if(!$error) + $balances = debitBalances($balances, $fees->TICK, $fees->AMOUNT); + + // Determine final status + $data->STATUS = $callback->STATUS = $status = ($error) ? $error : 'valid'; + + // Print status message + print "\n\t CALLBACK : {$data->TICK} : {$data->STATUS}"; + + // Create record in callbacks table + createCallback($callback); + + // If this was a valid transaction, then add records to the credits and debits array + if($status=='valid'){ + + // TODO: update to support dispensers (close dispenser) + + // Store the SOURCE, TICK, CALLBACK_TICK, and fee TICK in addresses list + addAddressTicker($data->SOURCE, [$data->TICK, $btInfo->CALLBACK_TICK, $fees->TICK]); + + // Debit CALLBACK_TOTAL from SOURCE address + createDebit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $btInfo->CALLBACK_TICK, $total->CALLBACK_TICK, $data->SOURCE); + + // Credit all TICK SUPPLY to SOURCE address + createCredit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $btInfo->TICK, $total->TICK, $data->SOURCE); + + // Handle any transaction FEE according the users's ADDRESS preferences + processTransactionFees('CALLBACK', $fees); + + // Loop through TICK holders + // TODO: Decimal precision needs a bit more work... + foreach($holders as $address => $amount){ + $callback_amount = bcmul($amount, $btInfo->CALLBACK_AMOUNT, $btInfo2->DECIMALS); + + // Skip including SOURCE address in credits/debits (already ) + if($address==$data->SOURCE) + continue; + + // Debit TICK from holders address + createDebit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $data->TICK, $amount, $address); + + // Credit CALLBACK_TICK to holders address except SOURCE + if($callback_amount>0) + createCredit('CALLBACK', $data->BLOCK_INDEX, $data->TX_HASH, $btInfo->CALLBACK_TICK, $callback_amount, $address); + + // Store the recipient ADDRESS, TICK, and CALLBACK_TICK in addresses list + addAddressTicker($address, [$data->TICK, $btInfo->CALLBACK_TICK]); + } + + } + + // If this is a reparse, bail out before updating balances + if($reparse) + return; + + // Update address balances + updateBalances(array_keys($addresses)); + + // Update supply for GAS (no other supply should change) + updateTokens($fees->TICK); + + // TODO: Remove this when decimal precision is perfect better + updateTokens($tickers); + } ?> \ No newline at end of file diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index 6b7adba..d99a952 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -1386,6 +1386,9 @@ function getTokenSupply( $tick=null, $tick_id=null, $block_index=null, $tx_index byeLog('Error while trying to get list of debits'); } $supply = bcsub($credits, $debits, $decimals); + // print "credits={$credits}\n"; + // print "debits={$debits}\n"; + // print "supply={$supply}\n"; return $supply; } @@ -1858,6 +1861,10 @@ function sanityCheck( $block=null ){ $supplyA = $supply[$tick]; // Supply from tokens table $supplyB = getTokenSupplyBalance($tick); // Supply from balances table $supplyC = getTokenSupply($tick); // Supply from credits/debits tables + // print "\nTick={$tick} tick_id={$tick_id}\n"; + // print "token supply = {$supplyA}\n"; + // print "balances supply = {$supplyB}\n"; + // print "credit/debit supply = {$supplyC}\n"; if($supplyA!=$supplyB) byeLog("SanityError: balances table supply does not match token supply : {$tick} ({$supplyB} != {$supplyA})"); if($supplyA!=$supplyC) @@ -2660,4 +2667,51 @@ function cleanupHolders($holders){ return $holders; } +// Create record in `callbacks` table +function createCallback( $data=null ){ + global $mysqli; + $tick_id = createTicker($data->TICK); + $callback_tick_id = createTicker($data->CALLBACK_TICK); + $source_id = createAddress($data->SOURCE); + $tx_hash_id = createTransaction($data->TX_HASH); + $memo_id = createMemo($data->MEMO); + $status_id = createStatus($data->STATUS); + $tx_index = $mysqli->real_escape_string($data->TX_INDEX); + $block_index = $mysqli->real_escape_string($data->BLOCK_INDEX); + $callback_amount = $mysqli->real_escape_string($data->CALLBACK_AMOUNT); + // Check if record already exists + $sql = "SELECT + tx_index + FROM + callbacks + WHERE + source_id='{$source_id}' AND + tx_hash_id='{$tx_hash_id}'"; + $results = $mysqli->query($sql); + if($results){ + if($results->num_rows){ + // UPDATE record + $sql = "UPDATE + callbacks + SET + tx_index='{$tx_index}', + block_index='{$block_index}', + tick_id='{$tick_id}', + memo_id='{$memo_id}', + status_id='{$status_id}' + WHERE + source_id='{$source_id}' AND + tx_hash_id='{$tx_hash_id}'"; + } else { + // INSERT record + $sql = "INSERT INTO callbacks (tx_index, source_id, tick_id, callback_tick_id, callback_amount, memo_id, tx_hash_id, block_index, status_id) values ('{$tx_index}', '{$source_id}', '{$tick_id}', '{$callback_tick_id}', '{$callback_amount}', '{$memo_id}', '{$tx_hash_id}', '{$block_index}', '{$status_id}')"; + } + $results = $mysqli->query($sql); + if(!$results) + byeLog('Error while trying to create / update a record in the callbacks table'); + } else { + byeLog('Error while trying to lookup record in callbacks table'); + } +} + ?> diff --git a/indexer/includes/protocol_changes.php b/indexer/includes/protocol_changes.php index c09e1dc..63dcb27 100644 --- a/indexer/includes/protocol_changes.php +++ b/indexer/includes/protocol_changes.php @@ -15,7 +15,7 @@ 'AIRDROP' => array(0, 10, 0, 789742, 2581842), 'BATCH' => array(0, 10, 0, 789742, 2581531), 'BET' => array(0, 10, 0, 9999999, 999999999), - 'CALLBACK' => array(0, 10, 0, 9999999, 999999999), + 'CALLBACK' => array(0, 10, 0, 9999999, 2473585), 'DESTROY' => array(0, 10, 0, 789742, 2473585), 'DISPENSER' => array(0, 10, 0, 9999999, 999999999), 'DIVIDEND' => array(0, 10, 0, 789742, 2473585), diff --git a/indexer/sql/callbacks.sql b/indexer/sql/callbacks.sql new file mode 100644 index 0000000..20bdcb7 --- /dev/null +++ b/indexer/sql/callbacks.sql @@ -0,0 +1,22 @@ +DROP TABLE IF EXISTS callbacks; +CREATE TABLE callbacks ( + tx_index INTEGER UNSIGNED, -- Unique transaction index + tx_hash_id INTEGER UNSIGNED, -- id of record in index_transactions + block_index INTEGER UNSIGNED, -- block index of CALLBACKS transaction + tick_id INTEGER UNSIGNED, -- id of record in index_tickers + callback_tick_id INTEGER UNSIGNED, -- id of record in index_tickers + callback_amount VARCHAR(250), -- Amount of token per unit + source_id INTEGER UNSIGNED, -- id of record in index_addresses table + memo_id INTEGER UNSIGNED, -- id of record in index_memos table + status_id INTEGER UNSIGNED -- id of record in index_statuses table +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE INDEX tx_index ON callbacks (tx_index); +CREATE INDEX source_id ON callbacks (source_id); +CREATE INDEX tick_id ON callbacks (tick_id); +CREATE INDEX callback_tick_id ON callbacks (callback_tick_id); +CREATE INDEX tx_hash_id ON callbacks (tx_hash_id); +CREATE INDEX block_index ON callbacks (block_index); +CREATE INDEX memo_id ON callbacks (memo_id); +CREATE INDEX status_id ON callbacks (status_id); + From 34bd107b982d5365bc8dd9af8d61622d15b43f71 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Mon, 20 May 2024 12:27:20 -0700 Subject: [PATCH 19/24] support rollbacks for `callbacks`, `dividends`, and `sweeps` tables --- indexer/includes/rollback.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indexer/includes/rollback.php b/indexer/includes/rollback.php index a83654e..38deefb 100644 --- a/indexer/includes/rollback.php +++ b/indexer/includes/rollback.php @@ -12,14 +12,17 @@ function btnsRollback($block_index=null){ 'addresses', 'batches', 'blocks', + 'callbacks', 'credits', 'debits', 'destroys', + 'dividends', 'fees', 'issues', 'lists', 'mints', 'sends', + 'sweeps', 'tokens', 'transactions' ]; From b2716f56343661686566928469b867f8223e4b16 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Wed, 22 May 2024 12:04:00 -0700 Subject: [PATCH 20/24] add support for `MEMO` in `MINT` action --- docs/actions/MINT.md | 3 ++- indexer/includes/actions/mint.php | 10 +++++++++- indexer/includes/functions.php | 4 +++- indexer/sql/mints.sql | 2 ++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/actions/MINT.md b/docs/actions/MINT.md index 1962124..f723a33 100644 --- a/docs/actions/MINT.md +++ b/docs/actions/MINT.md @@ -8,11 +8,12 @@ This command mints `BTNS` `token` supply | `TICK` | String | 1 to 250 characters in length | | `AMOUNT` | String | Amount of `tokens` to mint | | `DESTINATION` | String | Address to transfer minted `tokens` to | +| `MEMO` | String | An optional memo to include | ## Formats ### Version `0` -- `VERSION|TICK|AMOUNT|DESTINATION` +- `VERSION|TICK|AMOUNT|DESTINATION|MEMO` ## Examples ``` diff --git a/indexer/includes/actions/mint.php b/indexer/includes/actions/mint.php index 106d865..6bfc807 100644 --- a/indexer/includes/actions/mint.php +++ b/indexer/includes/actions/mint.php @@ -17,7 +17,7 @@ function btnsMint($params=null, $data=null, $error=null){ // Define list of known FORMATS $formats = array( - 0 => 'VERSION|TICK|AMOUNT|DESTINATION' + 0 => 'VERSION|TICK|AMOUNT|DESTINATION|MEMO' ); /***************************************************************** @@ -91,6 +91,14 @@ function btnsMint($params=null, $data=null, $error=null){ * General Validations ****************************************************************/ + // Verify no pipe in MEMO (BTNS uses pipe as field delimiter) + if(!$error && isset($data->MEMO) && strpos($data->MEMO,'|')!==false) + $error = 'invalid: MEMO (pipe)'; + + // Verify no semicolon in MEMO (BTNS uses semicolon as action delimiter) + if(!$error && isset($data->MEMO) && strpos($data->MEMO,';')!==false) + $error = 'invalid: MEMO (semicolon)'; + // Verify AMOUNT is less than MAX_MINT if(!$error && isset($data->AMOUNT) && $data->AMOUNT > $data->MAX_MINT) $error = 'invalid: AMOUNT > MAX_MINT'; diff --git a/indexer/includes/functions.php b/indexer/includes/functions.php index d99a952..85cceea 100644 --- a/indexer/includes/functions.php +++ b/indexer/includes/functions.php @@ -321,6 +321,7 @@ function createMint( $data=null ){ $source_id = createAddress($data->SOURCE); $destination_id = createAddress($data->DESTINATION); $tx_hash_id = createTransaction($data->TX_HASH); + $memo_id = createMemo($data->MEMO); $status_id = createStatus($data->STATUS); $tx_index = $mysqli->real_escape_string($data->TX_INDEX); $amount = $mysqli->real_escape_string($data->AMOUNT); @@ -340,12 +341,13 @@ function createMint( $data=null ){ source_id='{$source_id}', block_index='{$block_index}', tx_index='{$tx_index}', + memo_id='{$memo_id}', status_id='{$status_id}' WHERE tx_hash_id='{$tx_hash_id}'"; } else { // INSERT record - $sql = "INSERT INTO mints (tx_index, tick_id, amount, source_id, destination_id, tx_hash_id, block_index, status_id) values ('{$tx_index}','{$tick_id}', '{$amount}', '{$source_id}', '{$destination_id}', '{$tx_hash_id}', '{$block_index}', '{$status_id}')"; + $sql = "INSERT INTO mints (tx_index, tick_id, amount, source_id, destination_id, tx_hash_id, block_index, memo_id, status_id) values ('{$tx_index}','{$tick_id}', '{$amount}', '{$source_id}', '{$destination_id}', '{$tx_hash_id}', '{$block_index}', '{$memo_id}', '{$status_id}')"; } $results = $mysqli->query($sql); if(!$results) diff --git a/indexer/sql/mints.sql b/indexer/sql/mints.sql index 8a3ce76..65880e0 100644 --- a/indexer/sql/mints.sql +++ b/indexer/sql/mints.sql @@ -7,6 +7,7 @@ CREATE TABLE mints ( destination_id INTEGER UNSIGNED, -- id of record in index_addresses table (optional, mint and transfer) tx_hash_id INTEGER UNSIGNED, -- id of record in index_transactions block_index INTEGER UNSIGNED, -- block index of MINT transaction + memo_id INTEGER UNSIGNED, -- id of record in index_memos table status_id INTEGER UNSIGNED -- id of record in index_statuses table ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; @@ -15,4 +16,5 @@ CREATE INDEX tick_id ON mints (tick_id); CREATE INDEX tx_hash_id ON mints (tx_hash_id); CREATE INDEX source_id ON mints (source_id); CREATE INDEX destination_id ON mints (destination_id); +CREATE INDEX memo_id ON mints (memo_id); CREATE INDEX status_id ON mints (status_id); From cb1c6f320c1a99da44b1f577b7c4f491b050d983 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Wed, 22 May 2024 12:07:28 -0700 Subject: [PATCH 21/24] deactivate `CALLBACK` on testnet for now --- indexer/includes/protocol_changes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indexer/includes/protocol_changes.php b/indexer/includes/protocol_changes.php index 63dcb27..c09e1dc 100644 --- a/indexer/includes/protocol_changes.php +++ b/indexer/includes/protocol_changes.php @@ -15,7 +15,7 @@ 'AIRDROP' => array(0, 10, 0, 789742, 2581842), 'BATCH' => array(0, 10, 0, 789742, 2581531), 'BET' => array(0, 10, 0, 9999999, 999999999), - 'CALLBACK' => array(0, 10, 0, 9999999, 2473585), + 'CALLBACK' => array(0, 10, 0, 9999999, 999999999), 'DESTROY' => array(0, 10, 0, 789742, 2473585), 'DISPENSER' => array(0, 10, 0, 9999999, 999999999), 'DIVIDEND' => array(0, 10, 0, 789742, 2473585), From 6d71b517252f9397cf903e44e6a021f87f13d9bc Mon Sep 17 00:00:00 2001 From: J-Dog Date: Wed, 22 May 2024 12:08:36 -0700 Subject: [PATCH 22/24] refined `RUG` spec to include various options --- docs/actions/RUG.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/actions/RUG.md b/docs/actions/RUG.md index 1d31340..1532c66 100644 --- a/docs/actions/RUG.md +++ b/docs/actions/RUG.md @@ -1,11 +1,18 @@ # RUG command -This command performs a rug pull on a `token` +This command performs a rug pull on a `token` using various methods ## PARAMS -| Name | Type | Description | -| --------- | ------ | ----------------------------- | -| `VERSION` | String | Broadcast Format Version | -| `TICK` | String | 1 to 250 characters in length | +| Name | Type | Description | +| -------------------- | ------ | ---------------------------------------------------------------- | +| `VERSION` | String | Broadcast Format Version | +| `TICK` | String | 1 to 250 characters in length | +| `MINT_MAX_SUPPLY` | String | Mint `token` supply to `MAX_SUPPLY` | +| `SLEEP_FOREVER` | String | `SLEEP` token actions permanently (`SLEEP` with `value` set to `-1`) | +| `CLOSE_DISPENSERS` | String | Close any open dispensers immediately | +| `RECALL_SUPPLY` | String | Recall all `token` balances to `SOURCE` address | +| `BURN_OWNERSHIP` | String | Burn `token` ownership permanently (send to `BURN_ADDRESSS`) | +| `BURN_SUPPLY` | String | Burn any `token` supply in `SOURCE` address (send to `BURN_ADDRESSS`) | +| `DESTROY_SUPPLY` | String | Destroy any `token` supply in `SOURCE` address | ## Formats @@ -19,11 +26,8 @@ This example does a rugpull on the BRRR `token` ``` ## Rules -- Mints `token` supply up to `MAX_SUPPLY` -- Locks `MAX_SUPPLY` via `LOCK_SUPPLY` -- Locks `MAX_MINT` via `LOCK_MINT` -- Cancels all future BTNS `ACTION` commands for `token` -- Transfers `token` ownership to burn address -- Destroys all `TICK` `token` supply (including `tokens` held in addresses) -## Notes \ No newline at end of file + +## Notes +- This is a _HIGHLY DANGEROUS_ function, built for entertainment purposes only! +- `ISSUE` `TICK` with `LOCK_RUG` set to `1` to permanently prevent use of the `RUG` command on a `token` \ No newline at end of file From 4da941bf54e8b77e5877984be2b5b5b71e2a7da9 Mon Sep 17 00:00:00 2001 From: J-Dog Date: Wed, 22 May 2024 12:09:05 -0700 Subject: [PATCH 23/24] cleaned up `SLEEP` spec and added `MEMO` param --- docs/actions/SLEEP.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/actions/SLEEP.md b/docs/actions/SLEEP.md index 872b3e0..269f6d7 100644 --- a/docs/actions/SLEEP.md +++ b/docs/actions/SLEEP.md @@ -7,6 +7,7 @@ This command pauses all `token` `ACTIONS` until `RESUME_BLOCK` is reached | `VERSION` | String | Broadcast Format Version | | `TICK` | String | 1 to 250 characters in length | | `RESUME_BLOCK` | String | Block index to resume BTNS `ACTION` commands | +| `MEMO` | String | An optional memo to include | ## Formats @@ -22,7 +23,18 @@ This example pauses/sleeps ALL BTNS `ACTION` commands on JDOG `token` until bloc ## Rules ## Notes -- USE WITH CAUTION! `SLEEP` will stop/pause all BTNS actions, including dispenses. -- `SLEEP` can result in loss of user funds, as payments to BTNS dispensers will be ignored. -- Can use `LOCK_SLEEP` in `ISSUE` command to prevent `SLEEP` command -- Can issue a `SLEEP` before `RESUME_BLOCK` to extend a `SLEEP` \ No newline at end of file +- `SLEEP` does _NOT_ prevent `DISPENSER` dispenses, as that could result in a loss of user funds. +- `SLEEP` does _NOT_ prevent usage of the `SLEEP` command +- `SLEEP` with `RESUME_BLOCK` set to `0` value, will unpause actions immediately. +- `SLEEP` with `RESUME_BLOCK` set to `-1` value, will pause actions indefinitely. +- `ISSUE` `TICK` with `LOCK_SLEEP` set to `1` to permanently prevent use of the `SLEEP` command +- Can use `BATCH` commands to stop `SLEEP`, execute `ACTION` commands, and then resume `SLEEP`, etc. +` +bt:BATCH|0| +SLEEP|0|JDOG|0; +ISSUE|1|JDOG|We are working to resolve the problem; +ISSUE|2|JDOG||1000; +SEND|0|JDOG|1000|1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev|Funding contract address; +MINT|0|JDOG||1000|1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev; +SLEEP|0|JDOG|-1 +` From 55aa08c9a64f9955eb3e1acf134e96bee50ea37a Mon Sep 17 00:00:00 2001 From: J-Dog Date: Wed, 22 May 2024 12:27:29 -0700 Subject: [PATCH 24/24] version bump to `0.14.0` --- indexer/CHANGELOG.md | 9 +++++++++ indexer/includes/config.php | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/indexer/CHANGELOG.md b/indexer/CHANGELOG.md index a48f59b..629b3a0 100644 --- a/indexer/CHANGELOG.md +++ b/indexer/CHANGELOG.md @@ -1,5 +1,14 @@ CHANGELOG --- +0.14.0 +- `DIVIDEND` support +- `SWEEP` support +- `CALLBACK` support (disabled for now) +- fixed issue with `getTokenInfo()` setting `DECIMALS` incorrectly +- Added support for `MEMO` in `MINT` action +- Cleanup transaction fees code +- Updated `SEND` to consolidate sends by DESTINATION and TICK + 0.13.0 - Added support for `--compare=DB` - start using `—block=#` instead of `—reparse=#` diff --git a/indexer/includes/config.php b/indexer/includes/config.php index 2326191..b39af6a 100644 --- a/indexer/includes/config.php +++ b/indexer/includes/config.php @@ -9,7 +9,7 @@ // BTNS Indexer Version define("VERSION_MAJOR", 0); -define("VERSION_MINOR", 13); +define("VERSION_MINOR", 14); define("VERSION_REVISION",0); define("VERSION_STRING", VERSION_MAJOR . '.' . VERSION_MINOR . '.' . VERSION_REVISION);