From a5b57846089e141c6d205d0037369eb49227caa7 Mon Sep 17 00:00:00 2001 From: Nick Furfaro Date: Wed, 2 Feb 2022 15:33:45 -0700 Subject: [PATCH] Adding the transfer_to_output function to token library (#32) * Add force_transfer function to token lib * Add import of ContractID type * Add transfer_to_output function to token lib * Add imports and temp remove constants * Add else clause to if branch * Add better comment * Add final else clause to if branch * Add semicolons to if-else * style: add a comment and remove trailing semicolon from if statement in return position * fix: make some comments internal * fix: re-enable constants * fix: change OUTPUT_LENGTH_LOCATION from 48 to 56 * fix: clean up assembly and incorporate PR review feedback * docs: fix comments to reflect change to OUTPUT_LENGTH_LOCATION * fix: remove redundent local variable assignment * refactor: split asm blocks to avoid a second usage of the xos opcode * style: forc fmt * fix:change offset to 40 for getting amount --- src/token.sw | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/token.sw b/src/token.sw index 04e8eb24891..fa94613290d 100644 --- a/src/token.sw +++ b/src/token.sw @@ -2,6 +2,8 @@ library token; //! Functionality for performing common operations on tokens. use ::contract_id::ContractId; +use ::address::Address; +use ::chain::panic; /// Mint `amount` coins of the current contract's `asset_id`. pub fn mint(amount: u64) { @@ -25,3 +27,70 @@ pub fn force_transfer(amount: u64, asset_id: ContractId, contract_id: ContractId tr r3 r1 r2; } } + +/// Transfer `amount` coins of type `asset_id` to address `recipient`. +pub fn transfer_to_output(amount: u64, asset_id: ContractId, recipient: Address) { + // note: if tx format changes, the magic number "56" must be changed ! + // TransactionScript outputsCount has a 56 byte(7 words * 8 bytes) offset + // Transaction Script: https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/tx_format.md#transactionscript + // Output types: https://github.com/FuelLabs/fuel-specs/blob/master/specs/protocol/tx_format.md#output + const OUTPUT_LENGTH_LOCATION = 56; + const OUTPUT_VARIABLE_TYPE = 4; + + // get length of outputs from TransactionScript outputsCount: + let length: u8 = asm(outputs_length, outputs_length_ptr: OUTPUT_LENGTH_LOCATION) { + lb outputs_length outputs_length_ptr i0; + outputs_length: u8 + }; + // maintain a manual index as we only have `while` loops in sway atm: + let mut index: u8 = 0u8; + let mut outputIndex = 0; + let mut output_found = false; + + // If an output of type `OutputVariable` is found, check if its `amount` is zero. + // As one cannot transfer zero coins to an output without a panic, a variable output with a value of zero is by definition unused. + while index < length { + let output_start = asm(n: index, offset) { + xos offset n; // get the offset to the nth output + offset: u64 + }; + + let type = asm(offset: output_start, t) { + lb t offset i0; // load the type of the output at 'offset' into t + t: u8 + }; + + // if an ouput is found of type `OutputVariable`: + if type == OUTPUT_VARIABLE_TYPE { + let amount = asm(n: index, a, amount_ptr, output: output_start) { + addi amount_ptr output i40; + lw a amount_ptr i0; + a: u64 + }; + + // && if the amount is zero: + if amount == 0 { + // then store the index of the output and record the fact that we found a suitable output. + outputIndex = index; + output_found = true; + // todo: use "break" keyword when it lands ( tracked here: https://github.com/FuelLabs/sway/issues/587 ) + index = length; // break early and use the output we found + } else { + // otherwise, increment the index and continue the loop. + index = index + 1; + }; + } else { + index = length; // break early as there are no suitable outputs. + }; + } + + if !output_found { + // If no suitable output was found, revert. + panic(0); + } else { + // perform the transfer + asm(amnt: amount, id: asset_id.value, recipient: recipient.value, output: index) { + tro recipient output amnt id; + }; + } +}